pax_global_header00006660000000000000000000000064146622621110014513gustar00rootroot0000000000000052 comment=e3de03501ff66221d1f1f971022b248d5b38ba06 taglib-2.0.2/000077500000000000000000000000001466226211100127565ustar00rootroot00000000000000taglib-2.0.2/.astylerc000066400000000000000000000005361466226211100146110ustar00rootroot00000000000000--suffix=none --style=kr --indent=spaces=2 --indent-col1-comments --min-conditional-indent=0 --attach-extern-c --attach-namespaces --indent-namespaces --pad-oper --unpad-paren --align-pointer=name --align-reference=name --max-instatement-indent=40 --break-closing-brackets --remove-brackets --convert-tabs --max-code-length=100 --break-after-logical taglib-2.0.2/.editorconfig000066400000000000000000000006161466226211100154360ustar00rootroot00000000000000# EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Non-specified newlines with a newline ending every file [*] insert_final_newline = true # 2 space indentation [*.{h,cpp,tcc,cmake,yml}] indent_style = space indent_size = 2 # Trim trailing whitespaces [*.{h,cpp,tcc,cmake,yml}] trim_trailing_whitespace = true # UTF-8 without BOM [*] charset = utf-8 taglib-2.0.2/.github/000077500000000000000000000000001466226211100143165ustar00rootroot00000000000000taglib-2.0.2/.github/dependabot.yml000066400000000000000000000001661466226211100171510ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" taglib-2.0.2/.github/workflows/000077500000000000000000000000001466226211100163535ustar00rootroot00000000000000taglib-2.0.2/.github/workflows/build.yml000066400000000000000000000036041466226211100202000ustar00rootroot00000000000000name: Build on: [push, pull_request, workflow_dispatch] env: BUILD_TYPE: Release jobs: build: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] include: - os: windows-latest cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"' runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Ubuntu run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev if: matrix.os == 'ubuntu-latest' - name: Set up macOS run: | brew update brew install cppunit utf8cpp if: matrix.os == 'macos-latest' - name: Set up Windows run: vcpkg install cppunit utfcpp zlib --triplet x64-windows if: matrix.os == 'windows-latest' - name: Configure run: > cmake -B${{github.workspace}}/build -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{ matrix.cmake_extra_args }} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Test working-directory: ${{github.workspace}}/build run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error if: matrix.os != 'windows-latest' - name: Test Windows working-directory: ${{github.workspace}}/build run: | $env:Path += ";$PWD\taglib\Release;$PWD\bindings\c\Release" $env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin" $env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\utfcpp_x64-windows\bin" $env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin" ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error if: matrix.os == 'windows-latest' taglib-2.0.2/.gitignore000066400000000000000000000015531466226211100147520ustar00rootroot00000000000000cmake_install.cmake cmake_uninstall.cmake Makefile CTestTestfile.cmake CMakeFiles/ CMakeLists.txt.user* *.so *.so.* *.dylib *.vcproj *.ncb *.sln *.suo *.user .* !.github/workflows/ *~ /CMakeCache.txt /Doxyfile /config.h /taglib.pc /tests/test_runner /tests/Testing /taglib/libtag.a /taglib-config /bindings/c/libtag_c.a /bindings/c/taglib_c.pc /bindings/c/Debug /bindings/c/MinSizeRel /bindings/c/Release /bindings/c/tag_c.dir/Debug /bindings/c/tag_c.dir/MinSizeRel /bindings/c/tag_c.dir/Release /examples/framelist /examples/strip-id3v1 /examples/tagreader /examples/tagreader_c /examples/tagwriter /doc/html /taglib/Debug /taglib/MinSizeRel /taglib/Release /taglib/tag.dir/Debug /taglib/tag.dir/MinSizeRel /taglib/tag.dir/Release /ALL_BUILD.dir /ZERO_CHECK.dir taglib.h.stamp taglib.xcodeproj CMakeScripts /.clang-format /compile_commands.json /build/ .clangd .cache .idea taglib-2.0.2/.gitmodules000066400000000000000000000001431466226211100151310ustar00rootroot00000000000000[submodule "3rdparty/utfcpp"] path = 3rdparty/utfcpp url = https://github.com/nemtrif/utfcpp.git taglib-2.0.2/3rdparty/000077500000000000000000000000001466226211100145265ustar00rootroot00000000000000taglib-2.0.2/3rdparty/utfcpp/000077500000000000000000000000001466226211100160275ustar00rootroot00000000000000taglib-2.0.2/AUTHORS000066400000000000000000000015711466226211100140320ustar00rootroot00000000000000Scott Wheeler Author, maintainer Lukas Lalinsky Implementation of multiple new file formats, many bug fixes, maintainer Tsuda Kageyu A lot of bug fixes and performance improvements, maintainer. Stephen F. Booth DSF metadata implementation, bug fixes, maintainer. Ismael Orenstein Xing header implementation Allan Sandfeld Jensen FLAC metadata implementation Teemu Tervo Numerous bug reports and fixes Mathias Panzenböck Mod, S3M, IT and XM metadata implementations Damien Plisson DSDIFF metadata implementation Urs Fleisch Bug fixes, maintainer. Please send all patches and questions to taglib-devel@kde.org rather than to individual developers! taglib-2.0.2/CHANGELOG.md000066400000000000000000000601011466226211100145650ustar00rootroot00000000000000TagLib 2.0.2 (Aug 24, 2024) =========================== * Fix parsing of ID3v2.2 frames. * Tolerate MP4 files with unknown atom types as generated by Android tools. * Support setting properties with arbitrary names in MP4 tags. * Windows: Fix "-p" option in tagwriter example. * Support building with older utfcpp versions. TagLib 2.0.1 (Apr 9, 2024) ========================== * Fix aborting when _GLIBCXX_ASSERTIONS are enabled. * Fall back to utf8cpp header detection in the case that its CMake configuration is removed. * Improve compatibility with the SWIG interface compiler. * Build system fixes for testing without bindings, Emscripten and Illumos. * C bindings: Fix setting UTF-8 encoded property values. * Windows: Fix opening long paths. TagLib 2.0 (Jan 24, 2024) ========================= * New major version, binary incompatible, but mostly source-compatible with the latest 1.x release if no deprecated features are used. Simple applications should build without changes, more complex applications (e.g. extending classes of TagLib) will have to be adapted. * Requires a C++17 compiler and uses features of C++17. * Major code cleanup, fixed warnings issued by compilers and static analyzers. * Made methods virtual which should have been virtual but could not be changed to keep binary compatibility, remove related workarounds. * Removed deprecated functions: - APE::Item::Item(const String &, const String &) - APE::Item::toStringList(): Use values() - APE::Item::value(): Use binaryData() - ASF::Properties::setLength() - ByteVector::checksum() - ByteVector::isNull(): Use isEmpty() - ByteVector::null - FLAC::File::setID3v2FrameFactory() - FLAC::File::streamInfoData() - FLAC::File::streamLength() - FLAC::Properties::Properties(File *, ReadStyle) - FLAC::Properties::sampleWidth(): Use bitsPerSample() - File::isReadable(): Use system functions - File::isWritable(): Use system functions - FileName::str() - FileRef::create(): Use constructor - MP4::Tag::itemListMap(): Use itemMap() - MPC::File::remove(): Use strip() - MPC::Properties::Properties(const ByteVector &, long, ReadStyle) - MPEG::File::save(int, ...): Use overload - MPEG::File::setID3v2FrameFactory(): Use constructor - MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool) - MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use fileAlterPreservation() - MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool) - MPEG::ID3v2::Frame::Header::size(unsigned int): Use size() - MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation() - MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use checkTextEncoding(const StringList &, String::Type) - MPEG::ID3v2::Frame::headerSize(): Use Header::size() - MPEG::ID3v2::Frame::headerSize(unsigned int): Use Header::size(unsigned int) - MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool) - MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int): Use createFrame(const ByteVector &, const Header *) - MPEG::ID3v2::RelativeVolumeFrame::channelType() - MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType) - MPEG::ID3v2::RelativeVolumeFrame::setChannelType() - MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use setPeakVolume(const PeakVolume &, ChannelType) - MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use setVolumeAdjustment(float, ChannelType) - MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use setVolumeAdjustmentIndex(short, ChannelType) - MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use volumeAdjustment(ChannelType) - MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use volumeAdjustmentIndex(ChannelType) - MPEG::ID3v2::Tag::footer() - MPEG::ID3v2::Tag::render(int): Use render(Version) - MPEG::XingHeader::xingHeaderOffset() - Ogg::Page::getCopyWithNewPageSequenceNumber() - Ogg::XiphComment::removeField(): Use removeFields() - PropertyMap::unsupportedData(): Returns now const reference, use addUnsupportedData() to add keys - RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle) - RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle) - RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample() - RIFF::WAV::File::save(TagTypes, bool, int): Use save(TagTypes, StripTags, Version) - RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an ID3v2::Tag - String::isNull(): Use isEmpty() - String::null - TrueAudio::File::setID3v2FrameFactory(): Use constructor - WavPack::Properties::Properties(const ByteVector &, long, ReadStyle) * Made methods const: Frame::Header::size(), Frame::headerSize(), MP4::Atom::findall(), MP4::Atoms::find(), MP4::Atoms::path(). * Made classes non-virtual: APE::Footer, APE::Item, ASF::Attribute, ASF::Picture, MP4::CoverArt, MP4::Item, ID3v2::ExtendedHeader, ID3v2::Footer, ID3v2::Header, MPEG::Header, MPEG::XingHeader, Ogg::Page, Ogg::PageHeader. * Removed type definitions in TagLib namespace: wchar, uchar, ushort, uint, ulong, ulonglong, wstring: Use the standard types. * Removed include file taglib_config.h and its defines TAGLIB_WITH_ASF, TAGLIB_WITH_MP4: They were always 1 since version 1.8. * Behavioral changes: - The basic tag methods (e.g. genre()) separate multiple values with " / " instead of " ". - The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding. - MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE" instead of "----:com.apple.iTunes:originaldate". - MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now mapped to ENCODING. * Unified interface for complex properties like pictures. * Simplified the unified properties interface by providing its methods on FileRef. * C bindings: Support for properties (taglib_property_...) and complex properties like cover art (taglib_complex_property_...), memory I/O streams. * Support for Direct Stream Digital (DSD) stream files (DSF) and interchange file format (DSDIFF, DFF), ADTS (AAC) files. * The runtime version can be queried. * Additional utility functions ByteVector::fromUShort(), ByteVector::fromULongLong(), ByteVector::toULongLong(), ByteVector::toULongLong(), List::sort(). * Fixed List::setAutoDelete() affecting implicitly shared copies. * Build system: Direct support for CMake, find_package(TagLib) exports target TagLib::tag. * Build system: Fixed PackageConfig to support both relative and absolute paths. * Build system: utf8cpp is no longer included, it can be provided via a system package or a Git submodule. * ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME, FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME, ORIGINALLYRICIST. * ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF files. * MP4: Support additional properties OWNER, RELEASEDATE. * MP4: Introduced ItemFactory allowing clients to support new atom types. * MP4: Detect duration from mvhd atom if not present in mdhd atom. * MP4: Fixed type of hdvd atom to be integer instead of boolean. * MP4: Tolerate trailing garbage in M4A files. * MPC: Fixed content check in presence of an ID3v2 tag. * MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used. * RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM, COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING, ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER, RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER. * WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk. * Windows: Fixed support for files larger than 2GB. TagLib 1.13.1 (Jul 1, 2023) =========================== * Fixed parsing of TXXX frames without description. * Detect MP4 atoms with invalid length or type. * Do not miss ID3v2 frames when an extended header is present. * Use property "DISCSUBTITLE" for ID3v2 "TSST" frame. * Build system improvements: Use absolute path for macOS dylib install name, support --define-prefix when using pkg-config, fixed minimum required CppUnit version. * Code clean up using clang-tidy. TagLib 1.13 (Oct 27, 2022) ========================== * Added interface StreamTypeResolver to support streams which cannot be fopen()'ed, e.g. network files. * Added MP4::File::strip() to remove meta atom from MP4 file. * Added Map::value() to look up without creating entry. * Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame, use property "WORK" for ASF "WM/ContentGroupDescription", use property "COMPILATION" for ID3v2 "TCMP" frame. * Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS, cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang. * Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC tags. * Fixed detection of invalid file by extension when correct type can be detected by contents. * Fixed unnecessary creation of map entries in APE and FLAC tags if looked up tag does not exist. * Fixed parsing of MP4 non-full meta atoms. * Fixed potential ID3v1 false positive in the presence of an APE tag. * Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames. * Fixed parsing of multiple strings with a single BOM in ID3v2.4.0. * Fixed several smaller issues reported by clang-tidy. TagLib 1.12 (Feb 16, 2021) ========================== * Added support for WinRT. * Added support for Linux on POWER. * Added support for classical music tags of iTunes 12.5. * Added support for file descriptor to FileStream. * Added support for 'cmID', 'purl', 'egid' MP4 atoms. * Added support for 'GRP1' ID3v2 frame. * Added support for extensible WAV subformat. * Enabled FileRef to detect file types based on the stream content. * Dropped support for Windows 9x and NT 4.0 or older. * Check for mandatory header objects in ASF files. * More tolerant handling of RIFF padding, WAV files, broken MPEG streams. * Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates. * Improved Windows compatibility by storing FLAC picture after comments. * Fixed numerical genres in ID3v2.3.0 'TCON' frames. * Fixed consistency of API removing MP4 items when empty values are set. * Fixed consistency of API preferring COMM frames with no description. * Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439). * Fixed handling of empty MPEG files. * Fixed parsing MP4 mdhd timescale. * Fixed reading MP4 atoms with zero length. * Fixed reading FLAC files with zero-sized seektables. * Fixed handling of lowercase field names in Vorbis Comments. * Fixed handling of 'rate' atoms in MP4 files. * Fixed handling of invalid UTF-8 sequences. * Fixed possible file corruptions when saving Ogg files. * Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files. * TableOfContentsFrame::toString() improved. * UserTextIdentificationFrame::toString() improved. * Marked FileRef::create() deprecated. * Marked MPEG::File::save() with boolean parameters deprecated, provide overloads with enum parameters. * Several smaller bug fixes and performance improvements. TagLib 1.11.1 (Oct 24, 2016) ============================ * Fixed binary incompatible change in TagLib::String. * Fixed reading ID3v2 CTOC frames with a lot of entries. * Fixed seeking ByteVectorStream from the end. TagLib 1.11 (Apr 29, 2016) ========================== 1.11: * Fixed reading APE items with long keys. * Fixed reading ID3v2 SYLT frames when description is empty. 1.11 BETA 2: * Better handling of PCM WAV files with a 'fact' chunk. * Better handling of corrupted APE tags. * Efficient decoding of unsynchronized ID3v2 frames. * Fixed text encoding when saving certain frames in ID3v2.3 tags. * Fixed updating the size of RIFF files when removing chunks. * Several smaller bug fixes and performance improvements. 1.11 BETA: * New API for creating FileRef from IOStream. * Added support for ID3v2 PCST and WFED frames. * Added support for pictures in XiphComment. * Added String::clear(). * Added FLAC::File::strip() for removing non-standard tags. * Added alternative functions to XiphComment::removeField(). * Added BUILD_BINDINGS build option. * Added ENABLE_CCACHE build option. * Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS. * Better handling of duplicate ID3v2 tags in all kinds of files. * Better handling of duplicate tag chunks in WAV files. * Better handling of duplicate tag chunks in AIFF files. * Better handling of duplicate Vorbis comment blocks in FLAC files. * Better handling of broken MPEG audio frames. * Fixed crash when calling File::properties() after strip(). * Fixed crash when parsing certain MPEG files. * Fixed crash when saving Ogg files. * Fixed possible file corruptions when saving ASF files. * Fixed possible file corruptions when saving FLAC files. * Fixed possible file corruptions when saving MP4 files. * Fixed possible file corruptions when saving MPEG files. * Fixed possible file corruptions when saving APE files. * Fixed possible file corruptions when saving Musepack files. * Fixed possible file corruptions when saving WavPack files. * Fixed updating the comment field of Vorbis comments. * Fixed reading date and time in ID3v2.3 tags. * Marked ByteVector::null and ByteVector::isNull() deprecated. * Marked String::null and String::isNull() deprecated. * Marked XiphComment::removeField() deprecated. * Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null. * Marked custom integer types deprecated. * Many smaller bug fixes and performance improvements. TagLib 1.10 (Nov 11, 2015) ========================== 1.10: * Added new options to the tagwriter example. * Fixed self-assignment operator in some types. * Fixed extraction of MP4 tag keys with an empty list. 1.10 BETA: * New API for the audio length in milliseconds. * Added support for ID3v2 ETCO and SYLT frames. * Added support for album artist in PropertyMap API of MP4 files. * Added support for embedded frames in ID3v2 CHAP and CTOC frames. * Added support for AIFF-C files. * Better handling of duplicate ID3v2 tags in MPEG files. * Allowed generating taglib.pc on Windows. * Added ZLIB_SOURCE build option. * Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings. * Fixed crash when parsing certain FLAC files. * Fixed crash when encoding empty strings. * Fixed saving of certain XM files on OS X. * Changed Xiph and APE generic getters to return space-concatenated values. * Fixed possible file corruptions when removing tags from WAV files. * Added support for MP4 files with 64-bit atoms in certain 64-bit environments. * Prevented ID3v2 padding from being too large. * Fixed crash when parsing corrupted APE files. * Fixed crash when parsing corrupted WAV files. * Fixed crash when parsing corrupted Ogg FLAC files. * Fixed crash when parsing corrupted MPEG files. * Fixed saving empty tags in WAV files. * Fixed crash when parsing corrupted Musepack files. * Fixed possible memory leaks when parsing AIFF and WAV files. * Fixed crash when parsing corrupted MP4 files. * Stopped writing empty ID3v2 frames. * Fixed possible file corruptions when saving WMA files. * Added TagLib::MP4::Tag::isEmpty(). * Added accessors to manipulate MP4 tags. * Fixed crash when parsing corrupted WavPack files. * Fixed seeking MPEG frames. * Fixed reading FLAC files with zero-sized padding blocks. * Added support for reading the encoder information of WMA files. * Added support for reading the codec of WAV files. * Added support for multi channel WavPack files. * Added support for reading the nominal bitrate of Ogg Speex files. * Added support for VBR headers in MPEG files. * Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector. * Marked FLAC::File::streamLength() deprecated. It returns zero. * Fixed possible file corruptions when adding an ID3v1 tag to FLAC files. * Many smaller bug fixes and performance improvements. TagLib 1.9.1 (Oct 8, 2013) ========================== * Fixed binary incompatible change in TagLib::Map and TagLib::List. * Fixed constructing String from ByteVector. * Fixed compilation on MSVC with the /Zc:wchar_t- option. * Fixed detecting of RIFF files with invalid chunk sizes. * Added TagLib::MP4::Properties::codec(). TagLib 1.9 (Oct 6, 2013) ======================== * Added support for the Ogg Opus file format. * Added support for INFO tags in WAV files. * Changed FileStream to use Windows file API. * Included taglib-config.cmd script for Windows. * New ID3v1::Tag methods for working directly with genre numbers. * New MPEG::File methods for checking which tags are saved in the file. * Added support for the PropertyMap API to ASF and MP4 files. * Added MusicBrainz identifiers to the PropertyMap API. * Allowed reading of MP4 cover art without an explicitly specified format. * Better parsing of corrupted FLAC files. * Fixed saving of PropertyMap comments without description into ID3v2 tags. * Fixed crash when parsing certain XM files. * Fixed compilation of unit test with clang. * Better handling of files that can't be open or have read-only permissions. * Improved atomic reference counting. * New hookable API for debug messages. * More complete Windows install instructions. * Many smaller bug fixes and performance improvements. TagLib 1.8 (Sep 6, 2012) ======================== 1.8: * Added support for OWNE ID3 frames. * Changed key validation in the new PropertyMap API. * ID3v1::Tag::setStringHandler will no longer delete the previous handler, the caller is responsible for this. * File objects will also no longer delete the passed IOStream objects. It should be done in the caller code after the File object is no longer used. * Added ID3v2::Tag::setLatin1StringHandler for custom handling of latin1-encoded text in ID3v2 frames. * Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored). 1.8 BETA: * New API for accessing tags by name. * New abstract I/O stream layer to allow custom I/O handlers. * Support for writing ID3v2.3 tags. * Support for various module file formats (MOD, S3M, IT, XM). * Support for MP4 and ASF is now enabled by default. * Started using atomic int operations for reference counting. * Added methods for checking if WMA and MP4 files are DRM-protected. * Added taglib_free to the C bindings. * New method to allow removing pictures from FLAC files. * Support for reading audio properties from ALAC and Musepack SV8 files. * Added replay-gain information to Musepack audio properties. * Support for APEv2 binary tags. * Many AudioProperties subclasses now provide information about the total number of samples. * Various small bug fixes. TagLib 1.7.2 (Apr 20, 2012) =========================== * Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396). * Fixed compilation on Haiku. TagLib 1.7.1 (Mar 17, 2012) =========================== * Improved parsing of corrupted WMA, RIFF and OGG files. * Fixed a memory leak in the WMA parser. * Fixed a memory leak in the FLAC parser. * Fixed a possible division by zero in the APE parser. * Added detection of TTA2 files. * Fixed saving of multiple identically named tags to Vorbis Comments. TagLib 1.7 (Mar 11, 2011) ========================= 1.7: * Fixed memory leaks in the FLAC file format parser. * Fixed bitrate calculation for WAV files. 1.7 RC1: * Support for reading/writing tags from Monkey's Audio files. (BUG:210404) * Support for reading/writing embedded pictures from WMA files. * Support for reading/writing embedded pictures from FLAC files (BUG:218696). * Implemented APE::Tag::isEmpty() to check for all APE tags, not just the basic ones. * Added reading of WAV audio length. (BUG:116033) * Exposed FLAC MD5 signature of the uncompressed audio stream via FLAC::Properties::signature(). (BUG:160172) * Added function ByteVector::toHex() for hex-encoding of byte vectors. * WavPack reader now tries to get the audio length by finding the final block, if the header doesn't have the information. (BUG:258016) * Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007) * Fixed writing of RIFF files with even chunk sizes. (BUG:243954) * Fixed compilation on MSVC 2010. * Removed support for building using autoconf/automake. * API docs can be now built using "make docs". TagLib 1.6.3 (Apr 17, 2010) =========================== * Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros. * Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues). * New method `int String::toInt(bool *ok)` which can return whether the conversion to a number was successful. * Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly compressed frames). (BUG:231075) TagLib 1.6.2 (Apr 9, 2010) ========================== * Read Vorbis Comments from the first FLAC metadata block, if there are multiple ones. (BUG:211089) * Fixed a memory leak in FileRef's OGA format detection. * Fixed compilation with the Sun Studio compiler. (BUG:215225) * Handle WM/TrackNumber attributes with DWORD content in WMA files. (BUG:218526) * More strict check if something is a valid MP4 file. (BUG:216819) * Correctly save MP4 int-pair atoms with flags set to 0. * Fixed compilation of the test runner on Windows. * Store ASF attributes larger than 64k in the metadata library object. * Ignore trailing non-data atoms when parsing MP4 covr atoms. * Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968) TagLib 1.6.1 (Oct 31, 2009) =========================== * Better detection of the audio codec of .oga files in FileRef. * Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to include the Vorbis framing bit, which is only correct for Ogg Vorbis. * Public symbols now have explicitly set visibility to "default" on GCC. * Added missing exports for static ID3v1 functions. * Fixed a typo in taglib_c.pc * Fixed a failing test on ppc64. * Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them as text atoms, which corrupted them in some cases. * Fixed ID3v1-style genre to string conversion in MP4 files. TagLib 1.6 (Sep 13, 2009) ========================= 1.6: * New CMake option to build a static version - ENABLE_STATIC. * Added support for disabling dllimport/dllexport on Windows using the TAGLIB_STATIC macro. * Support for parsing the obsolete 'gnre' MP4 atom. * New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if TagLib was built with MP4/ASF support. 1.6 RC1: * Split Ogg packets larger than 64k into multiple pages. (BUG:171957) * TagLib can now use FLAC padding block. (BUG:107659) * ID3v2.2 frames are now not incorrectly saved. (BUG:176373) * Support for ID3v2.2 PIC frames. (BUG:167786) * Fixed a bug in ByteVectorList::split(). * XiphComment::year() now falls back to YEAR if DATE doesn't exist and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't exist. (BUG:144396) * Improved ID3v2.3 genre parsing. (BUG:188578) * Better checking of corrupted ID3v2 APIC data. (BUG:168382) * Bitrate calculating using the Xing header now uses floating point numbers. (BUG:172556) * New TagLib::String method rfind(). * Added support for MP4 file format with iTunes-style metadata [optional]. * Added support for ASF (WMA) file format [optional]. * Fixed crash when saving a Locator APEv2 tag. (BUG:169810) * Fixed a possible crash in the non-const version of String::operator[] and in String::operator+=. (BUG:169389) * Added support for PRIV ID3v2 frames. * Empty ID3v2 genres are no longer treated as numeric ID3v1 genres. * Added support for the POPM (rating/play count) ID3v2 frame. * Generic RIFF file format support: * Support for AIFF files with ID3v2 tags. * Support for WAV files with ID3v2 tags. * Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted frames. (BUG:161721) * Fixed overflow while calculating bitrate of FLAC files with a very high bitrate. taglib-2.0.2/CMakeLists.txt000066400000000000000000000162471466226211100155300ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) project(taglib) set(CMAKE_CXX_STANDARD 17) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") include(CTest) include(FeatureSummary) include(GNUInstallDirs) include(CMakePackageConfigHelpers) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) if(APPLE) option(BUILD_FRAMEWORK "Build an OS X framework" OFF) if(BUILD_FRAMEWORK) set(BUILD_SHARED_LIBS ON) #set(CMAKE_MACOSX_RPATH 1) set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.") endif() endif() if(NOT BUILD_SHARED_LIBS) add_definitions(-DTAGLIB_STATIC) endif() option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF) option(ENABLE_CCACHE "Use ccache when building libtag" OFF) if(ENABLE_CCACHE) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif() endif() option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF) option(BUILD_EXAMPLES "Build the examples" OFF) option(BUILD_BINDINGS "Build the bindings" ON) option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF) option(PLATFORM_WINRT "Enable WinRT support" OFF) if(PLATFORM_WINRT) add_definitions(-DPLATFORM_WINRT) endif() set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING "Suffix added to installed files (include directory, libraries, .pc)") add_definitions(-DHAVE_CONFIG_H) set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/") if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") endif() if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") endif() if(MSVC) if(ENABLE_STATIC_RUNTIME) foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endforeach(flag_var) endif() endif() # Read version information from file taglib/toolkit/taglib.h into variables # TAGLIB_LIB_MAJOR_VERSION, TAGLIB_LIB_MINOR_VERSION, TAGLIB_LIB_PATCH_VERSION. foreach(version_part MAJOR MINOR PATCH) set(version_var_name "TAGLIB_${version_part}_VERSION") file(STRINGS taglib/toolkit/taglib.h version_line REGEX "^#define +${version_var_name}") if(NOT version_line) message(FATAL_ERROR "${version_var_name} not found in taglib.h") endif() string(REGEX MATCH "${version_var_name} +([^ ]+)" result ${version_line}) set(TAGLIB_LIB_${version_part}_VERSION ${CMAKE_MATCH_1}) endforeach(version_part) # Only used to force cmake rerun when taglib.h changes. configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp) if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0") set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}") else() set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}") endif() # Major version: increase it if you break ABI compatibility. # Minor version: increase it if you add ABI compatible features. # Patch version: increase it for bug fix releases. set(TAGLIB_SOVERSION_MAJOR 2) set(TAGLIB_SOVERSION_MINOR 0) set(TAGLIB_SOVERSION_PATCH 2) include(ConfigureChecks.cmake) # Determine whether zlib is installed. option(WITH_ZLIB "Build with ZLIB" ON) if(WITH_ZLIB) find_package("ZLIB") set(HAVE_ZLIB ${ZLIB_FOUND}) if(ZLIB_FOUND) set(ZLIB_LIBRARIES_FLAGS -lz) if(NOT BUILD_SHARED_LIBS) # When linking TagLib statically, zlib has to be linked explicitly. set(ZLIB_INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) endif() endif() endif() if(NOT WIN32) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY) install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config") endif() if(WIN32) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd") install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd") endif() if(NOT BUILD_FRAMEWORK) if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR}) set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) else() set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR}) set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR}) else() set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc") endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h") option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF) if(TRACE_IN_RELEASE) set(TRACE_IN_RELEASE TRUE) endif() find_package(utf8cpp QUIET) if(utf8cpp_FOUND) message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}") else() find_path(utf8cpp_INCLUDE_DIR NAMES utf8.h PATH_SUFFIXES utf8cpp DOC "utf8cpp include directory") mark_as_advanced(utf8cpp_INCLUDE_DIR) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(utf8cpp REQUIRED_VARS utf8cpp_INCLUDE_DIR) if(utf8cpp_FOUND) set(utf8cpp_INCLUDE_DIRS "${utf8cpp_INCLUDE_DIR}") if(NOT TARGET utf8::cpp) add_library(utf8::cpp INTERFACE IMPORTED) set_target_properties(utf8::cpp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${utf8cpp_INCLUDE_DIR}") endif() message(STATUS "Using utfcpp from ${utf8cpp_INCLUDE_DIR}") else() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt) add_subdirectory("3rdparty/utfcpp") message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}") else() message(FATAL_ERROR "utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) " "or fetch the git submodule using\n" "git submodule update --init") endif() endif() endif() add_subdirectory(taglib) if(BUILD_BINDINGS) add_subdirectory(bindings) endif() if(BUILD_TESTING) find_package(CppUnit) if(CppUnit_FOUND) add_subdirectory(tests) else() message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.") endif() endif() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") add_custom_target(docs doxygen) # uninstall target configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) if(NOT TARGET uninstall) add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) taglib-2.0.2/COPYING.LGPL000066400000000000000000000636421466226211100145610ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! taglib-2.0.2/COPYING.MPL000066400000000000000000000622331466226211100144460ustar00rootroot00000000000000 MOZILLA PUBLIC LICENSE Version 1.1 --------------- 1. Definitions. 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. "Executable" means Covered Code in any form other than Source Code. 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. "License" means this document. 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. 6. Versions of the License. 6.1. New Versions. Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. 6.3. Derivative Works. If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) 7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 8. TERMINATION. 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that: (a) such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. (b) any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. 9. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 10. U.S. GOVERNMENT END USERS. The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. 11. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. 12. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. 13. MULTIPLE-LICENSED CODE. Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the NPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. EXHIBIT A -Mozilla Public License. ``The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. The Initial Developer of the Original Code is ________________________. Portions created by ______________________ are Copyright (C) ______ _______________________. All Rights Reserved. Contributor(s): ______________________________________. Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." [NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.] taglib-2.0.2/ConfigureChecks.cmake000066400000000000000000000050331466226211100170230ustar00rootroot00000000000000include(CheckLibraryExists) include(CheckTypeSize) include(CheckCXXSourceCompiles) # Check if the size of numeric types are suitable. check_type_size("short" SIZEOF_SHORT) if(NOT ${SIZEOF_SHORT} EQUAL 2) message(FATAL_ERROR "TagLib requires that short is 16-bit wide.") endif() check_type_size("int" SIZEOF_INT) if(NOT ${SIZEOF_INT} EQUAL 4) message(FATAL_ERROR "TagLib requires that int is 32-bit wide.") endif() check_type_size("long long" SIZEOF_LONGLONG) if(NOT ${SIZEOF_LONGLONG} EQUAL 8) message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.") endif() check_type_size("wchar_t" SIZEOF_WCHAR_T) if(NOT ${SIZEOF_WCHAR_T} GREATER 1) message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.") endif() check_type_size("float" SIZEOF_FLOAT) if(NOT ${SIZEOF_FLOAT} EQUAL 4) message(FATAL_ERROR "TagLib requires that float is 32-bit wide.") endif() check_type_size("double" SIZEOF_DOUBLE) if(NOT ${SIZEOF_DOUBLE} EQUAL 8) message(FATAL_ERROR "TagLib requires that double is 64-bit wide.") endif() # Determine which kind of byte swap functions your compiler supports. check_cxx_source_compiles(" int main() { __builtin_bswap16(0); __builtin_bswap32(0); __builtin_bswap64(0); return 0; } " HAVE_GCC_BYTESWAP) if(NOT HAVE_GCC_BYTESWAP) check_cxx_source_compiles(" #include int main() { __bswap_16(0); __bswap_32(0); __bswap_64(0); return 0; } " HAVE_GLIBC_BYTESWAP) if(NOT HAVE_GLIBC_BYTESWAP) check_cxx_source_compiles(" #include int main() { _byteswap_ushort(0); _byteswap_ulong(0); _byteswap_uint64(0); return 0; } " HAVE_MSC_BYTESWAP) if(NOT HAVE_MSC_BYTESWAP) check_cxx_source_compiles(" #include int main() { OSSwapInt16(0); OSSwapInt32(0); OSSwapInt64(0); return 0; } " HAVE_MAC_BYTESWAP) if(NOT HAVE_MAC_BYTESWAP) check_cxx_source_compiles(" #include int main() { swap16(0); swap32(0); swap64(0); return 0; } " HAVE_OPENBSD_BYTESWAP) endif() endif() endif() endif() # Determine whether your compiler supports ISO _strdup. check_cxx_source_compiles(" #include int main() { _strdup(0); return 0; } " HAVE_ISO_STRDUP) # Detect WinRT mode if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") set(PLATFORM_WINRT 1) endif() taglib-2.0.2/Doxyfile.cmake000066400000000000000000000273711466226211100155550ustar00rootroot00000000000000# Doxyfile 1.9.1 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = TagLib PROJECT_NUMBER = ${TAGLIB_LIB_VERSION_STRING} PROJECT_BRIEF = PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/taglib.svg OUTPUT_DIRECTORY = doc CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO JAVADOC_BANNER = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO PYTHON_DOCSTRING = YES INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO OPTIMIZE_OUTPUT_SLICE = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES TOC_INCLUDE_HEADINGS = 5 AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO GROUP_NESTED_COMPOUNDS = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_PRIV_VIRTUAL = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO RESOLVE_UNNAMED_PARAMS = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = NO GENERATE_TESTLIST = NO GENERATE_BUGLIST = NO GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = NO WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_AS_ERROR = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @CMAKE_SOURCE_DIR@/taglib INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h \ *.hh \ *.H \ *.dox RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO CLANG_ADD_INC_PATHS = YES CLANG_OPTIONS = CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = @CMAKE_SOURCE_DIR@/doc/api-header.html HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/api-footer.html HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES FORMULA_MACROFILE = USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = NO SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex LATEX_MAKEINDEX_CMD = makeindex COMPACT_LATEX = NO PAPER_TYPE = letter EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_STYLESHEET = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO LATEX_EMOJI_DIRECTORY = #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_SUBDIR = MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES XML_NS_MEMB_FILE_SCOPE = NO #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = DO_NOT_DOCUMENT \ DOXYGEN EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 DOT_UML_DETAILS = NO DOT_WRAP_THRESHOLD = 17 TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = svg INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DIAFILE_DIRS = PLANTUML_JAR_PATH = PLANTUML_CFG_FILE = PLANTUML_INCLUDE_PATH = DOT_GRAPH_MAX_NODES = 100 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES taglib-2.0.2/INSTALL.md000066400000000000000000000401071466226211100144100ustar00rootroot00000000000000# TagLib Installation TagLib uses the CMake build system. As a user, you will most likely want to build TagLib in release mode and install it into a system-wide location. This can be done using the following commands: cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release . make sudo make install In order to build the included examples, use the `BUILD_EXAMPLES` option: cmake -DBUILD_EXAMPLES=ON [...] If you want to build TagLib without ZLib, you can use cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF . make sudo make install See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for generic help on running CMake. ## Build Options These are the most important build options. For details, have a look into the CMakeLists.txt file. | Option | Description | |-------------------------|----------------------------------------------------| | `BUILD_SHARED_LIBS` | Build shared libraries | | `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel | | `BUILD_EXAMPLES` | Build examples | | `BUILD_BINDINGS` | Build C bindings | | `BUILD_TESTING` | Build unit tests | | `TRACE_IN_RELEASE` | Enable debug output in release builds | | `WITH_ZLIB` | Whether to build with ZLib (default ON) | | `ZLIB_ROOT` | Where to find ZLib's root directory | | `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory | | `ZLIB_LIBRARY` | Where to find ZLib's library | | `CMAKE_INSTALL_PREFIX` | Where to install Taglib | | `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... | | `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically | | `BUILD_FRAMEWORK` | Build a macOS framework | If you want to install TagLib 2 alongside TagLib 1, you can use `-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON` for both versions. The installed files will then include bin/taglib-2-config, include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc, pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2". ## Dependencies A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew, utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`. Optional dependencies are - [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`, build and install it from the sources or use a package (zlib1g-dev on Ubuntu, zlib in vcpkg). It is needed for compressed ID3v2 frames. - [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit tests, which are disabled by default. If you enable them with `-DBUILD_TESTING=ON`, you can build and install it from the sources or use a package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg). If the unit tests are enabled, you can run them with your build tool (`make check`, `ninja check`) or via CMake `cmake --build /path/to/build-dir --target check`. ## UNIX (Including Linux, BSD and macOS) #### Building TagLib On Linux, you can install the dependencies using the package manager of your distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp` to install the dependencies. ``` # Adapt these environment variables to your directories TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib TAGLIB_DST_DIR=$HOME/projects/taglib/src/build cd $TAGLIB_SRC_DIR cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \ -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \ -DCMAKE_BUILD_TYPE=Release cmake --build $TAGLIB_DST_DIR --config Release cmake --build $TAGLIB_DST_DIR --config Release --target check # Install to ~/pkg folder cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip # Run example from installed package LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file ``` #### Building a Project Using TagLib As an example for an external application using TagLib, we create a folder taglib-client and copy the file tagreader.cpp from the examples folder of the TagLib sources into it. We then add this simple CMakeLists.txt. ``` cmake_minimum_required(VERSION 3.5.0) project(taglib-client) set(CMAKE_CXX_STANDARD 17) find_package(ZLIB) find_package(TagLib 2.0.0 REQUIRED) add_executable(tagreader tagreader.cpp) target_link_libraries(tagreader TagLib::tag) ``` To build into a folder `build` inside this directory, just run ``` # Set this to the path where TagLib is installed TAGLIB_PREFIX=$HOME/pkg cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX cmake --build build --config Release build/tagreader /path/to/audio-file ``` If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH). To use the C-bindings with tagreader_c.c, you can change the last two lines in CMakeLists.txt to ``` add_executable(tagreader_c tagreader_c.c) target_link_libraries(tagreader_c TagLib::tag_c) ``` #### Building a Project Using pkg-config Before version 2.0, TagLib did not export CMake configuration, therefore `pkg-config` could be used. This is still possible. Note, however, that `pkg-config` makes it more difficult to use packages which are not installed in a standard location. You can point `pkg-config` to search in non-standard locations for .pc-files, but they still contain the install locations defined when building TagLib. Since we did not give a `CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used. ``` PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib -I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz ``` The following examples use the same build example with additional CMake parameters affecting the installation location. - Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`: ``` -I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz ``` - Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`: ``` -I/usr/include/taglib -ltag -lz ``` - Using absolute lib and include directories `-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`: ``` -I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz ``` - Using relative lib and include directories `-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`: ``` -I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz ``` This is the output of ``` PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib ``` You could add `--define-prefix` to the `pkg-config` arguments to have the effective location `$HOME/pkg/` instead of `/usr/` in the output. Therefore, the correct paths for our example package would be given when using the `--define-prefix` feature. ``` PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib ``` You can use pkg-config from CMake, however, relocation with `--define-prefix` is not supported. ``` cmake_minimum_required(VERSION 3.6.0) project(taglib-client) find_package(PkgConfig) pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib) add_executable(tagreader tagreader.cpp) target_link_libraries(tagreader PkgConfig::TAGLIB) ``` #### Framework on macOS On macOS, you might want to build a framework that can be easily integrated into your application. If you set the BUILD_FRAMEWORK option on, it will compile TagLib as a framework. For example, the following command can be used to build a framework with macOS 10.10 as the deployment target: mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ -DBUILD_FRAMEWORK=ON \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" make For a 10.10 static library, use: mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING=OFF \ -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" make After `make`, and `make install`, add `libtag.` to your XCode project, and add the include folder to the project's User Header Search Paths. ## Windows ### Using Visual Studio Build Tools For this example, we assume that you have the Visual Studio Build Tools 2022 installed. Additionally, you need [cmake](https://cmake.org/), which can be installed for example using [scoop](https://scoop.sh/) with `scoop install cmake`. #### Building TagLib (MSVC) You can build and install the dependencies [utf8cpp](https://github.com/nemtrif/utfcpp) and optionally [zlib](https://www.zlib.net/) and [CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is probably easier using a package manager such as [vcpkg](https://vcpkg.io/), which can be installed using `scoop install vcpkg` and then install the dependencies using `vcpkg install utfcpp zlib cppunit`. Now you can build TagLib from PowerShell. ``` # Adapt these environment variables to your directories $env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib" $env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build" cd $env:TAGLIB_SRC_DIR cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON ` -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON ` -DCMAKE_BUILD_TYPE=Release ` -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" ` -G "Visual Studio 17 2022" cmake --build $env:TAGLIB_DST_DIR --config Release # Add directories containing DLL dependencies to path $env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;", "$env:TAGLIB_DST_DIR\bindings\c\Release;", "$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;", "$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;", "$env:VCPKG_ROOT\packages\zlib_x64-windows\bin") cmake --build $env:TAGLIB_DST_DIR --config Release --target check # Install to \pkg folder on current drive cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip # Static library $env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build" cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON ` -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON ` -DCMAKE_BUILD_TYPE=Release ` -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" ` -G "Visual Studio 17 2022" cmake --build $env:TAGLIB_DST_DIR --config Release cmake --build $env:TAGLIB_DST_DIR --config Release --target check # Install to \pkg_static folder on current drive cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip ``` Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the static runtime library, rather than the DLL form of the runtime. #### Building a Project Using TagLib (MSVC) As an example for an external application using TagLib, we create a folder taglib-client and copy the file tagreader.cpp from the examples folder of the TagLib sources into it. We then add this simple CMakeLists.txt. ``` cmake_minimum_required(VERSION 3.5.0) project(taglib-client) set(CMAKE_CXX_STANDARD 17) find_package(ZLIB) find_package(TagLib 2.0.0 REQUIRED) add_executable(tagreader tagreader.cpp) target_link_libraries(tagreader TagLib::tag) ``` To build into a folder build inside this directory, just run ``` # Set this to the path where TagLib is installed $env:TAGLIB_PREFIX = "/pkg" cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" ` -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" ` -G "Visual Studio 17 2022" cmake --build build --config Release # Add directories containing DLL dependencies to path $env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin" build\Release\tagreader /path/to/audio-file ``` To use the C-bindings with tagreader_c.c, you can change the last two lines in CMakeLists.txt to ``` add_executable(tagreader_c tagreader_c.c) target_link_libraries(tagreader_c TagLib::tag_c) ``` If you link against a static TagLib, you have to adapt the installation path (e.g. "/pkg_static") and add the following line to CMakeLists.txt ``` target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC) ``` ### Using MSYS2 #### Building TagLib (MSYS2) To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/), then start the MSYS CLANG64 shell and install the dependencies as they are specified in the [MSYS recipe for TagLib]( https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64). ``` pacman -Suy pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \ mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \ mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja ``` Then you can build TagLib from the MSYS CLANG64 Bash. ``` # Adapt these environment variables to your directories TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build cd $TAGLIB_SRC_DIR cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \ -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \ -DCMAKE_BUILD_TYPE=Release -G Ninja cmake --build $TAGLIB_DST_DIR --config Release PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \ cmake --build $TAGLIB_DST_DIR --config Release --target check # Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/) cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip ``` #### Building a Project Using TagLib (MSYS2) As an example for an external application using TagLib, we create a folder taglib-client and copy the file tagreader.cpp from the examples folder of the TagLib sources into it. We then add this simple CMakeLists.txt. ``` cmake_minimum_required(VERSION 3.5.0) project(taglib-client) set(CMAKE_CXX_STANDARD 17) find_package(ZLIB) find_package(TagLib 2.0.0 REQUIRED) add_executable(tagreader tagreader.cpp) target_link_libraries(tagreader TagLib::tag) ``` To build into a folder build_msys inside this directory, just run ``` # Set this to the path where TagLib is installed TAGLIB_PREFIX=/pkg_msys cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja cmake --build build_msys --config Release PATH=$PATH:$TAGLIB_PREFIX/bin build_msys/tagreader /path/to/audio-file ``` To use the C-bindings with tagreader_c.c, you can change the last two lines in CMakeLists.txt to ``` add_executable(tagreader_c tagreader_c.c) target_link_libraries(tagreader_c TagLib::tag_c) ``` ### Using MinGW The instructions for MSYS2 can also be used to build with MinGW. You could use Git Bash together with the MinGW provided by Qt. ``` # Adapt these environment variables to your directories PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build cd $TAGLIB_SRC_DIR cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \ -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \ -DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles' cmake --build $TAGLIB_DST_DIR --config Release PATH=$PATH:$TAGLIB_DST_DIR/taglib \ $TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file # Install to C:\pkg_mingw cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip ``` The installed package can then be used by other projects. ``` TAGLIB_PREFIX=/c/pkg_mingw cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles' cmake --build build_mingw --config Release PATH=$PATH:$TAGLIB_PREFIX/bin build_mingw/tagreader /path/to/audio-file ``` taglib-2.0.2/README.md000066400000000000000000000017451466226211100142440ustar00rootroot00000000000000# TagLib [![Build Status](../../actions/workflows/build.yml/badge.svg)](../../actions) ### TagLib Audio Metadata Library https://taglib.org/ TagLib is a library for reading and editing the metadata of several popular audio formats. Currently, it supports both ID3v1 and ID3v2 for MP3 files, [Ogg Vorbis][] comments and ID3 tags in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE, ASF, DSF, DFF and AAC files. TagLib is distributed under the [GNU Lesser General Public License][] (LGPL) and [Mozilla Public License][] (MPL). Essentially that means that it may be used in proprietary applications, but if changes are made to TagLib they must be contributed back to the project. Please review the licenses if you are considering using TagLib in your project. [Ogg Vorbis]: https://xiph.org/vorbis/ [FLAC]: https://xiph.org/flac/ [GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html [Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html taglib-2.0.2/bindings/000077500000000000000000000000001466226211100145535ustar00rootroot00000000000000taglib-2.0.2/bindings/CMakeLists.txt000066400000000000000000000000241466226211100173070ustar00rootroot00000000000000add_subdirectory(c) taglib-2.0.2/bindings/README000066400000000000000000000004131466226211100154310ustar00rootroot00000000000000There are a few other people that have done bindings externally that I have been made aware of. I have not personally reviewed these bindings, but I'm listing them here so that those who find them useful are able to find them: https://taglib.org/#language-bindings taglib-2.0.2/bindings/c/000077500000000000000000000000001466226211100147755ustar00rootroot00000000000000taglib-2.0.2/bindings/c/CMakeLists.txt000066400000000000000000000105321466226211100175360ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff ) set(tag_c_HDRS tag_c.h) add_library(tag_c tag_c.cpp ${tag_c_HDRS}) target_include_directories(tag_c INTERFACE $ ) target_link_libraries(tag_c PRIVATE tag) set_target_properties(tag_c PROPERTIES PUBLIC_HEADER "${tag_c_HDRS}" DEFINE_SYMBOL MAKE_TAGLIB_LIB ) if(VISIBILITY_HIDDEN) set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden) endif() if(BUILD_FRAMEWORK) set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE) endif() # On Solaris we need to explicitly add the C++ standard and runtime # libraries to the libs used by the C bindings, because those C bindings # themselves won't pull in the C++ libs -- and if a C application is # using the C bindings then we get link errors. check_library_exists(Crun __RTTI___ "" HAVE_CRUN_LIB) if(HAVE_CRUN_LIB) # Which libraries to link depends critically on which # STL version is going to be used by your application # and which runtime is in use. While Crun is pretty much # the only game in town, the three available STLs -- Cstd, # stlport4 and stdcxx -- make this a mess. The KDE-Solaris # team supports stdcxx (Apache RogueWave stdcxx 4.1.3). # According to http://bugs.kde.org/show_bug.cgi?id=215225 the library can have the following two names: find_library(ROGUEWAVE_STDCXX_LIBRARY NAMES stdcxx4 stdcxx) if(NOT ROGUEWAVE_STDCXX_LIBRARY) message(FATAL_ERROR "Did not find supported STL library (tried stdcxx4 and stdcxx)") endif() target_link_libraries(tag_c ${ROGUEWAVE_STDCXX_LIBRARY} Crun) endif() set_target_properties(tag_c PROPERTIES VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH} SOVERSION ${TAGLIB_SOVERSION_MAJOR} DEFINE_SYMBOL MAKE_TAGLIB_C_LIB INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR} ) if(TAGLIB_INSTALL_SUFFIX) if(BUILD_SHARED_LIBS) set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}") endif() set_target_properties(tag_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX}) endif() install(TARGETS tag_c EXPORT taglibTargets FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX} ) if(NOT BUILD_FRAMEWORK) if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR}) set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) else() set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR}) set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR}) else() set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig RENAME taglib${TAGLIB_INSTALL_SUFFIX}_c.pc) endif() taglib-2.0.2/bindings/c/tag_c.cpp000066400000000000000000000534211466226211100165630ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2003 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #include "tag_c.h" #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "tstringlist.h" #include "tbytevectorstream.h" #include "tiostream.h" #include "tfile.h" #include "tpropertymap.h" #include "fileref.h" #include "asffile.h" #include "vorbisfile.h" #include "mpegfile.h" #include "flacfile.h" #include "oggflacfile.h" #include "mpcfile.h" #include "wavpackfile.h" #include "speexfile.h" #include "trueaudiofile.h" #include "mp4file.h" #include "aifffile.h" #include "wavfile.h" #include "apefile.h" #include "itfile.h" #include "modfile.h" #include "s3mfile.h" #include "xmfile.h" #include "opusfile.h" #include "dsffile.h" #include "dsdifffile.h" #include "tag.h" #include "id3v2framefactory.h" using namespace TagLib; namespace { List strings; bool unicodeStrings = true; bool stringManagementEnabled = true; char *stringToCharArray(const String &s) { const std::string str = s.to8Bit(unicodeStrings); #ifdef HAVE_ISO_STRDUP return ::_strdup(str.c_str()); #else return ::strdup(str.c_str()); #endif } String charArrayToString(const char *s) { return String(s, unicodeStrings ? String::UTF8 : String::Latin1); } } // namespace void taglib_set_strings_unicode(BOOL unicode) { unicodeStrings = (unicode != 0); } void taglib_set_string_management_enabled(BOOL management) { stringManagementEnabled = (management != 0); } void taglib_free(void* pointer) { free(pointer); } //////////////////////////////////////////////////////////////////////////////// // TagLib::IOStream wrapper //////////////////////////////////////////////////////////////////////////////// TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size) { return reinterpret_cast( new ByteVectorStream(ByteVector(data, size))); } void taglib_iostream_free(TagLib_IOStream *stream) { delete reinterpret_cast(stream); } //////////////////////////////////////////////////////////////////////////////// // TagLib::FileRef wrapper //////////////////////////////////////////////////////////////////////////////// TagLib_File *taglib_file_new(const char *filename) { return reinterpret_cast(new FileRef(filename)); } TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type) { File *file = NULL; switch(type) { case TagLib_File_MPEG: file = new MPEG::File(filename); break; case TagLib_File_OggVorbis: file = new Ogg::Vorbis::File(filename); break; case TagLib_File_FLAC: file = new FLAC::File(filename); break; case TagLib_File_MPC: file = new MPC::File(filename); break; case TagLib_File_OggFlac: file = new Ogg::FLAC::File(filename); break; case TagLib_File_WavPack: file = new WavPack::File(filename); break; case TagLib_File_Speex: file = new Ogg::Speex::File(filename); break; case TagLib_File_TrueAudio: file = new TrueAudio::File(filename); break; case TagLib_File_MP4: file = new MP4::File(filename); break; case TagLib_File_ASF: file = new ASF::File(filename); break; case TagLib_File_AIFF: file = new RIFF::AIFF::File(filename); break; case TagLib_File_WAV: file = new RIFF::WAV::File(filename); break; case TagLib_File_APE: file = new APE::File(filename); break; case TagLib_File_IT: file = new IT::File(filename); break; case TagLib_File_Mod: file = new Mod::File(filename); break; case TagLib_File_S3M: file = new S3M::File(filename); break; case TagLib_File_XM: file = new XM::File(filename); break; case TagLib_File_Opus: file = new Ogg::Opus::File(filename); break; case TagLib_File_DSF: file = new DSF::File(filename); break; case TagLib_File_DSDIFF: file = new DSDIFF::File(filename); break; default: break; } return file ? reinterpret_cast(new FileRef(file)) : NULL; } TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream) { return reinterpret_cast( new FileRef(reinterpret_cast(stream))); } void taglib_file_free(TagLib_File *file) { delete reinterpret_cast(file); } BOOL taglib_file_is_valid(const TagLib_File *file) { return !reinterpret_cast(file)->isNull(); } TagLib_Tag *taglib_file_tag(const TagLib_File *file) { auto f = reinterpret_cast(file); return reinterpret_cast(f->tag()); } const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file) { auto f = reinterpret_cast(file); return reinterpret_cast(f->audioProperties()); } BOOL taglib_file_save(TagLib_File *file) { return reinterpret_cast(file)->save(); } //////////////////////////////////////////////////////////////////////////////// // TagLib::Tag wrapper //////////////////////////////////////////////////////////////////////////////// char *taglib_tag_title(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); char *s = stringToCharArray(t->title()); if(stringManagementEnabled) strings.append(s); return s; } char *taglib_tag_artist(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); char *s = stringToCharArray(t->artist()); if(stringManagementEnabled) strings.append(s); return s; } char *taglib_tag_album(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); char *s = stringToCharArray(t->album()); if(stringManagementEnabled) strings.append(s); return s; } char *taglib_tag_comment(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); char *s = stringToCharArray(t->comment()); if(stringManagementEnabled) strings.append(s); return s; } char *taglib_tag_genre(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); char *s = stringToCharArray(t->genre()); if(stringManagementEnabled) strings.append(s); return s; } unsigned int taglib_tag_year(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); return t->year(); } unsigned int taglib_tag_track(const TagLib_Tag *tag) { auto t = reinterpret_cast(tag); return t->track(); } void taglib_tag_set_title(TagLib_Tag *tag, const char *title) { auto t = reinterpret_cast(tag); t->setTitle(charArrayToString(title)); } void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist) { auto t = reinterpret_cast(tag); t->setArtist(charArrayToString(artist)); } void taglib_tag_set_album(TagLib_Tag *tag, const char *album) { auto t = reinterpret_cast(tag); t->setAlbum(charArrayToString(album)); } void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment) { auto t = reinterpret_cast(tag); t->setComment(charArrayToString(comment)); } void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre) { auto t = reinterpret_cast(tag); t->setGenre(charArrayToString(genre)); } void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year) { auto t = reinterpret_cast(tag); t->setYear(year); } void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track) { auto t = reinterpret_cast(tag); t->setTrack(track); } void taglib_tag_free_strings() { if(!stringManagementEnabled) return; for(auto &string : std::as_const(strings)) free(string); strings.clear(); } //////////////////////////////////////////////////////////////////////////////// // TagLib::AudioProperties wrapper //////////////////////////////////////////////////////////////////////////////// int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties) { auto p = reinterpret_cast(audioProperties); return p->lengthInSeconds(); } int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties) { auto p = reinterpret_cast(audioProperties); return p->bitrate(); } int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties) { auto p = reinterpret_cast(audioProperties); return p->sampleRate(); } int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties) { auto p = reinterpret_cast(audioProperties); return p->channels(); } void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding) { String::Type type = String::Latin1; switch(encoding) { case TagLib_ID3v2_Latin1: type = String::Latin1; break; case TagLib_ID3v2_UTF16: type = String::UTF16; break; case TagLib_ID3v2_UTF16BE: type = String::UTF16BE; break; case TagLib_ID3v2_UTF8: type = String::UTF8; break; } ID3v2::FrameFactory::instance()->setDefaultTextEncoding(type); } /****************************************************************************** * Properties API ******************************************************************************/ namespace { void _taglib_property_set(TagLib_File *file, const char* prop, const char* value, bool append) { if(file == NULL || prop == NULL) return; auto tfile = reinterpret_cast(file); PropertyMap map = tfile->tag()->properties(); if(value) { auto property = map.find(prop); if(property == map.end()) { map.insert(prop, StringList(charArrayToString(value))); } else { if(append) { property->second.append(charArrayToString(value)); } else { property->second = StringList(charArrayToString(value)); } } } else { map.erase(prop); } tfile->setProperties(map); } } // namespace void taglib_property_set(TagLib_File *file, const char *prop, const char *value) { _taglib_property_set(file, prop, value, false); } void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value) { _taglib_property_set(file, prop, value, true); } char** taglib_property_keys(const TagLib_File *file) { if(file == NULL) return NULL; const PropertyMap map = reinterpret_cast(file)->properties(); if(map.isEmpty()) return NULL; auto props = static_cast(malloc(sizeof(char *) * (map.size() + 1))); char **pp = props; for(const auto &i : map) { *pp++ = stringToCharArray(i.first); } *pp = NULL; return props; } char **taglib_property_get(const TagLib_File *file, const char *prop) { if(file == NULL || prop == NULL) return NULL; const PropertyMap map = reinterpret_cast(file)->properties(); auto property = map.find(prop); if(property == map.end()) return NULL; auto props = static_cast(malloc(sizeof(char *) * (property->second.size() + 1))); char **pp = props; for(const auto &i : property->second) { *pp++ = stringToCharArray(i); } *pp = NULL; return props; } void taglib_property_free(char **props) { if(props == NULL) return; char **p = props; while(*p) { free(*p++); } free(props); } /****************************************************************************** * Complex Properties API ******************************************************************************/ namespace { bool _taglib_complex_property_set( TagLib_File *file, const char *key, const TagLib_Complex_Property_Attribute **value, bool append) { if(file == NULL || key == NULL) return false; auto tfile = reinterpret_cast(file); if(value == NULL) { return tfile->setComplexProperties(key, {}); } VariantMap map; const TagLib_Complex_Property_Attribute** attrPtr = value; while(*attrPtr) { const TagLib_Complex_Property_Attribute *attr = *attrPtr; String attrKey(attr->key); switch(attr->value.type) { case TagLib_Variant_Void: map.insert(attrKey, Variant()); break; case TagLib_Variant_Bool: map.insert(attrKey, attr->value.value.boolValue != 0); break; case TagLib_Variant_Int: map.insert(attrKey, attr->value.value.intValue); break; case TagLib_Variant_UInt: map.insert(attrKey, attr->value.value.uIntValue); break; case TagLib_Variant_LongLong: map.insert(attrKey, attr->value.value.longLongValue); break; case TagLib_Variant_ULongLong: map.insert(attrKey, attr->value.value.uLongLongValue); break; case TagLib_Variant_Double: map.insert(attrKey, attr->value.value.doubleValue); break; case TagLib_Variant_String: map.insert(attrKey, charArrayToString(attr->value.value.stringValue)); break; case TagLib_Variant_StringList: { StringList strs; if(attr->value.value.stringListValue) { char **s = attr->value.value.stringListValue;; while(*s) { strs.append(charArrayToString(*s++)); } } map.insert(attrKey, strs); break; } case TagLib_Variant_ByteVector: map.insert(attrKey, ByteVector(attr->value.value.byteVectorValue, attr->value.size)); break; } ++attrPtr; } return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map)) : tfile->setComplexProperties(key, {map}); } } // namespace BOOL taglib_complex_property_set( TagLib_File *file, const char *key, const TagLib_Complex_Property_Attribute **value) { return _taglib_complex_property_set(file, key, value, false); } BOOL taglib_complex_property_set_append( TagLib_File *file, const char *key, const TagLib_Complex_Property_Attribute **value) { return _taglib_complex_property_set(file, key, value, true); } char** taglib_complex_property_keys(const TagLib_File *file) { if(file == NULL) { return NULL; } const StringList strs = reinterpret_cast(file)->complexPropertyKeys(); if(strs.isEmpty()) { return NULL; } auto keys = static_cast(malloc(sizeof(char *) * (strs.size() + 1))); char **keyPtr = keys; for(const auto &str : strs) { *keyPtr++ = stringToCharArray(str); } *keyPtr = NULL; return keys; } TagLib_Complex_Property_Attribute*** taglib_complex_property_get( const TagLib_File *file, const char *key) { if(file == NULL || key == NULL) { return NULL; } const auto variantMaps = reinterpret_cast(file)->complexProperties(key); if(variantMaps.isEmpty()) { return NULL; } auto props = static_cast( malloc(sizeof(TagLib_Complex_Property_Attribute **) * (variantMaps.size() + 1))); TagLib_Complex_Property_Attribute ***propPtr = props; for(const auto &variantMap : variantMaps) { if(!variantMap.isEmpty()) { auto attrs = static_cast( malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1))); auto attr = static_cast( malloc(sizeof(TagLib_Complex_Property_Attribute) * variantMap.size())); TagLib_Complex_Property_Attribute **attrPtr = attrs; // The next assignment is redundant to silence the clang analyzer, // it is done at the end of the loop, which must be entered because // variantMap is not empty. *attrPtr = attr; for(const auto &[k, v] : variantMap) { attr->key = stringToCharArray(k); attr->value.size = 0; switch(v.type()) { case Variant::Void: attr->value.type = TagLib_Variant_Void; attr->value.value.stringValue = NULL; break; case Variant::Bool: attr->value.type = TagLib_Variant_Bool; attr->value.value.boolValue = v.value(); break; case Variant::Int: attr->value.type = TagLib_Variant_Int; attr->value.value.intValue = v.value(); break; case Variant::UInt: attr->value.type = TagLib_Variant_UInt; attr->value.value.uIntValue = v.value(); break; case Variant::LongLong: attr->value.type = TagLib_Variant_LongLong; attr->value.value.longLongValue = v.value(); break; case Variant::ULongLong: attr->value.type = TagLib_Variant_ULongLong; attr->value.value.uLongLongValue = v.value(); break; case Variant::Double: attr->value.type = TagLib_Variant_Double; attr->value.value.doubleValue = v.value(); break; case Variant::String: { attr->value.type = TagLib_Variant_String; auto str = v.value(); attr->value.value.stringValue = stringToCharArray(str); attr->value.size = str.size(); break; } case Variant::StringList: { attr->value.type = TagLib_Variant_StringList; auto strs = v.value(); auto strPtr = static_cast(malloc(sizeof(char *) * (strs.size() + 1))); attr->value.value.stringListValue = strPtr; attr->value.size = strs.size(); for(const auto &str : strs) { *strPtr++ = stringToCharArray(str); } *strPtr = NULL; break; } case Variant::ByteVector: { attr->value.type = TagLib_Variant_ByteVector; const ByteVector data = v.value(); auto bytePtr = static_cast(malloc(data.size())); attr->value.value.byteVectorValue = bytePtr; attr->value.size = data.size(); ::memcpy(bytePtr, data.data(), data.size()); break; } case Variant::ByteVectorList: case Variant::VariantList: case Variant::VariantMap: { attr->value.type = TagLib_Variant_String; std::stringstream ss; ss << v; attr->value.value.stringValue = stringToCharArray(ss.str()); break; } } *attrPtr++ = attr++; } *attrPtr = NULL; *propPtr++ = attrs; } } *propPtr = NULL; return props; } void taglib_picture_from_complex_property( TagLib_Complex_Property_Attribute*** properties, TagLib_Complex_Property_Picture_Data *picture) { if(!properties || !picture) { return; } std::memset(picture, 0, sizeof(*picture)); TagLib_Complex_Property_Attribute*** propPtr = properties; while(!picture->data && *propPtr) { TagLib_Complex_Property_Attribute** attrPtr = *propPtr; while(*attrPtr) { TagLib_Complex_Property_Attribute *attr = *attrPtr; switch(attr->value.type) { case TagLib_Variant_String: if(strcmp("mimeType", attr->key) == 0) { picture->mimeType = attr->value.value.stringValue; } else if(strcmp("description", attr->key) == 0) { picture->description = attr->value.value.stringValue; } else if(strcmp("pictureType", attr->key) == 0) { picture->pictureType = attr->value.value.stringValue; } break; case TagLib_Variant_ByteVector: if(strcmp("data", attr->key) == 0) { picture->data = attr->value.value.byteVectorValue; picture->size = attr->value.size; } break; default: break; } ++attrPtr; } ++propPtr; } } void taglib_complex_property_free_keys(char **keys) { if(keys == NULL) { return; } char **k = keys; while(*k) { free(*k++); } free(keys); } void taglib_complex_property_free( TagLib_Complex_Property_Attribute ***props) { if(props == NULL) { return; } TagLib_Complex_Property_Attribute*** propPtr = props; while(*propPtr) { TagLib_Complex_Property_Attribute** attrPtr = *propPtr; while(*attrPtr) { TagLib_Complex_Property_Attribute *attr = *attrPtr; switch(attr->value.type) { case TagLib_Variant_String: free(attr->value.value.stringValue); break; case TagLib_Variant_StringList: if(attr->value.value.stringListValue) { char **s = attr->value.value.stringListValue; while(*s) { free(*s++); } free(attr->value.value.stringListValue); } break; case TagLib_Variant_ByteVector: free(attr->value.value.byteVectorValue); break; case TagLib_Variant_Void: case TagLib_Variant_Bool: case TagLib_Variant_Int: case TagLib_Variant_UInt: case TagLib_Variant_LongLong: case TagLib_Variant_ULongLong: case TagLib_Variant_Double: break; } free(attr->key); ++attrPtr; } free(**propPtr); free(*propPtr++); } free(props); } taglib-2.0.2/bindings/c/tag_c.h000066400000000000000000000510431466226211100162260ustar00rootroot00000000000000/*************************************************************************** copyright : (C) 2003 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * ***************************************************************************/ #ifndef TAGLIB_TAG_C #define TAGLIB_TAG_C /* Do not include this in the main TagLib documentation. */ #ifndef DO_NOT_DOCUMENT #ifdef __cplusplus extern "C" { #endif #if defined(TAGLIB_STATIC) #define TAGLIB_C_EXPORT #elif defined(_WIN32) || defined(_WIN64) #ifdef MAKE_TAGLIB_C_LIB #define TAGLIB_C_EXPORT __declspec(dllexport) #else #define TAGLIB_C_EXPORT __declspec(dllimport) #endif #elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1) #define TAGLIB_C_EXPORT __attribute__ ((visibility("default"))) #else #define TAGLIB_C_EXPORT #endif #ifdef _MSC_VER /* minwindef.h contains typedef int BOOL */ #include #elif !defined BOOL #define BOOL int #endif /******************************************************************************* * [ TagLib C Binding ] * * This is an interface to TagLib's "simple" API, meaning that you can read and * modify media files in a generic, but not specialized way. This is a rough * representation of TagLib::File and TagLib::Tag, for which the documentation * is somewhat more complete and worth consulting. *******************************************************************************/ /* * These are used to give the C API some type safety (as opposed to * using void * ), but pointers to them are simply cast to the corresponding C++ * types in the implementation. */ typedef struct { int dummy; } TagLib_File; typedef struct { int dummy; } TagLib_Tag; typedef struct { int dummy; } TagLib_AudioProperties; typedef struct { int dummy; } TagLib_IOStream; /*! * By default all strings coming into or out of TagLib's C API are in UTF8. * However, it may be desirable for TagLib to operate on Latin1 (ISO-8859-1) * strings in which case this should be set to FALSE. */ TAGLIB_C_EXPORT void taglib_set_strings_unicode(BOOL unicode); /*! * TagLib can keep track of strings that are created when outputting tag values * and clear them using taglib_tag_clear_strings(). This is enabled by default. * However if you wish to do more fine grained management of strings, you can do * so by setting \a management to FALSE. */ TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management); /*! * Explicitly free a string returned from TagLib */ TAGLIB_C_EXPORT void taglib_free(void* pointer); /******************************************************************************* * Stream API ******************************************************************************/ /*! * Creates a byte vector stream from \a size bytes of \a data. * Such a stream can be used with taglib_file_new_iostream() to create a file * from memory. */ TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size); /*! * Frees and closes the stream. */ TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream); /******************************************************************************* * File API ******************************************************************************/ typedef enum { TagLib_File_MPEG, TagLib_File_OggVorbis, TagLib_File_FLAC, TagLib_File_MPC, TagLib_File_OggFlac, TagLib_File_WavPack, TagLib_File_Speex, TagLib_File_TrueAudio, TagLib_File_MP4, TagLib_File_ASF, TagLib_File_AIFF, TagLib_File_WAV, TagLib_File_APE, TagLib_File_IT, TagLib_File_Mod, TagLib_File_S3M, TagLib_File_XM, TagLib_File_Opus, TagLib_File_DSF, TagLib_File_DSDIFF } TagLib_File_Type; /*! * Creates a TagLib file based on \a filename. TagLib will try to guess the file * type. * * \returns NULL if the file type cannot be determined or the file cannot * be opened. */ TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename); /*! * Creates a TagLib file based on \a filename. Rather than attempting to guess * the type, it will use the one specified by \a type. */ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type); /*! * Creates a TagLib file from a \a stream. * A byte vector stream can be used to read a file from memory and write to * memory, e.g. when fetching network data. * The stream has to be created using taglib_memory_iostream_new() and shall be * freed using taglib_iostream_free() \e after this file is freed with * taglib_file_free(). */ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream); /*! * Frees and closes the file. */ TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file); /*! * Returns \c true if the file is open and readable and valid information for * the Tag and / or AudioProperties was found. */ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file); /*! * Returns a pointer to the tag associated with this file. This will be freed * automatically when the file is freed. */ TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file); /*! * Returns a pointer to the audio properties associated with this file. This * will be freed automatically when the file is freed. */ TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file); /*! * Saves the \a file to disk. */ TAGLIB_C_EXPORT BOOL taglib_file_save(TagLib_File *file); /****************************************************************************** * Tag API ******************************************************************************/ /*! * Returns a string with this tag's title. * * \note By default this string should be UTF8 encoded and its memory should be * freed using taglib_tag_free_strings(). */ TAGLIB_C_EXPORT char *taglib_tag_title(const TagLib_Tag *tag); /*! * Returns a string with this tag's artist. * * \note By default this string should be UTF8 encoded and its memory should be * freed using taglib_tag_free_strings(). */ TAGLIB_C_EXPORT char *taglib_tag_artist(const TagLib_Tag *tag); /*! * Returns a string with this tag's album name. * * \note By default this string should be UTF8 encoded and its memory should be * freed using taglib_tag_free_strings(). */ TAGLIB_C_EXPORT char *taglib_tag_album(const TagLib_Tag *tag); /*! * Returns a string with this tag's comment. * * \note By default this string should be UTF8 encoded and its memory should be * freed using taglib_tag_free_strings(). */ TAGLIB_C_EXPORT char *taglib_tag_comment(const TagLib_Tag *tag); /*! * Returns a string with this tag's genre. * * \note By default this string should be UTF8 encoded and its memory should be * freed using taglib_tag_free_strings(). */ TAGLIB_C_EXPORT char *taglib_tag_genre(const TagLib_Tag *tag); /*! * Returns the tag's year or 0 if the year is not set. */ TAGLIB_C_EXPORT unsigned int taglib_tag_year(const TagLib_Tag *tag); /*! * Returns the tag's track number or 0 if the track number is not set. */ TAGLIB_C_EXPORT unsigned int taglib_tag_track(const TagLib_Tag *tag); /*! * Sets the tag's title. * * \note By default this string should be UTF8 encoded. */ TAGLIB_C_EXPORT void taglib_tag_set_title(TagLib_Tag *tag, const char *title); /*! * Sets the tag's artist. * * \note By default this string should be UTF8 encoded. */ TAGLIB_C_EXPORT void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist); /*! * Sets the tag's album. * * \note By default this string should be UTF8 encoded. */ TAGLIB_C_EXPORT void taglib_tag_set_album(TagLib_Tag *tag, const char *album); /*! * Sets the tag's comment. * * \note By default this string should be UTF8 encoded. */ TAGLIB_C_EXPORT void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment); /*! * Sets the tag's genre. * * \note By default this string should be UTF8 encoded. */ TAGLIB_C_EXPORT void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre); /*! * Sets the tag's year. 0 indicates that this field should be cleared. */ TAGLIB_C_EXPORT void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year); /*! * Sets the tag's track number. 0 indicates that this field should be cleared. */ TAGLIB_C_EXPORT void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track); /*! * Frees all of the strings that have been created by the tag. */ TAGLIB_C_EXPORT void taglib_tag_free_strings(void); /****************************************************************************** * Audio Properties API ******************************************************************************/ /*! * Returns the length of the file in seconds. */ TAGLIB_C_EXPORT int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties); /*! * Returns the bitrate of the file in kb/s. */ TAGLIB_C_EXPORT int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties); /*! * Returns the sample rate of the file in Hz. */ TAGLIB_C_EXPORT int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties); /*! * Returns the number of channels in the audio stream. */ TAGLIB_C_EXPORT int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties); /******************************************************************************* * Special convenience ID3v2 functions *******************************************************************************/ typedef enum { TagLib_ID3v2_Latin1, TagLib_ID3v2_UTF16, TagLib_ID3v2_UTF16BE, TagLib_ID3v2_UTF8 } TagLib_ID3v2_Encoding; /*! * This sets the default encoding for ID3v2 frames that are written to tags. */ TAGLIB_C_EXPORT void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding); /****************************************************************************** * Properties API ******************************************************************************/ /*! * Sets the property \a prop with \a value. Use \a value = NULL to remove * the property, otherwise it will be replaced. */ TAGLIB_C_EXPORT void taglib_property_set(TagLib_File *file, const char *prop, const char *value); /*! * Appends \a value to the property \a prop (sets it if non-existing). * Use \a value = NULL to remove all values associated with the property. */ TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value); /*! * Get the keys of the property map. * * \return NULL terminated array of C-strings (char *), only NULL if empty. * It must be freed by the client using taglib_property_free(). */ TAGLIB_C_EXPORT char** taglib_property_keys(const TagLib_File *file); /*! * Get value(s) of property \a prop. * * \return NULL terminated array of C-strings (char *), only NULL if empty. * It must be freed by the client using taglib_property_free(). */ TAGLIB_C_EXPORT char** taglib_property_get(const TagLib_File *file, const char *prop); /*! * Frees the NULL terminated array \a props and the C-strings it contains. */ TAGLIB_C_EXPORT void taglib_property_free(char **props); /****************************************************************************** * Complex Properties API ******************************************************************************/ /*! * Types which can be stored in a TagLib_Variant. * * \related TagLib::Variant::Type * These correspond to TagLib::Variant::Type, but ByteVectorList, VariantList, * VariantMap are not supported and will be returned as their string * representation. */ typedef enum { TagLib_Variant_Void, TagLib_Variant_Bool, TagLib_Variant_Int, TagLib_Variant_UInt, TagLib_Variant_LongLong, TagLib_Variant_ULongLong, TagLib_Variant_Double, TagLib_Variant_String, TagLib_Variant_StringList, TagLib_Variant_ByteVector } TagLib_Variant_Type; /*! * Discriminated union used in complex property attributes. * * \e type must be set according to the \e value union used. * \e size is only required for TagLib_Variant_ByteVector and must contain * the number of bytes. * * \related TagLib::Variant. */ typedef struct { TagLib_Variant_Type type; unsigned int size; union { char *stringValue; char *byteVectorValue; char **stringListValue; BOOL boolValue; int intValue; unsigned int uIntValue; long long longLongValue; unsigned long long uLongLongValue; double doubleValue; } value; } TagLib_Variant; /*! * Attribute of a complex property. * Complex properties consist of a NULL-terminated array of pointers to * this structure with \e key and \e value. */ typedef struct { char *key; TagLib_Variant value; } TagLib_Complex_Property_Attribute; /*! * Picture data extracted from a complex property by the convenience function * taglib_picture_from_complex_property(). */ typedef struct { char *mimeType; char *description; char *pictureType; char *data; unsigned int size; } TagLib_Complex_Property_Picture_Data; /*! * Declare complex property attributes to set a picture. * Can be used to define a variable \a var which can be used with * taglib_complex_property_set() and a "PICTURE" key to set an * embedded picture with the picture data \a dat of size \a siz * and description \a desc, mime type \a mime and picture type * \a typ (size is unsigned int, the other input parameters char *). */ #define TAGLIB_COMPLEX_PROPERTY_PICTURE(var, dat, siz, desc, mime, typ) \ const TagLib_Complex_Property_Attribute \ var##Attrs[] = { \ { \ (char *)"data", \ { \ TagLib_Variant_ByteVector, \ (siz), \ { \ (char *)(dat) \ } \ } \ }, \ { \ (char *)"mimeType", \ { \ TagLib_Variant_String, \ 0U, \ { \ (char *)(mime) \ } \ } \ }, \ { \ (char *)"description", \ { \ TagLib_Variant_String, \ 0U, \ { \ (char *)(desc) \ } \ } \ }, \ { \ (char *)"pictureType", \ { \ TagLib_Variant_String, \ 0U, \ { \ (char *)(typ) \ } \ } \ } \ }; \ const TagLib_Complex_Property_Attribute *var[] = { \ &var##Attrs[0], &var##Attrs[1], &var##Attrs[2], \ &var##Attrs[3], NULL \ } /*! * Sets the complex property \a key with \a value. Use \a value = NULL to * remove the property, otherwise it will be replaced with the NULL * terminated array of attributes in \a value. * * A picture can be set with the TAGLIB_COMPLEX_PROPERTY_PICTURE macro: * * \code {.c} * TagLib_File *file = taglib_file_new("myfile.mp3"); * FILE *fh = fopen("mypicture.jpg", "rb"); * if(fh) { * fseek(fh, 0L, SEEK_END); * long size = ftell(fh); * fseek(fh, 0L, SEEK_SET); * char *data = (char *)malloc(size); * fread(data, size, 1, fh); * TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib", * "image/jpeg", "Front Cover"); * taglib_complex_property_set(file, "PICTURE", props); * taglib_file_save(file); * free(data); * fclose(fh); * } * \endcode */ TAGLIB_C_EXPORT BOOL taglib_complex_property_set( TagLib_File *file, const char *key, const TagLib_Complex_Property_Attribute **value); /*! * Appends \a value to the complex property \a key (sets it if non-existing). * Use \a value = NULL to remove all values associated with the \a key. */ TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append( TagLib_File *file, const char *key, const TagLib_Complex_Property_Attribute **value); /*! * Get the keys of the complex properties. * * \return NULL terminated array of C-strings (char *), only NULL if empty. * It must be freed by the client using taglib_complex_property_free_keys(). */ TAGLIB_C_EXPORT char** taglib_complex_property_keys(const TagLib_File *file); /*! * Get value(s) of complex property \a key. * * \return NULL terminated array of property values, which are themselves an * array of property attributes, only NULL if empty. * It must be freed by the client using taglib_complex_property_free(). */ TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get( const TagLib_File *file, const char *key); /*! * Extract the complex property values of a picture. * * This function can be used to get the data from a "PICTURE" complex property * without having to traverse the whole variant map. A picture can be * retrieved like this: * * \code {.c} * TagLib_File *file = taglib_file_new("myfile.mp3"); * TagLib_Complex_Property_Attribute*** properties = * taglib_complex_property_get(file, "PICTURE"); * TagLib_Complex_Property_Picture_Data picture; * taglib_picture_from_complex_property(properties, &picture); * // Do something with picture.mimeType, picture.description, * // picture.pictureType, picture.data, picture.size, e.g. extract it. * FILE *fh = fopen("mypicture.jpg", "wb"); * if(fh) { * fwrite(picture.data, picture.size, 1, fh); * fclose(fh); * } * taglib_complex_property_free(properties); * \endcode * * Note that the data in \a picture contains pointers to data in \a properties, * i.e. it only lives as long as the properties, until they are freed with * taglib_complex_property_free(). * If you want to access multiple pictures or additional properties of FLAC * pictures ("width", "height", "numColors", "colorDepth" int values), you * have to traverse the \a properties yourself. */ TAGLIB_C_EXPORT void taglib_picture_from_complex_property( TagLib_Complex_Property_Attribute*** properties, TagLib_Complex_Property_Picture_Data *picture); /*! * Frees the NULL terminated array \a keys (as returned by * taglib_complex_property_keys()) and the C-strings it contains. */ TAGLIB_C_EXPORT void taglib_complex_property_free_keys(char **keys); /*! * Frees the NULL terminated array \a props of property attribute arrays * (as returned by taglib_complex_property_get()) and the data such as * C-strings and byte vectors contained in these attributes. */ TAGLIB_C_EXPORT void taglib_complex_property_free( TagLib_Complex_Property_Attribute ***props); #ifdef __cplusplus } #endif #endif /* DO_NOT_DOCUMENT */ #endif taglib-2.0.2/bindings/c/taglib_c.pc.cmake000066400000000000000000000005261466226211100201470ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@CMAKE_PC_LIBDIR@ includedir=@CMAKE_PC_INCLUDEDIR@ Name: TagLib C Bindings Description: Audio meta-data library (C bindings) Requires: taglib Version: @TAGLIB_LIB_VERSION_STRING@ Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@ Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@ taglib-2.0.2/cmake/000077500000000000000000000000001466226211100140365ustar00rootroot00000000000000taglib-2.0.2/cmake/modules/000077500000000000000000000000001466226211100155065ustar00rootroot00000000000000taglib-2.0.2/cmake/modules/FindCppUnit.cmake000066400000000000000000000052241466226211100206760ustar00rootroot00000000000000# - Try to find the libcppunit libraries # Once done this will define # # CppUnit_FOUND - system has libcppunit # CPPUNIT_INCLUDE_DIR - the libcppunit include directory # CPPUNIT_LIBRARIES - libcppunit library include (MacroEnsureVersion) if(NOT CPPUNIT_MIN_VERSION) SET(CPPUNIT_MIN_VERSION 1.14.0) endif(NOT CPPUNIT_MIN_VERSION) FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config ) IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) # in cache already SET(CppUnit_FOUND TRUE) ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) SET(CPPUNIT_INCLUDE_DIR) SET(CPPUNIT_LIBRARIES) IF(CPPUNIT_CONFIG_EXECUTABLE) EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --cflags RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_CFLAGS) EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --libs RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_LIBRARIES) EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_INSTALLED_VERSION) STRING(REGEX REPLACE "-I(.+)" "\\1" CPPUNIT_CFLAGS "${CPPUNIT_CFLAGS}") ELSE(CPPUNIT_CONFIG_EXECUTABLE) # in case win32 needs to find it the old way? FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include ) FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib ) # how can we find cppunit version? MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}") SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION}) ENDIF(CPPUNIT_CONFIG_EXECUTABLE) SET(CPPUNIT_INCLUDE_DIR ${CPPUNIT_CFLAGS} "${CPPUNIT_CFLAGS}/cppunit") ENDIF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) SET(CppUnit_FOUND TRUE) if(NOT CppUnit_FIND_QUIETLY) MESSAGE (STATUS "Found cppunit: ${CPPUNIT_LIBRARIES}") endif(NOT CppUnit_FIND_QUIETLY) IF(CPPUNIT_CONFIG_EXECUTABLE) EXEC_PROGRAM(${CPPUNIT_CONFIG_EXECUTABLE} ARGS --version RETURN_VALUE _return_VALUE OUTPUT_VARIABLE CPPUNIT_INSTALLED_VERSION) ENDIF(CPPUNIT_CONFIG_EXECUTABLE) macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK ) IF(NOT CPPUNIT_INSTALLED_VERSION_OK) MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or newer is required") SET(CppUnit_FOUND FALSE) ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK) ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) SET(CppUnit_FOUND FALSE CACHE BOOL "Not found cppunit library") ENDIF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES) MARK_AS_ADVANCED(CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARIES) taglib-2.0.2/cmake/modules/MacroEnsureVersion.cmake000066400000000000000000000066561466226211100223160ustar00rootroot00000000000000# This macro compares version numbers of the form "x.y.z" # MACRO_ENSURE_VERSION( FOO_MIN_VERSION FOO_VERSION_FOUND FOO_VERSION_OK) # will set FOO_VERSIN_OK to true if FOO_VERSION_FOUND >= FOO_MIN_VERSION # where both have to be in a 3-part-version format, leading and trailing # text is ok, e.g. # MACRO_ENSURE_VERSION( "2.5.31" "flex 2.5.4a" VERSION_OK) # which means 2.5.31 is required and "flex 2.5.4a" is what was found on the system # Copyright (c) 2006, David Faure, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. MACRO(MACRO_ENSURE_VERSION requested_version found_version var_too_old) # parse the parts of the version string STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" req_major_vers "${requested_version}") STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" req_minor_vers "${requested_version}") STRING(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" req_patch_vers "${requested_version}") STRING(REGEX REPLACE "[^0-9]*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" found_major_vers "${found_version}") STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" found_minor_vers "${found_version}") STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" found_patch_vers "${found_version}") # compute an overall version number which can be compared at once MATH(EXPR req_vers_num "${req_major_vers}*10000 + ${req_minor_vers}*100 + ${req_patch_vers}") MATH(EXPR found_vers_num "${found_major_vers}*10000 + ${found_minor_vers}*100 + ${found_patch_vers}") if (found_vers_num LESS req_vers_num) set( ${var_too_old} FALSE ) else (found_vers_num LESS req_vers_num) set( ${var_too_old} TRUE ) endif (found_vers_num LESS req_vers_num) ENDMACRO(MACRO_ENSURE_VERSION) # This macro compares version numbers of the form "x.y" # MACRO_ENSURE_VERSION( FOO_MIN_VERSION FOO_VERSION_FOUND FOO_VERSION_OK) # will set FOO_VERSIN_OK to true if FOO_VERSION_FOUND >= FOO_MIN_VERSION # where both have to be in a 2-part-version format, leading and trailing # text is ok, e.g. # MACRO_ENSURE_VERSION( "0.5" "foo 0.6" VERSION_OK) # which means 0.5 is required and "foo 0.6" is what was found on the system # Copyright (c) 2006, David Faure, # Copyright (c) 2007, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. MACRO(MACRO_ENSURE_VERSION2 requested_version found_version var_too_old) # parse the parts of the version string STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" req_major_vers "${requested_version}") STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" req_minor_vers "${requested_version}") STRING(REGEX REPLACE "[^0-9]*([0-9]+)\\.[0-9]+.*" "\\1" found_major_vers "${found_version}") STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.([0-9]+).*" "\\1" found_minor_vers "${found_version}") # compute an overall version number which can be compared at once MATH(EXPR req_vers_num "${req_major_vers}*100 + ${req_minor_vers}") MATH(EXPR found_vers_num "${found_major_vers}*100 + ${found_minor_vers}") if (found_vers_num LESS req_vers_num) set( ${var_too_old} FALSE ) else (found_vers_num LESS req_vers_num) set( ${var_too_old} TRUE ) endif (found_vers_num LESS req_vers_num) ENDMACRO(MACRO_ENSURE_VERSION2) taglib-2.0.2/cmake_uninstall.cmake.in000066400000000000000000000014271466226211100175420ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif() file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif () else () message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif () endforeach() taglib-2.0.2/config.h.cmake000066400000000000000000000012131466226211100154500ustar00rootroot00000000000000/* config.h. Generated by cmake from config.h.cmake */ #ifndef TAGLIB_CONFIG_H #define TAGLIB_CONFIG_H /* Defined if your compiler supports some byte swap functions */ #cmakedefine HAVE_GCC_BYTESWAP 1 #cmakedefine HAVE_GLIBC_BYTESWAP 1 #cmakedefine HAVE_MSC_BYTESWAP 1 #cmakedefine HAVE_MAC_BYTESWAP 1 #cmakedefine HAVE_OPENBSD_BYTESWAP 1 /* Defined if your compiler supports ISO _strdup */ #cmakedefine HAVE_ISO_STRDUP 1 /* Defined if zlib is installed */ #cmakedefine HAVE_ZLIB 1 /* Indicates whether debug messages are shown even in release mode */ #cmakedefine TRACE_IN_RELEASE 1 #cmakedefine TESTS_DIR "@TESTS_DIR@" #endif taglib-2.0.2/doc/000077500000000000000000000000001466226211100135235ustar00rootroot00000000000000taglib-2.0.2/doc/README000066400000000000000000000001221466226211100143760ustar00rootroot00000000000000Run "make docs" in the parent directory to generate the TagLib API documentation. taglib-2.0.2/doc/api-footer.html000066400000000000000000000000221466226211100164500ustar00rootroot00000000000000 taglib-2.0.2/doc/api-header.html000066400000000000000000000031241466226211100164100ustar00rootroot00000000000000 $title ($projectname) $treeview $search $mathjax $extrastylesheet
$projectname  $projectnumber
$projectbrief
$projectbrief
$searchbox
taglib-2.0.2/doc/taglib.svg000066400000000000000000000253631466226211100155170ustar00rootroot00000000000000 taglib 2 Created with Sketch. MP3 VORBIS FLAC MP4 AAC WAV AIFF TA WP taglib-2.0.2/examples/000077500000000000000000000000001466226211100145745ustar00rootroot00000000000000taglib-2.0.2/examples/CMakeLists.txt000066400000000000000000000024151466226211100173360ustar00rootroot00000000000000include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ ) if(NOT BUILD_SHARED_LIBS) add_definitions(-DTAGLIB_STATIC) endif() ########### next target ############### add_executable(tagreader tagreader.cpp) target_link_libraries(tagreader tag) ########### next target ############### add_executable(tagreader_c tagreader_c.c) target_link_libraries(tagreader_c tag_c) ########### next target ############### add_executable(tagwriter tagwriter.cpp) target_link_libraries(tagwriter tag) ########### next target ############### add_executable(framelist framelist.cpp) target_link_libraries(framelist tag) ########### next target ############### add_executable(strip-id3v1 strip-id3v1.cpp) target_link_libraries(strip-id3v1 tag) install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) taglib-2.0.2/examples/framelist.cpp000066400000000000000000000076441466226211100173010ustar00rootroot00000000000000/* Copyright (C) 2003 Scott Wheeler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "tbytevector.h" #include "mpegfile.h" #include "id3v2tag.h" #include "id3v2frame.h" #include "id3v2header.h" #include "commentsframe.h" #include "id3v1tag.h" #include "apetag.h" using namespace TagLib; int main(int argc, char *argv[]) { // process the command line args for(int i = 1; i < argc; i++) { std::cout << "******************** \"" << argv[i] << "\"********************" << std::endl; MPEG::File f(argv[i]); ID3v2::Tag *id3v2tag = f.ID3v2Tag(); if(id3v2tag) { std::cout << "ID3v2." << id3v2tag->header()->majorVersion() << "." << id3v2tag->header()->revisionNumber() << ", " << id3v2tag->header()->tagSize() << " bytes in tag" << std::endl; const auto &frames = id3v2tag->frameList(); for(auto it = frames.begin(); it != frames.end(); it++) { std::cout << (*it)->frameID(); if(auto comment = dynamic_cast(*it)) if(!comment->description().isEmpty()) std::cout << " [" << comment->description() << "]"; std::cout << " - \"" << (*it)->toString() << "\"" << std::endl; } } else std::cout << "file does not have a valid id3v2 tag" << std::endl; std::cout << std::endl << "ID3v1" << std::endl; ID3v1::Tag *id3v1tag = f.ID3v1Tag(); if(id3v1tag) { std::cout << "title - \"" << id3v1tag->title() << "\"" << std::endl; std::cout << "artist - \"" << id3v1tag->artist() << "\"" << std::endl; std::cout << "album - \"" << id3v1tag->album() << "\"" << std::endl; std::cout << "year - \"" << id3v1tag->year() << "\"" << std::endl; std::cout << "comment - \"" << id3v1tag->comment() << "\"" << std::endl; std::cout << "track - \"" << id3v1tag->track() << "\"" << std::endl; std::cout << "genre - \"" << id3v1tag->genre() << "\"" << std::endl; } else std::cout << "file does not have a valid id3v1 tag" << std::endl; APE::Tag *ape = f.APETag(); std::cout << std::endl << "APE" << std::endl; if(ape) { const auto &items = ape->itemListMap(); for(auto it = items.begin(); it != items.end(); ++it) { if((*it).second.type() != APE::Item::Binary) std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl; else std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl; } } else std::cout << "file does not have a valid APE tag" << std::endl; std::cout << std::endl; } } taglib-2.0.2/examples/strip-id3v1.cpp000066400000000000000000000031741466226211100173720ustar00rootroot00000000000000/* Copyright (C) 2003 Scott Wheeler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "tstring.h" #include "mpegfile.h" using namespace TagLib; int main(int argc, char *argv[]) { for(int i = 1; i < argc; i++) { std::cout << "******************** Stripping ID3v1 Tag From: \"" << argv[i] << "\"********************" << std::endl; MPEG::File f(argv[i]); f.strip(MPEG::File::ID3v1); } } taglib-2.0.2/examples/tagreader.cpp000066400000000000000000000114551466226211100172440ustar00rootroot00000000000000/* Copyright (C) 2003 Scott Wheeler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "tpropertymap.h" #include "tstringlist.h" #include "tvariant.h" #include "fileref.h" #include "tag.h" int main(int argc, char *argv[]) { for(int i = 1; i < argc; i++) { std::cout << "******************** \"" << argv[i] << "\" ********************" << std::endl; TagLib::FileRef f(argv[i]); if(!f.isNull() && f.tag()) { TagLib::Tag *tag = f.tag(); std::cout << "-- TAG (basic) --" << std::endl; std::cout << "title - \"" << tag->title() << "\"" << std::endl; std::cout << "artist - \"" << tag->artist() << "\"" << std::endl; std::cout << "album - \"" << tag->album() << "\"" << std::endl; std::cout << "year - \"" << tag->year() << "\"" << std::endl; std::cout << "comment - \"" << tag->comment() << "\"" << std::endl; std::cout << "track - \"" << tag->track() << "\"" << std::endl; std::cout << "genre - \"" << tag->genre() << "\"" << std::endl; TagLib::PropertyMap tags = f.properties(); if(!tags.isEmpty()) { unsigned int longest = 0; for(auto j = tags.cbegin(); j != tags.cend(); ++j) { if (j->first.size() > longest) { longest = j->first.size(); } } std::cout << "-- TAG (properties) --" << std::endl; for(auto j = tags.cbegin(); j != tags.cend(); ++j) { for(auto k = j->second.begin(); k != j->second.end(); ++k) { std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl; } } } TagLib::StringList names = f.complexPropertyKeys(); for(const auto &name : names) { const auto& properties = f.complexProperties(name); for(const auto &property : properties) { std::cout << name << ":" << std::endl; for(const auto &[key, value] : property) { std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - "; if(value.type() == TagLib::Variant::ByteVector) { std::cout << "(" << value.value().size() << " bytes)" << std::endl; /* The picture could be extracted using: std::ofstream picture; TagLib::String fn(argv[i]); int slashPos = fn.rfind('/'); int dotPos = fn.rfind('.'); if(slashPos >= 0 && dotPos > slashPos) { fn = fn.substr(slashPos + 1, dotPos - slashPos - 1); } fn += ".jpg"; picture.open(fn.toCString(), std::ios_base::out | std::ios_base::binary); picture << value.value(); picture.close(); */ } else { std::cout << value << std::endl; } } } } } if(!f.isNull() && f.audioProperties()) { TagLib::AudioProperties *properties = f.audioProperties(); int seconds = properties->lengthInSeconds() % 60; int minutes = (properties->lengthInSeconds() - seconds) / 60; std::cout << "-- AUDIO --" << std::endl; std::cout << "bitrate - " << properties->bitrate() << std::endl; std::cout << "sample rate - " << properties->sampleRate() << std::endl; std::cout << "channels - " << properties->channels() << std::endl; std::cout << "length - " << minutes << ":" << std::setfill('0') << std::setw(2) << std::right << seconds << std::endl; } } return 0; } taglib-2.0.2/examples/tagreader_c.c000066400000000000000000000141221466226211100172000ustar00rootroot00000000000000/* Copyright (C) 2003 Scott Wheeler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "tag_c.h" #ifndef FALSE #define FALSE 0 #endif int main(int argc, char *argv[]) { int i; taglib_set_strings_unicode(1); for(i = 1; i < argc; i++) { TagLib_File *file; TagLib_Tag *tag; const TagLib_AudioProperties *properties; char **propertiesMap; char **complexKeys; printf("******************** \"%s\" ********************\n", argv[i]); file = taglib_file_new(argv[i]); if(file == NULL) break; tag = taglib_file_tag(file); properties = taglib_file_audioproperties(file); propertiesMap = taglib_property_keys(file); complexKeys = taglib_complex_property_keys(file); if(tag != NULL) { printf("-- TAG (basic) --\n"); printf("title - \"%s\"\n", taglib_tag_title(tag)); printf("artist - \"%s\"\n", taglib_tag_artist(tag)); printf("album - \"%s\"\n", taglib_tag_album(tag)); printf("year - \"%u\"\n", taglib_tag_year(tag)); printf("comment - \"%s\"\n", taglib_tag_comment(tag)); printf("track - \"%u\"\n", taglib_tag_track(tag)); printf("genre - \"%s\"\n", taglib_tag_genre(tag)); } if(propertiesMap != NULL) { char **keyPtr = propertiesMap; int longest = 0; while(*keyPtr) { int len = (int)strlen(*keyPtr++); if(len > longest) { longest = len; } } keyPtr = propertiesMap; printf("-- TAG (properties) --\n"); while(*keyPtr) { char **valPtr; char **propertyValues = valPtr = taglib_property_get(file, *keyPtr); while(valPtr && *valPtr) { printf("%-*s - \"%s\"\n", longest, *keyPtr, *valPtr++); } taglib_property_free(propertyValues); ++keyPtr; } } if(complexKeys != NULL) { char **keyPtr = complexKeys; while(*keyPtr) { TagLib_Complex_Property_Attribute*** props = taglib_complex_property_get(file, *keyPtr); if(props != NULL) { TagLib_Complex_Property_Attribute*** propPtr = props; while(*propPtr) { TagLib_Complex_Property_Attribute** attrPtr = *propPtr; printf("%s:\n", *keyPtr); while(*attrPtr) { TagLib_Complex_Property_Attribute *attr = *attrPtr; TagLib_Variant_Type type = attr->value.type; printf(" %-11s - ", attr->key); switch(type) { case TagLib_Variant_Void: printf("null\n"); break; case TagLib_Variant_Bool: printf("%s\n", attr->value.value.boolValue ? "true" : "false"); break; case TagLib_Variant_Int: printf("%d\n", attr->value.value.intValue); break; case TagLib_Variant_UInt: printf("%u\n", attr->value.value.uIntValue); break; case TagLib_Variant_LongLong: printf("%lld\n", attr->value.value.longLongValue); break; case TagLib_Variant_ULongLong: printf("%llu\n", attr->value.value.uLongLongValue); break; case TagLib_Variant_Double: printf("%f\n", attr->value.value.doubleValue); break; case TagLib_Variant_String: printf("\"%s\"\n", attr->value.value.stringValue); break; case TagLib_Variant_StringList: if(attr->value.value.stringListValue) { char **strs = attr->value.value.stringListValue; char **s = strs; while(*s) { if(s != strs) { printf(" "); } printf("%s", *s++); } } printf("\n"); break; case TagLib_Variant_ByteVector: printf("(%u bytes)\n", attr->value.size); break; } ++attrPtr; } ++propPtr; } taglib_complex_property_free(props); } ++keyPtr; } taglib_complex_property_free_keys(complexKeys); } if(properties != NULL) { int seconds = taglib_audioproperties_length(properties) % 60; int minutes = (taglib_audioproperties_length(properties) - seconds) / 60; printf("-- AUDIO --\n"); printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties)); printf("sample rate - %i\n", taglib_audioproperties_samplerate(properties)); printf("channels - %i\n", taglib_audioproperties_channels(properties)); printf("length - %i:%02i\n", minutes, seconds); } taglib_property_free(propertiesMap); taglib_tag_free_strings(); taglib_file_free(file); } return 0; } taglib-2.0.2/examples/tagwriter.cpp000066400000000000000000000147051466226211100173170ustar00rootroot00000000000000/* Copyright (C) 2004 Scott Wheeler * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "tlist.h" #include "tfile.h" #include "tpropertymap.h" #include "tvariant.h" #include "fileref.h" #include "tag.h" bool isArgument(const char *s) { return strlen(s) == 2 && s[0] == '-'; } bool isFile(const char *s) { #ifdef _WIN32 struct _stat64 st; return ::_stat64(s, &st) == 0 && (st.st_mode & S_IFREG); #else struct stat st; return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK)); #endif } void usage() { std::cout << std::endl; std::cout << "Usage: tagwriter " << std::endl; std::cout << std::endl; std::cout << "Where the valid fields are:" << std::endl; std::cout << " -t " << std::endl; std::cout << " -a <artist>" << std::endl; std::cout << " -A <album>" << std::endl; std::cout << " -c <comment>" << std::endl; std::cout << " -g <genre>" << std::endl; std::cout << " -y <year>" << std::endl; std::cout << " -T <track>" << std::endl; std::cout << " -R <tagname> <tagvalue>" << std::endl; std::cout << " -I <tagname> <tagvalue>" << std::endl; std::cout << " -D <tagname>" << std::endl; std::cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << std::endl; std::cout << std::endl; exit(1); } void checkForRejectedProperties(const TagLib::PropertyMap &tags) { // stolen from tagreader.cpp if(tags.size() > 0) { unsigned int longest = 0; for(auto i = tags.begin(); i != tags.end(); ++i) { if(i->first.size() > longest) { longest = i->first.size(); } } std::cout << "-- rejected TAGs (properties) --" << std::endl; for(auto i = tags.begin(); i != tags.end(); ++i) { for(auto j = i->second.begin(); j != i->second.end(); ++j) { std::cout << std::left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << std::endl; } } } } int main(int argc, char *argv[]) { TagLib::List<TagLib::FileRef> fileList; while(argc > 0 && isFile(argv[argc - 1])) { TagLib::FileRef f(argv[argc - 1]); if(!f.isNull() && f.tag()) fileList.append(f); argc--; } if(fileList.isEmpty()) usage(); int i = 1; while(i < argc - 1) { if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) { char field = argv[i][1]; TagLib::String value = argv[i + 1]; int numArgsConsumed = 2; for(auto &f : fileList) { TagLib::Tag *t = f.tag(); switch (field) { case 't': t->setTitle(value); break; case 'a': t->setArtist(value); break; case 'A': t->setAlbum(value); break; case 'c': t->setComment(value); break; case 'g': t->setGenre(value); break; case 'y': t->setYear(value.toInt()); break; case 'T': t->setTrack(value.toInt()); break; case 'R': case 'I': if(i + 2 < argc) { TagLib::PropertyMap map = f.properties(); if(field == 'R') { map.replace(value, TagLib::String(argv[i + 2])); } else { map.insert(value, TagLib::String(argv[i + 2])); } numArgsConsumed = 3; checkForRejectedProperties(f.setProperties(map)); } else { usage(); } break; case 'D': { TagLib::PropertyMap map = f.properties(); map.erase(value); checkForRejectedProperties(f.setProperties(map)); break; } case 'p': { if(i + 2 < argc) { numArgsConsumed = 3; if(!value.isEmpty()) { if(!isFile(value.toCString())) { std::cout << value.toCString() << " not found." << std::endl; return 1; } std::ifstream picture; picture.open(value.toCString(), std::ios::in | std::ios::binary); std::stringstream buffer; buffer << picture.rdbuf(); picture.close(); TagLib::String buf(buffer.str()); TagLib::ByteVector data(buf.data(TagLib::String::Latin1)); TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a") ? "image/png" : "image/jpeg"; TagLib::String description(argv[i + 2]); f.setComplexProperties("PICTURE", { { {"data", data}, {"pictureType", "Front Cover"}, {"mimeType", mimeType}, {"description", description} } }); } else { // empty value, remove pictures f.setComplexProperties("PICTURE", {}); } } else { usage(); } break; } default: usage(); break; } } i += numArgsConsumed; } else usage(); } for(auto &f : fileList) f.save(); return 0; } �����������������������������������������������������������taglib-2.0.2/taglib-config.cmake��������������������������������������������������������������������0000664�0000000�0000000�00000002034�14662262111�0016464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh usage() { echo "usage: $0 [OPTIONS]" cat << EOH options: [--libs] [--cflags] [--version] [--prefix] EOH exit 1 } # Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot. prefix= exec_prefix= if test -z "$prefix"; then includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ else includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ fi if test -z "$exec_prefix"; then libdir=@CMAKE_INSTALL_FULL_LIBDIR@ else libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ fi flags="" if test $# -eq 0 ; then usage fi while test $# -gt 0 do case $1 in --libs) flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@" ;; --cflags) flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@" ;; --version) echo @TAGLIB_LIB_VERSION_STRING@ ;; --prefix) echo ${prefix:-@CMAKE_INSTALL_PREFIX@} ;; *) echo "$0: unknown option $1" echo usage ;; esac shift done if test -n "$flags" then echo $flags fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib-config.cmake.in�����������������������������������������������������������������0000664�0000000�0000000�00000000417�14662262111�0017074�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/taglib-targets.cmake") set(TAGLIB_FOUND ${TagLib_FOUND}) set(TAGLIB_VERSION ${TagLib_VERSION}) check_required_components("TagLib") if(NOT TARGET TagLib::TagLib) add_library(TagLib::TagLib ALIAS TagLib::tag) endif() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib-config.cmd.cmake����������������������������������������������������������������0000664�0000000�0000000�00000002246�14662262111�0017233�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@echo off goto beginning * * It is what it is, you can do with it as you please. * * Just don't blame me if it teaches your computer to smoke! * * -Enjoy * fh :)_~ * :beginning if /i "%1#" == "--libs#" goto doit if /i "%1#" == "--cflags#" goto doit if /i "%1#" == "--version#" goto doit if /i "%1#" == "--prefix#" goto doit echo usage: %0 [OPTIONS] echo [--libs] echo [--cflags] echo [--version] echo [--prefix] goto theend * * NOTE: Windows does not assume libraries are prefixed with 'lib'. * NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process * to allow for static, shared or debug builds. * It would be preferable if the top level CMakeLists.txt provided the library name during config. ?? :doit if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag${TAGLIB_INSTALL_SUFFIX} if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX} if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING} if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX} :theend ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib.pc.cmake������������������������������������������������������������������������0000664�0000000�0000000�00000000534�14662262111�0015625�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@CMAKE_PC_LIBDIR@ includedir=@CMAKE_PC_INCLUDEDIR@ Name: TagLib Description: Audio meta-data library Requires: Version: @TAGLIB_LIB_VERSION_STRING@ Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@ Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/��������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014220�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/CMakeLists.txt������������������������������������������������������������������0000664�0000000�0000000�00000023435�14662262111�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/toolkit ${CMAKE_CURRENT_SOURCE_DIR}/asf ${CMAKE_CURRENT_SOURCE_DIR}/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/ogg ${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac ${CMAKE_CURRENT_SOURCE_DIR}/flac ${CMAKE_CURRENT_SOURCE_DIR}/mpc ${CMAKE_CURRENT_SOURCE_DIR}/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex ${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/ape ${CMAKE_CURRENT_SOURCE_DIR}/wavpack ${CMAKE_CURRENT_SOURCE_DIR}/trueaudio ${CMAKE_CURRENT_SOURCE_DIR}/riff ${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff ${CMAKE_CURRENT_SOURCE_DIR}/riff/wav ${CMAKE_CURRENT_SOURCE_DIR}/mod ${CMAKE_CURRENT_SOURCE_DIR}/s3m ${CMAKE_CURRENT_SOURCE_DIR}/it ${CMAKE_CURRENT_SOURCE_DIR}/xm ${CMAKE_CURRENT_SOURCE_DIR}/dsf ${CMAKE_CURRENT_SOURCE_DIR}/dsdiff ) set(tag_HDRS tag.h fileref.h audioproperties.h taglib_export.h toolkit/taglib.h toolkit/tstring.h toolkit/tlist.h toolkit/tlist.tcc toolkit/tstringlist.h toolkit/tbytevector.h toolkit/tbytevectorlist.h toolkit/tvariant.h toolkit/tbytevectorstream.h toolkit/tiostream.h toolkit/tfile.h toolkit/tfilestream.h toolkit/tmap.h toolkit/tmap.tcc toolkit/tpicturetype.h toolkit/tpropertymap.h toolkit/tdebuglistener.h toolkit/tversionnumber.h mpeg/mpegfile.h mpeg/mpegproperties.h mpeg/mpegheader.h mpeg/xingheader.h mpeg/id3v1/id3v1tag.h mpeg/id3v1/id3v1genres.h mpeg/id3v2/id3v2.h mpeg/id3v2/id3v2extendedheader.h mpeg/id3v2/id3v2frame.h mpeg/id3v2/id3v2header.h mpeg/id3v2/id3v2synchdata.h mpeg/id3v2/id3v2footer.h mpeg/id3v2/id3v2framefactory.h mpeg/id3v2/id3v2tag.h mpeg/id3v2/frames/attachedpictureframe.h mpeg/id3v2/frames/commentsframe.h mpeg/id3v2/frames/eventtimingcodesframe.h mpeg/id3v2/frames/generalencapsulatedobjectframe.h mpeg/id3v2/frames/ownershipframe.h mpeg/id3v2/frames/popularimeterframe.h mpeg/id3v2/frames/privateframe.h mpeg/id3v2/frames/relativevolumeframe.h mpeg/id3v2/frames/synchronizedlyricsframe.h mpeg/id3v2/frames/textidentificationframe.h mpeg/id3v2/frames/uniquefileidentifierframe.h mpeg/id3v2/frames/unknownframe.h mpeg/id3v2/frames/unsynchronizedlyricsframe.h mpeg/id3v2/frames/urllinkframe.h mpeg/id3v2/frames/chapterframe.h mpeg/id3v2/frames/tableofcontentsframe.h mpeg/id3v2/frames/podcastframe.h ogg/oggfile.h ogg/oggpage.h ogg/oggpageheader.h ogg/xiphcomment.h ogg/vorbis/vorbisfile.h ogg/vorbis/vorbisproperties.h ogg/flac/oggflacfile.h ogg/speex/speexfile.h ogg/speex/speexproperties.h ogg/opus/opusfile.h ogg/opus/opusproperties.h flac/flacfile.h flac/flacpicture.h flac/flacproperties.h flac/flacmetadatablock.h ape/apefile.h ape/apeproperties.h ape/apetag.h ape/apefooter.h ape/apeitem.h mpc/mpcfile.h mpc/mpcproperties.h wavpack/wavpackfile.h wavpack/wavpackproperties.h trueaudio/trueaudiofile.h trueaudio/trueaudioproperties.h riff/rifffile.h riff/aiff/aifffile.h riff/aiff/aiffproperties.h riff/wav/wavfile.h riff/wav/wavproperties.h riff/wav/infotag.h asf/asffile.h asf/asfproperties.h asf/asftag.h asf/asfattribute.h asf/asfpicture.h mp4/mp4file.h mp4/mp4atom.h mp4/mp4tag.h mp4/mp4item.h mp4/mp4properties.h mp4/mp4coverart.h mp4/mp4itemfactory.h mod/modfilebase.h mod/modfile.h mod/modtag.h mod/modproperties.h it/itfile.h it/itproperties.h s3m/s3mfile.h s3m/s3mproperties.h xm/xmfile.h xm/xmproperties.h dsf/dsffile.h dsf/dsfproperties.h dsdiff/dsdifffile.h dsdiff/dsdiffproperties.h dsdiff/dsdiffdiintag.h ) set(mpeg_SRCS mpeg/mpegfile.cpp mpeg/mpegproperties.cpp mpeg/mpegheader.cpp mpeg/xingheader.cpp ) set(id3v1_SRCS mpeg/id3v1/id3v1tag.cpp mpeg/id3v1/id3v1genres.cpp ) set(id3v2_SRCS mpeg/id3v2/id3v2framefactory.cpp mpeg/id3v2/id3v2synchdata.cpp mpeg/id3v2/id3v2tag.cpp mpeg/id3v2/id3v2header.cpp mpeg/id3v2/id3v2frame.cpp mpeg/id3v2/id3v2footer.cpp mpeg/id3v2/id3v2extendedheader.cpp ) set(frames_SRCS mpeg/id3v2/frames/attachedpictureframe.cpp mpeg/id3v2/frames/commentsframe.cpp mpeg/id3v2/frames/eventtimingcodesframe.cpp mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp mpeg/id3v2/frames/ownershipframe.cpp mpeg/id3v2/frames/popularimeterframe.cpp mpeg/id3v2/frames/privateframe.cpp mpeg/id3v2/frames/relativevolumeframe.cpp mpeg/id3v2/frames/synchronizedlyricsframe.cpp mpeg/id3v2/frames/textidentificationframe.cpp mpeg/id3v2/frames/uniquefileidentifierframe.cpp mpeg/id3v2/frames/unknownframe.cpp mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp mpeg/id3v2/frames/urllinkframe.cpp mpeg/id3v2/frames/chapterframe.cpp mpeg/id3v2/frames/tableofcontentsframe.cpp mpeg/id3v2/frames/podcastframe.cpp ) set(ogg_SRCS ogg/oggfile.cpp ogg/oggpage.cpp ogg/oggpageheader.cpp ogg/xiphcomment.cpp ) set(vorbis_SRCS ogg/vorbis/vorbisfile.cpp ogg/vorbis/vorbisproperties.cpp ) set(flacs_SRCS flac/flacfile.cpp flac/flacpicture.cpp flac/flacproperties.cpp flac/flacmetadatablock.cpp flac/flacunknownmetadatablock.cpp ) set(oggflacs_SRCS ogg/flac/oggflacfile.cpp ) set(mpc_SRCS mpc/mpcfile.cpp mpc/mpcproperties.cpp ) set(mp4_SRCS mp4/mp4file.cpp mp4/mp4atom.cpp mp4/mp4tag.cpp mp4/mp4item.cpp mp4/mp4properties.cpp mp4/mp4coverart.cpp mp4/mp4itemfactory.cpp ) set(ape_SRCS ape/apetag.cpp ape/apefooter.cpp ape/apeitem.cpp ape/apefile.cpp ape/apeproperties.cpp ) set(wavpack_SRCS wavpack/wavpackfile.cpp wavpack/wavpackproperties.cpp ) set(speex_SRCS ogg/speex/speexfile.cpp ogg/speex/speexproperties.cpp ) set(opus_SRCS ogg/opus/opusfile.cpp ogg/opus/opusproperties.cpp ) set(trueaudio_SRCS trueaudio/trueaudiofile.cpp trueaudio/trueaudioproperties.cpp ) set(asf_SRCS asf/asftag.cpp asf/asffile.cpp asf/asfproperties.cpp asf/asfattribute.cpp asf/asfpicture.cpp ) set(riff_SRCS riff/rifffile.cpp ) set(aiff_SRCS riff/aiff/aifffile.cpp riff/aiff/aiffproperties.cpp ) set(wav_SRCS riff/wav/wavfile.cpp riff/wav/wavproperties.cpp riff/wav/infotag.cpp ) set(mod_SRCS mod/modfilebase.cpp mod/modfile.cpp mod/modtag.cpp mod/modproperties.cpp ) set(s3m_SRCS s3m/s3mfile.cpp s3m/s3mproperties.cpp ) set(it_SRCS it/itfile.cpp it/itproperties.cpp ) set(xm_SRCS xm/xmfile.cpp xm/xmproperties.cpp ) set(dsf_SRCS dsf/dsffile.cpp dsf/dsfproperties.cpp ) set(dsdiff_SRCS dsdiff/dsdifffile.cpp dsdiff/dsdiffproperties.cpp dsdiff/dsdiffdiintag.cpp ) set(toolkit_SRCS toolkit/tstring.cpp toolkit/tstringlist.cpp toolkit/tbytevector.cpp toolkit/tbytevectorlist.cpp toolkit/tvariant.cpp toolkit/tbytevectorstream.cpp toolkit/tiostream.cpp toolkit/tfile.cpp toolkit/tfilestream.cpp toolkit/tdebug.cpp toolkit/tpicturetype.cpp toolkit/tpropertymap.cpp toolkit/tdebuglistener.cpp toolkit/tzlib.cpp toolkit/tversionnumber.cpp ) set(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS} ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} ${dsf_SRCS} ${dsdiff_SRCS} tag.cpp tagunion.cpp fileref.cpp audioproperties.cpp tagutils.cpp ) add_library(tag ${tag_LIB_SRCS} ${tag_HDRS}) target_include_directories(tag INTERFACE $<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}> ) target_link_libraries(tag PRIVATE $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>> $<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB> ) set_target_properties(tag PROPERTIES VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH} SOVERSION ${TAGLIB_SOVERSION_MAJOR} INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR} DEFINE_SYMBOL MAKE_TAGLIB_LIB INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}" PUBLIC_HEADER "${tag_HDRS}" ) if(VISIBILITY_HIDDEN) set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden) set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden) endif() if(BUILD_FRAMEWORK) unset(INSTALL_NAME_DIR) set_target_properties(tag PROPERTIES FRAMEWORK TRUE MACOSX_RPATH 1 VERSION "A" SOVERSION "A" ) endif() if(TAGLIB_INSTALL_SUFFIX) if(BUILD_SHARED_LIBS) set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}") else() set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}") endif() set_target_properties(tag PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX}) endif() install(TARGETS tag EXPORT taglibTargets FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX} ) configure_package_config_file( "${PROJECT_SOURCE_DIR}/taglib-config.cmake.in" "${PROJECT_BINARY_DIR}/taglib-config.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX} ) write_basic_package_version_file( "${PROJECT_BINARY_DIR}/taglib-config-version.cmake" VERSION "${TAGLIB_LIB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion ) install(EXPORT taglibTargets FILE taglib-targets.cmake NAMESPACE TagLib:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX} ) install(FILES "${PROJECT_BINARY_DIR}/taglib-config.cmake" "${PROJECT_BINARY_DIR}/taglib-config-version.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX} ) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014765�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/ape-tag-format.txt����������������������������������������������������������0000664�0000000�0000000�00000020113�14662262111�0020327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������================================================================================ = APE Tag Specification, Version 2.000 ================================================================================ Original Content (C) 2004, Frank Klemm <frank.klemm@elster.offl.uni-jena.de> Formatting / Editing (C) 2004, Scott Wheeler <wheeler@kde.org> ================================================================================ = Contents ================================================================================ 1 - APE Tag General Structure 2 - APE Tag Header / Footer Format 3 - APE Tag Flags 4 - APE Tag Item Format 5 - APE Tag Item Supported Keys 6 - APE Tag Item Content 7 - Data Types 7.1 - Data Types / UTF-8 7.2 - Data Types / Dates 7.3 - Data Types / Timestamps ================================================================================ = 1 - APE Tag General Structure ================================================================================ Member of Basic Components of SV8 Stream Note: It is strongly recommended that the data size be stored in the tags. The size should normally be in the roughly one kilobyte, never more than 8 kilobytes. Larger data should be stored externally using link entries. Linked data is much easier to process by normal programs, so for instance JPEG data should not be included inside the audio file. APE Tag Version 2.000 (with header, recommended): /================================\ | APE Tag Header | 32 bytes | |-------------------|------------| | APE Tag Item 1 | > 10 bytes | | APE Tag Item 2 | > 10 bytes | | APE Tag Item n-1 | > 10 bytes | | APE Tag Item n | > 10 bytes | |-------------------|------------| | APE Tag Footer | 32 bytes | \================================/ APE tag items should be sorted ascending by size. When streaming, parts of the APE tag may be dropped to reduce the danger of drop outs between tracks. This is not required, but is strongly recommended. It would be desirable for the items to be sorted by importance / size, but this is not feasible. This convention should only be broken when adding less important small items and it is not desirable to rewrite the entire tag. An APE tag at the end of a file (the recommended location) must have at least a footer; an APE tag at the beginning of a file (strongly discouraged) must have at least a header. APE Tag Version 1.000 (without header, deprecated) /================================\ | APE Tag Item 1 | > 10 bytes | | APE Tag Item 2 | > 10 bytes | | APE Tag Item n-1 | > 10 bytes | | APE Tag Item n | > 10 bytes | |-------------------|------------| | APE Tag Footer | 32 bytes | \================================/ ================================================================================ = 2 - APE Tag Header / Footer Format ================================================================================ Contains number, length and attributes of all tag items Header and Footer are different in 1 bit in the Tags Flags to distinguish between them. Member of APE Tag 2.0 /===========================================================================\ | Preamble | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' } | |----------------|---------|------------------------------------------------| | Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000 | |----------------|---------|------------------------------------------------| | Tag Size | 4 bytes | Tag size in bytes including footer and all tag | | | | items excluding the header (for 1.000 | | | | compatibility) | |----------------|---------|------------------------------------------------| | Item Count | 4 bytes | Number of items in the tag | |----------------|---------|------------------------------------------------| | Tag Flags | 4 bytes | Global flags | |----------------|---------|------------------------------------------------| | Reserved | 8 bytes | Must be zeroed | \===========================================================================/ ================================================================================ = 3 - APE Tag Flags ================================================================================ The general flag structure for either items or headers / footers is the same. Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are item specific. Note: APE Tags from Version 1.0 do not use any of the following. All flags in that version are zeroed and ignored when reading. /=================================================================\ | Contains Header | Bit 31 | 1 - has header | 0 - no header | |-----------------|-------------|---------------------------------| | Contains Footer | Bit 30 | 1 - has footer | 0 - no footer | |-----------------|-------------|---------------------------------| | Is Header | Bit 29 | 1 - is header | 0 - is footer | |-----------------|-------------|---------------------------------| | Undefined | Bits 28 - 3 | Undefined, must be zeroed | |-----------------|-------------|---------------------------------| | Encoding | Bits 2 - 1 | 00 - UTF-8 | | | | 01 - Binary Data * | | | | 10 - External Reference ** | | | | 11 - Reserved | |-----------------|-------------|---------------------------------| | Read Only | Bit 0 | 1 - read only | 0 - read/write | \=================================================================/ (*) Should be ignored by tools for editing text values (**) Allowed external reference formats: - http://host/directory/filename.ext - ftp://host/directory/filename.ext - filename.ext - /directory/filename.ext - DRIVE:/directory/filename.ext Note: External references are also UTF-8 encoded. ================================================================================ = 4 - APE Tag Item Format ================================================================================ APE Tag Items are stored as key-value pairs. APE Tags Item Key are case sensitive, however it is illegal to use keys which only differ in case and it is recommended that tag reading not be case sensitive. Every key can only occur (at most) once. It is not possible to repeat a key to signify updated contents. Tags can be partially or completely repeated in the streaming format. This makes it possible to display an artist and / or title if it was missed at the beginning of the stream. It is recommended that the important information like artist, album and title should occur approximately every 2 minutes in the stream and again 5 to 10 seconds before the end. However, care should be tak en not to replicate this information too often or during passages with high bitrate demands to avoid unnecessary drop-outs. /==============================================================================\ | Content Size | 4 bytes | Length of the value in bytes | |----------------|---------------|---------------------------------------------| | Flags | 4 bytes | Item flags | |----------------|---------------|---------------------------------------------| | Key | 2 - 255 bytes | Item key | |----------------|---------------|---------------------------------------------| | Key Terminator | 1 byte | Null byte that indicates the end of the key | |----------------|---------------|---------------------------------------------| | Value | variable | Content (formatted according to the flags) | \==============================================================================/ ================================================================================ Sections 5 - 7 haven't yet been converted from: http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apefile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000017002�14662262111�0017076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Alex Novichkov email : novichko@atnet.ru copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com (original WavPack implementation) copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "apefile.h" #include "tdebug.h" #include "tpropertymap.h" #include "id3v1tag.h" #include "id3v2header.h" #include "tagunion.h" #include "tagutils.h" #include "apetag.h" #include "apefooter.h" using namespace TagLib; namespace { enum { ApeAPEIndex = 0, ApeID3v1Index = 1 }; } // namespace class APE::File::FilePrivate { public: offset_t APELocation { -1 }; long APESize { 0 }; offset_t ID3v1Location { -1 }; std::unique_ptr<ID3v2::Header> ID3v2Header; offset_t ID3v2Location { -1 }; long ID3v2Size { 0 }; TagUnion tag; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool APE::File::isSupported(IOStream *stream) { // An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true); return buffer.find("MAC ") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } APE::File::~File() = default; TagLib::Tag *APE::File::tag() const { return &d->tag; } PropertyMap APE::File::properties() const { return d->tag.properties(); } void APE::File::removeUnsupportedProperties(const StringList &properties) { d->tag.removeUnsupportedProperties(properties); } PropertyMap APE::File::setProperties(const PropertyMap &properties) { if(ID3v1Tag()) ID3v1Tag()->setProperties(properties); return APETag(true)->setProperties(properties); } APE::Properties *APE::File::audioProperties() const { return d->properties.get(); } bool APE::File::save() { if(readOnly()) { debug("APE::File::save() -- File is read only."); return false; } // Update ID3v1 tag if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. if(d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; } } // Update APE tag if(APETag() && !APETag()->isEmpty()) { // APE tag is not empty. Update the old one or create a new one. if(d->APELocation < 0) { if(d->ID3v1Location >= 0) d->APELocation = d->ID3v1Location; else d->APELocation = length(); } const ByteVector data = APETag()->render(); insert(data, d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->APESize; d->APESize = data.size(); } else { // APE tag is empty. Remove the old one. if(d->APELocation >= 0) { removeBlock(d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location -= d->APESize; d->APELocation = -1; d->APESize = 0; } } return true; } ID3v1::Tag *APE::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(ApeID3v1Index, create); } APE::Tag *APE::File::APETag(bool create) { return d->tag.access<APE::Tag>(ApeAPEIndex, create); } void APE::File::strip(int tags) { if(tags & ID3v1) d->tag.set(ApeID3v1Index, nullptr); if(tags & APE) d->tag.set(ApeAPEIndex, nullptr); if(!ID3v1Tag()) APETag(true); } bool APE::File::hasAPETag() const { return d->APELocation >= 0; } bool APE::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void APE::File::read(bool readProperties) { // Look for an ID3v2 tag d->ID3v2Location = Utils::findID3v2(this); if(d->ID3v2Location >= 0) { seek(d->ID3v2Location); d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size())); d->ID3v2Size = d->ID3v2Header->completeTagSize(); } // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); // Look for an APE tag d->APELocation = Utils::findAPE(this, d->ID3v1Location); if(d->APELocation >= 0) { d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation)); d->APESize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; } if(d->ID3v1Location < 0) APETag(true); // Look for APE audio properties if(readProperties) { offset_t streamLength; if(d->APELocation >= 0) streamLength = d->APELocation; else if(d->ID3v1Location >= 0) streamLength = d->ID3v1Location; else streamLength = length(); if(d->ID3v2Location >= 0) { seek(d->ID3v2Location + d->ID3v2Size); streamLength -= d->ID3v2Location + d->ID3v2Size; } else { seek(0); } d->properties = std::make_unique<Properties>(this, streamLength); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apefile.h�������������������������������������������������������������������0000664�0000000�0000000�00000020606�14662262111�0016547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Alex Novichkov email : novichko@atnet.ru copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com (original WavPack implementation) copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_APEFILE_H #define TAGLIB_APEFILE_H #include "tfile.h" #include "taglib_export.h" #include "apeproperties.h" namespace TagLib { class Tag; namespace ID3v1 { class Tag; } namespace APE { class Tag; } //! An implementation of APE metadata /*! * This is an implementation of APE metadata. * * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * properties from the file. */ namespace APE { //! An implementation of TagLib::File with APE specific methods /*! * This implements and provides an interface for APE files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to APE files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches APE tags. APE = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs an APE file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs an APE file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * or a combination of the two. */ TagLib::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * If the file contains both an APE and an ID3v1 tag, only APE * will be converted to the PropertyMap. */ PropertyMap properties() const override; /*! * Removes unsupported properties. Forwards to the actual Tag's * removeUnsupportedProperties() function. */ void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. * Creates an APEv2 tag if necessary. A potentially existing ID3v1 * tag will be updated as well. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the APE::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. * * \note According to the official Monkey's Audio SDK, an APE file * can only have either ID3V1 or APE tags, so a parameter is used here. */ bool save() override; /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is \c true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the APE::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the APE tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag <b>is still</b> owned by the APE::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasAPETag() */ APE::Tag *APETag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \note This will also invalidate pointers to the tags * as their memory will be freed. * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an APE tag. * * \see APETag() */ bool hasAPETag() const; /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the given \a stream can be opened as an APE * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace APE } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apefooter.cpp���������������������������������������������������������������0000664�0000000�0000000�00000012450�14662262111�0017457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen (C) 2002 - 2008 by Scott Wheeler (id3v2header.cpp) email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "apefooter.h" #include <bitset> #include "taglib.h" using namespace TagLib; using namespace APE; class APE::Footer::FooterPrivate { public: unsigned int version { 0 }; bool footerPresent { true }; bool headerPresent { false }; bool isHeader { false }; unsigned int itemCount { 0 }; unsigned int tagSize { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// unsigned int APE::Footer::size() { return 32; } ByteVector APE::Footer::fileIdentifier() { return ByteVector("APETAGEX"); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// APE::Footer::Footer() : d(std::make_unique<FooterPrivate>()) { } APE::Footer::Footer(const ByteVector &data) : d(std::make_unique<FooterPrivate>()) { parse(data); } APE::Footer::~Footer() = default; unsigned int APE::Footer::version() const { return d->version; } bool APE::Footer::headerPresent() const { return d->headerPresent; } bool APE::Footer::footerPresent() const { return d->footerPresent; } bool APE::Footer::isHeader() const { return d->isHeader; } void APE::Footer::setHeaderPresent(bool b) const { d->headerPresent = b; } unsigned int APE::Footer::itemCount() const { return d->itemCount; } void APE::Footer::setItemCount(unsigned int s) { d->itemCount = s; } unsigned int APE::Footer::tagSize() const { return d->tagSize; } unsigned int APE::Footer::completeTagSize() const { if(d->headerPresent) return d->tagSize + size(); return d->tagSize; } void APE::Footer::setTagSize(unsigned int s) { d->tagSize = s; } void APE::Footer::setData(const ByteVector &data) { parse(data); } ByteVector APE::Footer::renderFooter() const { return render(false); } ByteVector APE::Footer::renderHeader() const { if(!d->headerPresent) return ByteVector(); return render(true); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void APE::Footer::parse(const ByteVector &data) { if(data.size() < size()) return; // The first eight bytes, data[0..7], are the File Identifier, "APETAGEX". // Read the version number d->version = data.toUInt(8, false); // Read the tag size d->tagSize = data.toUInt(12, false); // Read the item count d->itemCount = data.toUInt(16, false); // Read the flags std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false))); d->headerPresent = flags[31]; d->footerPresent = !flags[30]; d->isHeader = flags[29]; } ByteVector APE::Footer::render(bool isHeader) const { ByteVector v; // add the file identifier -- "APETAGEX" v.append(fileIdentifier()); // add the version number -- we always render a 2.000 tag regardless of what // the tag originally was. v.append(ByteVector::fromUInt(2000, false)); // add the tag size v.append(ByteVector::fromUInt(d->tagSize, false)); // add the item count v.append(ByteVector::fromUInt(d->itemCount, false)); // render and add the flags std::bitset<32> flags; flags[31] = d->headerPresent; flags[30] = false; // footer is always present flags[29] = isHeader; v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false)); // add the reserved 64bit v.append(ByteVector::fromLongLong(0)); return v; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apefooter.h�����������������������������������������������������������������0000664�0000000�0000000�00000012343�14662262111�0017125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_APEFOOTER_H #define TAGLIB_APEFOOTER_H #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { namespace APE { //! An implementation of APE footers /*! * This class implements APE footers (and headers). It attempts to follow, both * semantically and programmatically, the structure specified in * the APE v2.0 standard. The API is based on the properties of APE footer and * headers specified there. */ class TAGLIB_EXPORT Footer { public: /*! * Constructs an empty APE footer. */ Footer(); /*! * Constructs an APE footer based on \a data. parse() is called * immediately. */ Footer(const ByteVector &data); /*! * Destroys the footer. */ ~Footer(); Footer(const Footer &) = delete; Footer &operator=(const Footer &) = delete; /*! * Returns the version number. (Note: This is the 1000 or 2000.) */ unsigned int version() const; /*! * Returns \c true if a header is present in the tag. */ bool headerPresent() const; /*! * Returns \c true if a footer is present in the tag. */ bool footerPresent() const; /*! * Returns \c true if this is actually the header. */ bool isHeader() const; /*! * Sets whether the header should be rendered or not */ void setHeaderPresent(bool b) const; /*! * Returns the number of items in the tag. */ unsigned int itemCount() const; /*! * Set the item count to \a s. * \see itemCount() */ void setItemCount(unsigned int s); /*! * Returns the tag size in bytes. This is the size of the frame content and footer. * The size of the \e entire tag will be this plus the header size, if present. * * \see completeTagSize() */ unsigned int tagSize() const; /*! * Returns the tag size, including if present, the header * size. * * \see tagSize() */ unsigned int completeTagSize() const; /*! * Set the tag size to \a s. * \see tagSize() */ void setTagSize(unsigned int s); /*! * Returns the size of the footer. Presently this is always 32 bytes. */ static unsigned int size(); /*! * Returns the string used to identify an APE tag inside of a file. * Presently this is always "APETAGEX". */ static ByteVector fileIdentifier(); /*! * Sets the data that will be used as the footer. 32 bytes, * starting from \a data will be used. */ void setData(const ByteVector &data); /*! * Renders the footer back to binary format. */ ByteVector renderFooter() const; /*! * Renders the header corresponding to the footer. If headerPresent() is * \c false, it returns an empty ByteVector. */ ByteVector renderHeader() const; protected: /*! * Called by setData() to parse the footer data. It makes this information * available through the public API. */ void parse(const ByteVector &data); /*! * Called by renderFooter and renderHeader */ ByteVector render(bool isHeader) const; private: class FooterPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FooterPrivate> d; }; } // namespace APE } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apeitem.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000014315�14662262111�0017121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "apeitem.h" #include <utility> #include <numeric> #include "tdebug.h" using namespace TagLib; using namespace APE; class APE::Item::ItemPrivate { public: Item::ItemTypes type { Text }; String key; ByteVector value; StringList text; bool readOnly { false }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// APE::Item::Item() : d(std::make_unique<ItemPrivate>()) { } APE::Item::Item(const String &key, const StringList &values) : d(std::make_unique<ItemPrivate>()) { d->key = key; d->text = values; } APE::Item::Item(const String &key, const ByteVector &value, bool binary) : d(std::make_unique<ItemPrivate>()) { d->key = key; if(binary) { d->type = Binary; d->value = value; } else { d->text.append(value); } } APE::Item::Item(const Item &item) : d(std::make_unique<ItemPrivate>(*item.d)) { } APE::Item::~Item() = default; Item &APE::Item::operator=(const Item &item) { Item(item).swap(*this); return *this; } void APE::Item::swap(Item &item) noexcept { using std::swap; swap(d, item.d); } void APE::Item::setReadOnly(bool readOnly) { d->readOnly = readOnly; } bool APE::Item::isReadOnly() const { return d->readOnly; } void APE::Item::setType(APE::Item::ItemTypes val) { d->type = val; } APE::Item::ItemTypes APE::Item::type() const { return d->type; } String APE::Item::key() const { return d->key; } ByteVector APE::Item::binaryData() const { return d->value; } void APE::Item::setBinaryData(const ByteVector &value) { d->type = Binary; d->value = value; d->text.clear(); } void APE::Item::setKey(const String &key) { d->key = key; } void APE::Item::setValue(const String &value) { d->type = Text; d->text = value; d->value.clear(); } void APE::Item::setValues(const StringList &values) { d->type = Text; d->text = values; d->value.clear(); } void APE::Item::appendValue(const String &value) { d->type = Text; d->text.append(value); d->value.clear(); } void APE::Item::appendValues(const StringList &values) { d->type = Text; d->text.append(values); d->value.clear(); } int APE::Item::size() const { int result = 8 + d->key.size() + 1; switch(d->type) { case Text: if(!d->text.isEmpty()) { result = std::accumulate(d->text.cbegin(), d->text.cend(), result, [](int sz, const String &t) { return sz + 1 + t.data(String::UTF8).size(); }) - 1; } break; case Binary: case Locator: result += d->value.size(); break; } return result; } StringList APE::Item::values() const { return d->text; } String APE::Item::toString() const { if(d->type == Text && !isEmpty()) return d->text.front(); return String(); } bool APE::Item::isEmpty() const { switch(d->type) { case Text: if(d->text.isEmpty()) return true; return d->text.size() == 1 && d->text.front().isEmpty(); case Binary: case Locator: return d->value.isEmpty(); default: return false; } } void APE::Item::parse(const ByteVector &data) { // 11 bytes is the minimum size for an APE item if(data.size() < 11) { debug("APE::Item::parse() -- no data in item"); return; } const unsigned int valueLength = data.toUInt(0, false); const unsigned int flags = data.toUInt(4, false); // An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8. // We assume that the validity of the given key has been checked. d->key = String(&data[8], String::Latin1); const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength); setReadOnly(flags & 1); setType(static_cast<ItemTypes>((flags >> 1) & 3)); if(Text == d->type) d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8); else d->value = val; } ByteVector APE::Item::render() const { ByteVector data; unsigned int flags = (d->readOnly ? 1 : 0) | (d->type << 1); ByteVector val; if(isEmpty()) return data; if(d->type == Text) { auto it = d->text.cbegin(); val.append(it->data(String::UTF8)); for(it = std::next(it); it != d->text.cend(); ++it) { val.append('\0'); val.append(it->data(String::UTF8)); } d->value = val; } else val.append(d->value); data.append(ByteVector::fromUInt(val.size(), false)); data.append(ByteVector::fromUInt(flags, false)); data.append(d->key.data(String::Latin1)); data.append(ByteVector('\0')); data.append(val); return data; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apeitem.h�������������������������������������������������������������������0000664�0000000�0000000�00000013555�14662262111�0016573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_APEITEM_H #define TAGLIB_APEITEM_H #include "tbytevector.h" #include "tstring.h" #include "tstringlist.h" namespace TagLib { namespace APE { //! An implementation of APE-items /*! * This class provides the features of items in the APEv2 standard. */ class TAGLIB_EXPORT Item { public: /*! * Enum of types an Item can have. The value of 3 is reserved. */ enum ItemTypes { //! Item contains text information coded in UTF-8 Text = 0, //! Item contains binary information Binary = 1, //! Item is a locator of external stored information Locator = 2 }; /*! * Constructs an empty item. */ Item(); /*! * Constructs a text item with \a key and \a values. */ Item(const String &key, const StringList &values); /*! * Constructs an item with \a key and \a value. * If \a binary is \c true a Binary item will be created, otherwise \a value will be interpreted as text */ Item(const String &key, const ByteVector &value, bool binary); /*! * Construct an item as a copy of \a item. */ Item(const Item &item); /*! * Destroys the item. */ ~Item(); /*! * Copies the contents of \a item into this item. */ Item &operator=(const Item &item); /*! * Exchanges the content of this item with the content of \a item. */ void swap(Item &item) noexcept; /*! * Returns the key. */ String key() const; /*! * Returns the binary value. * If the item type is not \a Binary, always returns an empty ByteVector. */ ByteVector binaryData() const; /*! * Set the binary value to \a value * The item's type will also be set to \a Binary */ void setBinaryData(const ByteVector &value); /*! * Sets the key for the item to \a key. */ void setKey(const String &key); /*! * Sets the text value of the item to \a value and clears any previous contents. * * \see toString() */ void setValue(const String &value); /*! * Sets the text value of the item to the list of values in \a value and clears * any previous contents. * * \see values() */ void setValues(const StringList &values); /*! * Appends \a value to create (or extend) the current list of text values. * * \see toString() */ void appendValue(const String &value); /*! * Appends \a values to extend the current list of text values. * * \see values() */ void appendValues(const StringList &values); /*! * Returns the size of the full item. */ int size() const; /*! * Returns the value as a single string. In case of multiple strings, * the first is returned. If the data type is not \a Text, always returns * an empty String. */ String toString() const; /*! * Returns the list of text values. If the data type is not \a Text, always * returns an empty StringList. */ StringList values() const; /*! * Render the item to a ByteVector. */ ByteVector render() const; /*! * Parse the item from the ByteVector \a data. */ void parse(const ByteVector& data); /*! * Set the item to read-only. */ void setReadOnly(bool readOnly); /*! * Return \c true if the item is read-only. */ bool isReadOnly() const; /*! * Sets the type of the item to \a val. * * \see ItemTypes */ void setType(ItemTypes val); /*! * Returns the type of the item. */ ItemTypes type() const; /*! * Returns \c false if the item has any real content. */ bool isEmpty() const; private: class ItemPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ItemPrivate> d; }; } // namespace APE } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apeproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000015104�14662262111�0020354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Alex Novichkov email : novichko@atnet.ru copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com (original WavPack 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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "apeproperties.h" #include "tdebug.h" #include "apefile.h" #include "apefooter.h" #include "apetag.h" using namespace TagLib; class APE::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int version { 0 }; int bitsPerSample { 0 }; unsigned int sampleFrames { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file, streamLength); } APE::Properties::~Properties() = default; int APE::Properties::lengthInMilliseconds() const { return d->length; } int APE::Properties::bitrate() const { return d->bitrate; } int APE::Properties::sampleRate() const { return d->sampleRate; } int APE::Properties::channels() const { return d->channels; } int APE::Properties::version() const { return d->version; } int APE::Properties::bitsPerSample() const { return d->bitsPerSample; } unsigned int APE::Properties::sampleFrames() const { return d->sampleFrames; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// namespace { int headerVersion(const ByteVector &header) { if(header.size() < 6 || !header.startsWith("MAC ")) return -1; return header.toUShort(4, false); } } // namespace void APE::Properties::read(File *file, offset_t streamLength) { // First, we assume that the file pointer is set at the first descriptor. offset_t offset = file->tell(); int vers = headerVersion(file->readBlock(6)); // Next, we look for the descriptor. if(vers < 0) { offset = file->find("MAC ", offset); file->seek(offset); vers = headerVersion(file->readBlock(6)); } if(vers < 0) { debug("APE::Properties::read() -- APE descriptor not found"); return; } d->version = vers; if(d->version >= 3980) analyzeCurrent(file); else analyzeOld(file); if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } } void APE::Properties::analyzeCurrent(File *file) { // Read the descriptor file->seek(2, File::Current); const ByteVector descriptor = file->readBlock(44); if(descriptor.size() < 44) { debug("APE::Properties::analyzeCurrent() -- descriptor is too short."); return; } if(const unsigned int descriptorBytes = descriptor.toUInt(0, false); descriptorBytes - 52 > 0) file->seek(descriptorBytes - 52, File::Current); // Read the header const ByteVector header = file->readBlock(24); if(header.size() < 24) { debug("APE::Properties::analyzeCurrent() -- MAC header is too short."); return; } // Get the APE info d->channels = header.toShort(18, false); d->sampleRate = header.toUInt(20, false); d->bitsPerSample = header.toShort(16, false); const unsigned int totalFrames = header.toUInt(12, false); if(totalFrames == 0) return; const unsigned int blocksPerFrame = header.toUInt(4, false); const unsigned int finalFrameBlocks = header.toUInt(8, false); d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; } void APE::Properties::analyzeOld(File *file) { const ByteVector header = file->readBlock(26); if(header.size() < 26) { debug("APE::Properties::analyzeOld() -- MAC header is too short."); return; } const unsigned int totalFrames = header.toUInt(18, false); // Fail on 0 length APE files (catches non-finalized APE files) if(totalFrames == 0) return; const short compressionLevel = header.toShort(0, false); unsigned int blocksPerFrame; if(d->version >= 3950) blocksPerFrame = 73728 * 4; else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000)) blocksPerFrame = 73728; else blocksPerFrame = 9216; // Get the APE info d->channels = header.toShort(4, false); d->sampleRate = header.toUInt(6, false); const unsigned int finalFrameBlocks = header.toUInt(22, false); d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; // Get the bit depth from the RIFF-fmt chunk. file->seek(16, File::Current); const ByteVector fmt = file->readBlock(28); if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) { debug("APE::Properties::analyzeOld() -- fmt header is too short."); return; } d->bitsPerSample = fmt.toShort(26, false); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apeproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000007442�14662262111�0020027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Alex Novichkov email : novichko@atnet.ru copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com (original WavPack 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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_APEPROPERTIES_H #define TAGLIB_APEPROPERTIES_H #include "taglib.h" #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace APE { class File; //! An implementation of audio property reading for APE /*! * This reads the data from an APE stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of APE::Properties with the data read from the * APE::File \a file. */ Properties(File *file, offset_t streamLength, ReadStyle style = Average); /*! * Destroys this APE::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns the total number of audio samples in file. */ unsigned int sampleFrames() const; /*! * Returns the APE version. */ int version() const; private: void read(File *file, offset_t streamLength); void analyzeCurrent(File *file); void analyzeOld(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace APE } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apetag.cpp������������������������������������������������������������������0000664�0000000�0000000�00000033400�14662262111�0016732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130) // Sun Studio finds multiple specializations of Map because // it considers specializations with and without class types // to be different; this define forces Map to use only the // specialization with the class keyword. #define WANT_CLASS_INSTANTIATION_OF_MAP (1) #endif #include "apetag.h" #include <array> #include <utility> #include "tdebug.h" #include "tfile.h" #include "tpropertymap.h" #include "apefooter.h" #include "apeitem.h" using namespace TagLib; using namespace APE; namespace { constexpr unsigned int MinKeyLength = 2; constexpr unsigned int MaxKeyLength = 255; const String FRONT_COVER("COVER ART (FRONT)"); const String BACK_COVER("COVER ART (BACK)"); bool isKeyValid(const ByteVector &key) { static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" }; // only allow printable ASCII including space (32..126) return std::none_of(key.begin(), key.end(), [](unsigned char c) { return c < 32 || c > 126; }) && std::none_of(invalidKeys.begin(), invalidKeys.end(), [upperKey = String(key).upper()](auto k) { return upperKey == k; }); } } // namespace class APE::Tag::TagPrivate { public: File *file { nullptr }; offset_t footerLocation { 0 }; Footer footer; ItemListMap itemListMap; }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// APE::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) : d(std::make_unique<TagPrivate>()) { d->file = file; d->footerLocation = footerLocation; read(); } APE::Tag::~Tag() = default; ByteVector APE::Tag::fileIdentifier() { return ByteVector::fromCString("APETAGEX"); } String APE::Tag::title() const { Item val = d->itemListMap.value("TITLE"); return val.isEmpty() ? String() : joinTagValues(val.values()); } String APE::Tag::artist() const { Item val = d->itemListMap.value("ARTIST"); return val.isEmpty() ? String() : joinTagValues(val.values()); } String APE::Tag::album() const { Item val = d->itemListMap.value("ALBUM"); return val.isEmpty() ? String() : joinTagValues(val.values()); } String APE::Tag::comment() const { Item val = d->itemListMap.value("COMMENT"); return val.isEmpty() ? String() : joinTagValues(val.values()); } String APE::Tag::genre() const { Item val = d->itemListMap.value("GENRE"); return val.isEmpty() ? String() : joinTagValues(val.values()); } unsigned int APE::Tag::year() const { Item val = d->itemListMap.value("YEAR"); return val.isEmpty() ? 0 : val.toString().toInt(); } unsigned int APE::Tag::track() const { Item val = d->itemListMap.value("TRACK"); return val.isEmpty() ? 0 : val.toString().toInt(); } void APE::Tag::setTitle(const String &s) { addValue("TITLE", s, true); } void APE::Tag::setArtist(const String &s) { addValue("ARTIST", s, true); } void APE::Tag::setAlbum(const String &s) { addValue("ALBUM", s, true); } void APE::Tag::setComment(const String &s) { addValue("COMMENT", s, true); } void APE::Tag::setGenre(const String &s) { addValue("GENRE", s, true); } void APE::Tag::setYear(unsigned int i) { if(i == 0) removeItem("YEAR"); else addValue("YEAR", String::number(i), true); } void APE::Tag::setTrack(unsigned int i) { if(i == 0) removeItem("TRACK"); else addValue("TRACK", String::number(i), true); } namespace { // conversions of tag keys between what we use in PropertyMap and what's usual // for APE tags // usual, APE constexpr std::array keyConversions { std::pair("TRACKNUMBER", "TRACK"), std::pair("DATE", "YEAR"), std::pair("ALBUMARTIST", "ALBUM ARTIST"), std::pair("DISCNUMBER", "DISC"), std::pair("REMIXER", "MIXARTIST"), std::pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"), std::pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"), }; } // namespace PropertyMap APE::Tag::properties() const { PropertyMap properties; for(const auto &[tag, item] : std::as_const(itemListMap())) { // if the item is Binary or Locator, or if the key is an invalid string, // add to unsupportedData if(String tagName = tag.upper(); item.type() != Item::Text || tagName.isEmpty()) { properties.addUnsupportedData(tag); } else { // Some tags need to be handled specially for(const auto &[k, t] : keyConversions) { if(tagName == t) tagName = k; } properties[tagName].append(item.values()); } } return properties; } void APE::Tag::removeUnsupportedProperties(const StringList &properties) { for(const auto &property : properties) removeItem(property); } PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) { PropertyMap props(origProps); // make a local copy that can be modified // see comment in properties() for(const auto &[k, t] : keyConversions) if(props.contains(k)) { props.insert(t, props[k]); props.erase(k); } // first check if tags need to be removed completely StringList toRemove; for(const auto &[k, t] : std::as_const(itemListMap())) { // only remove if a) key is valid, b) type is text, c) key not contained in new properties if(String key = k.upper(); !key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key)) toRemove.append(k); } for(const auto &item : std::as_const(toRemove)) removeItem(item); // now sync in the "forward direction" PropertyMap invalid; for(const auto &[tagName, val] : std::as_const(props)) { if(!checkKey(tagName)) invalid.insert(tagName, val); else if(!itemListMap().contains(tagName) || itemListMap()[tagName].values() != val) { if(val.isEmpty()) removeItem(tagName); else { addValue(tagName, *val.begin(), true); for(auto it = std::next(val.begin()); it != val.end(); ++it) addValue(tagName, *it, false); } } } return invalid; } StringList APE::Tag::complexPropertyKeys() const { StringList keys; if(d->itemListMap.contains(FRONT_COVER) || d->itemListMap.contains(BACK_COVER)) { keys.append("PICTURE"); } return keys; } List<VariantMap> APE::Tag::complexProperties(const String &key) const { List<VariantMap> props; if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER); for(const auto &itemName: itemNames) { if(d->itemListMap.contains(itemName)) { if(Item picture = d->itemListMap.value(itemName); picture.type() == Item::Binary) { ByteVector data = picture.binaryData(); // Do not search for a description if the first byte could start JPG or PNG // data. int index = data.isEmpty() || data.at(0) == '\xff' || data.at(0) == '\x89' ? -1 : data.find('\0'); String description; if(index >= 0) { description = String(data.mid(0, index), String::UTF8); data = data.mid(index + 1); } VariantMap property; property.insert("data", data); if(!description.isEmpty()) { property.insert("description", description); } property.insert("pictureType", itemName == BACK_COVER ? "Back Cover" : "Front Cover"); props.append(property); } } } } return props; } bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { removeItem(FRONT_COVER); removeItem(BACK_COVER); auto frontItems = List<Item>(); auto backItems = List<Item>(); for(const auto &property : value) { auto data = property.value("description").value<String>().data(String::UTF8) .append('\0') .append(property.value("data").value<ByteVector>()); auto pictureType = property.value("pictureType").value<String>(); Item item; item.setType(Item::Binary); item.setBinaryData(data); if(pictureType == "Back Cover") { item.setKey(BACK_COVER); backItems.append(item); } else if(pictureType == "Front Cover") { item.setKey(FRONT_COVER); // prioritize pictures with correct type frontItems.prepend(item); } else { item.setKey(FRONT_COVER); frontItems.append(item); } } if(!frontItems.isEmpty()) { setItem(FRONT_COVER, frontItems.front()); } if(!backItems.isEmpty()) { setItem(BACK_COVER, backItems.front()); } } else { return false; } return true; } bool APE::Tag::checkKey(const String &key) { if(key.size() < MinKeyLength || key.size() > MaxKeyLength) return false; return isKeyValid(key.data(String::UTF8)); } APE::Footer *APE::Tag::footer() const { return &d->footer; } const APE::ItemListMap& APE::Tag::itemListMap() const { return d->itemListMap; } void APE::Tag::removeItem(const String &key) { d->itemListMap.erase(key.upper()); } void APE::Tag::addValue(const String &key, const String &value, bool replace) { if(replace) removeItem(key); if(value.isEmpty()) return; // Text items may contain more than one value. // Binary or locator items may have only one value, hence always replaced. auto it = d->itemListMap.find(key.upper()); if(it != d->itemListMap.end() && it->second.type() == Item::Text) it->second.appendValue(value); else setItem(key, Item(key, value)); } void APE::Tag::setData(const String &key, const ByteVector &value) { removeItem(key); if(value.isEmpty()) return; setItem(key, Item(key, value, true)); } void APE::Tag::setItem(const String &key, const Item &item) { if(!checkKey(key)) { debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key."); return; } d->itemListMap[key.upper()] = item; } bool APE::Tag::isEmpty() const { return d->itemListMap.isEmpty(); } //////////////////////////////////////////////////////////////////////////////// // protected methods //////////////////////////////////////////////////////////////////////////////// void APE::Tag::read() { if(d->file && d->file->isValid()) { d->file->seek(d->footerLocation); d->footer.setData(d->file->readBlock(Footer::size())); if(d->footer.tagSize() <= Footer::size() || d->footer.tagSize() > static_cast<unsigned long>(d->file->length())) return; d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize()); parse(d->file->readBlock(d->footer.tagSize() - Footer::size())); } } ByteVector APE::Tag::render() const { ByteVector data; unsigned int itemCount = 0; for(const auto &[_, list] : std::as_const(d->itemListMap)) { data.append(list.render()); itemCount++; } d->footer.setItemCount(itemCount); d->footer.setTagSize(data.size() + Footer::size()); d->footer.setHeaderPresent(true); return d->footer.renderHeader() + data + d->footer.renderFooter(); } void APE::Tag::parse(const ByteVector &data) { // 11 bytes is the minimum size for an APE item if(data.size() < 11) return; unsigned int pos = 0; for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) { const int nullPos = data.find('\0', pos + 8); if(nullPos < 0) { debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing."); return; } const unsigned int keyLength = nullPos - pos - 8; const unsigned int valLength = data.toUInt(pos, false); if(valLength >= data.size() || pos > data.size() - valLength) { debug("APE::Tag::parse() - Invalid val length. Stopped parsing."); return; } if(keyLength >= MinKeyLength && keyLength <= MaxKeyLength && isKeyValid(data.mid(pos + 8, keyLength))) { APE::Item item; item.parse(data.mid(pos)); d->itemListMap.insert(item.key().upper(), item); } else { debug("APE::Tag::parse() - Skipped an item due to an invalid key."); } pos += keyLength + valLength + 9; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ape/apetag.h��������������������������������������������������������������������0000664�0000000�0000000�00000016765�14662262111�0016416�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_APETAG_H #define TAGLIB_APETAG_H #include "tbytevector.h" #include "tmap.h" #include "tstring.h" #include "taglib.h" #include "taglib_export.h" #include "tag.h" #include "apeitem.h" namespace TagLib { class File; //! An implementation of the APE tagging format namespace APE { class Footer; /*! * A mapping between a list of item names, or keys, and the associated item. * * \see APE::Tag::itemListMap() */ using ItemListMap = Map<const String, Item>; //! An APE tag implementation class TAGLIB_EXPORT Tag : public TagLib::Tag { public: /*! * Create an APE tag with default values. */ Tag(); /*! * Create an APE tag and parse the data in \a file with APE footer at * \a tagOffset. */ Tag(TagLib::File *file, offset_t footerLocation); /*! * Destroys this Tag instance. */ ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; /*! * Renders the in memory values to a ByteVector suitable for writing to * the file. */ ByteVector render() const; /*! * Returns the string "APETAGEX" suitable for usage in locating the tag in a * file. */ static ByteVector fileIdentifier(); // Reimplementations. String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; /*! * Implements the unified tag dictionary interface -- export function. * APE tags are perfectly compatible with the dictionary interface because they * support both arbitrary tag names and multiple values. Currently only * APE items of type *Text* are handled by the dictionary interface; all *Binary* * and *Locator* items will be put into the unsupportedData list and can be * deleted on request using removeUnsupportedProperties(). The same happens * to Text items if their key is invalid for PropertyMap (which should actually * never happen). * * The only conversion done by this export function is to rename the APE tags * TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively, * (and a few other keys, see \ref p_propertymapping) * in order to be compliant with the names used in other formats. */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified tag dictionary interface -- import function. The same * comments as for the export function apply; additionally note that the APE tag * specification requires keys to have between 2 and 16 printable ASCII characters * with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+". */ PropertyMap setProperties(const PropertyMap &) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; /*! * Check if the given String is a valid APE tag key. */ static bool checkKey(const String&); /*! * Returns a pointer to the tag's footer. */ Footer *footer() const; /*! * Returns a reference to the item list map. This is an ItemListMap of * all of the items in the tag. * * This is the most powerful structure for accessing the items of the tag. * * APE tags are case-insensitive, all keys in this map have been converted * to upper case. * * \warning You should not modify this data structure directly, instead * use setItem() and removeItem(). */ const ItemListMap &itemListMap() const; /*! * Removes the \a key item from the tag */ void removeItem(const String &key); /*! * Adds to the text item specified by \a key the data \a value. If \a replace * is \c true, then all of the other values on the same key will be removed * first. If a binary item exists for \a key it will be removed first. */ void addValue(const String &key, const String &value, bool replace = true); /*! * Set the binary data for the key specified by \a item to \a value * This will convert the item to type \a Binary if it isn't already and * all of the other values on the same key will be removed. */ void setData(const String &key, const ByteVector &value); /*! * Sets the \a key item to the value of \a item. If an item with the \a key is already * present, it will be replaced. */ void setItem(const String &key, const Item &item); /*! * Returns \c true if the tag does not contain any data. */ bool isEmpty() const override; protected: /*! * Reads from the file specified in the constructor. */ void read(); /*! * Parses the body of the tag in \a data. */ void parse(const ByteVector &data); private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace APE } // namespace TagLib #endif �����������taglib-2.0.2/taglib/asf/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014771�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfattribute.cpp������������������������������������������������������������0000664�0000000�0000000�00000020277�14662262111�0020202�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asfattribute.h" #include "tdebug.h" #include "asffile.h" #include "asfutils.h" using namespace TagLib; class ASF::Attribute::AttributePrivate { public: AttributePrivate() : pictureValue(ASF::Picture::fromInvalid()) { } AttributeTypes type { UnicodeType }; String stringValue; ByteVector byteVectorValue; ASF::Picture pictureValue; unsigned long long numericValue { 0 }; int stream { 0 }; int language { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ASF::Attribute::Attribute() : d(std::make_shared<AttributePrivate>()) { d->type = UnicodeType; } ASF::Attribute::Attribute(const ASF::Attribute &) = default; ASF::Attribute::Attribute(const String &value) : d(std::make_shared<AttributePrivate>()) { d->type = UnicodeType; d->stringValue = value; } ASF::Attribute::Attribute(const ByteVector &value) : d(std::make_shared<AttributePrivate>()) { d->type = BytesType; d->byteVectorValue = value; } ASF::Attribute::Attribute(const ASF::Picture &value) : d(std::make_shared<AttributePrivate>()) { d->type = BytesType; d->pictureValue = value; } ASF::Attribute::Attribute(unsigned int value) : d(std::make_shared<AttributePrivate>()) { d->type = DWordType; d->numericValue = value; } ASF::Attribute::Attribute(unsigned long long value) : d(std::make_shared<AttributePrivate>()) { d->type = QWordType; d->numericValue = value; } ASF::Attribute::Attribute(unsigned short value) : d(std::make_shared<AttributePrivate>()) { d->type = WordType; d->numericValue = value; } ASF::Attribute::Attribute(bool value) : d(std::make_shared<AttributePrivate>()) { d->type = BoolType; d->numericValue = value; } ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default; void ASF::Attribute::swap(Attribute &other) noexcept { using std::swap; swap(d, other.d); } ASF::Attribute::~Attribute() = default; ASF::Attribute::AttributeTypes ASF::Attribute::type() const { return d->type; } String ASF::Attribute::toString() const { return d->stringValue; } ByteVector ASF::Attribute::toByteVector() const { if(d->pictureValue.isValid()) return d->pictureValue.render(); return d->byteVectorValue; } unsigned short ASF::Attribute::toBool() const { return d->numericValue ? 1 : 0; } unsigned short ASF::Attribute::toUShort() const { return static_cast<unsigned short>(d->numericValue); } unsigned int ASF::Attribute::toUInt() const { return static_cast<unsigned int>(d->numericValue); } unsigned long long ASF::Attribute::toULongLong() const { return d->numericValue; } ASF::Picture ASF::Attribute::toPicture() const { return d->pictureValue; } String ASF::Attribute::parse(ASF::File &file, int kind) { unsigned int size, nameLength; String name; d->pictureValue = Picture::fromInvalid(); // extended content descriptor if(kind == 0) { nameLength = readWORD(&file); name = readString(&file, nameLength); d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file)); size = readWORD(&file); } // metadata & metadata library else { int temp = readWORD(&file); // metadata library if(kind == 2) { d->language = temp; } d->stream = readWORD(&file); nameLength = readWORD(&file); d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file)); size = readDWORD(&file); name = readString(&file, nameLength); } if(kind != 2 && size > 65535) { debug("ASF::Attribute::parse() -- Value larger than 64kB"); } switch(d->type) { case WordType: d->numericValue = readWORD(&file); break; case BoolType: if(kind == 0) { d->numericValue = readDWORD(&file) != 0; } else { d->numericValue = readWORD(&file) != 0; } break; case DWordType: d->numericValue = readDWORD(&file); break; case QWordType: d->numericValue = readQWORD(&file); break; case UnicodeType: d->stringValue = readString(&file, size); break; case BytesType: case GuidType: d->byteVectorValue = file.readBlock(size); break; } if(d->type == BytesType && name == "WM/Picture") { d->pictureValue.parse(d->byteVectorValue); if(d->pictureValue.isValid()) { d->byteVectorValue.clear(); } } return name; } int ASF::Attribute::dataSize() const { switch (d->type) { case WordType: return 2; case BoolType: case DWordType: return 4; case QWordType: return 5; case UnicodeType: return d->stringValue.size() * 2 + 2; case BytesType: if(d->pictureValue.isValid()) { return d->pictureValue.dataSize(); } return d->byteVectorValue.size(); case GuidType: return d->byteVectorValue.size(); } return 0; } ByteVector ASF::Attribute::render(const String &name, int kind) const { ByteVector data; switch (d->type) { case WordType: data.append(ByteVector::fromShort(toUShort(), false)); break; case BoolType: if(kind == 0) { data.append(ByteVector::fromUInt(toBool(), false)); } else { data.append(ByteVector::fromShort(toBool(), false)); } break; case DWordType: data.append(ByteVector::fromUInt(toUInt(), false)); break; case QWordType: data.append(ByteVector::fromLongLong(toULongLong(), false)); break; case UnicodeType: data.append(renderString(d->stringValue)); break; case BytesType: if(d->pictureValue.isValid()) { data.append(d->pictureValue.render()); } else { data.append(d->byteVectorValue); } break; case GuidType: data.append(d->byteVectorValue); break; } if(kind == 0) { data = renderString(name, true) + ByteVector::fromShort(static_cast<int>(d->type), false) + ByteVector::fromShort(data.size(), false) + data; } else { ByteVector nameData = renderString(name); data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) + ByteVector::fromShort(d->stream, false) + ByteVector::fromShort(nameData.size(), false) + ByteVector::fromShort(static_cast<int>(d->type), false) + ByteVector::fromUInt(data.size(), false) + nameData + data; } return data; } int ASF::Attribute::language() const { return d->language; } void ASF::Attribute::setLanguage(int value) { d->language = value; } int ASF::Attribute::stream() const { return d->stream; } void ASF::Attribute::setStream(int value) { d->stream = value; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfattribute.h��������������������������������������������������������������0000664�0000000�0000000�00000014013�14662262111�0017636�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ASFATTRIBUTE_H #define TAGLIB_ASFATTRIBUTE_H #include "tstring.h" #include "tbytevector.h" #include "taglib_export.h" #include "asfpicture.h" namespace TagLib { namespace ASF { class File; class Picture; //! Attribute of ASF (WMA) metadata class TAGLIB_EXPORT Attribute { public: /*! * Enum of types an Attribute can have. */ enum AttributeTypes { UnicodeType = 0, BytesType = 1, BoolType = 2, DWordType = 3, QWordType = 4, WordType = 5, GuidType = 6 }; /*! * Constructs an empty attribute. */ Attribute(); /*! * Constructs an attribute with \a key and a UnicodeType \a value. */ Attribute(const String &value); /*! * Constructs an attribute with \a key and a BytesType \a value. */ Attribute(const ByteVector &value); /*! * Constructs an attribute with \a key and a Picture \a value. * * This attribute is compatible with the ID3 frame, APIC. The ID3 specification for the APIC frame stipulates that, * while there may be any number of APIC frames associated with a file, * only one may be of type 1 and only one may be of type 2. * * The specification also states that the description of the picture can be no longer than 64 characters, but can be empty. * WM/Picture attributes added with TagLib::ASF are not automatically validated to conform to ID3 specifications. * You must add code in your application to perform validations if you want to maintain complete compatibility with ID3. */ Attribute(const Picture &value); /*! * Constructs an attribute with \a key and a DWordType \a value. */ Attribute(unsigned int value); /*! * Constructs an attribute with \a key and a QWordType \a value. */ Attribute(unsigned long long value); /*! * Constructs an attribute with \a key and a WordType \a value. */ Attribute(unsigned short value); /*! * Constructs an attribute with \a key and a BoolType \a value. */ Attribute(bool value); /*! * Construct an attribute as a copy of \a other. */ Attribute(const Attribute &other); /*! * Copies the contents of \a other into this item. */ Attribute &operator=(const Attribute &other); /*! * Exchanges the content of the Attribute with the content of \a other. */ void swap(Attribute &other) noexcept; /*! * Destroys the attribute. */ ~Attribute(); /*! * Returns the type of the value. */ AttributeTypes type() const; /*! * Returns the BoolType \a value. */ unsigned short toBool() const; /*! * Returns the WordType \a value. */ unsigned short toUShort() const; /*! * Returns the DWordType \a value. */ unsigned int toUInt() const; /*! * Returns the QWordType \a value. */ unsigned long long toULongLong() const; /*! * Returns the UnicodeType \a value. */ String toString() const; /*! * Returns the BytesType \a value. */ ByteVector toByteVector() const; /*! * Returns the Picture \a value. */ Picture toPicture() const; /*! * Returns the language number, or 0 is no stream number was set. */ int language() const; /*! * Sets the language number. */ void setLanguage(int value); /*! * Returns the stream number, or 0 is no stream number was set. */ int stream() const; /*! * Sets the stream number. */ void setStream(int value); #ifndef DO_NOT_DOCUMENT /* THIS IS PRIVATE, DON'T TOUCH IT! */ String parse(ASF::File &file, int kind = 0); #endif //! Returns the size of the stored data int dataSize() const; private: friend class File; ByteVector render(const String &name, int kind = 0) const; class AttributePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<AttributePrivate> d; }; } // namespace ASF } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asffile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000052166�14662262111�0017120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asffile.h" #include <utility> #include "tdebug.h" #include "tpropertymap.h" #include "tbytevectorlist.h" #include "tagutils.h" #include "asftag.h" #include "asfproperties.h" #include "asfutils.h" using namespace TagLib; class ASF::File::FilePrivate { public: class BaseObject; class UnknownObject; class FilePropertiesObject; class StreamPropertiesObject; class ContentDescriptionObject; class ExtendedContentDescriptionObject; class HeaderExtensionObject; class CodecListObject; class MetadataObject; class MetadataLibraryObject; FilePrivate() { objects.setAutoDelete(true); } ~FilePrivate() = default; FilePrivate(const FilePrivate &) = delete; FilePrivate &operator=(const FilePrivate &) = delete; unsigned long long headerSize { 0 }; std::unique_ptr<ASF::Tag> tag; std::unique_ptr<ASF::Properties> properties; List<BaseObject *> objects; ContentDescriptionObject *contentDescriptionObject { nullptr }; ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr }; HeaderExtensionObject *headerExtensionObject { nullptr }; MetadataObject *metadataObject { nullptr }; MetadataLibraryObject *metadataLibraryObject { nullptr }; }; namespace { const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16); const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16); const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16); const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16); const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16); const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16); const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16); } // namespace class ASF::File::FilePrivate::BaseObject { public: ByteVector data; virtual ~BaseObject() = default; virtual ByteVector guid() const = 0; virtual void parse(ASF::File *file, long long size); virtual ByteVector render(ASF::File *file); }; class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject { ByteVector myGuid; public: UnknownObject(const ByteVector &guid); ByteVector guid() const override; }; class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject { public: ByteVector guid() const override; void parse(ASF::File *file, long long size) override; }; class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject { public: ByteVector guid() const override; void parse(ASF::File *file, long long size) override; }; class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { public: ByteVector guid() const override; void parse(ASF::File *file, long long size) override; ByteVector render(ASF::File *file) override; }; class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; ByteVector guid() const override; void parse(ASF::File *file, long long size) override; ByteVector render(ASF::File *file) override; }; class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; ByteVector guid() const override; void parse(ASF::File *file, long long size) override; ByteVector render(ASF::File *file) override; }; class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; ByteVector guid() const override; void parse(ASF::File *file, long long size) override; ByteVector render(ASF::File *file) override; }; class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject { public: List<ASF::File::FilePrivate::BaseObject *> objects; HeaderExtensionObject(); ByteVector guid() const override; void parse(ASF::File *file, long long size) override; ByteVector render(ASF::File *file) override; }; class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject { public: ByteVector guid() const override; void parse(ASF::File *file, long long size) override; private: enum CodecType { Video = 0x0001, Audio = 0x0002, Unknown = 0xFFFF }; }; void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size) { data.clear(); if(size > 24 && size <= file->length()) data = file->readBlock(size - 24); else data = ByteVector(); } ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/) { return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data; } ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) { } ByteVector ASF::File::FilePrivate::UnknownObject::guid() const { return myGuid; } ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const { return filePropertiesGuid; } void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size) { BaseObject::parse(file, size); if(data.size() < 64) { debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short."); return; } const long long duration = data.toLongLong(40, false); const long long preroll = data.toLongLong(56, false); file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5)); } ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const { return streamPropertiesGuid; } void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size) { BaseObject::parse(file, size); if(data.size() < 70) { debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short."); return; } file->d->properties->setCodec(data.toUShort(54, false)); file->d->properties->setChannels(data.toUShort(56, false)); file->d->properties->setSampleRate(data.toUInt(58, false)); file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5)); file->d->properties->setBitsPerSample(data.toUShort(68, false)); } ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const { return contentDescriptionGuid; } void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/) { const int titleLength = readWORD(file); const int artistLength = readWORD(file); const int copyrightLength = readWORD(file); const int commentLength = readWORD(file); const int ratingLength = readWORD(file); file->d->tag->setTitle(readString(file,titleLength)); file->d->tag->setArtist(readString(file,artistLength)); file->d->tag->setCopyright(readString(file,copyrightLength)); file->d->tag->setComment(readString(file,commentLength)); file->d->tag->setRating(readString(file,ratingLength)); } ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file) { const ByteVector v1 = renderString(file->d->tag->title()); const ByteVector v2 = renderString(file->d->tag->artist()); const ByteVector v3 = renderString(file->d->tag->copyright()); const ByteVector v4 = renderString(file->d->tag->comment()); const ByteVector v5 = renderString(file->d->tag->rating()); data.clear(); data.append(ByteVector::fromShort(v1.size(), false)); data.append(ByteVector::fromShort(v2.size(), false)); data.append(ByteVector::fromShort(v3.size(), false)); data.append(ByteVector::fromShort(v4.size(), false)); data.append(ByteVector::fromShort(v5.size(), false)); data.append(v1); data.append(v2); data.append(v3); data.append(v4); data.append(v5); return BaseObject::render(file); } ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const { return extendedContentDescriptionGuid; } void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/) { int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file); file->d->tag->addAttribute(name, attribute); } } ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(attributeData.toByteVector("")); return BaseObject::render(file); } ByteVector ASF::File::FilePrivate::MetadataObject::guid() const { return metadataGuid; } void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/) { int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file, 1); file->d->tag->addAttribute(name, attribute); } } ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(attributeData.toByteVector("")); return BaseObject::render(file); } ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const { return metadataLibraryGuid; } void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/) { int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file, 2); file->d->tag->addAttribute(name, attribute); } } ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); data.append(attributeData.toByteVector("")); return BaseObject::render(file); } ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject() { objects.setAutoDelete(true); } ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const { return headerExtensionGuid; } void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/) { file->seek(18, File::Current); long long dataSize = readDWORD(file); long long dataPos = 0; while(dataPos < dataSize) { ByteVector uid = file->readBlock(16); if(uid.size() != 16) { file->setValid(false); break; } bool ok; long long size = readQWORD(file, &ok); if(!ok || size < 0 || size > dataSize - dataPos) { file->setValid(false); break; } BaseObject *obj; if(uid == metadataGuid) { file->d->metadataObject = new MetadataObject(); obj = file->d->metadataObject; } else if(uid == metadataLibraryGuid) { file->d->metadataLibraryObject = new MetadataLibraryObject(); obj = file->d->metadataLibraryObject; } else { obj = new UnknownObject(uid); } obj->parse(file, size); objects.append(obj); dataPos += size; } } ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file) { data.clear(); for(const auto &object : std::as_const(objects)) { data.append(object->render(file)); } data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data; return BaseObject::render(file); } ByteVector ASF::File::FilePrivate::CodecListObject::guid() const { return codecListGuid; } void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size) { BaseObject::parse(file, size); if(data.size() <= 20) { debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short."); return; } unsigned int pos = 16; const int count = data.toUInt(pos, false); pos += 4; for(int i = 0; i < count; ++i) { if(pos >= data.size()) break; const auto type = static_cast<CodecType>(data.toUShort(pos, false)); pos += 2; int nameLength = data.toUShort(pos, false); pos += 2; const unsigned int namePos = pos; pos += nameLength * 2; const int descLength = data.toUShort(pos, false); pos += 2; const unsigned int descPos = pos; pos += descLength * 2; const int infoLength = data.toUShort(pos, false); pos += 2 + infoLength * 2; if(type == CodecListObject::Audio) { // First audio codec found. const String name(data.mid(namePos, nameLength * 2), String::UTF16LE); file->d->properties->setCodecName(name.stripWhiteSpace()); const String desc(data.mid(descPos, descLength * 2), String::UTF16LE); file->d->properties->setCodecDescription(desc.stripWhiteSpace()); break; } } } //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool ASF::File::isSupported(IOStream *stream) { // An ASF file has to start with the designated GUID. const ByteVector id = Utils::readHeader(stream, 16, false); return id == headerGuid; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ASF::File::File(FileName file, bool, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(); } ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(); } ASF::File::~File() = default; ASF::Tag *ASF::File::tag() const { return d->tag.get(); } PropertyMap ASF::File::properties() const { return d->tag->properties(); } void ASF::File::removeUnsupportedProperties(const StringList &properties) { d->tag->removeUnsupportedProperties(properties); } PropertyMap ASF::File::setProperties(const PropertyMap &properties) { return d->tag->setProperties(properties); } ASF::Properties *ASF::File::audioProperties() const { return d->properties.get(); } bool ASF::File::save() { if(readOnly()) { debug("ASF::File::save() -- File is read only."); return false; } if(!isValid()) { debug("ASF::File::save() -- Trying to save invalid file."); return false; } if(!d->contentDescriptionObject) { d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject(); d->objects.append(d->contentDescriptionObject); } if(!d->extendedContentDescriptionObject) { d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject(); d->objects.append(d->extendedContentDescriptionObject); } if(!d->headerExtensionObject) { d->headerExtensionObject = new FilePrivate::HeaderExtensionObject(); d->objects.append(d->headerExtensionObject); } if(!d->metadataObject) { d->metadataObject = new FilePrivate::MetadataObject(); d->headerExtensionObject->objects.append(d->metadataObject); } if(!d->metadataLibraryObject) { d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject(); d->headerExtensionObject->objects.append(d->metadataLibraryObject); } d->extendedContentDescriptionObject->attributeData.clear(); d->metadataObject->attributeData.clear(); d->metadataLibraryObject->attributeData.clear(); for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) { bool inExtendedContentDescriptionObject = false; bool inMetadataObject = false; for(const auto &attribute : attributes) { const bool largeValue = attribute.dataSize() > 65535; const bool guid = attribute.type() == Attribute::GuidType; if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) { d->extendedContentDescriptionObject->attributeData.append(attribute.render(name)); inExtendedContentDescriptionObject = true; } else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) { d->metadataObject->attributeData.append(attribute.render(name, 1)); inMetadataObject = true; } else { d->metadataLibraryObject->attributeData.append(attribute.render(name, 2)); } } } ByteVector data; for(const auto &object : std::as_const(d->objects)) { data.append(object->render(this)); } seek(16); writeBlock(ByteVector::fromLongLong(data.size() + 30, false)); writeBlock(ByteVector::fromUInt(d->objects.size(), false)); writeBlock(ByteVector("\x01\x02", 2)); insert(data, 30, static_cast<unsigned long>(d->headerSize - 30)); d->headerSize = data.size() + 30; return true; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void ASF::File::read() { if(!isValid()) return; if(readBlock(16) != headerGuid) { debug("ASF::File::read(): Not an ASF file."); setValid(false); return; } d->tag = std::make_unique<ASF::Tag>(); d->properties = std::make_unique<ASF::Properties>(); bool ok; d->headerSize = readQWORD(this, &ok); if(!ok) { setValid(false); return; } int numObjects = readDWORD(this, &ok); if(!ok) { setValid(false); return; } seek(2, Current); FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr; FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr; for(int i = 0; i < numObjects; i++) { const ByteVector guid = readBlock(16); if(guid.size() != 16) { setValid(false); break; } auto size = readQWORD(this, &ok); if(!ok) { setValid(false); break; } FilePrivate::BaseObject *obj; if(guid == filePropertiesGuid) { filePropertiesObject = new FilePrivate::FilePropertiesObject(); obj = filePropertiesObject; } else if(guid == streamPropertiesGuid) { streamPropertiesObject = new FilePrivate::StreamPropertiesObject(); obj = streamPropertiesObject; } else if(guid == contentDescriptionGuid) { d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject(); obj = d->contentDescriptionObject; } else if(guid == extendedContentDescriptionGuid) { d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject(); obj = d->extendedContentDescriptionObject; } else if(guid == headerExtensionGuid) { d->headerExtensionObject = new FilePrivate::HeaderExtensionObject(); obj = d->headerExtensionObject; } else if(guid == codecListGuid) { obj = new FilePrivate::CodecListObject(); } else { if(guid == contentEncryptionGuid || guid == extendedContentEncryptionGuid || guid == advancedContentEncryptionGuid) { d->properties->setEncrypted(true); } obj = new FilePrivate::UnknownObject(guid); } obj->parse(this, size); d->objects.append(obj); } if(!filePropertiesObject || !streamPropertiesObject) { debug("ASF::File::read(): Missing mandatory header objects."); setValid(false); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asffile.h�������������������������������������������������������������������0000664�0000000�0000000�00000012043�14662262111�0016553�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ASFFILE_H #define TAGLIB_ASFFILE_H #include "tfile.h" #include "taglib_export.h" #include "tag.h" #include "asfproperties.h" #include "asftag.h" namespace TagLib { //! An implementation of ASF (WMA) metadata namespace ASF { //! An implementation of TagLib::File with ASF specific methods /*! * This implements and provides an interface for ASF files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to ASF files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * Constructs an ASF file from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs an ASF file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns a pointer to the ASF tag of the file. * * ASF::Tag implements the tag interface, so this serves as the * reimplementation of TagLib::File::tag(). * * \note The Tag <b>is still</b> owned by the ASF::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ Tag *tag() const override; /*! * Implements the unified property interface -- export function. */ PropertyMap properties() const override; /*! * Removes unsupported properties. Forwards to the actual Tag's * removeUnsupportedProperties() function. */ void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the ASF audio properties for this file. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns whether or not the given \a stream can be opened as an ASF * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace ASF } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfpicture.cpp��������������������������������������������������������������0000664�0000000�0000000�00000010440�14662262111�0017641�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Anton Sergunov email : setosha@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asfpicture.h" #include "asffile.h" #include "asfutils.h" using namespace TagLib; class ASF::Picture::PicturePrivate { public: bool valid; Type type; String mimeType; String description; ByteVector picture; }; //////////////////////////////////////////////////////////////////////////////// // Picture class members //////////////////////////////////////////////////////////////////////////////// ASF::Picture::Picture() : d(std::make_shared<PicturePrivate>()) { d->valid = true; } ASF::Picture::Picture(const Picture &) = default; ASF::Picture::~Picture() = default; bool ASF::Picture::isValid() const { return d->valid; } String ASF::Picture::mimeType() const { return d->mimeType; } void ASF::Picture::setMimeType(const String &value) { d->mimeType = value; } ASF::Picture::Type ASF::Picture::type() const { return d->type; } void ASF::Picture::setType(const ASF::Picture::Type& t) { d->type = t; } String ASF::Picture::description() const { return d->description; } void ASF::Picture::setDescription(const String &desc) { d->description = desc; } ByteVector ASF::Picture::picture() const { return d->picture; } void ASF::Picture::setPicture(const ByteVector &p) { d->picture = p; } int ASF::Picture::dataSize() const { return 9 + (d->mimeType.length() + d->description.length()) * 2 + d->picture.size(); } ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default; void ASF::Picture::swap(Picture &other) noexcept { using std::swap; swap(d, other.d); } ByteVector ASF::Picture::render() const { if(!isValid()) return ByteVector(); return ByteVector(static_cast<char>(d->type)) + ByteVector::fromUInt(d->picture.size(), false) + renderString(d->mimeType) + renderString(d->description) + d->picture; } void ASF::Picture::parse(const ByteVector& bytes) { d->valid = false; if(bytes.size() < 9) return; int pos = 0; d->type = static_cast<Type>(bytes[0]); ++pos; const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4; const ByteVector nullStringTerminator(2, 0); int endPos = bytes.find(nullStringTerminator, pos, 2); if(endPos < 0) return; d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE); pos = endPos+2; endPos = bytes.find(nullStringTerminator, pos, 2); if(endPos < 0) return; d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE); pos = endPos+2; if(dataLen + pos != bytes.size()) return; d->picture = bytes.mid(pos, dataLen); d->valid = true; } ASF::Picture ASF::Picture::fromInvalid() { Picture ret; ret.d->valid = false; return ret; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfpicture.h����������������������������������������������������������������0000664�0000000�0000000�00000012603�14662262111�0017311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Anton Sergunov email : setosha@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef ASFPICTURE_H #define ASFPICTURE_H #include "tstring.h" #include "tbytevector.h" #include "tpicturetype.h" #include "taglib_export.h" namespace TagLib { namespace ASF { //! An ASF attached picture interface implementation /*! * This is an implementation of ASF attached pictures. Pictures may be * included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture * attributes in a single tag). These pictures are usually in either JPEG or * PNG format. * \see Attribute::toPicture() * \see Attribute::Attribute(const Picture& picture) */ class TAGLIB_EXPORT Picture { public: /* * This describes the function or content of the picture. */ DECLARE_PICTURE_TYPE_ENUM(Type) /*! * Constructs an empty picture. */ Picture(); /*! * Construct a picture as a copy of \a other. */ Picture(const Picture& other); /*! * Destroys the picture. */ ~Picture(); /*! * Copies the contents of \a other into this picture. */ Picture& operator=(const Picture& other); /*! * Exchanges the content of the Picture with the content of \a other. */ void swap(Picture &other) noexcept; /*! * Returns \c true if Picture stores valid picture */ bool isValid() const; /*! * Returns the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". * \see setMimeType(const String &) * \see picture() * \see setPicture(const ByteArray&) */ String mimeType() const; /*! * Sets the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". * \see setMimeType(const String &) * \see picture() * \see setPicture(const ByteArray&) */ void setMimeType(const String &value); /*! * Returns the type of the image. * * \see Type * \see setType() */ Type type() const; /*! * Sets the type for the image. * * \see Type * \see type() */ void setType(const ASF::Picture::Type& t); /*! * Returns a text description of the image. * * \see setDescription() */ String description() const; /*! * Sets a textual description of the image to \a desc. * * \see description() */ void setDescription(const String &desc); /*! * Returns the image data as a ByteVector. * * \note ByteVector has a data() method that returns a <tt>const char *</tt> which * should make it easy to export this data to external programs. * * \see setPicture() * \see mimeType() */ ByteVector picture() const; /*! * Sets the image data to \a p. \a p should be of the type specified in * this frame's mime-type specification. * * \see picture() * \see mimeType() * \see setMimeType() */ void setPicture(const ByteVector &p); /*! * Returns picture as binary raw data \a value */ ByteVector render() const; /*! * Returns picture as binary raw data \a value */ int dataSize() const; #ifndef DO_NOT_DOCUMENT /* THIS IS PRIVATE, DON'T TOUCH IT! */ void parse(const ByteVector& ); static Picture fromInvalid(); #endif private: class PicturePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<PicturePrivate> d; }; } // namespace ASF } // namespace TagLib #endif // ASFPICTURE_H �����������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000010203�14662262111�0020357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asfproperties.h" using namespace TagLib; class ASF::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int bitsPerSample { 0 }; ASF::Properties::Codec codec { ASF::Properties::Unknown }; String codecName; String codecDescription; bool encrypted { false }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ASF::Properties::Properties() : AudioProperties(AudioProperties::Average), d(std::make_unique<PropertiesPrivate>()) { } ASF::Properties::~Properties() = default; int ASF::Properties::lengthInMilliseconds() const { return d->length; } int ASF::Properties::bitrate() const { return d->bitrate; } int ASF::Properties::sampleRate() const { return d->sampleRate; } int ASF::Properties::channels() const { return d->channels; } int ASF::Properties::bitsPerSample() const { return d->bitsPerSample; } ASF::Properties::Codec ASF::Properties::codec() const { return d->codec; } String ASF::Properties::codecName() const { return d->codecName; } String ASF::Properties::codecDescription() const { return d->codecDescription; } bool ASF::Properties::isEncrypted() const { return d->encrypted; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void ASF::Properties::setLengthInMilliseconds(int value) { d->length = value; } void ASF::Properties::setBitrate(int value) { d->bitrate = value; } void ASF::Properties::setSampleRate(int value) { d->sampleRate = value; } void ASF::Properties::setChannels(int value) { d->channels = value; } void ASF::Properties::setBitsPerSample(int value) { d->bitsPerSample = value; } void ASF::Properties::setCodec(int value) { switch(value) { case 0x0160: d->codec = WMA1; break; case 0x0161: d->codec = WMA2; break; case 0x0162: d->codec = WMA9Pro; break; case 0x0163: d->codec = WMA9Lossless; break; default: d->codec = Unknown; break; } } void ASF::Properties::setCodecName(const String &value) { d->codecName = value; } void ASF::Properties::setCodecDescription(const String &value) { d->codecDescription = value; } void ASF::Properties::setEncrypted(bool value) { d->encrypted = value; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000011446�14662262111�0020036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ASFPROPERTIES_H #define TAGLIB_ASFPROPERTIES_H #include "tstring.h" #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace ASF { //! An implementation of ASF audio properties class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Audio codec types which can be used in ASF files. */ enum Codec { /*! * Couldn't detect the codec. */ Unknown = 0, /*! * Windows Media Audio 1 */ WMA1, /*! * Windows Media Audio 2 or above */ WMA2, /*! * Windows Media Audio 9 Professional */ WMA9Pro, /*! * Windows Media Audio 9 Lossless */ WMA9Lossless, }; /*! * Creates an instance of ASF::Properties. */ Properties(); /*! * Destroys this ASF::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns the codec used in the file. * * \see codecName() * \see codecDescription() */ Codec codec() const; /*! * Returns the concrete codec name, for example "Windows Media Audio 9.1" * used in the file if available, otherwise an empty string. * * \see codec() * \see codecDescription() */ String codecName() const; /*! * Returns the codec description, typically contains the encoder settings, * for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available, * otherwise an empty string. * * \see codec() * \see codecName() */ String codecDescription() const; /*! * Returns whether or not the file is encrypted. */ bool isEncrypted() const; #ifndef DO_NOT_DOCUMENT void setLengthInMilliseconds(int value); void setBitrate(int value); void setSampleRate(int value); void setChannels(int value); void setBitsPerSample(int value); void setCodec(int value); void setCodecName(const String &value); void setCodecDescription(const String &value); void setEncrypted(bool value); #endif private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace ASF } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asftag.cpp������������������������������������������������������������������0000664�0000000�0000000�00000030321�14662262111�0016741�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asftag.h" #include <array> #include <utility> #include "tpropertymap.h" #include "asfattribute.h" #include "asfpicture.h" using namespace TagLib; namespace { StringList attributeListToStringList(const ASF::AttributeList &attributes) { StringList strs; for(const auto &attribute : attributes) { strs.append(attribute.toString()); } return strs; } } // namespace class ASF::Tag::TagPrivate { public: String title; String artist; String copyright; String comment; String rating; AttributeListMap attributeListMap; }; ASF::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } ASF::Tag::~Tag() = default; String ASF::Tag::title() const { return d->title; } String ASF::Tag::artist() const { return d->artist; } String ASF::Tag::album() const { if(d->attributeListMap.contains("WM/AlbumTitle")) return joinTagValues( attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle"))); return String(); } String ASF::Tag::copyright() const { return d->copyright; } String ASF::Tag::comment() const { return d->comment; } String ASF::Tag::rating() const { return d->rating; } unsigned int ASF::Tag::year() const { if(d->attributeListMap.contains("WM/Year")) return d->attributeListMap["WM/Year"][0].toString().toInt(); return 0; } unsigned int ASF::Tag::track() const { if(d->attributeListMap.contains("WM/TrackNumber")) { const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0]; if(attr.type() == ASF::Attribute::DWordType) return attr.toUInt(); return attr.toString().toInt(); } if(d->attributeListMap.contains("WM/Track")) return d->attributeListMap["WM/Track"][0].toUInt(); return 0; } String ASF::Tag::genre() const { if(d->attributeListMap.contains("WM/Genre")) return joinTagValues( attributeListToStringList(d->attributeListMap.value("WM/Genre"))); return String(); } void ASF::Tag::setTitle(const String &value) { d->title = value; } void ASF::Tag::setArtist(const String &value) { d->artist = value; } void ASF::Tag::setCopyright(const String &value) { d->copyright = value; } void ASF::Tag::setComment(const String &value) { d->comment = value; } void ASF::Tag::setRating(const String &value) { d->rating = value; } void ASF::Tag::setAlbum(const String &value) { setAttribute("WM/AlbumTitle", value); } void ASF::Tag::setGenre(const String &value) { setAttribute("WM/Genre", value); } void ASF::Tag::setYear(unsigned int value) { setAttribute("WM/Year", String::number(value)); } void ASF::Tag::setTrack(unsigned int value) { setAttribute("WM/TrackNumber", String::number(value)); } ASF::AttributeListMap& ASF::Tag::attributeListMap() { return d->attributeListMap; } const ASF::AttributeListMap &ASF::Tag::attributeListMap() const { return d->attributeListMap; } bool ASF::Tag::contains(const String &key) const { return d->attributeListMap.contains(key); } void ASF::Tag::removeItem(const String &key) { d->attributeListMap.erase(key); } ASF::AttributeList ASF::Tag::attribute(const String &name) const { return d->attributeListMap[name]; } void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) { AttributeList val; val.append(attribute); d->attributeListMap.insert(name, val); } void ASF::Tag::setAttribute(const String &name, const AttributeList &values) { d->attributeListMap.insert(name, values); } void ASF::Tag::addAttribute(const String &name, const Attribute &attribute) { if(d->attributeListMap.contains(name)) { d->attributeListMap[name].append(attribute); } else { setAttribute(name, attribute); } } bool ASF::Tag::isEmpty() const { return TagLib::Tag::isEmpty() && copyright().isEmpty() && rating().isEmpty() && d->attributeListMap.isEmpty(); } namespace { constexpr std::array keyTranslation { std::pair("WM/AlbumTitle", "ALBUM"), std::pair("WM/AlbumArtist", "ALBUMARTIST"), std::pair("WM/AuthorURL", "ARTISTWEBPAGE"), std::pair("WM/Composer", "COMPOSER"), std::pair("WM/Writer", "LYRICIST"), std::pair("WM/Conductor", "CONDUCTOR"), std::pair("WM/ModifiedBy", "REMIXER"), std::pair("WM/Year", "DATE"), std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"), std::pair("WM/OriginalArtist", "ORIGINALARTIST"), std::pair("WM/OriginalFilename", "ORIGINALFILENAME"), std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"), std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"), std::pair("WM/Producer", "PRODUCER"), std::pair("WM/ContentGroupDescription", "WORK"), std::pair("WM/SubTitle", "SUBTITLE"), std::pair("WM/SetSubTitle", "DISCSUBTITLE"), std::pair("WM/TrackNumber", "TRACKNUMBER"), std::pair("WM/PartOfSet", "DISCNUMBER"), std::pair("WM/Genre", "GENRE"), std::pair("WM/BeatsPerMinute", "BPM"), std::pair("WM/Mood", "MOOD"), std::pair("WM/InitialKey", "INITIALKEY"), std::pair("WM/ISRC", "ISRC"), std::pair("WM/Lyrics", "LYRICS"), std::pair("WM/Media", "MEDIA"), std::pair("WM/Publisher", "LABEL"), std::pair("WM/CatalogNo", "CATALOGNUMBER"), std::pair("WM/Barcode", "BARCODE"), std::pair("WM/EncodedBy", "ENCODEDBY"), std::pair("WM/EncodingSettings", "ENCODING"), std::pair("WM/EncodingTime", "ENCODINGTIME"), std::pair("WM/AudioFileURL", "FILEWEBPAGE"), std::pair("WM/AlbumSortOrder", "ALBUMSORT"), std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"), std::pair("WM/ArtistSortOrder", "ARTISTSORT"), std::pair("WM/TitleSortOrder", "TITLESORT"), std::pair("WM/Script", "SCRIPT"), std::pair("WM/Language", "LANGUAGE"), std::pair("WM/ARTISTS", "ARTISTS"), std::pair("ASIN", "ASIN"), std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"), std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"), std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"), std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"), std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"), std::pair("MusicBrainz/Album Status", "RELEASESTATUS"), std::pair("MusicBrainz/Album Type", "RELEASETYPE"), std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"), std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"), std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"), std::pair("MusicIP/PUID", "MUSICIP_PUID"), std::pair("Acoustid/Id", "ACOUSTID_ID"), std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"), }; String translateKey(const String &key) { for(const auto &[k, t] : keyTranslation) { if(key == k) return t; } return String(); } } // namespace PropertyMap ASF::Tag::properties() const { PropertyMap props; if(!d->title.isEmpty()) { props["TITLE"] = d->title; } if(!d->artist.isEmpty()) { props["ARTIST"] = d->artist; } if(!d->copyright.isEmpty()) { props["COPYRIGHT"] = d->copyright; } if(!d->comment.isEmpty()) { props["COMMENT"] = d->comment; } for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) { if(const String key = translateKey(k); !key.isEmpty()) { for(const auto &attr : attributes) { if(key == "TRACKNUMBER") { if(attr.type() == ASF::Attribute::DWordType) props.insert(key, String::number(attr.toUInt())); else props.insert(key, attr.toString()); } else { props.insert(key, attr.toString()); } } } else { props.addUnsupportedData(k); } } return props; } void ASF::Tag::removeUnsupportedProperties(const StringList &props) { for(const auto &prop : props) d->attributeListMap.erase(prop); } PropertyMap ASF::Tag::setProperties(const PropertyMap &props) { static Map<String, String> reverseKeyMap; if(reverseKeyMap.isEmpty()) { for(const auto &[k, t] : keyTranslation) { reverseKeyMap[t] = k; } } const PropertyMap origProps = properties(); for(const auto &[prop, _] : origProps) { if(!props.contains(prop) || props[prop].isEmpty()) { if(prop == "TITLE") { d->title.clear(); } else if(prop == "ARTIST") { d->artist.clear(); } else if(prop == "COMMENT") { d->comment.clear(); } else if(prop == "COPYRIGHT") { d->copyright.clear(); } else { d->attributeListMap.erase(reverseKeyMap[prop]); } } } PropertyMap ignoredProps; for(const auto &[prop, attributes] : props) { if(reverseKeyMap.contains(prop)) { String name = reverseKeyMap[prop]; removeItem(name); for(const auto &attr : attributes) { addAttribute(name, attr); } } else if(prop == "TITLE") { d->title = attributes.toString(); } else if(prop == "ARTIST") { d->artist = attributes.toString(); } else if(prop == "COMMENT") { d->comment = attributes.toString(); } else if(prop == "COPYRIGHT") { d->copyright = attributes.toString(); } else { ignoredProps.insert(prop, attributes); } } return ignoredProps; } StringList ASF::Tag::complexPropertyKeys() const { StringList keys; if(d->attributeListMap.contains("WM/Picture")) { keys.append("PICTURE"); } return keys; } List<VariantMap> ASF::Tag::complexProperties(const String &key) const { List<VariantMap> props; if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { const AttributeList pictures = d->attributeListMap.value("WM/Picture"); for(const Attribute &attr : pictures) { ASF::Picture picture = attr.toPicture(); VariantMap property; property.insert("data", picture.picture()); property.insert("mimeType", picture.mimeType()); property.insert("description", picture.description()); property.insert("pictureType", ASF::Picture::typeToString(picture.type())); props.append(property); } } return props; } bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { removeItem("WM/Picture"); for(const auto &property : value) { ASF::Picture picture; picture.setPicture(property.value("data").value<ByteVector>()); picture.setMimeType(property.value("mimeType").value<String>()); picture.setDescription(property.value("description").value<String>()); picture.setType(ASF::Picture::typeFromString( property.value("pictureType").value<String>())); addAttribute("WM/Picture", Attribute(picture)); } } else { return false; } return true; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asftag.h��������������������������������������������������������������������0000664�0000000�0000000�00000015603�14662262111�0016414�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ASFTAG_H #define TAGLIB_ASFTAG_H #include "tlist.h" #include "tmap.h" #include "taglib_export.h" #include "tag.h" #include "asfattribute.h" namespace TagLib { namespace ASF { using AttributeList = List<Attribute>; using AttributeListMap = Map<String, AttributeList>; //! An implementation of ASF (WMA) tags class TAGLIB_EXPORT Tag : public TagLib::Tag { friend class File; public: Tag(); ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; /*! * Returns the track name. */ String title() const override; /*! * Returns the artist name. */ String artist() const override; /*! * Returns the album name; if no album name is present in the tag * an empty string will be returned. */ String album() const override; /*! * Returns the track comment. */ String comment() const override; /*! * Returns the genre name; if no genre is present in the tag an empty string * will be returned. */ String genre() const override; /*! * Returns the rating. */ virtual String rating() const; /*! * Returns the copyright information; if no copyright information is * present in the tag an empty string will be returned. */ virtual String copyright() const; /*! * Returns the year; if there is no year set, this will return 0. */ unsigned int year() const override; /*! * Returns the track number; if there is no track number set, this will * return 0. */ unsigned int track() const override; /*! * Sets the title to \a value. */ void setTitle(const String &value) override; /*! * Sets the artist to \a value. */ void setArtist(const String &value) override; /*! * Sets the album to \a value. If \a value is an empty string then this value will be * cleared. */ void setAlbum(const String &value) override; /*! * Sets the comment to \a value. */ void setComment(const String &value) override; /*! * Sets the rating to \a value. */ virtual void setRating(const String &value); /*! * Sets the copyright to \a value. */ virtual void setCopyright(const String &value); /*! * Sets the genre to \a value. */ void setGenre(const String &value) override; /*! * Sets the year to \a value. If \a value is 0 then this value will be cleared. */ void setYear(unsigned int value) override; /*! * Sets the track to \a value. If \a value is 0 then this value will be cleared. */ void setTrack(unsigned int value) override; /*! * Returns \c true if the tag does not contain any data. This should be * reimplemented in subclasses that provide more than the basic tagging * abilities in this class. */ bool isEmpty() const override; /*! * \warning You should not modify this data structure directly, instead * use attributeListMap() const, contains(), removeItem(), * attribute(), setAttribute(), addAttribute(). */ AttributeListMap &attributeListMap(); /*! * Returns a reference to the item list map. This is an AttributeListMap of * all of the items in the tag. */ const AttributeListMap &attributeListMap() const; /*! * \return \c true if a value for \a key is currently set. */ bool contains(const String &key) const; /*! * Removes the \a key attribute from the tag */ void removeItem(const String &key); /*! * \return The list of values for the key \a name, or an empty list if no * values have been set. */ AttributeList attribute(const String &name) const; /*! * Sets the \a name attribute to the value of \a attribute. If an attribute * with the \a name is already present, it will be replaced. */ void setAttribute(const String &name, const Attribute &attribute); /*! * Sets multiple \a values to the key \a name. */ void setAttribute(const String &name, const AttributeList &values); /*! * Sets the \a name attribute to the value of \a attribute. If an attribute * with the \a name is already present, it will be added to the list. */ void addAttribute(const String &name, const Attribute &attribute); PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &props) override; PropertyMap setProperties(const PropertyMap &props) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace ASF } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/asf/asfutils.h������������������������������������������������������������������0000664�0000000�0000000�00000007060�14662262111�0016777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ASFUTILS_H #define TAGLIB_ASFUTILS_H // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header namespace TagLib { namespace ASF { namespace { inline unsigned short readWORD(File *file, bool *ok = nullptr) { const ByteVector v = file->readBlock(2); if(v.size() != 2) { if(ok) *ok = false; return 0; } if(ok) *ok = true; return v.toUShort(false); } inline unsigned int readDWORD(File *file, bool *ok = nullptr) { const ByteVector v = file->readBlock(4); if(v.size() != 4) { if(ok) *ok = false; return 0; } if(ok) *ok = true; return v.toUInt(false); } inline long long readQWORD(File *file, bool *ok = nullptr) { const ByteVector v = file->readBlock(8); if(v.size() != 8) { if(ok) *ok = false; return 0; } if(ok) *ok = true; return v.toLongLong(false); } inline String readString(File *file, int length) { ByteVector data = file->readBlock(length); unsigned int size = data.size(); while (size >= 2) { if(data[size - 1] != '\0' || data[size - 2] != '\0') { break; } size -= 2; } if(size != data.size()) { data.resize(size); } return String(data, String::UTF16LE); } inline ByteVector renderString(const String &str, bool includeLength = false) { ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false); if(includeLength) { data = ByteVector::fromShort(data.size(), false) + data; } return data; } } // namespace } // namespace ASF } // namespace TagLib #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/audioproperties.cpp�������������������������������������������������������������0000664�0000000�0000000�00000005105�14662262111�0020143�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "audioproperties.h" using namespace TagLib; class AudioProperties::AudioPropertiesPrivate { }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// AudioProperties::~AudioProperties() = default; int AudioProperties::length() const { return lengthInSeconds(); } int AudioProperties::lengthInSeconds() const { return lengthInMilliseconds() / 1000; } int AudioProperties::lengthInMilliseconds() const { return 0; } int AudioProperties::bitrate() const { return 0; } int AudioProperties::sampleRate() const { return 0; } //////////////////////////////////////////////////////////////////////////////// // protected methods //////////////////////////////////////////////////////////////////////////////// AudioProperties::AudioProperties(ReadStyle) { } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/audioproperties.h���������������������������������������������������������������0000664�0000000�0000000�00000011270�14662262111�0017610�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_AUDIOPROPERTIES_H #define TAGLIB_AUDIOPROPERTIES_H #include <memory> #include "taglib.h" #include "taglib_export.h" namespace TagLib { //! A simple, abstract interface to common audio properties /*! * The values here are common to most audio formats. For more specific, codec * dependent values, please see the subclasses APIs. This is meant to * compliment the TagLib::File and TagLib::Tag APIs in providing a simple * interface that is sufficient for most applications. */ class TAGLIB_EXPORT AudioProperties { public: /*! * Reading audio properties from a file can sometimes be very time consuming * and for the most accurate results can often involve reading the entire * file. Because in many situations speed is critical or the accuracy of the * values is not particularly important this allows the level of desired * accuracy to be set. */ enum ReadStyle { //! Read as little of the file as possible Fast, //! Read more of the file and make better values guesses Average, //! Read as much of the file as needed to report accurate values Accurate }; /*! * Destroys this AudioProperties instance. */ virtual ~AudioProperties(); AudioProperties(const AudioProperties &) = delete; AudioProperties &operator=(const AudioProperties &) = delete; /*! * Returns the length of the file in seconds. The length is rounded down to * the nearest whole second. * * \note This method is just an alias of lengthInSeconds(). * * \deprecated Use lengthInSeconds(). */ TAGLIB_DEPRECATED virtual int length() const; /*! * Returns the length of the file in seconds. The length is rounded down to * the nearest whole second. * * \see lengthInMilliseconds() */ virtual int lengthInSeconds() const; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ virtual int lengthInMilliseconds() const; /*! * Returns the most appropriate bit rate for the file in kb/s. For constant * bitrate formats this is simply the bitrate of the file. For variable * bitrate formats this is either the average or nominal bitrate. */ virtual int bitrate() const; /*! * Returns the sample rate in Hz. */ virtual int sampleRate() const; /*! * Returns the number of audio channels. */ virtual int channels() const = 0; protected: /*! * Construct an audio properties instance. This is protected as this class * should not be instantiated directly, but should be instantiated via its * subclasses and can be fetched from the FileRef or File APIs. * * \see ReadStyle */ AudioProperties(ReadStyle style); private: class AudioPropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<AudioPropertiesPrivate> d; }; } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015457�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.cpp��������������������������������������������������������0000664�0000000�0000000�00000010313�14662262111�0020760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "dsdiffdiintag.h" #include <utility> #include "tstringlist.h" #include "tpropertymap.h" #include "tdebug.h" using namespace TagLib; using namespace DSDIFF::DIIN; class DSDIFF::DIIN::Tag::TagPrivate { public: String title; String artist; }; DSDIFF::DIIN::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } DSDIFF::DIIN::Tag::~Tag() = default; String DSDIFF::DIIN::Tag::title() const { return d->title; } String DSDIFF::DIIN::Tag::artist() const { return d->artist; } String DSDIFF::DIIN::Tag::album() const { return String(); } String DSDIFF::DIIN::Tag::comment() const { return String(); } String DSDIFF::DIIN::Tag::genre() const { return String(); } unsigned int DSDIFF::DIIN::Tag::year() const { return 0; } unsigned int DSDIFF::DIIN::Tag::track() const { return 0; } void DSDIFF::DIIN::Tag::setTitle(const String &title) { d->title = title; } void DSDIFF::DIIN::Tag::setArtist(const String &artist) { d->artist = artist; } void DSDIFF::DIIN::Tag::setAlbum(const String &) { debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag."); } void DSDIFF::DIIN::Tag::setComment(const String &) { debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag."); } void DSDIFF::DIIN::Tag::setGenre(const String &) { debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag."); } void DSDIFF::DIIN::Tag::setYear(unsigned int) { debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag."); } void DSDIFF::DIIN::Tag::setTrack(unsigned int) { debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag."); } PropertyMap DSDIFF::DIIN::Tag::properties() const { PropertyMap properties; properties["TITLE"] = d->title; properties["ARTIST"] = d->artist; return properties; } PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps) { PropertyMap props(origProps); props.removeEmpty(); StringList oneValueSet; if(props.contains("TITLE")) { d->title = props["TITLE"].front(); oneValueSet.append("TITLE"); } else d->title.clear(); if(props.contains("ARTIST")) { d->artist = props["ARTIST"].front(); oneValueSet.append("ARTIST"); } else d->artist.clear(); // for each tag that has been set above, remove the first entry in the corresponding // value list. The others will be returned as unsupported by this format. for(const auto &entry : std::as_const(oneValueSet)) { if(props[entry].size() == 1) props.erase(entry); else props[entry].erase(props[entry].begin()); } return props; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdiffdiintag.h����������������������������������������������������������0000664�0000000�0000000�00000011653�14662262111�0020435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DSDIFFDIINTAG_H #define TAGLIB_DSDIFFDIINTAG_H #include "tag.h" namespace TagLib { namespace DSDIFF { namespace DIIN { /*! * Tags from the Edited Master Chunk Info * * Only Title and Artist tags are supported */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: Tag(); ~Tag() override; /*! * Returns the track name; if no track name is present in the tag * String() will be returned. */ String title() const override; /*! * Returns the artist name; if no artist name is present in the tag * String() will be returned. */ String artist() const override; /*! * Not supported. Therefore always returns String(). */ String album() const override; /*! * Not supported. Therefore always returns String(). */ String comment() const override; /*! * Not supported. Therefore always returns String(). */ String genre() const override; /*! * Not supported. Therefore always returns 0. */ unsigned int year() const override; /*! * Not supported. Therefore always returns 0. */ unsigned int track() const override; /*! * Sets the title to \a title. If \a title is String() then this * value will be cleared. */ void setTitle(const String &title) override; /*! * Sets the artist to \a artist. If \a artist is String() then this * value will be cleared. */ void setArtist(const String &artist) override; /*! * Not supported and therefore ignored. */ void setAlbum(const String &album) override; /*! * Not supported and therefore ignored. */ void setComment(const String &comment) override; /*! * Not supported and therefore ignored. */ void setGenre(const String &genre) override; /*! * Not supported and therefore ignored. */ void setYear(unsigned int year) override; /*! * Not supported and therefore ignored. */ void setTrack(unsigned int track) override; /*! * Implements the unified property interface -- export function. * Since the DIIN tag is very limited, the exported map is as well. */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Because of the limitations of the DIIN file tag, any tags besides * TITLE and ARTIST, will be * returned. Additionally, if the map contains tags with multiple values, * all but the first will be contained in the returned map of unsupported * properties. */ PropertyMap setProperties(const PropertyMap &) override; private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace DIIN } // namespace DSDIFF } // namespace TagLib #endif �������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdifffile.cpp�����������������������������������������������������������0000664�0000000�0000000�00000071332�14662262111�0020270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "dsdifffile.h" #include <array> #include <memory> #include "tbytevector.h" #include "tstringlist.h" #include "tpropertymap.h" #include "tdebug.h" #include "id3v2tag.h" #include "tagutils.h" #include "tagunion.h" using namespace TagLib; namespace { struct Chunk64 { ByteVector name; unsigned long long offset; unsigned long long size; char padding; }; using ChunkList = std::vector<Chunk64>; int chunkIndex(const ChunkList &chunks, const ByteVector &id) { for(size_t i = 0; i < chunks.size(); i++) { if(chunks[i].name == id) return static_cast<int>(i); } return -1; } bool isValidChunkID(const ByteVector &name) { if(name.size() != 4) return false; for(int i = 0; i < 4; i++) { if(name[i] < 32 || name[i] > 126) return false; } return true; } enum { ID3v2Index = 0, DIINIndex = 1 }; enum { PROPChunk = 0, DIINChunk = 1 }; } // namespace class DSDIFF::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory) : ID3v2FrameFactory(frameFactory ? frameFactory : ID3v2::FrameFactory::instance()) { } ~FilePrivate() = default; const ID3v2::FrameFactory *ID3v2FrameFactory; Endianness endianness { BigEndian }; ByteVector type; unsigned long long size { 0 }; ByteVector format; ChunkList chunks; std::array<ChunkList, 2> childChunks; std::array<int, 2> childChunkIndex { -1, -1 }; /* * Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level */ bool isID3InPropChunk { false }; /* * ID3 chunks are present. This is then the index of the one in PROP chunk that * will be removed upon next save to remove duplicates. */ int duplicateID3V2chunkIndex { -1 }; std::unique_ptr<Properties> properties; TagUnion tag; ByteVector id3v2TagChunkID { "ID3 " }; bool hasID3v2 { false }; bool hasDiin { false }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool DSDIFF::File::isSupported(IOStream *stream) { // A DSDIFF file has to start with "FRM8????????DSD ". const ByteVector id = Utils::readHeader(stream, 16, false); return id.startsWith("FRM8") && id.containsAt("DSD ", 12); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// DSDIFF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties, propertiesStyle); } DSDIFF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties, propertiesStyle); } DSDIFF::File::~File() = default; TagLib::Tag *DSDIFF::File::tag() const { return &d->tag; } ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const { return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory); } bool DSDIFF::File::hasID3v2Tag() const { return d->hasID3v2; } DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const { return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create); } bool DSDIFF::File::hasDIINTag() const { return d->hasDiin; } PropertyMap DSDIFF::File::properties() const { return d->tag.properties(); } void DSDIFF::File::removeUnsupportedProperties(const StringList &properties) { d->tag.removeUnsupportedProperties(properties); } PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties) { return ID3v2Tag(true)->setProperties(properties); } DSDIFF::Properties *DSDIFF::File::audioProperties() const { return d->properties.get(); } bool DSDIFF::File::save() { return save(AllTags); } bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version) { if(readOnly()) { debug("DSDIFF::File::save() -- File is read only."); return false; } if(!isValid()) { debug("DSDIFF::File::save() -- Trying to save invalid file."); return false; } if(strip == StripOthers) File::strip(~tags); // First: save ID3V2 chunk if(const ID3v2::Tag *id3v2Tag = ID3v2Tag(); (tags & ID3v2) && id3v2Tag) { if(d->isID3InPropChunk) { if(!id3v2Tag->isEmpty()) { setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk); d->hasID3v2 = true; } else { // Empty tag: remove it setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk); d->hasID3v2 = false; } } else { if(!id3v2Tag->isEmpty()) { setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version)); d->hasID3v2 = true; } else { // Empty tag: remove it setRootChunkData(d->id3v2TagChunkID, ByteVector()); d->hasID3v2 = false; } } } // Second: save the DIIN chunk if(const DSDIFF::DIIN::Tag *diinTag = DIINTag(); (tags & DIIN) && diinTag) { if(!diinTag->title().isEmpty()) { ByteVector diinTitle; diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian)); diinTitle.append(ByteVector::fromCString(diinTag->title().toCString())); setChildChunkData("DITI", diinTitle, DIINChunk); } else setChildChunkData("DITI", ByteVector(), DIINChunk); if(!diinTag->artist().isEmpty()) { ByteVector diinArtist; diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian)); diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString())); setChildChunkData("DIAR", diinArtist, DIINChunk); } else setChildChunkData("DIAR", ByteVector(), DIINChunk); } // Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any if(d->duplicateID3V2chunkIndex >= 0) { setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk); d->duplicateID3V2chunkIndex = -1; } return true; } void DSDIFF::File::strip(int tags) { if(tags & ID3v2) { removeRootChunk("ID3 "); removeRootChunk("id3 "); removeChildChunk("ID3 ", PROPChunk); removeChildChunk("id3 ", PROPChunk); d->hasID3v2 = false; d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory)); d->duplicateID3V2chunkIndex = -1; d->isID3InPropChunk = false; d->id3v2TagChunkID.setData("ID3 "); } if(tags & DIIN) { removeChildChunk("DITI", DIINChunk); removeChildChunk("DIAR", DIINChunk); if(d->childChunks[DIINIndex].empty()) { removeRootChunk("DIIN"); } d->hasDiin = false; d->tag.set(DIINIndex, new DIIN::Tag); } } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void DSDIFF::File::removeRootChunk(unsigned int i) { unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12; d->size -= chunkSize; insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); removeBlock(d->chunks[i].offset - 12, chunkSize); // Update the internal offsets d->chunks.erase(d->chunks.begin() + i); for(int &cci : d->childChunkIndex) { if(cci > static_cast<int>(i)) { --cci; } } updateRootChunksStructure(i); } void DSDIFF::File::removeRootChunk(const ByteVector &id) { int i = chunkIndex(d->chunks, id); if(i >= 0) removeRootChunk(i); } void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) { if(data.isEmpty()) { removeRootChunk(i); return; } // Non null data: update chunk // First we update the global size d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); // Now update the specific chunk writeChunk(d->chunks[i].name, data, d->chunks[i].offset - 12, static_cast<unsigned long>(d->chunks[i].size + d->chunks[i].padding + 12)); d->chunks[i].size = data.size(); d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; // Finally update the internal offsets updateRootChunksStructure(i + 1); } void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data) { if(d->chunks.empty()) { debug("DSDIFF::File::setRootChunkData('" + name + "') - No valid chunks found."); return; } int i = chunkIndex(d->chunks, name); if(i >= 0) { setRootChunkData(i, data); return; } // Couldn't find an existing chunk, so let's create a new one. i = static_cast<int>(d->chunks.size()) - 1; unsigned long long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding; // First we update the global size d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12; insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); // Now add the chunk to the file const unsigned long long fileLength = length(); writeChunk(name, data, offset, static_cast<unsigned long>(fileLength > offset ? fileLength - offset : 0), (offset & 1) ? 1 : 0); Chunk64 chunk; chunk.name = name; chunk.size = data.size(); chunk.offset = offset + 12; chunk.padding = (data.size() & 0x01) ? 1 : 0; d->chunks.push_back(chunk); } void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum) { ChunkList &childChunks = d->childChunks[childChunkNum]; // Update global size unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12; d->size -= removedChunkTotalSize; insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); // Update child chunk size d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize; insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, d->endianness == BigEndian), d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); // Remove the chunk removeBlock(childChunks[i].offset - 12, removedChunkTotalSize); // Update the internal offsets // For child chunks if(i + 1 < childChunks.size()) { childChunks[i + 1].offset = childChunks[i].offset; for(unsigned int c = i + 2; c < childChunks.size(); ++c) childChunks[c].offset = childChunks[c - 1].offset + 12 + childChunks[c - 1].size + childChunks[c - 1].padding; } // And for root chunks childChunks.erase(childChunks.begin() + i); updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); } void DSDIFF::File::removeChildChunk(const ByteVector &id, unsigned int childChunkNum) { int i = chunkIndex(d->childChunks[childChunkNum], id); if(i >= 0) removeChildChunk(i, childChunkNum); } void DSDIFF::File::setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum) { ChunkList &childChunks = d->childChunks[childChunkNum]; if(data.isEmpty()) { removeChildChunk(i, childChunkNum); return; } // Non null data: update chunk // First we update the global size d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding); insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); // And the PROP chunk size d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding); insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, d->endianness == BigEndian), d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); // Now update the specific chunk writeChunk(childChunks[i].name, data, childChunks[i].offset - 12, static_cast<unsigned long>(childChunks[i].size + childChunks[i].padding + 12)); childChunks[i].size = data.size(); childChunks[i].padding = (data.size() & 0x01) ? 1 : 0; // Now update the internal offsets // For child Chunks for(i++; i < childChunks.size(); i++) childChunks[i].offset = childChunks[i - 1].offset + 12 + childChunks[i - 1].size + childChunks[i - 1].padding; // And for root chunks updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); } void DSDIFF::File::setChildChunkData(const ByteVector &name, const ByteVector &data, unsigned int childChunkNum) { ChunkList &childChunks = d->childChunks[childChunkNum]; if(int i = chunkIndex(childChunks, name); i >= 0) { setChildChunkData(i, data, childChunkNum); return; } // Do not attempt to remove a non existing chunk if(data.isEmpty()) return; // Couldn't find an existing chunk, so let's create a new one. unsigned long long offset = 0; if(!childChunks.empty()) { size_t i = childChunks.size() - 1; offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding; } else if(childChunkNum == DIINChunk) { int i = d->childChunkIndex[DIINChunk]; if(i < 0) { setRootChunkData("DIIN", ByteVector()); if(const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1; lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") { i = lastChunkIndex; d->childChunkIndex[DIINChunk] = lastChunkIndex; d->hasDiin = true; } } if(i >= 0) { offset = d->chunks[i].offset; // 12 is already added in setRootChunkData() } } if(offset == 0) { debug("DSDIFF::File::setChildChunkData - No valid chunks found."); return; } // First we update the global size d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12; insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); // And the child chunk size d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1) + ((data.size() + 1) & ~1) + 12; insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, d->endianness == BigEndian), d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); // Now add the chunk to the file unsigned long long nextRootChunkIdx = length(); if(d->childChunkIndex[childChunkNum] + 1 < static_cast<int>(d->chunks.size())) nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12; writeChunk(name, data, offset, static_cast<unsigned long>( nextRootChunkIdx > offset ? nextRootChunkIdx - offset : 0), offset & 1 ? 1 : 0); // For root chunks updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); Chunk64 chunk; chunk.name = name; chunk.size = data.size(); chunk.offset = offset + 12; chunk.padding = data.size() & 0x01 ? 1 : 0; childChunks.push_back(chunk); } void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk) { for(unsigned int i = startingChunk; i < d->chunks.size(); i++) d->chunks[i].offset = d->chunks[i - 1].offset + 12 + d->chunks[i - 1].size + d->chunks[i - 1].padding; // Update child chunks structure as well if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) { if(ChunkList &childChunksToUpdate = d->childChunks[PROPChunk]; !childChunksToUpdate.empty()) { childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12; for(unsigned int i = 1; i < childChunksToUpdate.size(); i++) childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding; } } if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) { if(ChunkList &childChunksToUpdate = d->childChunks[DIINChunk]; !childChunksToUpdate.empty()) { childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12; for(unsigned int i = 1; i < childChunksToUpdate.size(); i++) childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding; } } } void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) { bool bigEndian = d->endianness == BigEndian; d->type = readBlock(4); d->size = readBlock(8).toLongLong(bigEndian); d->format = readBlock(4); // + 12: chunk header at least, fix for additional junk bytes while(tell() + 12 <= length()) { ByteVector chunkName = readBlock(4); unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian); if(!isValidChunkID(chunkName)) { debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID"); setValid(false); break; } if(static_cast<unsigned long long>(tell()) + chunkSize > static_cast<unsigned long long>(length())) { debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); setValid(false); break; } Chunk64 chunk; chunk.name = chunkName; chunk.size = chunkSize; chunk.offset = tell(); seek(chunk.size, Current); // Check padding chunk.padding = 0; if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) { if(ByteVector iByte = readBlock(1); iByte.size() != 1 || iByte[0] != 0) // Not well formed, re-seek seek(uPosNotPadded, Beginning); else chunk.padding = 1; } d->chunks.push_back(chunk); } // For DSD uncompressed unsigned long long lengthDSDSamplesTimeChannels = 0; // For computing bitrate unsigned long long audioDataSizeinBytes = 0; // For DST compressed frames unsigned long dstNumFrames = 0; // For DST compressed frames unsigned short dstFrameRate = 0; for(unsigned int i = 0; i < d->chunks.size(); i++) { if(d->chunks[i].name == "DSD ") { lengthDSDSamplesTimeChannels = d->chunks[i].size * 8; audioDataSizeinBytes = d->chunks[i].size; } else if(d->chunks[i].name == "DST ") { // Now decode the chunks inside the DST chunk to read the DST Frame Information one long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size; seek(d->chunks[i].offset); audioDataSizeinBytes = d->chunks[i].size; while(tell() + 12 <= dstChunkEnd) { ByteVector dstChunkName = readBlock(4); long long dstChunkSize = readBlock(8).toLongLong(bigEndian); if(!isValidChunkID(dstChunkName)) { debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID"); setValid(false); break; } if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) { debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid size (larger than the DST chunk)"); setValid(false); break; } if(dstChunkName == "FRTE") { // Found the DST frame information chunk dstNumFrames = readBlock(4).toUInt(bigEndian); dstFrameRate = readBlock(2).toUShort(bigEndian); // Found the wanted one, no need to look at the others break; } seek(dstChunkSize, Current); // Check padding if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) { if(ByteVector iByte = readBlock(1); iByte.size() != 1 || iByte[0] != 0) // Not well formed, re-seek seek(uPosNotPadded, Beginning); } } } else if(d->chunks[i].name == "PROP") { d->childChunkIndex[PROPChunk] = i; // Now decodes the chunks inside the PROP chunk long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size; // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk seek(d->chunks[i].offset + 4); while(tell() + 12 <= propChunkEnd) { ByteVector propChunkName = readBlock(4); long long propChunkSize = readBlock(8).toLongLong(bigEndian); if(!isValidChunkID(propChunkName)) { debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID"); setValid(false); break; } if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) { debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid size (larger than the PROP chunk)"); setValid(false); break; } Chunk64 chunk; chunk.name = propChunkName; chunk.size = propChunkSize; chunk.offset = tell(); seek(chunk.size, Current); // Check padding chunk.padding = 0; if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) { if(ByteVector iByte = readBlock(1); iByte.size() != 1 || iByte[0] != 0) // Not well formed, re-seek seek(uPosNotPadded, Beginning); else chunk.padding = 1; } d->childChunks[PROPChunk].push_back(chunk); } } else if(d->chunks[i].name == "DIIN") { d->childChunkIndex[DIINChunk] = i; d->hasDiin = true; // Now decode the chunks inside the DIIN chunk long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size; seek(d->chunks[i].offset); while(tell() + 12 <= diinChunkEnd) { ByteVector diinChunkName = readBlock(4); long long diinChunkSize = readBlock(8).toLongLong(bigEndian); if(!isValidChunkID(diinChunkName)) { debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID"); setValid(false); break; } if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) { debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid size (larger than the DIIN chunk)"); setValid(false); break; } Chunk64 chunk; chunk.name = diinChunkName; chunk.size = diinChunkSize; chunk.offset = tell(); seek(chunk.size, Current); // Check padding chunk.padding = 0; if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) { if(ByteVector iByte = readBlock(1); iByte.size() != 1 || iByte[0] != 0) // Not well formed, re-seek seek(uPosNotPadded, Beginning); else chunk.padding = 1; } d->childChunks[DIINChunk].push_back(chunk); } } else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") { d->id3v2TagChunkID = d->chunks[i].name; d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset, d->ID3v2FrameFactory)); d->isID3InPropChunk = false; d->hasID3v2 = true; } } if(!isValid()) return; if(d->childChunkIndex[PROPChunk] < 0) { debug("DSDIFF::File::read() -- no PROP chunk found"); setValid(false); return; } // Read properties unsigned int sampleRate = 0; unsigned short channels = 0; for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) { if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") { if(d->hasID3v2) { d->duplicateID3V2chunkIndex = i; // ID3V2 tag has already been found at root level continue; } d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name; d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset, d->ID3v2FrameFactory)); d->isID3InPropChunk = true; d->hasID3v2 = true; } else if(d->childChunks[PROPChunk][i].name == "FS ") { // Sample rate seek(d->childChunks[PROPChunk][i].offset); sampleRate = readBlock(4).toUInt(0, 4, bigEndian); } else if(d->childChunks[PROPChunk][i].name == "CHNL") { // Channels seek(d->childChunks[PROPChunk][i].offset); channels = readBlock(2).toShort(0, bigEndian); } } // Read title & artist from DIIN chunk d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true); if(d->hasDiin) { for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) { if(d->childChunks[DIINChunk][i].name == "DITI") { seek(d->childChunks[DIINChunk][i].offset); if(unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian); titleStrLength <= d->childChunks[DIINChunk][i].size) { ByteVector titleStr = readBlock(titleStrLength); d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr); } } else if(d->childChunks[DIINChunk][i].name == "DIAR") { seek(d->childChunks[DIINChunk][i].offset); if(unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian); artistStrLength <= d->childChunks[DIINChunk][i].size) { ByteVector artistStr = readBlock(artistStrLength); d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr); } } } } if(readProperties) { if(lengthDSDSamplesTimeChannels == 0) { // DST compressed signal : need to compute length of DSD uncompressed frames if(dstFrameRate > 0) lengthDSDSamplesTimeChannels = static_cast<unsigned long long>(dstNumFrames) * static_cast<unsigned long long>(sampleRate) / static_cast<unsigned long long>(dstFrameRate); else lengthDSDSamplesTimeChannels = 0; } else { // In DSD uncompressed files, the read number of samples is the total for each channel if(channels > 0) lengthDSDSamplesTimeChannels /= channels; } int bitrate = 0; if(lengthDSDSamplesTimeChannels > 0) bitrate = static_cast<int>( audioDataSizeinBytes * 8 * sampleRate / lengthDSDSamplesTimeChannels / 1000); d->properties = std::make_unique<Properties>(sampleRate, channels, lengthDSDSamplesTimeChannels, bitrate, propertiesStyle); } if(!ID3v2Tag()) { d->tag.access<ID3v2::Tag>(ID3v2Index, true, d->ID3v2FrameFactory); // By default, ID3 chunk is at root level d->isID3InPropChunk = false; d->hasID3v2 = false; } } void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, unsigned long long offset, unsigned long replace, unsigned int leadingPadding) { ByteVector combined; if(leadingPadding) combined.append(ByteVector(leadingPadding, '\x00')); combined.append(name); combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian)); combined.append(data); if((data.size() & 0x01) != 0) combined.append('\x00'); insert(combined, offset, replace); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdifffile.h�������������������������������������������������������������0000664�0000000�0000000�00000025466�14662262111�0017744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DSDIFFFILE_H #define TAGLIB_DSDIFFFILE_H #include "rifffile.h" #include "id3v2tag.h" #include "dsdiffproperties.h" #include "dsdiffdiintag.h" namespace TagLib { //! An implementation of DSDIFF metadata /*! * This is an implementation of DSDIFF metadata. * * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF * chunk as well as properties from the file. * Description of the DSDIFF format is available at * <a href="https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf"> * DSDIFF_1.5_Spec.pdf</a>. * The DSDIFF standard does not explicitly specify the ID3 chunk. * It can be found at the root level, but also sometimes inside the PROP chunk. * In addition, title and artist info are stored as part of the standard. */ namespace DSDIFF { //! An implementation of TagLib::File with DSDIFF specific methods. /*! * This implements and provides an interface for DSDIFF files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to DSDIFF files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches DIIN tags. DIIN = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs a DSDIFF file from \a file. If \a readProperties is \c true * the file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a DSDIFF file from \a stream. If \a readProperties is \c true * the file's audio properties will also be read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Destroys this instance of the File. */ ~File() override; /*! * Returns a pointer to a tag that is the union of the ID3v2 and DIIN * tags. The ID3v2 tag is given priority in reading the information -- if * requested information exists in both the ID3v2 tag and the ID3v1 tag, * the information from the ID3v2 tag will be returned. * * If you would like more granular control over the content of the tags, * with the concession of generality, use the tag-type specific calls. * * \note As this tag is not implemented as an ID3v2 tag or a DIIN tag, * but a union of the two this pointer may not be cast to the specific * tag types. * * \see ID3v2Tag() * \see DIINTag() */ Tag *tag() const override; /*! * Returns the ID3V2 Tag for this file. * * \note This always returns a valid pointer regardless of whether or not * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the * file on disk actually has an ID3v2 tag. * * \see hasID3v2Tag() */ ID3v2::Tag *ID3v2Tag(bool create = false) const; /*! * Returns the DSDIFF DIIN Tag for this file * */ DSDIFF::DIIN::Tag *DIINTag(bool create = false) const; /*! * Implements the unified property interface -- export function. * This method forwards to ID3v2::Tag::properties(). */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. * This method forwards to ID3v2::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the AIFF::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. If at least one tag -- ID3v1 or DIIN -- exists this * will duplicate its content into the other tag. This returns \c true * if saving was successful. * * If neither exists or if both tags are empty, this will strip the tags * from the file. * * This is the same as calling save(AllTags); * * If you would like more granular control over the content of the tags, * with the concession of generality, use parameterized save call below. * * \see save(int tags) */ bool save() override; /*! * Save the file. If \a strip is specified, it is possible to choose if * tags not specified in \a tags should be stripped from the file or * retained. With \a version, it is possible to specify whether ID3v2.4 * or ID3v2.3 should be used. */ bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4); /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns \c true if the tags are * successfully stripped. * * \note This will update the file immediately. */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Returns whether or not the file on disk actually has the DSDIFF * title and artist tags. * * \see DIINTag() */ bool hasDIINTag() const; /*! * Returns whether or not the given \a stream can be opened as a DSDIFF * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); protected: enum Endianness { BigEndian, LittleEndian }; private: void removeRootChunk(const ByteVector &id); void removeRootChunk(unsigned int i); void removeChildChunk(const ByteVector &id, unsigned int childChunkNum); void removeChildChunk(unsigned int i, unsigned int childChunkNum); /*! * Sets the data for the specified chunk at root level to \a data. * * \warning This will update the file immediately. */ void setRootChunkData(unsigned int i, const ByteVector &data); /*! * Sets the data for the root-level chunk \a name to \a data. * If a root-level chunk with the given name already exists * it will be overwritten, otherwise it will be * created after the existing chunks. * * \warning This will update the file immediately. */ void setRootChunkData(const ByteVector &name, const ByteVector &data); /*! * Sets the data for the specified child chunk to \a data. * * If data is null, then remove the chunk * * \warning This will update the file immediately. */ void setChildChunkData(unsigned int i, const ByteVector &data, unsigned int childChunkNum); /*! * Sets the data for the child chunk \a name to \a data. If a chunk with * the given name already exists it will be overwritten, otherwise it will * be created after the existing chunks inside the child chunk. * * If data is null, then remove the chunks with \a name name * * \warning This will update the file immediately. */ void setChildChunkData(const ByteVector &name, const ByteVector &data, unsigned int childChunkNum); void updateRootChunksStructure(unsigned int startingChunk); void read(bool readProperties, Properties::ReadStyle propertiesStyle); void writeChunk(const ByteVector &name, const ByteVector &data, unsigned long long offset, unsigned long replace = 0, unsigned int leadingPadding = 0); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace DSDIFF } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdiffproperties.cpp�����������������������������������������������������0000664�0000000�0000000�00000006440�14662262111�0021543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "dsdiffproperties.h" #include "tstring.h" using namespace TagLib; class DSDIFF::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int sampleWidth { 0 }; unsigned long long sampleCount { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// DSDIFF::Properties::Properties(unsigned int sampleRate, unsigned short channels, unsigned long long samplesCount, int bitrate, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { d->channels = channels; d->sampleCount = samplesCount; d->sampleWidth = 1; d->sampleRate = sampleRate; d->bitrate = bitrate; d->length = d->sampleRate > 0 ? static_cast<int>(d->sampleCount * 1000.0 / d->sampleRate + 0.5) : 0; } DSDIFF::Properties::~Properties() = default; int DSDIFF::Properties::lengthInSeconds() const { return d->length / 1000; } int DSDIFF::Properties::lengthInMilliseconds() const { return d->length; } int DSDIFF::Properties::bitrate() const { return d->bitrate; } int DSDIFF::Properties::sampleRate() const { return d->sampleRate; } int DSDIFF::Properties::channels() const { return d->channels; } int DSDIFF::Properties::bitsPerSample() const { return d->sampleWidth; } long long DSDIFF::Properties::sampleCount() const { return d->sampleCount; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsdiff/dsdiffproperties.h�������������������������������������������������������0000664�0000000�0000000�00000005761�14662262111�0021215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Damien Plisson, Audirvana email : damien78@audirvana.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DSDIFFPROPERTIES_H #define TAGLIB_DSDIFFPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace DSDIFF { class File; //! An implementation of audio property reading for DSDIFF /*! * This reads the data from a DSDIFF stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of DSDIFF::Properties with the data read from the * ByteVector \a data. */ Properties(unsigned int sampleRate, unsigned short channels, unsigned long long samplesCount, int bitrate, ReadStyle style); /*! * Destroys this DSDIFF::Properties instance. */ ~Properties() override; // Reimplementations. int lengthInSeconds() const override; int lengthInMilliseconds() const override; int bitrate() const override; int sampleRate() const override; int channels() const override; int bitsPerSample() const; long long sampleCount() const; private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace DSDIFF } // namespace TagLib #endif ���������������taglib-2.0.2/taglib/dsf/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014774�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsf/dsffile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000015651�14662262111�0017124�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013-2023 Stephen F. Booth email : me@sbooth.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "dsffile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; class DSF::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory) : ID3v2FrameFactory(frameFactory ? frameFactory : ID3v2::FrameFactory::instance()) { } ~FilePrivate() = default; FilePrivate(const FilePrivate &) = delete; FilePrivate &operator=(const FilePrivate &) = delete; const ID3v2::FrameFactory *ID3v2FrameFactory; long long fileSize = 0; long long metadataOffset = 0; std::unique_ptr<Properties> properties; std::unique_ptr<ID3v2::Tag> tag; }; bool DSF::File::isSupported(IOStream *stream) { // A DSF file has to start with "DSD " const ByteVector id = Utils::readHeader(stream, 4, false); return id.startsWith("DSD "); } DSF::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(propertiesStyle); } DSF::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(propertiesStyle); } DSF::File::~File() = default; ID3v2::Tag *DSF::File::tag() const { return d->tag.get(); } PropertyMap DSF::File::properties() const { return d->tag->properties(); } PropertyMap DSF::File::setProperties(const PropertyMap &properties) { return d->tag->setProperties(properties); } DSF::Properties *DSF::File::audioProperties() const { return d->properties.get(); } bool DSF::File::save() { return save(ID3v2::v4); } bool DSF::File::save(ID3v2::Version version) { if(readOnly()) { debug("DSF::File::save() - Cannot save to a read only file."); return false; } // Three things must be updated: the file size, the tag data, and the metadata offset if(d->tag->isEmpty()) { long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize; // Update the file size if(d->fileSize != newFileSize) { insert(ByteVector::fromLongLong(newFileSize, false), 12, 8); d->fileSize = newFileSize; } // Update the metadata offset to 0 since there is no longer a tag if(d->metadataOffset) { insert(ByteVector::fromLongLong(0ULL, false), 20, 8); d->metadataOffset = 0; } // Delete the old tag truncate(newFileSize); } else { ByteVector tagData = d->tag->render(version); long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize; long long newFileSize = newMetadataOffset + tagData.size(); long long oldTagSize = d->fileSize - newMetadataOffset; // Update the file size if(d->fileSize != newFileSize) { insert(ByteVector::fromLongLong(newFileSize, false), 12, 8); d->fileSize = newFileSize; } // Update the metadata offset if(d->metadataOffset != newMetadataOffset) { insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8); d->metadataOffset = newMetadataOffset; } // Delete the old tag and write the new one insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize)); } return true; } void DSF::File::read(AudioProperties::ReadStyle propertiesStyle) { if(!isOpen()) return; // A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk // The file format is not chunked in the sense of a RIFF File, though // DSD chunk ByteVector chunkName = readBlock(4); if(chunkName != "DSD ") { debug("DSF::File::read() -- Not a DSF file."); setValid(false); return; } long long dsdHeaderSize = readBlock(8).toLongLong(false); // Integrity check if(dsdHeaderSize != 28) { debug("DSF::File::read() -- File is corrupted, wrong DSD header size"); setValid(false); return; } d->fileSize = readBlock(8).toLongLong(false); // File is malformed or corrupted, allow trailing garbage if(d->fileSize > length()) { debug("DSF::File::read() -- File is corrupted wrong length"); setValid(false); return; } d->metadataOffset = readBlock(8).toLongLong(false); // File is malformed or corrupted if(d->metadataOffset > d->fileSize) { debug("DSF::File::read() -- Invalid metadata offset."); setValid(false); return; } // Format chunk chunkName = readBlock(4); if(chunkName != "fmt ") { debug("DSF::File::read() -- Missing 'fmt ' chunk."); setValid(false); return; } long long fmtHeaderSize = readBlock(8).toLongLong(false); if(fmtHeaderSize != 52) { debug("DSF::File::read() -- File is corrupted, wrong FMT header size"); setValid(false); return; } d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle); // Skip the data chunk // A metadata offset of 0 indicates the absence of an ID3v2 tag if(d->metadataOffset == 0) d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory); else d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset, d->ID3v2FrameFactory); } ���������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsf/dsffile.h�������������������������������������������������������������������0000664�0000000�0000000�00000013150�14662262111�0016561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013-2023 Stephen F. Booth email : me@sbooth.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DSFFILE_H #define TAGLIB_DSFFILE_H #include <memory> #include "taglib_export.h" #include "tfile.h" #include "dsfproperties.h" #include "id3v2tag.h" namespace TagLib { //! An implementation of DSF metadata /*! * This is an implementation of DSF metadata using an ID3v2 tag inside the * metadata chunk. * The DSF specification is located at * http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf */ namespace DSF { //! An implementation of TagLib::File with DSF specific methods /*! * This implements and provides an interface for DSF files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to DSF files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * Constructs a DSD stream file from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a DSD stream file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Destroys this instance of the File. */ ~File() override; /*! * Returns the ID3v2 Tag for this file. */ ID3v2::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * Forwards to ID3v2::Tag::properties(). */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Forwards to ID3v2::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the DSF::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Save the file. * * \a version specifies the ID3v2 version to be used for writing tags. */ bool save(ID3v2::Version version); /*! * Returns whether or not the given \a stream can be opened as a DSF * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(AudioProperties::ReadStyle propertiesStyle); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace DSF } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsf/dsfproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000010153�14662262111�0020371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013-2023 Stephen F. Booth email : me@sbooth.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "dsfproperties.h" using namespace TagLib; class DSF::Properties::PropertiesPrivate { public: PropertiesPrivate() = default; ~PropertiesPrivate() = default; PropertiesPrivate(const PropertiesPrivate &) = delete; PropertiesPrivate &operator=(const PropertiesPrivate &) = delete; // Nomenclature is from DSF file format specification unsigned int formatVersion = 0; unsigned int formatID = 0; unsigned int channelType = 0; unsigned int channelNum = 0; unsigned int samplingFrequency = 0; unsigned int bitsPerSample = 0; long long sampleCount = 0; unsigned int blockSizePerChannel = 0; // Computed unsigned int bitrate = 0; unsigned int length = 0; }; DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(data); } DSF::Properties::~Properties() = default; int DSF::Properties::lengthInMilliseconds() const { return d->length; } int DSF::Properties::bitrate() const { return d->bitrate; } int DSF::Properties::sampleRate() const { return d->samplingFrequency; } int DSF::Properties::channels() const { return d->channelNum; } int DSF::Properties::formatVersion() const { return d->formatVersion; } int DSF::Properties::formatID() const { return d->formatID; } int DSF::Properties::channelType() const { return d->channelType; } int DSF::Properties::bitsPerSample() const { return d->bitsPerSample; } long long DSF::Properties::sampleCount() const { return d->sampleCount; } int DSF::Properties::blockSizePerChannel() const { return d->blockSizePerChannel; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void DSF::Properties::read(const ByteVector &data) { d->formatVersion = data.toUInt(0U,false); d->formatID = data.toUInt(4U,false); d->channelType = data.toUInt(8U,false); d->channelNum = data.toUInt(12U,false); d->samplingFrequency = data.toUInt(16U,false); d->bitsPerSample = data.toUInt(20U,false); d->sampleCount = data.toLongLong(24U,false); d->blockSizePerChannel = data.toUInt(32U,false); d->bitrate = static_cast<unsigned int>( d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5); d->length = d->samplingFrequency > 0 ? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/dsf/dsfproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000005711�14662262111�0020042�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013-2023 Stephen F. Booth email : me@sbooth.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DSFPROPERTIES_H #define TAGLIB_DSFPROPERTIES_H #include <memory> #include "taglib_export.h" #include "tbytevector.h" #include "audioproperties.h" namespace TagLib { namespace DSF { //! An implementation of audio properties for DSF class TAGLIB_EXPORT Properties : public AudioProperties { public: Properties(const ByteVector &data, ReadStyle style); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; int lengthInMilliseconds() const override; int bitrate() const override; int sampleRate() const override; int channels() const override; int formatVersion() const; int formatID() const; /*! * Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels, * 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels */ int channelType() const; int bitsPerSample() const; long long sampleCount() const; int blockSizePerChannel() const; private: void read(const ByteVector &data); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace DSF } // namespace TagLib #endif �������������������������������������������������������taglib-2.0.2/taglib/fileref.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000040175�14662262111�0016347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2010 by Alex Novichkov email : novichko@atnet.ru (added APE file support) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "fileref.h" #include <cstring> #include <utility> #include "tfilestream.h" #include "tpropertymap.h" #include "tstringlist.h" #include "tvariant.h" #include "tdebug.h" #include "aifffile.h" #include "apefile.h" #include "asffile.h" #include "flacfile.h" #include "itfile.h" #include "modfile.h" #include "mp4file.h" #include "mpcfile.h" #include "mpegfile.h" #include "oggflacfile.h" #include "opusfile.h" #include "s3mfile.h" #include "speexfile.h" #include "trueaudiofile.h" #include "vorbisfile.h" #include "wavfile.h" #include "wavpackfile.h" #include "xmfile.h" #include "dsffile.h" #include "dsdifffile.h" using namespace TagLib; class FileRef::FileTypeResolver::FileTypeResolverPrivate { }; class FileRef::StreamTypeResolver::StreamTypeResolverPrivate { }; namespace { List<const FileRef::FileTypeResolver *> fileTypeResolvers; // Detect the file type by user-defined resolvers. File *detectByResolvers(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { #ifdef _WIN32 if(::wcslen(fileName) == 0) return nullptr; #else if(::strlen(fileName) == 0) return nullptr; #endif for(const auto &resolver : std::as_const(fileTypeResolvers)) { File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle); if(file) return file; } return nullptr; } File *detectByResolvers(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { for(const auto &resolver : std::as_const(fileTypeResolvers)) { if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) { if(File *file = streamResolver->createFileFromStream( stream, readAudioProperties, audioPropertiesStyle)) return file; } } return nullptr; } // Detect the file type based on the file extension. File* detectByExtension(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { #ifdef _WIN32 const String s = stream->name().toString(); #else const String s(stream->name()); #endif String ext; if(const int pos = s.rfind("."); pos != -1) ext = s.substr(pos + 1).upper(); // If this list is updated, the method defaultFileExtensions() should also be // updated. However at some point that list should be created at the same time // that a default file type resolver is created. if(ext.isEmpty()) return nullptr; // .oga can be any audio in the Ogg container. So leave it to content-based detection. File *file = nullptr; if(ext == "MP3" || ext == "MP2" || ext == "AAC") file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "OGG") file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "OGA") { /* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */ file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle); if(!file->isValid()) { delete file; file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); } } else if(ext == "FLAC") file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "MPC") file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "WV") file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "SPX") file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "OPUS") file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "TTA") file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V") file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "WMA" || ext == "ASF") file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "WAV") file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "APE") file = new APE::File(stream, readAudioProperties, audioPropertiesStyle); // module, nst and wow are possible but uncommon extensions else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW") file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "S3M") file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "IT") file = new IT::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "XM") file = new XM::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "DSF") file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle); else if(ext == "DFF" || ext == "DSDIFF") file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle); // if file is not valid, leave it to content-based detection. if(file) { if(file->isValid()) return file; delete file; } return nullptr; } // Detect the file type based on the actual content of the stream. File *detectByContent(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { File *file = nullptr; if(MPEG::File::isSupported(stream)) file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle); else if(Ogg::Vorbis::File::isSupported(stream)) file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle); else if(Ogg::FLAC::File::isSupported(stream)) file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle); else if(FLAC::File::isSupported(stream)) file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle); else if(MPC::File::isSupported(stream)) file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle); else if(WavPack::File::isSupported(stream)) file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle); else if(Ogg::Speex::File::isSupported(stream)) file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle); else if(Ogg::Opus::File::isSupported(stream)) file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle); else if(TrueAudio::File::isSupported(stream)) file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle); else if(MP4::File::isSupported(stream)) file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle); else if(ASF::File::isSupported(stream)) file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle); else if(RIFF::AIFF::File::isSupported(stream)) file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle); else if(RIFF::WAV::File::isSupported(stream)) file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle); else if(APE::File::isSupported(stream)) file = new APE::File(stream, readAudioProperties, audioPropertiesStyle); else if(DSF::File::isSupported(stream)) file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle); else if(DSDIFF::File::isSupported(stream)) file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle); // isSupported() only does a quick check, so double check the file here. if(file) { if(file->isValid()) return file; delete file; } return nullptr; } } // namespace class FileRef::FileRefPrivate { public: FileRefPrivate() = default; ~FileRefPrivate() { delete file; delete stream; } FileRefPrivate(const FileRefPrivate &) = delete; FileRefPrivate &operator=(const FileRefPrivate &) = delete; bool isNull() const { return !file || !file->isValid(); } bool isNullWithDebugMessage(const String &methodName) const { if(isNull()) { debug("FileRef::" + methodName + "() - Called without a valid file."); return true; } return false; } File *file { nullptr }; IOStream *stream { nullptr }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FileRef::FileRef() : d(std::make_shared<FileRefPrivate>()) { } FileRef::FileRef(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : d(std::make_shared<FileRefPrivate>()) { parse(fileName, readAudioProperties, audioPropertiesStyle); } FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : d(std::make_shared<FileRefPrivate>()) { parse(stream, readAudioProperties, audioPropertiesStyle); } FileRef::FileRef(File *file) : d(std::make_shared<FileRefPrivate>()) { d->file = file; } FileRef::FileRef(const FileRef &) = default; FileRef::~FileRef() = default; Tag *FileRef::tag() const { if(d->isNullWithDebugMessage(__func__)) { return nullptr; } return d->file->tag(); } PropertyMap FileRef::properties() const { if(d->isNullWithDebugMessage(__func__)) { return PropertyMap(); } return d->file->properties(); } void FileRef::removeUnsupportedProperties(const StringList& properties) { if(d->isNullWithDebugMessage(__func__)) { return; } return d->file->removeUnsupportedProperties(properties); } PropertyMap FileRef::setProperties(const PropertyMap &properties) { if(d->isNullWithDebugMessage(__func__)) { return PropertyMap(); } return d->file->setProperties(properties); } StringList FileRef::complexPropertyKeys() const { if(d->isNullWithDebugMessage(__func__)) { return StringList(); } return d->file->complexPropertyKeys(); } List<VariantMap> FileRef::complexProperties(const String &key) const { if(d->isNullWithDebugMessage(__func__)) { return List<VariantMap>(); } return d->file->complexProperties(key); } bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value) { if(d->isNullWithDebugMessage(__func__)) { return false; } return d->file->setComplexProperties(key, value); } AudioProperties *FileRef::audioProperties() const { if(d->isNullWithDebugMessage(__func__)) { return nullptr; } return d->file->audioProperties(); } File *FileRef::file() const { return d->file; } bool FileRef::save() { if(d->isNullWithDebugMessage(__func__)) { return false; } return d->file->save(); } const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static { fileTypeResolvers.prepend(resolver); return resolver; } void FileRef::clearFileTypeResolvers() // static { fileTypeResolvers.clear(); } StringList FileRef::defaultFileExtensions() { StringList l; l.append("ogg"); l.append("flac"); l.append("oga"); l.append("opus"); l.append("mp3"); l.append("mp2"); l.append("mpc"); l.append("wv"); l.append("spx"); l.append("tta"); l.append("aac"); l.append("m4a"); l.append("m4r"); l.append("m4b"); l.append("m4p"); l.append("3g2"); l.append("mp4"); l.append("m4v"); l.append("wma"); l.append("asf"); l.append("aif"); l.append("aiff"); l.append("afc"); l.append("aifc"); l.append("wav"); l.append("ape"); l.append("mod"); l.append("module"); // alias for "mod" l.append("nst"); // alias for "mod" l.append("wow"); // alias for "mod" l.append("s3m"); l.append("it"); l.append("xm"); l.append("dsf"); l.append("dff"); l.append("dsdiff"); // alias for "dff" return l; } bool FileRef::isNull() const { return d->isNull(); } FileRef &FileRef::operator=(const FileRef &) = default; void FileRef::swap(FileRef &ref) noexcept { using std::swap; swap(d, ref.d); } bool FileRef::operator==(const FileRef &ref) const { return ref.d->file == d->file; } bool FileRef::operator!=(const FileRef &ref) const { return ref.d->file != d->file; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void FileRef::parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { // Try user-defined resolvers. d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle); if(d->file) return; // Try to resolve file types based on the file extension. d->stream = new FileStream(fileName); d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle); if(d->file) return; // At last, try to resolve file types based on the actual content. d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle); if(d->file) return; // Stream have to be closed here if failed to resolve file types. delete d->stream; d->stream = nullptr; } void FileRef::parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) { // Try user-defined stream resolvers. d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle); if(d->file) return; // Try user-defined resolvers. d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle); if(d->file) return; // Try to resolve file types based on the file extension. d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle); if(d->file) return; // At last, try to resolve file types based on the actual content of the file. d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle); } FileRef::FileTypeResolver::FileTypeResolver() = default; FileRef::FileTypeResolver::~FileTypeResolver() = default; FileRef::StreamTypeResolver::StreamTypeResolver() = default; FileRef::StreamTypeResolver::~StreamTypeResolver() = default; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/fileref.h�����������������������������������������������������������������������0000664�0000000�0000000�00000037641�14662262111�0016020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FILEREF_H #define TAGLIB_FILEREF_H #include "tfile.h" #include "tstringlist.h" #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { class Tag; //! This class provides a simple abstraction for creating and handling files /*! * FileRef exists to provide a minimal, generic and value-based wrapper around * a File. It is lightweight and implicitly shared, and as such suitable for * pass-by-value use. This hides some of the uglier details of TagLib::File * and the non-generic portions of the concrete file implementations. * * This class is useful in a "simple usage" situation where it is desirable * to be able to get and set some of the tag information that is similar * across file types. * * Also note that it is probably a good idea to plug this into your mime * type system rather than using the constructor that accepts a file name using * the FileTypeResolver. * * \see FileTypeResolver * \see addFileTypeResolver() */ class TAGLIB_EXPORT FileRef { public: //! A class for pluggable file type resolution. /*! * %File type resolver, better implement StreamTypeResolver in order to * support both file and stream resolution. */ class TAGLIB_EXPORT FileTypeResolver { public: FileTypeResolver(); /*! * Destroys this FileTypeResolver instance. */ virtual ~FileTypeResolver() = 0; FileTypeResolver(const FileTypeResolver &) = delete; FileTypeResolver &operator=(const FileTypeResolver &) = delete; /*! * This method must be overridden to provide an additional file type * resolver. If the resolver is able to determine the file type it should * return a valid File object; if not it should return nullptr. * * \note The created file is then owned by the FileRef and should not be * deleted. Deletion will happen automatically when the FileRef passes * out of scope. */ virtual File *createFile(FileName fileName, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average) const = 0; private: class FileTypeResolverPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FileTypeResolverPrivate> d; }; //! A class for pluggable stream type resolution. /*! * This class is used to extend TagLib's very basic file name based file * type resolution. * * This can be accomplished with: * * \code * * class MyStreamTypeResolver : StreamTypeResolver * { * TagLib::File *createFile(TagLib::FileName *fileName, bool readProps, * AudioProperties::ReadStyle readStyle) const override * { * if(someCheckForAnMP3File(fileName)) * return new TagLib::MPEG::File(fileName, readProps, readStyle); * return nullptr; * } * * TagLib::File *createFileFromStream(TagLib::IOStream *s, bool readProps, * AudioProperties::ReadStyle readStyle) const override * { * if(someCheckForAnMP3Stream(s)) * return new TagLib::MPEG::File(s, readProps, readStyle); * return nullptr; * } * } * * FileRef::addFileTypeResolver(new MyStreamTypeResolver); * * \endcode * * Naturally a less contrived example would be slightly more complex. This * can be used to plug in mime-type detection systems or to add new file types * to TagLib. */ class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver { public: StreamTypeResolver(); /*! * Destroys this StreamTypeResolver instance. */ virtual ~StreamTypeResolver() override = 0; // virtual is needed by SWIG StreamTypeResolver(const StreamTypeResolver &) = delete; StreamTypeResolver &operator=(const StreamTypeResolver &) = delete; /*! * This method must be overridden to provide an additional stream type * resolver. If the resolver is able to determine the file type it should * return a valid File object; if not it should return nullptr. * * \note The created file is then owned by the FileRef and should not be * deleted. Deletion will happen automatically when the FileRef passes * out of scope. * * \see createFile() */ virtual File *createFileFromStream(IOStream *stream, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average) const = 0; private: class StreamTypeResolverPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<StreamTypeResolverPrivate> d; }; /*! * Creates a null FileRef. */ FileRef(); /*! * Create a FileRef from \a fileName. If \a readAudioProperties is \c true then * the audio properties will be read using \a audioPropertiesStyle. If * \a readAudioProperties is \c false then \a audioPropertiesStyle will be * ignored. * * Also see the note in the class documentation about why you may not want to * use this method in your application. */ explicit FileRef(FileName fileName, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); /*! * Construct a FileRef from an opened \a IOStream. If \a readAudioProperties * is \c true then the audio properties will be read using \a audioPropertiesStyle. * If \a readAudioProperties is \c false then \a audioPropertiesStyle will be * ignored. * * Also see the note in the class documentation about why you may not want to * use this method in your application. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ explicit FileRef(IOStream* stream, bool readAudioProperties = true, AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); /*! * Construct a FileRef using \a file. The FileRef now takes ownership of the * pointer and will delete the File when it passes out of scope. */ explicit FileRef(File *file); /*! * Make a copy of \a ref. */ FileRef(const FileRef &ref); /*! * Destroys this FileRef instance. */ ~FileRef(); /*! * Returns a pointer to the represented file's tag. * * \warning This pointer will become invalid when this FileRef and all * copies pass out of scope. * * \warning Do not cast it to any subclasses of Tag. * Use tag returning methods of appropriate subclasses of File instead. * * \see File::tag() */ Tag *tag() const; /*! * Exports the tags of the file as dictionary mapping (human readable) tag * names (uppercase Strings) to StringLists of tag values. Calls this * method on the wrapped File instance. * For each metadata object of the file that could not be parsed into the PropertyMap * format, the returned map's unsupportedData() list will contain one entry identifying * that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties() * to remove (a subset of) them. * For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2 * tag) only the most "modern" one will be exported (ID3v2 in this case). */ PropertyMap properties() const; /*! * Removes unsupported properties, or a subset of them, from the file's metadata. * The parameter \a properties must contain only entries from * properties().unsupportedData(). */ void removeUnsupportedProperties(const StringList& properties); /*! * Sets the tags of the wrapped File to those specified in \a properties. * If some value(s) could not be written to the specific metadata format, * the returned PropertyMap will contain those value(s). Otherwise it will be empty, * indicating that no problems occurred. * With file types that support several tag formats (for instance, MP3 files can have * ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one * (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't * be taken into account for the return value of this function. * See the documentation of the subclass implementations for detailed descriptions. */ PropertyMap setProperties(const PropertyMap &properties); /*! * Get the keys of complex properties, i.e. properties which cannot be * represented simply by a string. * Because such properties might be expensive to fetch, there are separate * operations to get the available keys - which is expected to be cheap - * and getting and setting the property values. * Calls the method on the wrapped File, which collects the keys from one * or more of its tags. */ StringList complexPropertyKeys() const; /*! * Get the complex properties for a given \a key. * In order to be flexible for different metadata formats, the properties * are represented as variant maps. Despite this dynamic nature, some * degree of standardization should be achieved between formats: * * - PICTURE * - data: ByteVector with picture data * - description: String with description * - pictureType: String with type as specified for ID3v2, * e.g. "Front Cover", "Back Cover", "Band" * - mimeType: String with image format, e.g. "image/jpeg" * - optionally more information found in the tag, such as * "width", "height", "numColors", "colorDepth" int values * in FLAC pictures * - GENERALOBJECT * - data: ByteVector with object data * - description: String with description * - fileName: String with file name * - mimeType: String with MIME type * - this is currently only implemented for ID3v2 GEOB frames * * Calls the method on the wrapped File, which gets the properties from one * or more of its tags. */ List<VariantMap> complexProperties(const String &key) const; /*! * Set all complex properties for a given \a key using variant maps as * \a value with the same format as returned by complexProperties(). * An empty list as \a value removes all complex properties for \a key. */ bool setComplexProperties(const String &key, const List<VariantMap> &value); /*! * Returns the audio properties for this FileRef. If no audio properties * were read then this will return a null pointer. */ AudioProperties *audioProperties() const; /*! * Returns a pointer to the file represented by this handler class. * * As a general rule this call should be avoided since if you need to work * with file objects directly, you are probably better served instantiating * the File subclasses (i.e. MPEG::File) manually and working with their APIs. * * This <i>handle</i> exists to provide a minimal, generic and value-based * wrapper around a File. Accessing the file directly generally indicates * a moving away from this simplicity (and into things beyond the scope of * FileRef). * * \warning This pointer will become invalid when this FileRef and all * copies pass out of scope. */ File *file() const; /*! * Saves the file. Returns \c true on success. */ bool save(); /*! * Adds a FileTypeResolver to the list of those used by TagLib. Each * additional FileTypeResolver is added to the front of a list of resolvers * that are tried. If the FileTypeResolver returns zero the next resolver * is tried. * * Returns a pointer to the added resolver (the same one that's passed in -- * this is mostly so that static initializers have something to use for * assignment). * * \see FileTypeResolver */ static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver); /*! * Remove all resolvers added by addFileTypeResolver(). */ static void clearFileTypeResolvers(); /*! * As is mentioned elsewhere in this class's documentation, the default file * type resolution code provided by TagLib only works by comparing file * extensions. * * This method returns the list of file extensions that are used by default. * * The extensions are all returned in lowercase, though the comparison used * by TagLib for resolution is case-insensitive. * * \note This does not account for any additional file type resolvers that * are plugged in. Also note that this is not intended to replace a proper * mime-type resolution system, but is just here for reference. * * \see FileTypeResolver */ static StringList defaultFileExtensions(); /*! * Returns \c true if the file (and as such other pointers) are null. */ bool isNull() const; /*! * Assign the file pointed to by \a ref to this FileRef. */ FileRef &operator=(const FileRef &ref); /*! * Exchanges the content of the FileRef with the content of \a ref. */ void swap(FileRef &ref) noexcept; /*! * Returns \c true if this FileRef and \a ref point to the same File object. */ bool operator==(const FileRef &ref) const; /*! * Returns \c true if this FileRef and \a ref do not point to the same File * object. */ bool operator!=(const FileRef &ref) const; private: void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle); void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle); class FileRefPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<FileRefPrivate> d; }; } // namespace TagLib #endif �����������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015125�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacfile.cpp���������������������������������������������������������������0000664�0000000�0000000�00000041657�14662262111�0017413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003-2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "flacfile.h" #include <algorithm> #include <utility> #include "tdebug.h" #include "tpropertymap.h" #include "tagunion.h" #include "tagutils.h" #include "id3v2tag.h" #include "id3v1tag.h" #include "xiphcomment.h" #include "flacpicture.h" #include "flacmetadatablock.h" #include "flacunknownmetadatablock.h" using namespace TagLib; namespace { enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 }; constexpr long MinPaddingLength = 4096; constexpr long MaxPaddingLegnth = 1024 * 1024; constexpr char LastBlockFlag = '\x80'; } // namespace class FLAC::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory) { blocks.setAutoDelete(true); } const ID3v2::FrameFactory *ID3v2FrameFactory; offset_t ID3v2Location { -1 }; long ID3v2OriginalSize { 0 }; offset_t ID3v1Location { -1 }; TagUnion tag; std::unique_ptr<Properties> properties; ByteVector xiphCommentData; List<FLAC::MetadataBlock *> blocks; offset_t flacStart { 0 }; offset_t streamStart { 0 }; bool scanned { false }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool FLAC::File::isSupported(IOStream *stream) { // A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true); return buffer.find("fLaC") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties); } FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } FLAC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties); } FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } FLAC::File::~File() = default; TagLib::Tag *FLAC::File::tag() const { return &d->tag; } PropertyMap FLAC::File::properties() const { return d->tag.properties(); } void FLAC::File::removeUnsupportedProperties(const StringList &unsupported) { d->tag.removeUnsupportedProperties(unsupported); } PropertyMap FLAC::File::setProperties(const PropertyMap &properties) { return xiphComment(true)->setProperties(properties); } StringList FLAC::File::complexPropertyKeys() const { StringList keys = TagLib::File::complexPropertyKeys(); if(!keys.contains("PICTURE")) { if(std::any_of(d->blocks.cbegin(), d->blocks.cend(), [](MetadataBlock *block) { return dynamic_cast<Picture *>(block) != nullptr; })) { keys.append("PICTURE"); } } return keys; } List<VariantMap> FLAC::File::complexProperties(const String &key) const { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { List<VariantMap> props; for(const auto &block : std::as_const(d->blocks)) { if(auto picture = dynamic_cast<Picture *>(block)) { VariantMap property; property.insert("data", picture->data()); property.insert("mimeType", picture->mimeType()); property.insert("description", picture->description()); property.insert("pictureType", FLAC::Picture::typeToString(picture->type())); property.insert("width", picture->width()); property.insert("height", picture->height()); property.insert("numColors", picture->numColors()); property.insert("colorDepth", picture->colorDepth()); props.append(property); } } return props; } return TagLib::File::complexProperties(key); } bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { removePictures(); for(const auto &property : value) { auto picture = new FLAC::Picture; picture->setData(property.value("data").value<ByteVector>()); picture->setMimeType(property.value("mimeType").value<String>()); picture->setDescription(property.value("description").value<String>()); picture->setType(FLAC::Picture::typeFromString( property.value("pictureType").value<String>())); picture->setWidth(property.value("width").value<int>()); picture->setHeight(property.value("height").value<int>()); picture->setNumColors(property.value("numColors").value<int>()); picture->setColorDepth(property.value("colorDepth").value<int>()); addPicture(picture); } } else { return TagLib::File::setComplexProperties(key, value); } return true; } FLAC::Properties *FLAC::File::audioProperties() const { return d->properties.get(); } bool FLAC::File::save() { if(readOnly()) { debug("FLAC::File::save() - Cannot save to a read only file."); return false; } if(!isValid()) { debug("FLAC::File::save() -- Trying to save invalid file."); return false; } // Create new vorbis comments if(!hasXiphComment()) Tag::duplicate(&d->tag, xiphComment(true), false); d->xiphCommentData = xiphComment()->render(false); // Replace metadata blocks MetadataBlock *commentBlock = new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData); for(auto it = d->blocks.begin(); it != d->blocks.end();) { if((*it)->code() == MetadataBlock::VorbisComment) { // Remove the old Vorbis Comment block delete *it; it = d->blocks.erase(it); continue; } if(commentBlock && (*it)->code() == MetadataBlock::Picture) { // Set the new Vorbis Comment block before the first picture block d->blocks.insert(it, commentBlock); commentBlock = nullptr; } ++it; } if(commentBlock) d->blocks.append(commentBlock); // Render data for the metadata blocks ByteVector data; for(const auto &block : std::as_const(d->blocks)) { ByteVector blockData = block->render(); ByteVector blockHeader = ByteVector::fromUInt(blockData.size()); blockHeader[0] = block->code(); data.append(blockHeader); data.append(blockData); } // Compute the amount of padding, and append that to data. offset_t originalLength = d->streamStart - d->flacStart; offset_t paddingLength = originalLength - data.size() - 4; if(paddingLength <= 0) { paddingLength = MinPaddingLength; } else { // Padding won't increase beyond 1% of the file size or 1MB. offset_t threshold = length() / 100; threshold = std::max<offset_t>(threshold, MinPaddingLength); threshold = std::min<offset_t>(threshold, MaxPaddingLegnth); if(paddingLength > threshold) paddingLength = MinPaddingLength; } ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength)); paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag); data.append(paddingHeader); data.resize(static_cast<unsigned int>(data.size() + paddingLength)); // Write the data to the file insert(data, d->flacStart, originalLength); d->streamStart += static_cast<long>(data.size()) - originalLength; if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - originalLength; // Update ID3 tags if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) { // ID3v2 tag is not empty. Update the old one or create a new one. if(d->ID3v2Location < 0) d->ID3v2Location = 0; data = ID3v2Tag()->render(); insert(data, d->ID3v2Location, d->ID3v2OriginalSize); d->flacStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize; d->streamStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize; if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->ID3v2OriginalSize; d->ID3v2OriginalSize = data.size(); } else { // ID3v2 tag is empty. Remove the old one. if(d->ID3v2Location >= 0) { removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); d->flacStart -= d->ID3v2OriginalSize; d->streamStart -= d->ID3v2OriginalSize; if(d->ID3v1Location >= 0) d->ID3v1Location -= d->ID3v2OriginalSize; d->ID3v2Location = -1; d->ID3v2OriginalSize = 0; } } if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. if(d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; } } return true; } ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) { return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create, d->ID3v2FrameFactory); } ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(FlacID3v1Index, create); } Ogg::XiphComment *FLAC::File::xiphComment(bool create) { return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create); } List<FLAC::Picture *> FLAC::File::pictureList() { List<Picture *> pictures; for(const auto &block : std::as_const(d->blocks)) { if(auto picture = dynamic_cast<Picture *>(block)) { pictures.append(picture); } } return pictures; } void FLAC::File::addPicture(Picture *picture) { d->blocks.append(picture); } void FLAC::File::removePicture(Picture *picture, bool del) { auto it = d->blocks.find(picture); if(it != d->blocks.end()) d->blocks.erase(it); if(del) delete picture; } void FLAC::File::removePictures() { for(auto it = d->blocks.begin(); it != d->blocks.end(); ) { if(dynamic_cast<Picture *>(*it)) { delete *it; it = d->blocks.erase(it); } else { ++it; } } } void FLAC::File::strip(int tags) { if(tags & ID3v1) d->tag.set(FlacID3v1Index, nullptr); if(tags & ID3v2) d->tag.set(FlacID3v2Index, nullptr); if(tags & XiphComment) { xiphComment()->removeAllFields(); xiphComment()->removeAllPictures(); } } bool FLAC::File::hasXiphComment() const { return !d->xiphCommentData.isEmpty(); } bool FLAC::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } bool FLAC::File::hasID3v2Tag() const { return d->ID3v2Location >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void FLAC::File::read(bool readProperties) { // Look for an ID3v2 tag d->ID3v2Location = Utils::findID3v2(this); if(d->ID3v2Location >= 0) { d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); } // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); // Look for FLAC metadata, including vorbis comments scan(); if(!isValid()) return; if(!d->xiphCommentData.isEmpty()) d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData)); else d->tag.set(FlacXiphIndex, new Ogg::XiphComment()); if(readProperties) { // First block should be the stream_info metadata const ByteVector infoData = d->blocks.front()->render(); offset_t streamLength; if(d->ID3v1Location >= 0) streamLength = d->ID3v1Location - d->streamStart; else streamLength = length() - d->streamStart; d->properties = std::make_unique<Properties>(infoData, streamLength); } } void FLAC::File::scan() { // Scan the metadata pages if(d->scanned) return; if(!isValid()) return; offset_t nextBlockOffset; if(d->ID3v2Location >= 0) nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize); else nextBlockOffset = find("fLaC"); if(nextBlockOffset < 0) { debug("FLAC::File::scan() -- FLAC stream not found"); setValid(false); return; } nextBlockOffset += 4; d->flacStart = nextBlockOffset; while(true) { seek(nextBlockOffset); const ByteVector header = readBlock(4); if(header.size() != 4) { debug("FLAC::File::scan() -- Failed to read a block header"); setValid(false); return; } // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE // 0 : STREAMINFO // 1 : PADDING // .. // 4 : VORBIS_COMMENT // .. // 6 : PICTURE // .. // <24> Length of metadata to follow const char blockType = header[0] & ~LastBlockFlag; const bool isLastBlock = (header[0] & LastBlockFlag) != 0; const unsigned int blockLength = header.toUInt(1U, 3U); // First block should be the stream_info metadata if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) { debug("FLAC::File::scan() -- First block should be the stream_info metadata"); setValid(false); return; } if(blockLength == 0 && blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable) { debug("FLAC::File::scan() -- Zero-sized metadata block found"); setValid(false); return; } const ByteVector data = readBlock(blockLength); if(data.size() != blockLength) { debug("FLAC::File::scan() -- Failed to read a metadata block"); setValid(false); return; } MetadataBlock *block = nullptr; // Found the vorbis-comment if(blockType == MetadataBlock::VorbisComment) { if(d->xiphCommentData.isEmpty()) { d->xiphCommentData = data; block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data); } else { debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding"); } } else if(blockType == MetadataBlock::Picture) { auto picture = new FLAC::Picture(); if(picture->parse(data)) { block = picture; } else { debug("FLAC::File::scan() -- invalid picture found, discarding"); delete picture; } } else if(blockType == MetadataBlock::Padding) { // Skip all padding blocks. } else { block = new UnknownMetadataBlock(blockType, data); } if(block) d->blocks.append(block); nextBlockOffset += blockLength + 4; if(isLastBlock) break; } // End of metadata, now comes the datastream d->streamStart = nextBlockOffset; d->scanned = true; } ���������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacfile.h�����������������������������������������������������������������0000664�0000000�0000000�00000031403�14662262111�0017044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FLACFILE_H #define TAGLIB_FLACFILE_H #include "tfile.h" #include "tlist.h" #include "taglib_export.h" #include "tag.h" #include "flacpicture.h" #include "flacproperties.h" namespace TagLib { class Tag; namespace ID3v2 { class FrameFactory; class Tag; } namespace ID3v1 { class Tag; } namespace Ogg { class XiphComment; } //! An implementation of FLAC metadata /*! * This is an implementation of FLAC metadata for non-Ogg FLAC files. At some * point when Ogg / FLAC is more common there will be a similar implementation * under the Ogg hierarchy. * * This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream * properties from the file. */ namespace FLAC { //! An implementation of TagLib::File with FLAC specific methods /*! * This implements and provides an interface for FLAC files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to FLAC files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches Vorbis comments. XiphComment = 0x0001, //! Matches ID3v1 tags. ID3v1 = 0x0002, //! Matches ID3v2 tags. ID3v2 = 0x0004, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs a FLAC file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a FLAC file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs a FLAC file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a FLAC file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. This will be a union of XiphComment, * ID3v1 and ID3v2 tags. * * \see ID3v2Tag() * \see ID3v1Tag() * \see XiphComment() */ TagLib::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * If the file contains more than one tag (e.g. XiphComment and ID3v1), * only the first one (in the order XiphComment, ID3v2, ID3v1) will be * converted to the PropertyMap. */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &) override; /*! * Implements the unified property interface -- import function. * This always creates a Xiph comment, if none exists. The return value * relates to the Xiph comment only. * Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed * in the FLAC specification. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE. */ StringList complexPropertyKeys() const override; /*! * Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties * for \a key "PICTURE". */ List<VariantMap> complexProperties(const String &key) const override; /*! * Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE * for \a key "PICTURE". */ bool setComplexProperties(const String &key, const List<VariantMap> &value) override; /*! * Returns the FLAC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. This will primarily save the XiphComment, but * will also keep any old ID3-tags up to date. If the file * has no XiphComment, one will be constructed from the ID3-tags. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns a pointer to the ID3v2 tag of the file. * * If \a create is \c false (the default) this returns a null pointer * if there is no valid ID3v2 tag. If \a create is \c true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag <b>is still</b> owned by the FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v2Tag() */ ID3v2::Tag *ID3v2Tag(bool create = false); /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this returns a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the XiphComment for the file. * * If \a create is \c false (the default) this returns a null pointer * if there is no valid XiphComment. If \a create is \c true it will create * a XiphComment if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has a XiphComment. Use hasXiphComment() to check if the * file on disk actually has a XiphComment. * * \note The Tag <b>is still</b> owned by the FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasXiphComment() */ Ogg::XiphComment *xiphComment(bool create = false); /*! * Returns a list of pictures attached to the FLAC file. */ List<Picture *> pictureList(); /*! * Removes an attached picture. If \a del is \c true the picture's memory * will be freed; if it is \c false, it must be deleted by the user. */ void removePicture(Picture *picture, bool del = true); /*! * Remove all attached images. */ void removePictures(); /*! * Add a new picture to the file. The file takes ownership of the * picture and will handle freeing its memory. * * \note The file will be saved only after calling save(). */ void addPicture(Picture *picture); /*! * This will remove the tags that match the OR-ed together TagTypes from * the file. By default it removes all tags. * * \warning This will also invalidate pointers to the tags as their memory * will be freed. * * \note In order to make the removal permanent save() still needs to be * called. * * \note This won't remove the Vorbis comment block completely. The * vendor ID will be preserved. */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has a XiphComment. * * \see xiphComment() */ bool hasXiphComment() const; /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Returns whether or not the given \a stream can be opened as a FLAC * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); void scan(); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace FLAC } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacmetadatablock.cpp������������������������������������������������������0000664�0000000�0000000�00000003613�14662262111�0021255�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "flacmetadatablock.h" using namespace TagLib; class FLAC::MetadataBlock::MetadataBlockPrivate { }; FLAC::MetadataBlock::MetadataBlock() = default; FLAC::MetadataBlock::~MetadataBlock() = default; ���������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacmetadatablock.h��������������������������������������������������������0000664�0000000�0000000�00000005276�14662262111�0020731�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FLACMETADATABLOCK_H #define TAGLIB_FLACMETADATABLOCK_H #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { namespace FLAC { //! FLAC metadata block class TAGLIB_EXPORT MetadataBlock { public: MetadataBlock(); virtual ~MetadataBlock(); MetadataBlock(const MetadataBlock &item) = delete; MetadataBlock &operator=(const MetadataBlock &item) = delete; enum BlockType { StreamInfo = 0, Padding, Application, SeekTable, VorbisComment, CueSheet, Picture }; /*! * Returns the FLAC metadata block type. */ virtual int code() const = 0; /*! * Render the content of the block. */ virtual ByteVector render() const = 0; private: class MetadataBlockPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<MetadataBlockPrivate> d; }; } // namespace FLAC } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacpicture.cpp������������������������������������������������������������0000664�0000000�0000000�00000012442�14662262111�0020135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "flacpicture.h" #include "tdebug.h" using namespace TagLib; class FLAC::Picture::PicturePrivate { public: Type type { FLAC::Picture::Other }; String mimeType; String description; int width { 0 }; int height { 0 }; int colorDepth { 0 }; int numColors { 0 }; ByteVector data; }; FLAC::Picture::Picture() : d(std::make_unique<PicturePrivate>()) { } FLAC::Picture::Picture(const ByteVector &data) : d(std::make_unique<PicturePrivate>()) { parse(data); } FLAC::Picture::~Picture() = default; int FLAC::Picture::code() const { return FLAC::MetadataBlock::Picture; } bool FLAC::Picture::parse(const ByteVector &data) { if(data.size() < 32) { debug("A picture block must contain at least 5 bytes."); return false; } unsigned int pos = 0; d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos)); pos += 4; unsigned int mimeTypeLength = data.toUInt(pos); pos += 4; if(pos + mimeTypeLength + 24 > data.size()) { debug("Invalid picture block."); return false; } d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8); pos += mimeTypeLength; unsigned int descriptionLength = data.toUInt(pos); pos += 4; if(pos + descriptionLength + 20 > data.size()) { debug("Invalid picture block."); return false; } d->description = String(data.mid(pos, descriptionLength), String::UTF8); pos += descriptionLength; d->width = data.toUInt(pos); pos += 4; d->height = data.toUInt(pos); pos += 4; d->colorDepth = data.toUInt(pos); pos += 4; d->numColors = data.toUInt(pos); pos += 4; unsigned int dataLength = data.toUInt(pos); pos += 4; if(pos + dataLength > data.size()) { debug("Invalid picture block."); return false; } d->data = data.mid(pos, dataLength); return true; } ByteVector FLAC::Picture::render() const { ByteVector result; result.append(ByteVector::fromUInt(d->type)); ByteVector mimeTypeData = d->mimeType.data(String::UTF8); result.append(ByteVector::fromUInt(mimeTypeData.size())); result.append(mimeTypeData); ByteVector descriptionData = d->description.data(String::UTF8); result.append(ByteVector::fromUInt(descriptionData.size())); result.append(descriptionData); result.append(ByteVector::fromUInt(d->width)); result.append(ByteVector::fromUInt(d->height)); result.append(ByteVector::fromUInt(d->colorDepth)); result.append(ByteVector::fromUInt(d->numColors)); result.append(ByteVector::fromUInt(d->data.size())); result.append(d->data); return result; } FLAC::Picture::Type FLAC::Picture::type() const { return d->type; } void FLAC::Picture::setType(FLAC::Picture::Type type) { d->type = type; } String FLAC::Picture::mimeType() const { return d->mimeType; } void FLAC::Picture::setMimeType(const String &mimeType) { d->mimeType = mimeType; } String FLAC::Picture::description() const { return d->description; } void FLAC::Picture::setDescription(const String &description) { d->description = description; } int FLAC::Picture::width() const { return d->width; } void FLAC::Picture::setWidth(int width) { d->width = width; } int FLAC::Picture::height() const { return d->height; } void FLAC::Picture::setHeight(int height) { d->height = height; } int FLAC::Picture::colorDepth() const { return d->colorDepth; } void FLAC::Picture::setColorDepth(int colorDepth) { d->colorDepth = colorDepth; } int FLAC::Picture::numColors() const { return d->numColors; } void FLAC::Picture::setNumColors(int numColors) { d->numColors = numColors; } ByteVector FLAC::Picture::data() const { return d->data; } void FLAC::Picture::setData(const ByteVector &data) { d->data = data; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacpicture.h��������������������������������������������������������������0000664�0000000�0000000�00000011243�14662262111�0017600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FLACPICTURE_H #define TAGLIB_FLACPICTURE_H #include "tlist.h" #include "tstring.h" #include "tbytevector.h" #include "tpicturetype.h" #include "taglib_export.h" #include "flacmetadatablock.h" namespace TagLib { namespace FLAC { //! FLAC picture class TAGLIB_EXPORT Picture : public MetadataBlock { public: /* * This describes the function or content of the picture. */ DECLARE_PICTURE_TYPE_ENUM(Type) Picture(); Picture(const ByteVector &data); ~Picture() override; Picture(const Picture &item) = delete; Picture &operator=(const Picture &item) = delete; /*! * Returns the type of the image. */ Type type() const; /*! * Sets the type of the image. */ void setType(Type type); /*! * Returns the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". */ String mimeType() const; /*! * Sets the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". */ void setMimeType(const String &mimeType); /*! * Returns a text description of the image. */ String description() const; /*! * Sets a textual description of the image to \a description. */ void setDescription(const String &description); /*! * Returns the width of the image. */ int width() const; /*! * Sets the width of the image. */ void setWidth(int width); /*! * Returns the height of the image. */ int height() const; /*! * Sets the height of the image. */ void setHeight(int height); /*! * Returns the color depth (in bits-per-pixel) of the image. */ int colorDepth() const; /*! * Sets the color depth (in bits-per-pixel) of the image. */ void setColorDepth(int colorDepth); /*! * Returns the number of colors used on the image.. */ int numColors() const; /*! * Sets the number of colors used on the image (for indexed images). */ void setNumColors(int numColors); /*! * Returns the image data. */ ByteVector data() const; /*! * Sets the image data. */ void setData(const ByteVector &data); /*! * Returns the FLAC metadata block type. */ int code() const override; /*! * Render the content to the FLAC picture block format. */ ByteVector render() const override; /*! * Parse the picture data in the FLAC picture block format. */ bool parse(const ByteVector &data); private: class PicturePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PicturePrivate> d; }; using PictureList = List<Picture>; } // namespace FLAC } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacproperties.cpp���������������������������������������������������������0000664�0000000�0000000�00000010474�14662262111�0020661�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "flacproperties.h" #include "tstring.h" #include "tdebug.h" using namespace TagLib; class FLAC::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int bitsPerSample { 0 }; int channels { 0 }; unsigned long long sampleFrames { 0 }; ByteVector signature; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(data, streamLength); } FLAC::Properties::~Properties() = default; int FLAC::Properties::lengthInMilliseconds() const { return d->length; } int FLAC::Properties::bitrate() const { return d->bitrate; } int FLAC::Properties::sampleRate() const { return d->sampleRate; } int FLAC::Properties::bitsPerSample() const { return d->bitsPerSample; } int FLAC::Properties::channels() const { return d->channels; } unsigned long long FLAC::Properties::sampleFrames() const { return d->sampleFrames; } ByteVector FLAC::Properties::signature() const { return d->signature; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void FLAC::Properties::read(const ByteVector &data, offset_t streamLength) { if(data.size() < 18) { debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes."); return; } unsigned int pos = 0; // Minimum block size (in samples) pos += 2; // Maximum block size (in samples) pos += 2; // Minimum frame size (in bytes) pos += 3; // Maximum frame size (in bytes) pos += 3; const unsigned int flags = data.toUInt(pos, true); pos += 4; d->sampleRate = flags >> 12; d->channels = ((flags >> 9) & 7) + 1; d->bitsPerSample = ((flags >> 4) & 31) + 1; // The last 4 bits are the most significant 4 bits for the 36 bit // stream length in samples. (Audio files measured in days) const unsigned long long hi = flags & 0xf; const unsigned long long lo = data.toUInt(pos, true); pos += 4; d->sampleFrames = (hi << 32) | lo; if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } if(data.size() >= pos + 16) d->signature = data.mid(pos, 16); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacproperties.h�����������������������������������������������������������0000664�0000000�0000000�00000007321�14662262111�0020323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FLACPROPERTIES_H #define TAGLIB_FLACPROPERTIES_H #include "tbytevector.h" #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace FLAC { //! An implementation of audio property reading for FLAC /*! * This reads the data from a FLAC stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of FLAC::Properties with the data read from the * ByteVector \a data. */ Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average); /*! * Destroys this FLAC::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample as read from the FLAC * identification header. */ int bitsPerSample() const; /*! * Return the number of sample frames. */ unsigned long long sampleFrames() const; /*! * Returns the MD5 signature of the uncompressed audio stream as read * from the stream info header. */ ByteVector signature() const; private: void read(const ByteVector &data, offset_t streamLength); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace FLAC } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/flac/flacunknownmetadatablock.cpp�����������������������������������������������0000664�0000000�0000000�00000004733�14662262111�0022701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "flacunknownmetadatablock.h" using namespace TagLib; class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate { public: int code { 0 }; ByteVector data; }; FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) : d(std::make_unique<UnknownMetadataBlockPrivate>()) { d->code = code; d->data = data; } FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default; int FLAC::UnknownMetadataBlock::code() const { return d->code; } void FLAC::UnknownMetadataBlock::setCode(int code) { d->code = code; } ByteVector FLAC::UnknownMetadataBlock::data() const { return d->data; } void FLAC::UnknownMetadataBlock::setData(const ByteVector &data) { d->data = data; } ByteVector FLAC::UnknownMetadataBlock::render() const { return d->data; } �������������������������������������taglib-2.0.2/taglib/flac/flacunknownmetadatablock.h�������������������������������������������������0000664�0000000�0000000�00000005773�14662262111�0022353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2010 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FLACUNKNOWNMETADATABLOCK_H #define TAGLIB_FLACUNKNOWNMETADATABLOCK_H #include "tlist.h" #include "tbytevector.h" #include "taglib_export.h" #include "flacmetadatablock.h" namespace TagLib { namespace FLAC { //! Unknown FLAC metadata block class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock { public: UnknownMetadataBlock(int code, const ByteVector &data); ~UnknownMetadataBlock() override; UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete; UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete; /*! * Returns the FLAC metadata block type. */ int code() const override; /*! * Sets the FLAC metadata block type. */ void setCode(int code); /*! * Returns the FLAC metadata block type. */ ByteVector data() const; /*! * Sets the FLAC metadata block type. */ void setData(const ByteVector &data); /*! * Render the content of the block. */ ByteVector render() const override; private: class UnknownMetadataBlockPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UnknownMetadataBlockPrivate> d; }; } // namespace FLAC } // namespace TagLib #endif �����taglib-2.0.2/taglib/it/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014634�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/it/itfile.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000023146�14662262111�0016622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "itfile.h" #include "tstringlist.h" #include "tdebug.h" #include "modfileprivate.h" using namespace TagLib; using namespace IT; class IT::File::FilePrivate { public: FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) { } Mod::Tag tag; IT::Properties properties; }; IT::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } IT::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } IT::File::~File() = default; Mod::Tag *IT::File::tag() const { return &d->tag; } IT::Properties *IT::File::audioProperties() const { return &d->properties; } bool IT::File::save() { if(readOnly()) { debug("IT::File::save() - Cannot save to a read only file."); return false; } seek(4); writeString(d->tag.title(), 25); writeByte(0); seek(2, Current); unsigned short length = 0; unsigned short instrumentCount = 0; unsigned short sampleCount = 0; if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount)) return false; seek(15, Current); // write comment as instrument and sample names: StringList lines = d->tag.comment().split("\n"); for(unsigned short i = 0; i < instrumentCount; ++ i) { seek(192L + length + (static_cast<long>(i) << 2)); unsigned long instrumentOffset = 0; if(!readU32L(instrumentOffset)) return false; seek(instrumentOffset + 32); if(i < lines.size()) writeString(lines[i], 25); else writeString(String(), 25); writeByte(0); } for(unsigned short i = 0; i < sampleCount; ++ i) { seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2)); unsigned long sampleOffset = 0; if(!readU32L(sampleOffset)) return false; seek(sampleOffset + 20); if(static_cast<unsigned int>(i + instrumentCount) < lines.size()) writeString(lines[i + instrumentCount], 25); else writeString(String(), 25); writeByte(0); } // write rest as message: StringList messageLines; for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i) messageLines.append(lines[i]); ByteVector message = messageLines.toString("\r").data(String::Latin1); // it's actually not really stated if the message needs a // terminating NUL but it does not hurt to add one: if(message.size() > 7999) message.resize(7999); message.append(static_cast<char>(0)); unsigned short special = 0; unsigned short messageLength = 0; unsigned long messageOffset = 0; seek(46); if(!readU16L(special)) return false; auto fileSize = static_cast<unsigned long>(File::length()); if(special & Properties::MessageAttached) { seek(54); if(!readU16L(messageLength) || !readU32L(messageOffset)) return false; if(messageLength == 0) messageOffset = fileSize; } else { messageOffset = fileSize; seek(46); writeU16L(special | 0x1); } if(messageOffset + messageLength >= fileSize) { // append new message seek(54); writeU16L(message.size()); writeU32L(messageOffset); seek(messageOffset); writeBlock(message); truncate(messageOffset + message.size()); } else { // Only overwrite existing message. // I'd need to parse (understand!) the whole file for more. // Although I could just move the message to the end of file // and let the existing one be, but that would waste space. message.resize(messageLength, 0); seek(messageOffset); writeBlock(message); } return true; } void IT::File::read(bool) { if(!isOpen()) return; seek(0); READ_ASSERT(readBlock(4) == "IMPM"); READ_STRING(d->tag.setTitle, 26); seek(2, Current); READ_U16L_AS(length); READ_U16L_AS(instrumentCount); READ_U16L_AS(sampleCount); d->properties.setInstrumentCount(instrumentCount); d->properties.setSampleCount(sampleCount); READ_U16L(d->properties.setPatternCount); READ_U16L(d->properties.setVersion); READ_U16L(d->properties.setCompatibleVersion); READ_U16L(d->properties.setFlags); READ_U16L_AS(special); d->properties.setSpecial(special); READ_BYTE(d->properties.setGlobalVolume); READ_BYTE(d->properties.setMixVolume); READ_BYTE(d->properties.setBpmSpeed); READ_BYTE(d->properties.setTempo); READ_BYTE(d->properties.setPanningSeparation); READ_BYTE(d->properties.setPitchWheelDepth); // IT supports some kind of comment tag. Still, the // sample/instrument names are abused as comments so // I just add all together. String message; if(special & Properties::MessageAttached) { READ_U16L_AS(messageLength); READ_U32L_AS(messageOffset); seek(messageOffset); ByteVector messageBytes = readBlock(messageLength); READ_ASSERT(messageBytes.size() == messageLength); int index = messageBytes.find(static_cast<char>(0)); if(index > -1) messageBytes.resize(index, 0); messageBytes.replace('\r', '\n'); message = messageBytes; } seek(64); ByteVector pannings = readBlock(64); ByteVector volumes = readBlock(64); READ_ASSERT(pannings.size() == 64 && volumes.size() == 64); int channels = 0; for(int i = 0; i < 64; ++ i) { // Strictly speaking an IT file has always 64 channels, but // I don't count disabled and muted channels. // But this always gives 64 channels for all my files anyway. // Strangely VLC does report other values. I wonder how VLC // gets its values. if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0) ++channels; } d->properties.setChannels(channels); // real length might be shorter because of skips and terminator unsigned short realLength = 0; for(unsigned short i = 0; i < length; ++ i) { READ_BYTE_AS(order); if(order == 255) break; if(order != 254) ++ realLength; } d->properties.setLengthInPatterns(realLength); StringList comment; // Note: I found files that have nil characters somewhere // in the instrument/sample names and more characters // afterwards. The spec does not mention such a case. // Currently I just discard anything after a nil, but // e.g. VLC seems to interpret a nil as a space. I // don't know what is the proper behaviour. for(unsigned short i = 0; i < instrumentCount; ++ i) { seek(192L + length + (static_cast<long>(i) << 2)); READ_U32L_AS(instrumentOffset); seek(instrumentOffset); ByteVector instrumentMagic = readBlock(4); READ_ASSERT(instrumentMagic == "IMPI"); READ_STRING_AS(dosFileName, 13); seek(15, Current); READ_STRING_AS(instrumentName, 26); comment.append(instrumentName); } for(unsigned short i = 0; i < sampleCount; ++ i) { seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2)); READ_U32L_AS(sampleOffset); seek(sampleOffset); ByteVector sampleMagic = readBlock(4); READ_ASSERT(sampleMagic == "IMPS"); READ_STRING_AS(dosFileName, 13); READ_BYTE_AS(globalVolume); READ_BYTE_AS(sampleFlags); READ_BYTE_AS(sampleVolume); READ_STRING_AS(sampleName, 26); /* READ_BYTE_AS(sampleCvt); READ_BYTE_AS(samplePanning); READ_U32L_AS(sampleLength); READ_U32L_AS(loopStart); READ_U32L_AS(loopStop); READ_U32L_AS(c5speed); READ_U32L_AS(sustainLoopStart); READ_U32L_AS(sustainLoopEnd); READ_U32L_AS(sampleDataOffset); READ_BYTE_AS(vibratoSpeed); READ_BYTE_AS(vibratoDepth); READ_BYTE_AS(vibratoRate); READ_BYTE_AS(vibratoType); */ comment.append(sampleName); } if(!message.isEmpty()) comment.append(message); d->tag.setComment(comment.toString("\n")); d->tag.setTrackerName("Impulse Tracker"); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/it/itfile.h���������������������������������������������������������������������0000664�0000000�0000000�00000010001�14662262111�0016251�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_ITFILE_H #define TAGLIB_ITFILE_H #include "tfile.h" #include "taglib_export.h" #include "audioproperties.h" #include "modfilebase.h" #include "modtag.h" #include "itproperties.h" namespace TagLib { //! An implementation of Impulse Tracker metadata /*! * This is an implementation of Impulse Tracker metadata. */ namespace IT { //! An implementation of TagLib::File with IT specific methods /*! * This implements and provides an interface for IT files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to IT files. */ class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! * Constructs an Impulse Tracker file from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Constructs an Impulse Tracker file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; Mod::Tag *tag() const override; /*! * Returns the IT::Properties for this file. If no audio properties * were read then this will return a null pointer. */ IT::Properties *audioProperties() const override; /*! * Save the file. * This is the same as calling save(AllTags); * * \note Saving Impulse Tracker tags is not supported. */ bool save() override; private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace IT } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/it/itproperties.cpp�������������������������������������������������������������0000664�0000000�0000000�00000012333�14662262111�0020073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright :(C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "itproperties.h" using namespace TagLib; using namespace IT; class IT::Properties::PropertiesPrivate { public: int channels { 0 }; unsigned short lengthInPatterns { 0 }; unsigned short instrumentCount { 0 }; unsigned short sampleCount { 0 }; unsigned short patternCount { 0 }; unsigned short version { 0 }; unsigned short compatibleVersion { 0 }; unsigned short flags { 0 }; unsigned short special { 0 }; unsigned char globalVolume { 0 }; unsigned char mixVolume { 0 }; unsigned char tempo { 0 }; unsigned char bpmSpeed { 0 }; unsigned char panningSeparation { 0 }; unsigned char pitchWheelDepth { 0 }; }; IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(std::make_unique<PropertiesPrivate>()) { } IT::Properties::~Properties() = default; int IT::Properties::channels() const { return d->channels; } unsigned short IT::Properties::lengthInPatterns() const { return d->lengthInPatterns; } bool IT::Properties::stereo() const { return d->flags & Stereo; } unsigned short IT::Properties::instrumentCount() const { return d->instrumentCount; } unsigned short IT::Properties::sampleCount() const { return d->sampleCount; } unsigned short IT::Properties::patternCount() const { return d->patternCount; } unsigned short IT::Properties::version() const { return d->version; } unsigned short IT::Properties::compatibleVersion() const { return d->compatibleVersion; } unsigned short IT::Properties::flags() const { return d->flags; } unsigned short IT::Properties::special() const { return d->special; } unsigned char IT::Properties::globalVolume() const { return d->globalVolume; } unsigned char IT::Properties::mixVolume() const { return d->mixVolume; } unsigned char IT::Properties::tempo() const { return d->tempo; } unsigned char IT::Properties::bpmSpeed() const { return d->bpmSpeed; } unsigned char IT::Properties::panningSeparation() const { return d->panningSeparation; } unsigned char IT::Properties::pitchWheelDepth() const { return d->pitchWheelDepth; } void IT::Properties::setChannels(int channels) { d->channels = channels; } void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns) { d->lengthInPatterns = lengthInPatterns; } void IT::Properties::setInstrumentCount(unsigned short instrumentCount) { d->instrumentCount = instrumentCount; } void IT::Properties::setSampleCount(unsigned short sampleCount) { d->sampleCount = sampleCount; } void IT::Properties::setPatternCount(unsigned short patternCount) { d->patternCount = patternCount; } void IT::Properties::setFlags(unsigned short flags) { d->flags = flags; } void IT::Properties::setSpecial(unsigned short special) { d->special = special; } void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion) { d->compatibleVersion = compatibleVersion; } void IT::Properties::setVersion(unsigned short version) { d->version = version; } void IT::Properties::setGlobalVolume(unsigned char globalVolume) { d->globalVolume = globalVolume; } void IT::Properties::setMixVolume(unsigned char mixVolume) { d->mixVolume = mixVolume; } void IT::Properties::setTempo(unsigned char tempo) { d->tempo = tempo; } void IT::Properties::setBpmSpeed(unsigned char bpmSpeed) { d->bpmSpeed = bpmSpeed; } void IT::Properties::setPanningSeparation(unsigned char panningSeparation) { d->panningSeparation = panningSeparation; } void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth) { d->pitchWheelDepth = pitchWheelDepth; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/it/itproperties.h���������������������������������������������������������������0000664�0000000�0000000�00000010350�14662262111�0017535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ITPROPERTIES_H #define TAGLIB_ITPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace IT { //! An implementation of audio property reading for IT class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! Flag bits. */ enum { Stereo = 1, Vol0MixOptimizations = 2, UseInstruments = 4, LinearSlides = 8, OldEffects = 16, LinkEffects = 32, UseMidiPitchController = 64, RequestEmbeddedMidiConf = 128 }; /*! Special bits. */ enum { MessageAttached = 1, MidiConfEmbedded = 8 }; Properties(AudioProperties::ReadStyle propertiesStyle); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; int channels() const override; unsigned short lengthInPatterns() const; bool stereo() const; unsigned short instrumentCount() const; unsigned short sampleCount() const; unsigned short patternCount() const; unsigned short version() const; unsigned short compatibleVersion() const; unsigned short flags() const; unsigned short special() const; unsigned char globalVolume() const; unsigned char mixVolume() const; unsigned char tempo() const; unsigned char bpmSpeed() const; unsigned char panningSeparation() const; unsigned char pitchWheelDepth() const; void setChannels(int channels); void setLengthInPatterns(unsigned short lengthInPatterns); void setInstrumentCount(unsigned short instrumentCount); void setSampleCount(unsigned short sampleCount); void setPatternCount(unsigned short patternCount); void setVersion(unsigned short version); void setCompatibleVersion(unsigned short compatibleVersion); void setFlags(unsigned short flags); void setSpecial(unsigned short special); void setGlobalVolume(unsigned char globalVolume); void setMixVolume(unsigned char mixVolume); void setTempo(unsigned char tempo); void setBpmSpeed(unsigned char bpmSpeed); void setPanningSeparation(unsigned char panningSeparation); void setPitchWheelDepth(unsigned char pitchWheelDepth); private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace IT } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014777�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modfile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000013370�14662262111�0017126�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "modfile.h" #include "tstringlist.h" #include "tdebug.h" #include "tpropertymap.h" #include "modfileprivate.h" using namespace TagLib; using namespace Mod; class Mod::File::FilePrivate { public: FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) { } Mod::Tag tag; Mod::Properties properties; }; Mod::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } Mod::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } Mod::File::~File() = default; Mod::Tag *Mod::File::tag() const { return &d->tag; } Mod::Properties *Mod::File::audioProperties() const { return &d->properties; } PropertyMap Mod::File::properties() const { return d->tag.properties(); } PropertyMap Mod::File::setProperties(const PropertyMap &properties) { return d->tag.setProperties(properties); } bool Mod::File::save() { if(readOnly()) { debug("Mod::File::save() - Cannot save to a read only file."); return false; } seek(0); writeString(d->tag.title(), 20); StringList lines = d->tag.comment().split("\n"); unsigned int n = std::min(lines.size(), d->properties.instrumentCount()); for(unsigned int i = 0; i < n; ++ i) { writeString(lines[i], 22); seek(8, Current); } for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) { writeString(String(), 22); seek(8, Current); } return true; } void Mod::File::read(bool) { if(!isOpen()) return; seek(1080); ByteVector modId = readBlock(4); READ_ASSERT(modId.size() == 4); int channels = 4; unsigned int instruments = 31; if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") { d->tag.setTrackerName("ProTracker"); channels = 4; } else if(modId.startsWith("FLT") || modId.startsWith("TDZ")) { d->tag.setTrackerName("StarTrekker"); char digit = modId[3]; READ_ASSERT(digit >= '0' && digit <= '9'); channels = digit - '0'; } else if(modId.endsWith("CHN")) { d->tag.setTrackerName("StarTrekker"); char digit = modId[0]; READ_ASSERT(digit >= '0' && digit <= '9'); channels = digit - '0'; } else if(modId == "CD81" || modId == "OKTA") { d->tag.setTrackerName("Atari Oktalyzer"); channels = 8; } else if(modId.endsWith("CH") || modId.endsWith("CN")) { d->tag.setTrackerName("TakeTracker"); char digit = modId[0]; READ_ASSERT(digit >= '0' && digit <= '9'); channels = (digit - '0') * 10; digit = modId[1]; READ_ASSERT(digit >= '0' && digit <= '9'); channels += digit - '0'; } else { // Not sure if this is correct. I'd need a file // created with NoiseTracker to check this. d->tag.setTrackerName("NoiseTracker"); // probably channels = 4; instruments = 15; } d->properties.setChannels(channels); d->properties.setInstrumentCount(instruments); seek(0); READ_STRING(d->tag.setTitle, 20); offset_t pos = 20; StringList comment; for(unsigned int i = 0; i < instruments; ++ i) { READ_STRING_AS(instrumentName, 22); // skip unused data pos += 22 + 2 + 1 + 1 + 2 + 2; seek(pos); // // value in words, * 2 (<< 1) for bytes: // READ_U16B_AS(sampleLength); // READ_BYTE_AS(fineTuneByte); // int fineTune = fineTuneByte & 0xF; // // > 7 means negative value // if(fineTune > 7) fineTune -= 16; // READ_BYTE_AS(volume); // if(volume > 64) volume = 64; // // volume in decibels: 20 * log10(volume / 64) // // value in words, * 2 (<< 1) for bytes: // READ_U16B_AS(repeatStart); // // value in words, * 2 (<< 1) for bytes: // READ_U16B_AS(repeatLength); comment.append(instrumentName); } READ_BYTE(d->properties.setLengthInPatterns); d->tag.setComment(comment.toString("\n")); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modfile.h�������������������������������������������������������������������0000664�0000000�0000000�00000011121�14662262111�0016563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MODFILE_H #define TAGLIB_MODFILE_H #include "tfile.h" #include "taglib_export.h" #include "audioproperties.h" #include "modfilebase.h" #include "modtag.h" #include "modproperties.h" namespace TagLib { //! An implementation of Protracker metadata /*! * This is an implementation of Protracker metadata. */ namespace Mod { //! An implementation of TagLib::File with Mod specific methods /*! * This implements and provides an interface for Mod files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to Mod files. */ class TAGLIB_EXPORT File : public TagLib::Mod::FileBase { public: /*! * Constructs a Protracker file from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Constructs a Protracker file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; Mod::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * Forwards to Mod::Tag::properties(). */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Forwards to Mod::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the Mod::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Mod::Properties *audioProperties() const override; /*! * Save the file. * This is the same as calling save(AllTags); * * \note Saving Protracker tags is not supported. */ bool save() override; private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace Mod } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modfilebase.cpp�������������������������������������������������������������0000664�0000000�0000000�00000007576�14662262111�0017774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "modfilebase.h" using namespace TagLib; using namespace Mod; class Mod::FileBase::FileBasePrivate { }; Mod::FileBase::~FileBase() = default; Mod::FileBase::FileBase(FileName file) : TagLib::File(file) { } Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream) { } void Mod::FileBase::writeString(const String &s, unsigned long size, char padding) { ByteVector data(s.data(String::Latin1)); data.resize(static_cast<unsigned int>(size), padding); writeBlock(data); } bool Mod::FileBase::readString(String &s, unsigned long size) { ByteVector data(readBlock(size)); if(data.size() < size) return false; int index = data.find(static_cast<char>(0)); if(index > -1) { data.resize(index); } data.replace('\xff', ' '); s = data; return true; } void Mod::FileBase::writeByte(unsigned char byte) { ByteVector data(1, byte); writeBlock(data); } void Mod::FileBase::writeU16L(unsigned short number) { writeBlock(ByteVector::fromShort(number, false)); } void Mod::FileBase::writeU32L(unsigned long number) { writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), false)); } void Mod::FileBase::writeU16B(unsigned short number) { writeBlock(ByteVector::fromShort(number, true)); } void Mod::FileBase::writeU32B(unsigned long number) { writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), true)); } bool Mod::FileBase::readByte(unsigned char &byte) { ByteVector data(readBlock(1)); if(data.size() < 1) return false; byte = data[0]; return true; } bool Mod::FileBase::readU16L(unsigned short &number) { ByteVector data(readBlock(2)); if(data.size() < 2) return false; number = data.toUShort(false); return true; } bool Mod::FileBase::readU32L(unsigned long &number) { ByteVector data(readBlock(4)); if(data.size() < 4) return false; number = data.toUInt(false); return true; } bool Mod::FileBase::readU16B(unsigned short &number) { ByteVector data(readBlock(2)); if(data.size() < 2) return false; number = data.toUShort(true); return true; } bool Mod::FileBase::readU32B(unsigned long &number) { ByteVector data(readBlock(4)); if(data.size() < 4) return false; number = data.toUInt(true); return true; } ����������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modfilebase.h���������������������������������������������������������������0000664�0000000�0000000�00000005706�14662262111�0017432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MODFILEBASE_H #define TAGLIB_MODFILEBASE_H #include "taglib.h" #include "tfile.h" #include "tlist.h" #include "taglib_export.h" namespace TagLib { namespace Mod { //! Base class for module files class TAGLIB_EXPORT FileBase : public TagLib::File { public: ~FileBase() override; FileBase(const FileBase &) = delete; FileBase& operator=(const FileBase &) = delete; protected: FileBase(FileName file); FileBase(IOStream *stream); void writeString(const String &s, unsigned long size, char padding = 0); void writeByte(unsigned char byte); void writeU16L(unsigned short number); void writeU32L(unsigned long number); void writeU16B(unsigned short number); void writeU32B(unsigned long number); bool readString(String &s, unsigned long size); bool readByte(unsigned char &byte); bool readU16L(unsigned short &number); bool readU32L(unsigned long &number); bool readU16B(unsigned short &number); bool readU32B(unsigned long &number); private: class FileBasePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FileBasePrivate> d; }; } // namespace Mod } // namespace TagLib #endif ����������������������������������������������������������taglib-2.0.2/taglib/mod/modfileprivate.h������������������������������������������������������������0000664�0000000�0000000�00000007410�14662262111�0020164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ #ifndef TAGLIB_MODFILEPRIVATE_H #define TAGLIB_MODFILEPRIVATE_H // some helper-macros only used internally by (s3m|it|xm)file.cpp #define READ_ASSERT(cond) \ do { \ if(!(cond)) { \ setValid(false); \ return; \ } \ } while(0) #define READ(setter, type, read) \ do { \ type number; \ READ_ASSERT(read(number)); \ setter(number); \ } while(0) #define READ_BYTE(setter) READ(setter, unsigned char, readByte) #define READ_U16L(setter) READ(setter, unsigned short, readU16L) #define READ_U32L(setter) READ(setter, unsigned long, readU32L) #define READ_U16B(setter) READ(setter, unsigned short, readU16B) #define READ_U32B(setter) READ(setter, unsigned long, readU32B) #define READ_STRING(setter, size) \ do { \ String s; \ READ_ASSERT(readString(s, size)); \ setter(s); \ } while(0) #define READ_AS(type, name, read) \ type name = 0; \ READ_ASSERT(read(name)) #define READ_BYTE_AS(name) READ_AS(unsigned char, name, readByte) #define READ_U16L_AS(name) READ_AS(unsigned short, name, readU16L) #define READ_U32L_AS(name) READ_AS(unsigned long, name, readU32L) #define READ_U16B_AS(name) READ_AS(unsigned short, name, readU16B) #define READ_U32B_AS(name) READ_AS(unsigned long, name, readU32B) #define READ_STRING_AS(name, size) \ String name; \ READ_ASSERT(readString(name, size)) #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000005423�14662262111�0020403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "modproperties.h" using namespace TagLib; using namespace Mod; class Mod::Properties::PropertiesPrivate { public: int channels { 0 }; unsigned int instrumentCount { 0 }; unsigned char lengthInPatterns { 0 }; }; Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(std::make_unique<PropertiesPrivate>()) { } Mod::Properties::~Properties() = default; int Mod::Properties::bitrate() const { return 0; } int Mod::Properties::sampleRate() const { return 0; } int Mod::Properties::channels() const { return d->channels; } unsigned int Mod::Properties::instrumentCount() const { return d->instrumentCount; } unsigned char Mod::Properties::lengthInPatterns() const { return d->lengthInPatterns; } void Mod::Properties::setChannels(int channels) { d->channels = channels; } void Mod::Properties::setInstrumentCount(unsigned int instrumentCount) { d->instrumentCount = instrumentCount; } void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns) { d->lengthInPatterns = lengthInPatterns; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000005335�14662262111�0020052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MODPROPERTIES_H #define TAGLIB_MODPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace Mod { //! An implementation of audio property reading for Mod class TAGLIB_EXPORT Properties : public AudioProperties { public: Properties(AudioProperties::ReadStyle propertiesStyle); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; int bitrate() const override; int sampleRate() const override; int channels() const override; unsigned int instrumentCount() const; unsigned char lengthInPatterns() const; void setChannels(int channels); void setInstrumentCount(unsigned int instrumentCount); void setLengthInPatterns(unsigned char lengthInPatterns); private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace Mod } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modtag.cpp������������������������������������������������������������������0000664�0000000�0000000�00000010117�14662262111�0016756�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "modtag.h" #include <utility> #include "tstringlist.h" #include "tpropertymap.h" using namespace TagLib; using namespace Mod; class Mod::Tag::TagPrivate { public: String title; String comment; String trackerName; }; Mod::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } Mod::Tag::~Tag() = default; String Mod::Tag::title() const { return d->title; } String Mod::Tag::artist() const { return String(); } String Mod::Tag::album() const { return String(); } String Mod::Tag::comment() const { return d->comment; } String Mod::Tag::genre() const { return String(); } unsigned int Mod::Tag::year() const { return 0; } unsigned int Mod::Tag::track() const { return 0; } String Mod::Tag::trackerName() const { return d->trackerName; } void Mod::Tag::setTitle(const String &title) { d->title = title; } void Mod::Tag::setArtist(const String &) { } void Mod::Tag::setAlbum(const String &) { } void Mod::Tag::setComment(const String &comment) { d->comment = comment; } void Mod::Tag::setGenre(const String &) { } void Mod::Tag::setYear(unsigned int) { } void Mod::Tag::setTrack(unsigned int) { } void Mod::Tag::setTrackerName(const String &trackerName) { d->trackerName = trackerName; } PropertyMap Mod::Tag::properties() const { PropertyMap properties; properties["TITLE"] = d->title; properties["COMMENT"] = d->comment; if(!d->trackerName.isEmpty()) properties["TRACKERNAME"] = d->trackerName; return properties; } PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps) { PropertyMap props(origProps); props.removeEmpty(); StringList oneValueSet; if(props.contains("TITLE")) { d->title = props["TITLE"].front(); oneValueSet.append("TITLE"); } else d->title.clear(); if(props.contains("COMMENT")) { d->comment = props["COMMENT"].front(); oneValueSet.append("COMMENT"); } else d->comment.clear(); if(props.contains("TRACKERNAME")) { d->trackerName = props["TRACKERNAME"].front(); oneValueSet.append("TRACKERNAME"); } else d->trackerName.clear(); // for each tag that has been set above, remove the first entry in the corresponding // value list. The others will be returned as unsupported by this format. for(const auto &entry : std::as_const(oneValueSet)) { if(props[entry].size() == 1) props.erase(entry); else props[entry].erase(props[entry].begin()); } return props; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mod/modtag.h��������������������������������������������������������������������0000664�0000000�0000000�00000016210�14662262111�0016423�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MODTAG_H #define TAGLIB_MODTAG_H #include "tag.h" namespace TagLib { namespace Mod { //! A module file tag implementation /*! * Tags for module files (Mod, S3M, IT, XM). * * Note that only the \a title is supported as such by most * module file formats. Except for XM files the \a trackerName * is derived from the file format or the flavour of the file * format. For XM files it is stored in the file. * * The \a comment tag is not strictly supported by module files, * but it is common practice to abuse instrument/sample/pattern * names as multiline comments. TagLib does so as well. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: Tag(); ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; /*! * Returns the track name; if no track name is present in the tag * an empty string will be returned. */ String title() const override; /*! * Not supported by module files. Therefore always returns an empty string. */ String artist() const override; /*! * Not supported by module files. Therefore always returns an empty string. */ String album() const override; /*! * Returns the track comment derived from the instrument/sample/pattern * names; if no comment is present in the tag an empty string will be * returned. */ String comment() const override; /*! * Not supported by module files. Therefore always returns an empty string. */ String genre() const override; /*! * Not supported by module files. Therefore always returns 0. */ unsigned int year() const override; /*! * Not supported by module files. Therefore always returns 0. */ unsigned int track() const override; /*! * Returns the name of the tracker used to create/edit the module file. * Only XM files store this tag to the file as such, for other formats * (Mod, S3M, IT) this is derived from the file type or the flavour of * the file type. Therefore only XM files might have an empty * tracker name. */ String trackerName() const; /*! * Sets the title to \a title. If \a title is an empty string then this * value will be cleared. * * The length limits per file type are (1 character = 1 byte): * Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20 * characters. */ void setTitle(const String &title) override; /*! * Not supported by module files and therefore ignored. */ void setArtist(const String &artist) override; /*! * Not supported by module files and therefore ignored. */ void setAlbum(const String &album) override; /*! * Sets the comment to \a comment. If \a comment is an empty string then * this value will be cleared. * * Note that module file formats don't actually support a comment tag. * Instead the names of instruments/patterns/samples are abused as * a multiline comment. Because of this the number of lines in a * module file is fixed to the number of instruments/patterns/samples. * * Also note that the instrument/pattern/sample name length is limited * and thus the line length in comments are limited. Too big comments * will be truncated. * * The line length limits per file type are (1 character = 1 byte): * Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22 * characters. */ void setComment(const String &comment) override; /*! * Not supported by module files and therefore ignored. */ void setGenre(const String &genre) override; /*! * Not supported by module files and therefore ignored. */ void setYear(unsigned int year) override; /*! * Not supported by module files and therefore ignored. */ void setTrack(unsigned int track) override; /*! * Sets the tracker name to \a trackerName. If \a trackerName is * an empty string then this value will be cleared. * * Note that only XM files support this tag. Setting the * tracker name for other module file formats will be ignored. * * The length of this tag is limited to 20 characters (1 character * = 1 byte). */ void setTrackerName(const String &trackerName); /*! * Implements the unified property interface -- export function. * Since the module tag is very limited, the exported map is as well. */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Because of the limitations of the module file tag, any tags besides * COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be * returned. Additionally, if the map contains tags with multiple values, * all but the first will be contained in the returned map of unsupported * properties. */ PropertyMap setProperties(const PropertyMap &) override; private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace Mod } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014720�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4atom.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000020325�14662262111�0017007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4atom.h" #include <array> #include <climits> #include <utility> #include "tdebug.h" using namespace TagLib; namespace { constexpr std::array containers { "moov", "udta", "mdia", "meta", "ilst", "stbl", "minf", "moof", "traf", "trak", "stsd" }; } // namespace class MP4::Atom::AtomPrivate { public: explicit AtomPrivate(offset_t ofs) : offset(ofs) {} offset_t offset; offset_t length { 0 }; TagLib::ByteVector name; AtomList children; }; MP4::Atom::Atom(File *file) : d(std::make_unique<AtomPrivate>(file->tell())) { d->children.setAutoDelete(true); ByteVector header = file->readBlock(8); if(header.size() != 8) { // The atom header must be 8 bytes long, otherwise there is either // trailing garbage or the file is truncated debug("MP4: Couldn't read 8 bytes of data for atom header"); d->length = 0; file->seek(0, File::End); return; } d->length = header.toUInt(); if(d->length == 0) { // The last atom which extends to the end of the file. d->length = file->length() - d->offset; } else if(d->length == 1) { // The atom has a 64-bit length. if(const long long longLength = file->readBlock(8).toLongLong(); longLength <= LONG_MAX) { // The actual length fits in long. That's always the case if long is 64-bit. d->length = static_cast<long>(longLength); } else { debug("MP4: 64-bit atoms are not supported"); d->length = 0; file->seek(0, File::End); return; } } if(d->length < 8 || d->length > file->length() - d->offset) { debug("MP4: Invalid atom size"); d->length = 0; file->seek(0, File::End); return; } d->name = header.mid(4, 4); for(auto c : containers) { if(d->name == c) { if(d->name == "meta") { offset_t posAfterMeta = file->tell(); static constexpr std::array metaChildrenNames { "hdlr", "ilst", "mhdr", "ctry", "lang" }; // meta is not a full atom (i.e. not followed by version, flags). It // is followed by the size and type of the first child atom. auto metaIsFullAtom = std::none_of(metaChildrenNames.begin(), metaChildrenNames.end(), [nextSize = file->readBlock(8).mid(4, 4)](const auto &child) { return nextSize == child; }); // Only skip next four bytes, which contain version and flags, if meta // is a full atom. file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0)); } else if(d->name == "stsd") { file->seek(8, File::Current); } while(file->tell() < d->offset + d->length) { auto child = new MP4::Atom(file); d->children.append(child); if(child->d->length == 0) return; } return; } } file->seek(d->offset + d->length); } MP4::Atom::~Atom() = default; MP4::Atom * MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4) { if(name1 == nullptr) { return this; } auto it = std::find_if(d->children.cbegin(), d->children.cend(), [&name1](const Atom *child) { return child->d->name == name1; }); return it != d->children.cend() ? (*it)->find(name2, name3, name4) : nullptr; } MP4::AtomList MP4::Atom::findall(const char *name, bool recursive) const { MP4::AtomList result; for(const auto &child : std::as_const(d->children)) { if(child->d->name == name) { result.append(child); } if(recursive) { result.append(child->findall(name, recursive)); } } return result; } bool MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3) { path.append(this); if(name1 == nullptr) { return true; } auto it = std::find_if(d->children.cbegin(), d->children.cend(), [&name1](const Atom *child) { return child->d->name == name1; }); return it != d->children.cend() ? (*it)->path(path, name2, name3) : false; } void MP4::Atom::addToOffset(offset_t delta) { d->offset += delta; } void MP4::Atom::prependChild(Atom *atom) { d->children.prepend(atom); } bool MP4::Atom::removeChild(Atom *meta) { auto it = d->children.find(meta); if(it != d->children.end()) { d->children.erase(it); return true; } return false; } offset_t MP4::Atom::offset() const { return d->offset; } offset_t MP4::Atom::length() const { return d->length; } const ByteVector &MP4::Atom::name() const { return d->name; } const MP4::AtomList &MP4::Atom::children() const { return d->children; } class MP4::Atoms::AtomsPrivate { public: AtomList atoms; }; MP4::Atoms::Atoms(File *file) : d(std::make_unique<AtomsPrivate>()) { d->atoms.setAutoDelete(true); file->seek(0, File::End); offset_t end = file->tell(); file->seek(0); while(file->tell() + 8 <= end) { auto atom = new MP4::Atom(file); d->atoms.append(atom); if (atom->length() == 0) break; } } MP4::Atoms::~Atoms() = default; MP4::Atom * MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) const { auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(), [&name1](const Atom *atom) { return atom->name() == name1; }); return it != d->atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr; } MP4::AtomList MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) const { MP4::AtomList path; auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(), [&name1](const Atom *atom) { return atom->name() == name1; }); if(it != d->atoms.cend()) { if(!(*it)->path(path, name2, name3, name4)) { path.clear(); } return path; } return path; } namespace { bool checkValid(const MP4::AtomList &list) { return std::none_of(list.begin(), list.end(), [](const auto &a) { return a->length() == 0 || !checkValid(a->children()); }); } } // namespace bool MP4::Atoms::checkRootLevelAtoms() { bool moovValid = false; for(auto it = d->atoms.begin(); it != d->atoms.end(); ++it) { bool invalid = (*it)->length() == 0 || !checkValid((*it)->children()); if(!moovValid && !invalid && (*it)->name() == "moov") { moovValid = true; } if(invalid) { if(!moovValid || (*it)->name() == "moof") return false; // Only the root level atoms "moov" and (if present) "moof" are // modified. If they are valid, ignore following invalid root level // atoms as trailing garbage. while(it != d->atoms.end()) { delete *it; it = d->atoms.erase(it); } return true; } } return true; } const MP4::AtomList &MP4::Atoms::atoms() const { return d->atoms; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4atom.h�������������������������������������������������������������������0000664�0000000�0000000�00000012432�14662262111�0016454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ // This file is not part of the public API! #ifndef TAGLIB_MP4ATOM_H #define TAGLIB_MP4ATOM_H #include "tfile.h" #include "tlist.h" namespace TagLib { namespace MP4 { enum AtomDataType { TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed TypeUTF8 = 1, // without any count or null terminator TypeUTF16 = 2, // also known as UTF-16BE TypeSJIS = 3, // deprecated unless it is needed for special Japanese characters TypeHTML = 6, // the HTML file header specifies which HTML version TypeXML = 7, // the XML header must identify the DTD or schemas TypeUUID = 8, // also known as GUID; stored as 16 bytes in binary (valid as an ID) TypeISRC = 9, // stored as UTF-8 text (valid as an ID) TypeMI3P = 10, // stored as UTF-8 text (valid as an ID) TypeGIF = 12, // (deprecated) a GIF image TypeJPEG = 13, // a JPEG image TypePNG = 14, // a PNG image TypeURL = 15, // absolute, in UTF-8 characters TypeDuration = 16, // in milliseconds, 32-bit integer TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits TypeGenred = 18, // a list of enumerated values TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit integer TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID) TypeBMP = 27, // Windows bitmap image TypeUndefined = 255 // undefined }; #ifndef DO_NOT_DOCUMENT struct AtomData { AtomData(AtomDataType type, const ByteVector &data) : type(type), data(data) { } AtomDataType type; int locale { 0 }; ByteVector data; }; class Atom; using AtomList = TagLib::List<Atom *>; using AtomDataList = TagLib::List<AtomData>; class TAGLIB_EXPORT Atom { public: Atom(File *file); ~Atom(); Atom(const Atom &) = delete; Atom &operator=(const Atom &) = delete; Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr); AtomList findall(const char *name, bool recursive = false) const; void addToOffset(offset_t delta); void prependChild(Atom *atom); bool removeChild(Atom *meta); offset_t offset() const; offset_t length() const; const ByteVector &name() const; const AtomList &children() const; private: class AtomPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<AtomPrivate> d; }; //! Root-level atoms class TAGLIB_EXPORT Atoms { public: Atoms(File *file); ~Atoms(); Atoms(const Atoms &) = delete; Atoms &operator=(const Atoms &) = delete; Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr) const; AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr) const; bool checkRootLevelAtoms(); const AtomList &atoms() const; private: class AtomsPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<AtomsPrivate> d; }; #endif // DO_NOT_DOCUMENT } // namespace MP4 } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4coverart.cpp�������������������������������������������������������������0000664�0000000�0000000�00000005417�14662262111�0017701�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2009 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4coverart.h" using namespace TagLib; class MP4::CoverArt::CoverArtPrivate { public: Format format { MP4::CoverArt::JPEG }; ByteVector data; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MP4::CoverArt::CoverArt(Format format, const ByteVector &data) : d(std::make_shared<CoverArtPrivate>()) { d->format = format; d->data = data; } MP4::CoverArt::CoverArt(const CoverArt &) = default; MP4::CoverArt &MP4::CoverArt::operator=(const CoverArt &) = default; void MP4::CoverArt::swap(CoverArt &item) noexcept { using std::swap; swap(d, item.d); } MP4::CoverArt::~CoverArt() = default; MP4::CoverArt::Format MP4::CoverArt::format() const { return d->format; } ByteVector MP4::CoverArt::data() const { return d->data; } bool MP4::CoverArt::operator==(const CoverArt &other) const { return format() == other.format() && data() == other.data(); } bool MP4::CoverArt::operator!=(const CoverArt &other) const { return !(*this == other); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4coverart.h���������������������������������������������������������������0000664�0000000�0000000�00000006345�14662262111�0017347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2009 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4COVERART_H #define TAGLIB_MP4COVERART_H #include "tlist.h" #include "tbytevector.h" #include "taglib_export.h" #include "mp4atom.h" namespace TagLib { namespace MP4 { //! MP4 picture class TAGLIB_EXPORT CoverArt { public: /*! * This describes the image type. */ enum Format { JPEG = TypeJPEG, PNG = TypePNG, BMP = TypeBMP, GIF = TypeGIF, Unknown = TypeImplicit, }; CoverArt(Format format, const ByteVector &data); ~CoverArt(); CoverArt(const CoverArt &item); /*! * Copies the contents of \a item into this CoverArt. */ CoverArt &operator=(const CoverArt &item); /*! * Exchanges the content of the CoverArt with the content of \a item. */ void swap(CoverArt &item) noexcept; //! Format of the image Format format() const; //! The image data ByteVector data() const; /*! * Returns \c true if the CoverArt and \a other are of the same format and * contain the same data. */ bool operator==(const CoverArt &other) const; /*! * Returns \c true if the CoverArt and \a other differ in format or data. */ bool operator!=(const CoverArt &other) const; private: class CoverArtPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<CoverArtPrivate> d; }; using CoverArtList = List<CoverArt>; } // namespace MP4 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4file.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000011641�14662262111�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4file.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" #include "mp4itemfactory.h" using namespace TagLib; class MP4::File::FilePrivate { public: FilePrivate(const MP4::ItemFactory *mp4ItemFactory) : itemFactory(mp4ItemFactory ? mp4ItemFactory : MP4::ItemFactory::instance()) { } ~FilePrivate() = default; const ItemFactory *itemFactory; std::unique_ptr<MP4::Tag> tag; std::unique_ptr<MP4::Atoms> atoms; std::unique_ptr<MP4::Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool MP4::File::isSupported(IOStream *stream) { // An MP4 file has to have an "ftyp" box first. const ByteVector id = Utils::readHeader(stream, 8, false); return id.containsAt("ftyp", 4); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle, ItemFactory *itemFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>(itemFactory)) { if(isOpen()) read(readProperties); } MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle, ItemFactory *itemFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>(itemFactory)) { if(isOpen()) read(readProperties); } MP4::File::~File() = default; MP4::Tag *MP4::File::tag() const { return d->tag.get(); } PropertyMap MP4::File::properties() const { return d->tag->properties(); } void MP4::File::removeUnsupportedProperties(const StringList &properties) { d->tag->removeUnsupportedProperties(properties); } PropertyMap MP4::File::setProperties(const PropertyMap &properties) { return d->tag->setProperties(properties); } MP4::Properties *MP4::File::audioProperties() const { return d->properties.get(); } void MP4::File::read(bool readProperties) { if(!isValid()) return; d->atoms = std::make_unique<Atoms>(this); if(!d->atoms->checkRootLevelAtoms()) { setValid(false); return; } // must have a moov atom, otherwise consider it invalid if(!d->atoms->find("moov")) { setValid(false); return; } d->tag = std::make_unique<Tag>(this, d->atoms.get(), d->itemFactory); if(readProperties) { d->properties = std::make_unique<Properties>(this, d->atoms.get()); } } bool MP4::File::save() { if(readOnly()) { debug("MP4::File::save() -- File is read only."); return false; } if(!isValid()) { debug("MP4::File::save() -- Trying to save invalid file."); return false; } return d->tag->save(); } bool MP4::File::strip(int tags) { if(readOnly()) { debug("MP4::File::strip() - Cannot strip tags from a read only file."); return false; } if(!isValid()) { debug("MP4::File::strip() -- Cannot strip tags from an invalid file."); return false; } if(tags & MP4) { return d->tag->strip(); } return true; } bool MP4::File::hasMP4Tag() const { return d->atoms->find("moov", "udta", "meta", "ilst") != nullptr; } �����������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4file.h�������������������������������������������������������������������0000664�0000000�0000000�00000014304�14662262111�0016433�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4FILE_H #define TAGLIB_MP4FILE_H #include "tfile.h" #include "taglib_export.h" #include "mp4tag.h" #include "tag.h" #include "mp4properties.h" namespace TagLib { //! An implementation of MP4 (AAC, ALAC, ...) metadata namespace MP4 { class Atoms; class ItemFactory; //! An implementation of TagLib::File with MP4 specific methods /*! * This implements and provides an interface for MP4 files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to MP4 files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for strip() and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches MP4 tags. MP4 = 0x0001, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs an MP4 file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. * * The items will be created using \a itemFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average, ItemFactory *itemFactory = nullptr); /*! * Constructs an MP4 file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. * * The items will be created using \a itemFactory (default if null). */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average, ItemFactory *itemFactory = nullptr); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns a pointer to the MP4 tag of the file. * * MP4::Tag implements the tag interface, so this serves as the * reimplementation of TagLib::File::tag(). * * \note The Tag <b>is still</b> owned by the MP4::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. */ Tag *tag() const override; /*! * Implements the unified property interface -- export function. */ PropertyMap properties() const override; /*! * Removes unsupported properties. Forwards to the actual Tag's * removeUnsupportedProperties() function. */ void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the MP4 audio properties for this file. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns \c true if the tags are * successfully stripped. * * \note This will update the file immediately. */ bool strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an MP4 tag, or the * file has a Metadata Item List (ilst) atom. */ bool hasMP4Tag() const; /*! * Returns whether or not the given \a stream can be opened as an ASF * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace MP4 } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4item.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000013113�14662262111�0017002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4item.h" using namespace TagLib; class MP4::Item::ItemPrivate { public: Type type; bool valid { true }; AtomDataType atomDataType { TypeUndefined }; union { bool m_bool; int m_int; IntPair m_intPair; unsigned char m_byte; unsigned int m_uint; long long m_longlong; }; StringList m_stringList; ByteVectorList m_byteVectorList; MP4::CoverArtList m_coverArtList; }; MP4::Item::Item() : d(std::make_shared<ItemPrivate>()) { d->type = Type::Void; d->valid = false; } MP4::Item::Item(const Item &) = default; MP4::Item &MP4::Item::operator=(const Item &) = default; void MP4::Item::swap(Item &item) noexcept { using std::swap; swap(d, item.d); } MP4::Item::~Item() = default; MP4::Item::Item(bool value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::Bool; d->m_bool = value; } MP4::Item::Item(int value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::Int; d->m_int = value; } MP4::Item::Item(unsigned char value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::Byte; d->m_byte = value; } MP4::Item::Item(unsigned int value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::UInt; d->m_uint = value; } MP4::Item::Item(long long value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::LongLong; d->m_longlong = value; } MP4::Item::Item(int value1, int value2) : d(std::make_shared<ItemPrivate>()) { d->type = Type::IntPair; d->m_intPair.first = value1; d->m_intPair.second = value2; } MP4::Item::Item(const ByteVectorList &value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::ByteVectorList; d->m_byteVectorList = value; } MP4::Item::Item(const StringList &value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::StringList; d->m_stringList = value; } MP4::Item::Item(const MP4::CoverArtList &value) : d(std::make_shared<ItemPrivate>()) { d->type = Type::CoverArtList; d->m_coverArtList = value; } void MP4::Item::setAtomDataType(MP4::AtomDataType type) { d->atomDataType = type; } MP4::AtomDataType MP4::Item::atomDataType() const { return d->atomDataType; } bool MP4::Item::toBool() const { return d->m_bool; } int MP4::Item::toInt() const { return d->m_int; } unsigned char MP4::Item::toByte() const { return d->m_byte; } unsigned int MP4::Item::toUInt() const { return d->m_uint; } long long MP4::Item::toLongLong() const { return d->m_longlong; } MP4::Item::IntPair MP4::Item::toIntPair() const { return d->m_intPair; } StringList MP4::Item::toStringList() const { return d->m_stringList; } ByteVectorList MP4::Item::toByteVectorList() const { return d->m_byteVectorList; } MP4::CoverArtList MP4::Item::toCoverArtList() const { return d->m_coverArtList; } bool MP4::Item::isValid() const { return d->valid; } MP4::Item::Type MP4::Item::type() const { return d->type; } bool MP4::Item::operator==(const Item &other) const { if(isValid() && other.isValid() && type() == other.type() && atomDataType() == other.atomDataType()) { switch(type()) { case Type::Void: return true; case Type::Bool: return toBool() == other.toBool(); case Type::Int: return toInt() == other.toInt(); case Type::IntPair: { const auto lhs = toIntPair(); const auto rhs = other.toIntPair(); return lhs.first == rhs.first && lhs.second == rhs.second; } case Type::Byte: return toByte() == other.toByte(); case Type::UInt: return toUInt() == other.toUInt(); case Type::LongLong: return toLongLong() == other.toLongLong(); case Type::StringList: return toStringList() == other.toStringList(); case Type::ByteVectorList: return toByteVectorList() == other.toByteVectorList(); case Type::CoverArtList: return toCoverArtList() == other.toCoverArtList(); } } return false; } bool MP4::Item::operator!=(const Item &other) const { return !(*this == other); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4item.h�������������������������������������������������������������������0000664�0000000�0000000�00000007535�14662262111�0016462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4ITEM_H #define TAGLIB_MP4ITEM_H #include "tstringlist.h" #include "taglib_export.h" #include "mp4coverart.h" namespace TagLib { namespace MP4 { //! MP4 item class TAGLIB_EXPORT Item { public: /*! * The data type stored in the item. */ enum class Type : unsigned char { Void, Bool, Int, IntPair, Byte, UInt, LongLong, StringList, ByteVectorList, CoverArtList }; struct IntPair { int first, second; }; Item(); Item(const Item &item); /*! * Copies the contents of \a item into this Item. */ Item &operator=(const Item &item); /*! * Exchanges the content of the Item with the content of \a item. */ void swap(Item &item) noexcept; ~Item(); Item(int value); Item(unsigned char value); Item(unsigned int value); Item(long long value); Item(bool value); Item(int value1, int value2); Item(const StringList &value); Item(const ByteVectorList &value); Item(const CoverArtList &value); void setAtomDataType(AtomDataType type); AtomDataType atomDataType() const; int toInt() const; unsigned char toByte() const; unsigned int toUInt() const; long long toLongLong() const; bool toBool() const; IntPair toIntPair() const; StringList toStringList() const; ByteVectorList toByteVectorList() const; CoverArtList toCoverArtList() const; bool isValid() const; Type type() const; /*! * Returns \c true if the Item and \a other are of the same type and * contain the same value. */ bool operator==(const Item &other) const; /*! * Returns \c true if the Item and \a other differ in type or value. */ bool operator!=(const Item &other) const; private: class ItemPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<ItemPrivate> d; }; using ItemMap = TagLib::Map<String, Item>; } // namespace MP4 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4itemfactory.cpp����������������������������������������������������������0000664�0000000�0000000�00000060676�14662262111�0020412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4itemfactory.h" #include <utility> #include "tbytevector.h" #include "tdebug.h" #include "id3v1genres.h" using namespace TagLib; using namespace MP4; namespace { constexpr char freeFormPrefix[] = "----:com.apple.iTunes:"; } // namespace class ItemFactory::ItemFactoryPrivate { public: NameHandlerMap handlerTypeForName; Map<ByteVector, String> propertyKeyForName; Map<String, ByteVector> nameForPropertyKey; }; ItemFactory ItemFactory::factory; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ItemFactory *ItemFactory::instance() { return &factory; } std::pair<String, Item> ItemFactory::parseItem( const Atom *atom, const ByteVector &data) const { switch(handlerTypeForName(atom->name())) { case ItemHandlerType::Unknown: break; case ItemHandlerType::FreeForm: return parseFreeForm(atom, data); case ItemHandlerType::IntPair: case ItemHandlerType::IntPairNoTrailing: return parseIntPair(atom, data); case ItemHandlerType::Bool: return parseBool(atom, data); case ItemHandlerType::Int: return parseInt(atom, data); case ItemHandlerType::TextOrInt: return parseTextOrInt(atom, data); case ItemHandlerType::UInt: return parseUInt(atom, data); case ItemHandlerType::LongLong: return parseLongLong(atom, data); case ItemHandlerType::Byte: return parseByte(atom, data); case ItemHandlerType::Gnre: return parseGnre(atom, data); case ItemHandlerType::Covr: return parseCovr(atom, data); case ItemHandlerType::TextImplicit: return parseText(atom, data, -1); case ItemHandlerType::Text: return parseText(atom, data); } return {atom->name(), Item()}; } ByteVector ItemFactory::renderItem( const String &itemName, const Item &item) const { if(itemName.startsWith("----")) { return renderFreeForm(itemName, item); } const ByteVector name = itemName.data(String::Latin1); switch(handlerTypeForName(name)) { case ItemHandlerType::Unknown: debug("MP4: Unknown item name \"" + name + "\""); break; case ItemHandlerType::FreeForm: return renderFreeForm(name, item); case ItemHandlerType::IntPair: return renderIntPair(name, item); case ItemHandlerType::IntPairNoTrailing: return renderIntPairNoTrailing(name, item); case ItemHandlerType::Bool: return renderBool(name, item); case ItemHandlerType::Int: return renderInt(name, item); case ItemHandlerType::TextOrInt: return renderTextOrInt(name, item); case ItemHandlerType::UInt: return renderUInt(name, item); case ItemHandlerType::LongLong: return renderLongLong(name, item); case ItemHandlerType::Byte: return renderByte(name, item); case ItemHandlerType::Gnre: return renderInt(name, item); case ItemHandlerType::Covr: return renderCovr(name, item); case ItemHandlerType::TextImplicit: return renderText(name, item, TypeImplicit); case ItemHandlerType::Text: return renderText(name, item); } return ByteVector(); } std::pair<ByteVector, Item> ItemFactory::itemFromProperty( const String &key, const StringList &values) const { ByteVector name = nameForPropertyKey(key); if(!name.isEmpty()) { if(values.isEmpty()) { return {name, values}; } switch(name.startsWith("----") ? ItemHandlerType::FreeForm : handlerTypeForName(name)) { case ItemHandlerType::IntPair: case ItemHandlerType::IntPairNoTrailing: if(StringList parts = StringList::split(values.front(), "/"); !parts.isEmpty()) { int first = parts[0].toInt(); int second = 0; if(parts.size() > 1) { second = parts[1].toInt(); } return {name, Item(first, second)}; } break; case ItemHandlerType::Int: case ItemHandlerType::Gnre: return {name, Item(values.front().toInt())}; case ItemHandlerType::UInt: return {name, Item(static_cast<unsigned int>(values.front().toInt()))}; case ItemHandlerType::LongLong: return {name, Item(static_cast<long long>(values.front().toInt()))}; case ItemHandlerType::Byte: return {name, Item(static_cast<unsigned char>(values.front().toInt()))}; case ItemHandlerType::Bool: return {name, Item(values.front().toInt() != 0)}; case ItemHandlerType::FreeForm: case ItemHandlerType::TextOrInt: case ItemHandlerType::TextImplicit: case ItemHandlerType::Text: return {name, values}; case ItemHandlerType::Covr: debug("MP4: Invalid item \"" + name + "\" for property"); break; case ItemHandlerType::Unknown: debug("MP4: Unknown item name \"" + name + "\" for property"); break; } } return {name, Item()}; } std::pair<String, StringList> ItemFactory::itemToProperty( const ByteVector &itemName, const Item &item) const { if(const String key = propertyKeyForName(itemName); !key.isEmpty()) { switch(itemName.startsWith("----") ? ItemHandlerType::FreeForm : handlerTypeForName(itemName)) { case ItemHandlerType::IntPair: case ItemHandlerType::IntPairNoTrailing: { auto [vn, tn] = item.toIntPair(); String value = String::number(vn); if(tn) { value += "/" + String::number(tn); } return {key, value}; } case ItemHandlerType::Int: case ItemHandlerType::Gnre: return {key, String::number(item.toInt())}; case ItemHandlerType::UInt: return {key, String::number(item.toUInt())}; case ItemHandlerType::LongLong: return {key, String::fromLongLong(item.toLongLong())}; case ItemHandlerType::Byte: return {key, String::number(item.toByte())}; case ItemHandlerType::Bool: return {key, String::number(item.toBool())}; case ItemHandlerType::FreeForm: case ItemHandlerType::TextOrInt: case ItemHandlerType::TextImplicit: case ItemHandlerType::Text: return {key, item.toStringList()}; case ItemHandlerType::Covr: debug("MP4: Invalid item \"" + itemName + "\" for property"); break; case ItemHandlerType::Unknown: debug("MP4: Unknown item name \"" + itemName + "\" for property"); break; } } return {String(), StringList()}; } String ItemFactory::propertyKeyForName(const ByteVector &name) const { if(d->propertyKeyForName.isEmpty()) { d->propertyKeyForName = namePropertyMap(); } String key = d->propertyKeyForName.value(name); if(key.isEmpty() && name.startsWith(freeFormPrefix)) { key = name.mid(std::size(freeFormPrefix) - 1); } return key; } ByteVector ItemFactory::nameForPropertyKey(const String &key) const { if(d->nameForPropertyKey.isEmpty()) { if(d->propertyKeyForName.isEmpty()) { d->propertyKeyForName = namePropertyMap(); } for(const auto &[k, t] : std::as_const(d->propertyKeyForName)) { d->nameForPropertyKey[t] = k; } } ByteVector name = d->nameForPropertyKey.value(key); if(name.isEmpty() && !key.isEmpty()) { const auto &firstChar = key[0]; if(firstChar >= 'A' && firstChar <= 'Z') { name = (freeFormPrefix + key).data(String::UTF8); } } return name; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// ItemFactory::ItemFactory() : d(std::make_unique<ItemFactoryPrivate>()) { } ItemFactory::~ItemFactory() = default; ItemFactory::NameHandlerMap ItemFactory::nameHandlerMap() const { return { {"----", ItemHandlerType::FreeForm}, {"trkn", ItemHandlerType::IntPair}, {"disk", ItemHandlerType::IntPairNoTrailing}, {"cpil", ItemHandlerType::Bool}, {"pgap", ItemHandlerType::Bool}, {"pcst", ItemHandlerType::Bool}, {"shwm", ItemHandlerType::Bool}, {"tmpo", ItemHandlerType::Int}, {"\251mvi", ItemHandlerType::Int}, {"\251mvc", ItemHandlerType::Int}, {"hdvd", ItemHandlerType::Int}, {"rate", ItemHandlerType::TextOrInt}, {"tvsn", ItemHandlerType::UInt}, {"tves", ItemHandlerType::UInt}, {"cnID", ItemHandlerType::UInt}, {"sfID", ItemHandlerType::UInt}, {"atID", ItemHandlerType::UInt}, {"geID", ItemHandlerType::UInt}, {"cmID", ItemHandlerType::UInt}, {"plID", ItemHandlerType::LongLong}, {"stik", ItemHandlerType::Byte}, {"rtng", ItemHandlerType::Byte}, {"akID", ItemHandlerType::Byte}, {"gnre", ItemHandlerType::Gnre}, {"covr", ItemHandlerType::Covr}, {"purl", ItemHandlerType::TextImplicit}, {"egid", ItemHandlerType::TextImplicit}, }; } ItemFactory::ItemHandlerType ItemFactory::handlerTypeForName( const ByteVector &name) const { if(d->handlerTypeForName.isEmpty()) { d->handlerTypeForName = nameHandlerMap(); } auto type = d->handlerTypeForName.value(name, ItemHandlerType::Unknown); if (type == ItemHandlerType::Unknown && name.size() == 4) { type = ItemHandlerType::Text; } return type; } Map<ByteVector, String> ItemFactory::namePropertyMap() const { return { {"\251nam", "TITLE"}, {"\251ART", "ARTIST"}, {"\251alb", "ALBUM"}, {"\251cmt", "COMMENT"}, {"\251gen", "GENRE"}, {"\251day", "DATE"}, {"\251wrt", "COMPOSER"}, {"\251grp", "GROUPING"}, {"aART", "ALBUMARTIST"}, {"trkn", "TRACKNUMBER"}, {"disk", "DISCNUMBER"}, {"cpil", "COMPILATION"}, {"tmpo", "BPM"}, {"cprt", "COPYRIGHT"}, {"\251lyr", "LYRICS"}, {"\251too", "ENCODING"}, {"\251enc", "ENCODEDBY"}, {"soal", "ALBUMSORT"}, {"soaa", "ALBUMARTISTSORT"}, {"soar", "ARTISTSORT"}, {"sonm", "TITLESORT"}, {"soco", "COMPOSERSORT"}, {"sosn", "SHOWSORT"}, {"shwm", "SHOWWORKMOVEMENT"}, {"pgap", "GAPLESSPLAYBACK"}, {"pcst", "PODCAST"}, {"catg", "PODCASTCATEGORY"}, {"desc", "PODCASTDESC"}, {"egid", "PODCASTID"}, {"purl", "PODCASTURL"}, {"tves", "TVEPISODE"}, {"tven", "TVEPISODEID"}, {"tvnn", "TVNETWORK"}, {"tvsn", "TVSEASON"}, {"tvsh", "TVSHOW"}, {"\251wrk", "WORK"}, {"\251mvn", "MOVEMENTNAME"}, {"\251mvi", "MOVEMENTNUMBER"}, {"\251mvc", "MOVEMENTCOUNT"}, {"ownr", "OWNER"}, {"----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID"}, {"----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID"}, {"----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID"}, {"----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"}, {"----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"}, {"----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID"}, {"----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID"}, {"----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY"}, {"----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS"}, {"----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE"}, {"----:com.apple.iTunes:ARTISTS", "ARTISTS"}, {"----:com.apple.iTunes:ORIGINALDATE", "ORIGINALDATE"}, {"----:com.apple.iTunes:RELEASEDATE", "RELEASEDATE"}, {"----:com.apple.iTunes:ASIN", "ASIN"}, {"----:com.apple.iTunes:LABEL", "LABEL"}, {"----:com.apple.iTunes:LYRICIST", "LYRICIST"}, {"----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR"}, {"----:com.apple.iTunes:REMIXER", "REMIXER"}, {"----:com.apple.iTunes:ENGINEER", "ENGINEER"}, {"----:com.apple.iTunes:PRODUCER", "PRODUCER"}, {"----:com.apple.iTunes:DJMIXER", "DJMIXER"}, {"----:com.apple.iTunes:MIXER", "MIXER"}, {"----:com.apple.iTunes:SUBTITLE", "SUBTITLE"}, {"----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE"}, {"----:com.apple.iTunes:MOOD", "MOOD"}, {"----:com.apple.iTunes:ISRC", "ISRC"}, {"----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER"}, {"----:com.apple.iTunes:BARCODE", "BARCODE"}, {"----:com.apple.iTunes:SCRIPT", "SCRIPT"}, {"----:com.apple.iTunes:LANGUAGE", "LANGUAGE"}, {"----:com.apple.iTunes:LICENSE", "LICENSE"}, {"----:com.apple.iTunes:MEDIA", "MEDIA"} }; } MP4::AtomDataList ItemFactory::parseData2( const MP4::Atom *atom, const ByteVector &data, int expectedFlags, bool freeForm) { AtomDataList result; int i = 0; unsigned int pos = 0; while(pos < data.size()) { const auto length = static_cast<int>(data.toUInt(pos)); if(length < 12) { debug("MP4: Too short atom"); return result; } const ByteVector name = data.mid(pos + 4, 4); const auto flags = static_cast<int>(data.toUInt(pos + 8)); if(freeForm && i < 2) { if(i == 0 && name != "mean") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\""); return result; } if(i == 1 && name != "name") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\""); return result; } result.append(AtomData(static_cast<AtomDataType>(flags), data.mid(pos + 12, length - 12))); } else { if(name != "data") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); return result; } if(expectedFlags == -1 || flags == expectedFlags) { result.append(AtomData(static_cast<AtomDataType>(flags), data.mid(pos + 16, length - 16))); } } pos += length; i++; } return result; } ByteVectorList ItemFactory::parseData( const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags, bool freeForm) { const AtomDataList data = parseData2(atom, bytes, expectedFlags, freeForm); ByteVectorList result; for(const auto &atom : data) { result.append(atom.data); } return result; } std::pair<String, Item> ItemFactory::parseInt( const MP4::Atom *atom, const ByteVector &bytes) { ByteVectorList data = parseData(atom, bytes); return { atom->name(), !data.isEmpty() ? Item(static_cast<int>(data[0].toShort())) : Item() }; } std::pair<String, Item> ItemFactory::parseTextOrInt( const MP4::Atom *atom, const ByteVector &bytes) { if(AtomDataList data = parseData2(atom, bytes); !data.isEmpty()) { AtomData val = data[0]; return { atom->name(), val.type == TypeUTF8 ? Item(StringList(String(val.data, String::UTF8))) : Item(static_cast<int>(val.data.toShort())) }; } return {atom->name(), Item()}; } std::pair<String, Item> ItemFactory::parseUInt( const MP4::Atom *atom, const ByteVector &bytes) { ByteVectorList data = parseData(atom, bytes); return { atom->name(), !data.isEmpty() ? Item(data[0].toUInt()) : Item() }; } std::pair<String, Item> ItemFactory::parseLongLong( const MP4::Atom *atom, const ByteVector &bytes) { ByteVectorList data = parseData(atom, bytes); return { atom->name(), !data.isEmpty() ?Item (data[0].toLongLong()) : Item() }; } std::pair<String, Item> ItemFactory::parseByte( const MP4::Atom *atom, const ByteVector &bytes) { ByteVectorList data = parseData(atom, bytes); return { atom->name(), !data.isEmpty() ? Item(static_cast<unsigned char>(data[0].at(0))) : Item() }; } std::pair<String, Item> ItemFactory::parseGnre( const MP4::Atom *atom, const ByteVector &bytes) { if(ByteVectorList data = parseData(atom, bytes); !data.isEmpty()) { int idx = static_cast<int>(data[0].toShort()); if(idx > 0) { return { "\251gen", Item(StringList(ID3v1::genre(idx - 1))) }; } } return {"\251gen", Item()}; } std::pair<String, Item> ItemFactory::parseIntPair( const MP4::Atom *atom, const ByteVector &bytes) { if(ByteVectorList data = parseData(atom, bytes); !data.isEmpty()) { const int a = data[0].toShort(2U); const int b = data[0].toShort(4U); return {atom->name(), Item(a, b)}; } return {atom->name(), Item()}; } std::pair<String, Item> ItemFactory::parseBool( const MP4::Atom *atom, const ByteVector &bytes) { if(ByteVectorList data = parseData(atom, bytes); !data.isEmpty()) { bool value = !data[0].isEmpty() && data[0][0] != '\0'; return {atom->name(), Item(value)}; } return {atom->name(), Item()}; } std::pair<String, Item> ItemFactory::parseText( const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags) { if(const ByteVectorList data = parseData(atom, bytes, expectedFlags); !data.isEmpty()) { StringList value; for(const auto &byte : data) { value.append(String(byte, String::UTF8)); } return {atom->name(), Item(value)}; } return {atom->name(), Item()}; } std::pair<String, Item> ItemFactory::parseFreeForm( const MP4::Atom *atom, const ByteVector &bytes) { if(const AtomDataList data = parseData2(atom, bytes, -1, true); data.size() > 2) { auto itBegin = data.begin(); String name = "----:"; name += String((itBegin++)->data, String::UTF8); // data[0].data name += ':'; name += String((itBegin++)->data, String::UTF8); // data[1].data AtomDataType type = itBegin->type; // data[2].type for(auto it = itBegin; it != data.end(); ++it) { if(it->type != type) { debug("MP4: We currently don't support values with multiple types"); break; } } if(type == TypeUTF8) { StringList value; for(auto it = itBegin; it != data.end(); ++it) { value.append(String(it->data, String::UTF8)); } Item item(value); item.setAtomDataType(type); return {name, item}; } ByteVectorList value; for(auto it = itBegin; it != data.end(); ++it) { value.append(it->data); } Item item(value); item.setAtomDataType(type); return {name, item}; } return {atom->name(), Item()}; } std::pair<String, Item> ItemFactory::parseCovr( const MP4::Atom *atom, const ByteVector &data) { MP4::CoverArtList value; unsigned int pos = 0; while(pos < data.size()) { const int length = static_cast<int>(data.toUInt(pos)); if(length < 12) { debug("MP4: Too short atom"); break; } const ByteVector name = data.mid(pos + 4, 4); const int flags = static_cast<int>(data.toUInt(pos + 8)); if(name != "data") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); break; } if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) { value.append(MP4::CoverArt(static_cast<MP4::CoverArt::Format>(flags), data.mid(pos + 16, length - 16))); } else { debug("MP4: Unknown covr format " + String::number(flags)); } pos += length; } return { atom->name(), !value.isEmpty() ? Item(value) : Item() }; } ByteVector ItemFactory::renderAtom( const ByteVector &name, const ByteVector &data) { return ByteVector::fromUInt(data.size() + 8) + name + data; } ByteVector ItemFactory::renderData( const ByteVector &name, int flags, const ByteVectorList &data) { ByteVector result; for(const auto &byte : data) { result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + byte)); } return renderAtom(name, result); } ByteVector ItemFactory::renderBool( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); return renderData(name, TypeInteger, data); } ByteVector ItemFactory::renderInt( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromShort(item.toInt())); return renderData(name, TypeInteger, data); } ByteVector ItemFactory::renderTextOrInt( const ByteVector &name, const MP4::Item &item) { StringList value = item.toStringList(); return value.isEmpty() ? renderInt(name, item) : renderText(name, item); } ByteVector ItemFactory::renderUInt( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromUInt(item.toUInt())); return renderData(name, TypeInteger, data); } ByteVector ItemFactory::renderLongLong( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector::fromLongLong(item.toLongLong())); return renderData(name, TypeInteger, data); } ByteVector ItemFactory::renderByte( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector(1, item.toByte())); return renderData(name, TypeInteger, data); } ByteVector ItemFactory::renderIntPair( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector(2, '\0') + ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().second) + ByteVector(2, '\0')); return renderData(name, TypeImplicit, data); } ByteVector ItemFactory::renderIntPairNoTrailing( const ByteVector &name, const MP4::Item &item) { ByteVectorList data; data.append(ByteVector(2, '\0') + ByteVector::fromShort(item.toIntPair().first) + ByteVector::fromShort(item.toIntPair().second)); return renderData(name, TypeImplicit, data); } ByteVector ItemFactory::renderText( const ByteVector &name, const MP4::Item &item, int flags) { ByteVectorList data; const StringList values = item.toStringList(); for(const auto &value : values) { data.append(value.data(String::UTF8)); } return renderData(name, flags, data); } ByteVector ItemFactory::renderCovr( const ByteVector &name, const MP4::Item &item) { ByteVector data; const MP4::CoverArtList values = item.toCoverArtList(); for(const auto &value : values) { data.append(renderAtom("data", ByteVector::fromUInt(value.format()) + ByteVector(4, '\0') + value.data())); } return renderAtom(name, data); } ByteVector ItemFactory::renderFreeForm( const String &name, const MP4::Item &item) { StringList header = StringList::split(name, ":"); if(header.size() != 3) { debug("MP4: Invalid free-form item name \"" + name + "\""); return ByteVector(); } ByteVector data; data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8))); data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8))); AtomDataType type = item.atomDataType(); if(type == TypeUndefined) { if(!item.toStringList().isEmpty()) { type = TypeUTF8; } else { type = TypeImplicit; } } if(type == TypeUTF8) { const StringList values = item.toStringList(); for(const auto &value : values) { data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value.data(String::UTF8))); } } else { const ByteVectorList values = item.toByteVectorList(); for(const auto &value : values) { data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value)); } } return renderAtom("----", data); } ������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4itemfactory.h������������������������������������������������������������0000664�0000000�0000000�00000024453�14662262111�0020050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4ITEMFACTORY_H #define TAGLIB_MP4ITEMFACTORY_H #include <memory> #include "taglib_export.h" #include "mp4item.h" namespace TagLib { namespace MP4 { //! A factory for creating MP4 items during parsing /*! * This factory abstracts away the parsing and rendering between atom data * and MP4 items. * * Reimplementing this factory is the key to adding support for atom types * not directly supported by TagLib to your application. To do so you would * subclass this factory and reimplement nameHandlerMap() to add support * for new atoms. If the new atoms do not have the same behavior as * other supported atoms, it may be necessary to reimplement parseItem() and * renderItem(). Then by setting your factory in the MP4::Tag constructor * you can implement behavior that will allow for new atom types to be used. * * A custom item factory adding support for a "tsti" integer atom and a * "tstt" text atom can be implemented like this: * * \code * class CustomItemFactory : public MP4::ItemFactory { * protected: * NameHandlerMap nameHandlerMap() const override * { * return MP4::ItemFactory::nameHandlerMap() * .insert("tsti", ItemHandlerType::Int) * .insert("tstt", ItemHandlerType::Text); * } * }; * \endcode * * If the custom item shall also be accessible via a property, * namePropertyMap() can be overridden in the same way. */ class TAGLIB_EXPORT ItemFactory { public: ItemFactory(const ItemFactory &) = delete; ItemFactory &operator=(const ItemFactory &) = delete; static ItemFactory *instance(); /*! * Create an MP4 item from the \a data bytes of an \a atom. * Returns the name (in most cases atom->name) and an item, * an invalid item on failure. * The default implementation uses the map returned by nameHandlerMap(). */ virtual std::pair<String, Item> parseItem( const Atom *atom, const ByteVector &data) const; /*! * Render an MP4 \a item to the data bytes of an atom \a itemName. * An empty byte vector is returned if the item is invalid or unknown. * The default implementation uses the map returned by nameHandlerMap(). */ virtual ByteVector renderItem( const String &itemName, const Item &item) const; /*! * Create an MP4 item from a property with \a key and \a values. * If the property is not supported, an invalid item is returned. * The default implementation uses the map returned by namePropertyMap(). */ virtual std::pair<ByteVector, Item> itemFromProperty( const String &key, const StringList &values) const; /*! * Get an MP4 item as a property. * If no property exists for \a itemName, an empty string is returned as * the property key. * The default implementation uses the map returned by namePropertyMap(). */ virtual std::pair<String, StringList> itemToProperty( const ByteVector &itemName, const Item &item) const; /*! * Returns property key for atom \a name, empty if no property exists for * this atom. * The default method looks up the map created by namePropertyMap() and * should be enough for most uses. */ virtual String propertyKeyForName(const ByteVector &name) const; /*! * Returns atom name for property \a key, empty if no property is * supported for this key. * The default method uses the reverse mapping of propertyKeyForName() * and should be enough for most uses. */ virtual ByteVector nameForPropertyKey(const String &key) const; protected: /*! * Type that determines the parsing and rendering between the data and * the item representation of an atom. */ enum class ItemHandlerType { Unknown, FreeForm, IntPair, IntPairNoTrailing, Bool, Int, TextOrInt, UInt, LongLong, Byte, Gnre, Covr, TextImplicit, Text }; /*! Mapping of atom name to handler type. */ using NameHandlerMap = Map<ByteVector, ItemHandlerType>; /*! * Constructs an item factory. Because this is a singleton this method is * protected, but may be used for subclasses. */ ItemFactory(); /*! * Destroys the frame factory. */ ~ItemFactory(); /*! * Returns mapping between atom names and handler types. * This method is called once by handlerTypeForName() to initialize its * internal cache. * To add support for a new atom, it is sufficient in most cases to just * reimplement this method and add new entries. */ virtual NameHandlerMap nameHandlerMap() const; /*! * Returns handler type for atom \a name. * The default method looks up the map created by nameHandlerMap() and * should be enough for most uses. */ virtual ItemHandlerType handlerTypeForName(const ByteVector &name) const; /*! * Returns mapping between atom names and property keys. * This method is called once by propertyKeyForName() to initialize its * internal cache. * To add support for a new atom with a property, it is sufficient in most * cases to just reimplement this method and add new entries. */ virtual Map<ByteVector, String> namePropertyMap() const; // Functions used by parseItem() to create items from atom data. static MP4::AtomDataList parseData2( const MP4::Atom *atom, const ByteVector &data, int expectedFlags = -1, bool freeForm = false); static ByteVectorList parseData( const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags = -1, bool freeForm = false); static std::pair<String, Item> parseText( const MP4::Atom *atom, const ByteVector &bytes, int expectedFlags = 1); static std::pair<String, Item> parseFreeForm( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseInt( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseByte( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseTextOrInt( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseUInt( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseLongLong( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseGnre( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseIntPair( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseBool( const MP4::Atom *atom, const ByteVector &bytes); static std::pair<String, Item> parseCovr( const MP4::Atom *atom, const ByteVector &data); // Functions used by renderItem() to render atom data for items. static ByteVector renderAtom( const ByteVector &name, const ByteVector &data); static ByteVector renderData( const ByteVector &name, int flags, const ByteVectorList &data); static ByteVector renderText( const ByteVector &name, const MP4::Item &item, int flags = TypeUTF8); static ByteVector renderFreeForm( const String &name, const MP4::Item &item); static ByteVector renderBool( const ByteVector &name, const MP4::Item &item); static ByteVector renderInt( const ByteVector &name, const MP4::Item &item); static ByteVector renderTextOrInt( const ByteVector &name, const MP4::Item &item); static ByteVector renderByte( const ByteVector &name, const MP4::Item &item); static ByteVector renderUInt( const ByteVector &name, const MP4::Item &item); static ByteVector renderLongLong( const ByteVector &name, const MP4::Item &item); static ByteVector renderIntPair( const ByteVector &name, const MP4::Item &item); static ByteVector renderIntPairNoTrailing( const ByteVector &name, const MP4::Item &item); static ByteVector renderCovr( const ByteVector &name, const MP4::Item &item); private: static ItemFactory factory; class ItemFactoryPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ItemFactoryPrivate> d; }; } // namespace MP4 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4properties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000016262�14662262111�0020250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4properties.h" #include "tdebug.h" #include "tstring.h" #include "mp4file.h" #include "mp4atom.h" using namespace TagLib; namespace { // Calculate the total bytes used by audio data, used to calculate the bitrate long long calculateMdatLength(const MP4::AtomList &list) { long long totalLength = 0; for(const auto &atom : list) { offset_t length = atom->length(); if(length == 0) return 0; // for safety, see checkValid() in mp4file.cpp if(atom->name() == "mdat") totalLength += length; totalLength += calculateMdatLength(atom->children()); } return totalLength; } } // namespace class MP4::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int bitsPerSample { 0 }; bool encrypted { false }; Codec codec { MP4::Properties::Unknown }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MP4::Properties::Properties(File *file, const MP4::Atoms *atoms, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file, atoms); } MP4::Properties::~Properties() = default; int MP4::Properties::channels() const { return d->channels; } int MP4::Properties::sampleRate() const { return d->sampleRate; } int MP4::Properties::lengthInMilliseconds() const { return d->length; } int MP4::Properties::bitrate() const { return d->bitrate; } int MP4::Properties::bitsPerSample() const { return d->bitsPerSample; } bool MP4::Properties::isEncrypted() const { return d->encrypted; } MP4::Properties::Codec MP4::Properties::codec() const { return d->codec; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MP4::Properties::read(File *file, const Atoms *atoms) { MP4::Atom *moov = atoms->find("moov"); if(!moov) { debug("MP4: Atom 'moov' not found"); return; } MP4::Atom *trak = nullptr; ByteVector data; const MP4::AtomList trakList = moov->findall("trak"); for(const auto &track : trakList) { const MP4::Atom *hdlr = track->find("mdia", "hdlr"); if(!hdlr) { debug("MP4: Atom 'trak.mdia.hdlr' not found"); return; } trak = track; file->seek(hdlr->offset()); data = file->readBlock(hdlr->length()); if(data.containsAt("soun", 16)) { break; } trak = nullptr; } if(!trak) { debug("MP4: No audio tracks"); return; } const MP4::Atom *mdhd = trak->find("mdia", "mdhd"); if(!mdhd) { debug("MP4: Atom 'trak.mdia.mdhd' not found"); return; } file->seek(mdhd->offset()); data = file->readBlock(mdhd->length()); const unsigned int version = data.at(8); long long unit; long long length; if(version == 1) { if(data.size() < 36 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } unit = data.toUInt(28U); length = data.toLongLong(32U); } else { if(data.size() < 24 + 8) { debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); return; } unit = data.toUInt(20U); length = data.toUInt(24U); } if(length == 0) { // No length found in the media header (mdhd), try the movie header (mvhd) if(const MP4::Atom *mvhd = moov->find("mvhd")) { file->seek(mvhd->offset()); data = file->readBlock(mvhd->length()); if(data.size() >= 24 + 4) { unit = data.toUInt(20U); length = data.toUInt(24U); } } } if(unit > 0 && length > 0) d->length = static_cast<int>(length * 1000.0 / unit + 0.5); MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); if(!atom) { return; } file->seek(atom->offset()); data = file->readBlock(atom->length()); if(data.containsAt("mp4a", 20)) { d->codec = AAC; d->channels = data.toShort(40U); d->bitsPerSample = data.toShort(42U); d->sampleRate = data.toUInt(46U); if(data.containsAt("esds", 56) && data.at(64) == 0x03) { unsigned int pos = 65; if(data.containsAt("\x80\x80\x80", pos)) { pos += 3; } pos += 4; if(data.at(pos) == 0x04) { pos += 1; if(data.containsAt("\x80\x80\x80", pos)) { pos += 3; } pos += 10; if(const unsigned int bitrateValue = data.toUInt(pos); bitrateValue != 0 || d->length <= 0) { d->bitrate = static_cast<int>((bitrateValue + 500) / 1000.0 + 0.5); } else { d->bitrate = static_cast<int>( calculateMdatLength(atoms->atoms()) * 8 / d->length); } } } } else if(data.containsAt("alac", 20)) { if(atom->length() == 88 && data.containsAt("alac", 56)) { d->codec = ALAC; d->bitsPerSample = data.at(69); d->channels = data.at(73); d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5); d->sampleRate = data.toUInt(84U); if(d->bitrate == 0 && d->length > 0) { // There are files which do not contain a nominal bitrate, e.g. those // generated by refalac64.exe. Calculate the bitrate from the audio // data size (mdat atoms) and the duration. d->bitrate = static_cast<int>(calculateMdatLength(atoms->atoms()) * 8 / d->length); } } } if(atom->find("drms")) { d->encrypted = true; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4properties.h�������������������������������������������������������������0000664�0000000�0000000�00000006505�14662262111�0017714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4PROPERTIES_H #define TAGLIB_MP4PROPERTIES_H #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace MP4 { class Atoms; class File; //! An implementation of MP4 audio properties class TAGLIB_EXPORT Properties : public AudioProperties { public: enum Codec { Unknown = 0, AAC, ALAC }; Properties(File *file, const Atoms *atoms, ReadStyle style = Average); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ virtual int bitsPerSample() const; /*! * Returns whether or not the file is encrypted. */ bool isEncrypted() const; /*! * Returns the codec used in the file. */ Codec codec() const; private: void read(File *file, const Atoms *atoms); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace MP4 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4tag.cpp������������������������������������������������������������������0000664�0000000�0000000�00000034607�14662262111�0016632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mp4tag.h" #include <utility> #include "tdebug.h" #include "tpropertymap.h" #include "mp4itemfactory.h" #include "mp4atom.h" #include "mp4coverart.h" using namespace TagLib; class MP4::Tag::TagPrivate { public: TagPrivate(const ItemFactory *itemFactory) : factory(itemFactory ? itemFactory : ItemFactory::instance()) { } ~TagPrivate() = default; const ItemFactory *factory; TagLib::File *file { nullptr }; Atoms *atoms { nullptr }; ItemMap items; }; MP4::Tag::Tag() : d(std::make_unique<TagPrivate>(ItemFactory::instance())) { } MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms, const MP4::ItemFactory *factory) : d(std::make_unique<TagPrivate>(factory)) { d->file = file; d->atoms = atoms; const MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst"); if(!ilst) { //debug("Atom moov.udta.meta.ilst not found."); return; } for(const auto &atom : ilst->children()) { file->seek(atom->offset() + 8); ByteVector data = d->file->readBlock(atom->length() - 8); if(const auto &[name, itm] = d->factory->parseItem(atom, data); itm.isValid()) { addItem(name, itm); } } } MP4::Tag::~Tag() = default; ByteVector MP4::Tag::padIlst(const ByteVector &data, int length) const { if(length == -1) { length = ((data.size() + 1023) & ~1023) - data.size(); } return renderAtom("free", ByteVector(length, '\1')); } ByteVector MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const { return ByteVector::fromUInt(data.size() + 8) + name + data; } bool MP4::Tag::save() { ByteVector data; for(const auto &[name, itm] : std::as_const(d->items)) { data.append(d->factory->renderItem(name, itm)); } data = renderAtom("ilst", data); AtomList path = d->atoms->path("moov", "udta", "meta", "ilst"); if(path.size() == 4) { saveExisting(data, path); } else { saveNew(data); } return true; } bool MP4::Tag::strip() { d->items.clear(); AtomList path = d->atoms->path("moov", "udta", "meta", "ilst"); if(path.size() == 4) { saveExisting(ByteVector(), path); } return true; } void MP4::Tag::updateParents(const AtomList &path, offset_t delta, int ignore) { if(static_cast<int>(path.size()) <= ignore) return; auto itEnd = path.end(); std::advance(itEnd, 0 - ignore); for(auto it = path.begin(); it != itEnd; ++it) { d->file->seek((*it)->offset()); long size = d->file->readBlock(4).toUInt(); // 64-bit if (size == 1) { d->file->seek(4, File::Current); // Skip name long long longSize = d->file->readBlock(8).toLongLong(); // Seek the offset of the 64-bit size d->file->seek((*it)->offset() + 8); d->file->writeBlock(ByteVector::fromLongLong(longSize + delta)); } // 32-bit else { d->file->seek((*it)->offset()); d->file->writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(size + delta))); } } } void MP4::Tag::updateOffsets(offset_t delta, offset_t offset) { if(MP4::Atom *moov = d->atoms->find("moov")) { const MP4::AtomList stco = moov->findall("stco", true); for(const auto &atom : stco) { if(atom->offset() > offset) { atom->addToOffset(delta); } d->file->seek(atom->offset() + 12); ByteVector data = d->file->readBlock(atom->length() - 12); unsigned int count = data.toUInt(); d->file->seek(atom->offset() + 16); unsigned int pos = 4; while(count--) { auto o = static_cast<offset_t>(data.toUInt(pos)); if(o > offset) { o += delta; } d->file->writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(o))); pos += 4; } } const MP4::AtomList co64 = moov->findall("co64", true); for(const auto &atom : co64) { if(atom->offset() > offset) { atom->addToOffset(delta); } d->file->seek(atom->offset() + 12); ByteVector data = d->file->readBlock(atom->length() - 12); unsigned int count = data.toUInt(); d->file->seek(atom->offset() + 16); unsigned int pos = 4; while(count--) { long long o = data.toLongLong(pos); if(o > offset) { o += delta; } d->file->writeBlock(ByteVector::fromLongLong(o)); pos += 8; } } } if(MP4::Atom *moof = d->atoms->find("moof")) { const MP4::AtomList tfhd = moof->findall("tfhd", true); for(const auto &atom : tfhd) { if(atom->offset() > offset) { atom->addToOffset(delta); } d->file->seek(atom->offset() + 9); ByteVector data = d->file->readBlock(atom->length() - 9); if(const unsigned int flags = data.toUInt(0, 3, true); flags & 1) { long long o = data.toLongLong(7U); if(o > offset) { o += delta; } d->file->seek(atom->offset() + 16); d->file->writeBlock(ByteVector::fromLongLong(o)); } } } } void MP4::Tag::saveNew(ByteVector data) { data = renderAtom("meta", ByteVector(4, '\0') + renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") + ByteVector(9, '\0')) + data + padIlst(data)); AtomList path = d->atoms->path("moov", "udta"); if(path.size() != 2) { path = d->atoms->path("moov"); data = renderAtom("udta", data); } offset_t offset = path.back()->offset() + 8; d->file->insert(data, offset, 0); updateParents(path, data.size()); updateOffsets(data.size(), offset); // Insert the newly created atoms into the tree to keep it up-to-date. d->file->seek(offset); path.back()->prependChild(new Atom(d->file)); } void MP4::Tag::saveExisting(ByteVector data, const AtomList &path) { auto it = path.end(); MP4::Atom *ilst = *(--it); offset_t offset = ilst->offset(); offset_t length = ilst->length(); MP4::Atom *meta = *(--it); auto index = meta->children().cfind(ilst); // check if there is an atom before 'ilst', and possibly use it as padding if(index != meta->children().cbegin()) { auto prevIndex = std::prev(index); if(const MP4::Atom *prev = *prevIndex; prev->name() == "free") { offset = prev->offset(); length += prev->length(); } } // check if there is an atom after 'ilst', and possibly use it as padding auto nextIndex = std::next(index); if(nextIndex != meta->children().cend()) { if(const MP4::Atom *next = *nextIndex; next->name() == "free") { length += next->length(); } } offset_t delta = data.size() - length; if(!data.isEmpty()) { if(delta > 0 || (delta < 0 && delta > -8)) { data.append(padIlst(data)); delta = data.size() - length; } else if(delta < 0) { data.append(padIlst(data, static_cast<int>(-delta - 8))); delta = 0; } d->file->insert(data, offset, length); if(delta) { updateParents(path, delta, 1); updateOffsets(delta, offset); } } else { // Strip meta if data is empty, only the case when called from strip(). if(MP4::Atom *udta = *std::prev(it); udta->removeChild(meta)) { offset = meta->offset(); delta = - meta->length(); d->file->removeBlock(meta->offset(), meta->length()); delete meta; if(delta) { updateParents(path, delta, 2); updateOffsets(delta, offset); } } } } String MP4::Tag::title() const { if(d->items.contains("\251nam")) return d->items["\251nam"].toStringList().toString(", "); return String(); } String MP4::Tag::artist() const { if(d->items.contains("\251ART")) return d->items["\251ART"].toStringList().toString(", "); return String(); } String MP4::Tag::album() const { if(d->items.contains("\251alb")) return d->items["\251alb"].toStringList().toString(", "); return String(); } String MP4::Tag::comment() const { if(d->items.contains("\251cmt")) return d->items["\251cmt"].toStringList().toString(", "); return String(); } String MP4::Tag::genre() const { if(d->items.contains("\251gen")) return d->items["\251gen"].toStringList().toString(", "); return String(); } unsigned int MP4::Tag::year() const { if(d->items.contains("\251day")) return d->items["\251day"].toStringList().toString().toInt(); return 0; } unsigned int MP4::Tag::track() const { if(d->items.contains("trkn")) return d->items["trkn"].toIntPair().first; return 0; } void MP4::Tag::setTitle(const String &value) { setTextItem("\251nam", value); } void MP4::Tag::setArtist(const String &value) { setTextItem("\251ART", value); } void MP4::Tag::setAlbum(const String &value) { setTextItem("\251alb", value); } void MP4::Tag::setComment(const String &value) { setTextItem("\251cmt", value); } void MP4::Tag::setGenre(const String &value) { setTextItem("\251gen", value); } void MP4::Tag::setTextItem(const String &key, const String &value) { if (!value.isEmpty()) { d->items[key] = StringList(value); } else { d->items.erase(key); } } void MP4::Tag::setYear(unsigned int value) { if (value == 0) { d->items.erase("\251day"); } else { d->items["\251day"] = StringList(String::number(value)); } } void MP4::Tag::setTrack(unsigned int value) { if (value == 0) { d->items.erase("trkn"); } else { d->items["trkn"] = MP4::Item(value, 0); } } bool MP4::Tag::isEmpty() const { return d->items.isEmpty(); } const MP4::ItemMap &MP4::Tag::itemMap() const { return d->items; } MP4::Item MP4::Tag::item(const String &key) const { return d->items[key]; } void MP4::Tag::setItem(const String &key, const Item &value) { d->items[key] = value; } void MP4::Tag::removeItem(const String &key) { d->items.erase(key); } bool MP4::Tag::contains(const String &key) const { return d->items.contains(key); } PropertyMap MP4::Tag::properties() const { PropertyMap props; for(const auto &[k, t] : std::as_const(d->items)) { if(auto [key, val] = d->factory->itemToProperty(k.data(String::Latin1), t); !key.isEmpty()) { props[key] = val; } else { props.addUnsupportedData(k); } } return props; } void MP4::Tag::removeUnsupportedProperties(const StringList &props) { for(const auto &prop : props) d->items.erase(prop); } PropertyMap MP4::Tag::setProperties(const PropertyMap &props) { const PropertyMap origProps = properties(); for(const auto &[prop, _] : origProps) { if(!props.contains(prop) || props[prop].isEmpty()) { d->items.erase(d->factory->nameForPropertyKey(prop)); } } PropertyMap ignoredProps; for(const auto &[prop, val] : props) { if(auto [name, itm] = d->factory->itemFromProperty(prop, val); itm.isValid()) { d->items[name] = itm; } else { ignoredProps.insert(prop, val); } } return ignoredProps; } StringList MP4::Tag::complexPropertyKeys() const { StringList keys; if(d->items.contains("covr")) { keys.append("PICTURE"); } return keys; } List<VariantMap> MP4::Tag::complexProperties(const String &key) const { List<VariantMap> props; if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { const CoverArtList pictures = d->items.value("covr").toCoverArtList(); for(const CoverArt &picture : pictures) { String mimeType = "image/"; switch(picture.format()) { case CoverArt::BMP: mimeType.append("bmp"); break; case CoverArt::JPEG: mimeType.append("jpeg"); break; case CoverArt::GIF: mimeType.append("gif"); break; case CoverArt::PNG: mimeType.append("png"); break; case CoverArt::Unknown: break; } VariantMap property; property.insert("data", picture.data()); property.insert("mimeType", mimeType); props.append(property); } } return props; } bool MP4::Tag::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { CoverArtList pictures; for(const auto &property : value) { auto mimeType = property.value("mimeType").value<String>(); CoverArt::Format format; if(mimeType == "image/bmp") { format = CoverArt::BMP; } else if(mimeType == "image/png") { format = CoverArt::PNG; } else if(mimeType == "image/gif") { format = CoverArt::GIF; } else if(mimeType == "image/jpeg") { format = CoverArt::JPEG; } else { format = CoverArt::Unknown; } pictures.append(CoverArt(format, property.value("data").value<ByteVector>())); } d->items["covr"] = pictures; } else { return false; } return true; } void MP4::Tag::addItem(const String &name, const Item &value) { if(!d->items.contains(name)) { d->items.insert(name, value); } else { debug("MP4: Ignoring duplicate atom \"" + name + "\""); } } �������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mp4/mp4tag.h��������������������������������������������������������������������0000664�0000000�0000000�00000012151�14662262111�0016265�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** copyright : (C) 2007,2011 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MP4TAG_H #define TAGLIB_MP4TAG_H #include "tfile.h" #include "tmap.h" #include "tstringlist.h" #include "taglib_export.h" #include "tag.h" #include "mp4atom.h" #include "mp4item.h" namespace TagLib { namespace MP4 { class ItemFactory; //! An MP4 tag implementation class TAGLIB_EXPORT Tag: public TagLib::Tag { public: Tag(); Tag(TagLib::File *file, Atoms *atoms, const ItemFactory *factory = nullptr); ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; bool save(); String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &value) override; void setArtist(const String &value) override; void setAlbum(const String &value) override; void setComment(const String &value) override; void setGenre(const String &value) override; void setYear(unsigned int value) override; void setTrack(unsigned int value) override; bool isEmpty() const override; /*! * Returns a string-keyed map of the MP4::Items for this tag. */ const ItemMap &itemMap() const; /*! * \return The item, if any, corresponding to \a key. */ Item item(const String &key) const; /*! * Sets the value of \a key to \a value, overwriting any previous value. */ void setItem(const String &key, const Item &value); /*! * Removes the entry with \a key from the tag, or does nothing if it does * not exist. */ void removeItem(const String &key); /*! * \return \c true if the tag contains an entry for \a key. */ bool contains(const String &key) const; /*! * Saves the associated file with the tag stripped. */ bool strip(); PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &props) override; PropertyMap setProperties(const PropertyMap &props) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; protected: /*! * Sets the value of \a key to \a value, overwriting any previous value. * If \a value is empty, the item is removed. */ void setTextItem(const String &key, const String &value); private: ByteVector padIlst(const ByteVector &data, int length = -1) const; ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const; void updateParents(const AtomList &path, offset_t delta, int ignore = 0); void updateOffsets(offset_t delta, offset_t offset); void saveNew(ByteVector data); void saveExisting(ByteVector data, const AtomList &path); void addItem(const String &name, const Item &value); class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace MP4 } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpc/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014777�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpc/mpcfile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000017165�14662262111�0017134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mpcfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagunion.h" #include "tagutils.h" #include "id3v1tag.h" #include "id3v2header.h" #include "apetag.h" #include "apefooter.h" using namespace TagLib; namespace { enum { MPCAPEIndex = 0, MPCID3v1Index = 1 }; } // namespace class MPC::File::FilePrivate { public: offset_t APELocation { -1 }; long APESize { 0 }; offset_t ID3v1Location { -1 }; std::unique_ptr<ID3v2::Header> ID3v2Header; offset_t ID3v2Location { -1 }; long ID3v2Size { 0 }; TagUnion tag; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool MPC::File::isSupported(IOStream *stream) { // A newer MPC file has to start with "MPCK" or "MP+", but older files don't // have keys to do a quick check. An ID3v2 tag may precede. const ByteVector id = Utils::readHeader(stream, 4, true); return id == "MPCK" || id.startsWith("MP+"); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } MPC::File::~File() = default; TagLib::Tag *MPC::File::tag() const { return &d->tag; } PropertyMap MPC::File::properties() const { return d->tag.properties(); } void MPC::File::removeUnsupportedProperties(const StringList &properties) { d->tag.removeUnsupportedProperties(properties); } PropertyMap MPC::File::setProperties(const PropertyMap &properties) { if(ID3v1Tag()) ID3v1Tag()->setProperties(properties); return APETag(true)->setProperties(properties); } MPC::Properties *MPC::File::audioProperties() const { return d->properties.get(); } bool MPC::File::save() { if(readOnly()) { debug("MPC::File::save() -- File is read only."); return false; } // Possibly strip ID3v2 tag if(!d->ID3v2Header && d->ID3v2Location >= 0) { removeBlock(d->ID3v2Location, d->ID3v2Size); if(d->APELocation >= 0) d->APELocation -= d->ID3v2Size; if(d->ID3v1Location >= 0) d->ID3v1Location -= d->ID3v2Size; d->ID3v2Location = -1; d->ID3v2Size = 0; } // Update ID3v1 tag if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. if(d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; } } // Update APE tag if(APETag() && !APETag()->isEmpty()) { // APE tag is not empty. Update the old one or create a new one. if(d->APELocation < 0) { if(d->ID3v1Location >= 0) d->APELocation = d->ID3v1Location; else d->APELocation = length(); } const ByteVector data = APETag()->render(); insert(data, d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->APESize; d->APESize = data.size(); } else { // APE tag is empty. Remove the old one. if(d->APELocation >= 0) { removeBlock(d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location -= d->APESize; d->APELocation = -1; d->APESize = 0; } } return true; } ID3v1::Tag *MPC::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(MPCID3v1Index, create); } APE::Tag *MPC::File::APETag(bool create) { return d->tag.access<APE::Tag>(MPCAPEIndex, create); } void MPC::File::strip(int tags) { if(tags & ID3v1) d->tag.set(MPCID3v1Index, nullptr); if(tags & APE) d->tag.set(MPCAPEIndex, nullptr); if(!ID3v1Tag()) APETag(true); if(tags & ID3v2) { d->ID3v2Header = nullptr; } } bool MPC::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } bool MPC::File::hasAPETag() const { return d->APELocation >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MPC::File::read(bool readProperties) { // Look for an ID3v2 tag d->ID3v2Location = Utils::findID3v2(this); if(d->ID3v2Location >= 0) { seek(d->ID3v2Location); d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size())); d->ID3v2Size = d->ID3v2Header->completeTagSize(); } // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); // Look for an APE tag d->APELocation = Utils::findAPE(this, d->ID3v1Location); if(d->APELocation >= 0) { d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation)); d->APESize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; } if(d->ID3v1Location < 0) APETag(true); // Look for MPC metadata if(readProperties) { offset_t streamLength; if(d->APELocation >= 0) streamLength = d->APELocation; else if(d->ID3v1Location >= 0) streamLength = d->ID3v1Location; else streamLength = length(); if(d->ID3v2Location >= 0) { seek(d->ID3v2Location + d->ID3v2Size); streamLength -= d->ID3v2Location + d->ID3v2Size; } else { seek(0); } d->properties = std::make_unique<Properties>(this, streamLength); } } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpc/mpcfile.h�������������������������������������������������������������������0000664�0000000�0000000�00000020301�14662262111�0016563�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPCFILE_H #define TAGLIB_MPCFILE_H #include "tfile.h" #include "taglib_export.h" #include "tag.h" #include "mpcproperties.h" namespace TagLib { class Tag; namespace ID3v1 { class Tag; } namespace APE { class Tag; } //! An implementation of MPC metadata /*! * This is an implementation of MPC metadata. * * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * properties from the file. ID3v2 tags are invalid in MPC-files, but will be skipped * and ignored. */ namespace MPC { //! An implementation of TagLib::File with MPC specific methods /*! * This implements and provides an interface for MPC files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to MPC files. * The only invalid tag combination supported is an ID3v1 tag after an APE tag. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, //! Matches APE tags. APE = 0x0004, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs an MPC file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs an MPC file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * or a combination of the two. */ TagLib::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * If the file contains both an APE and an ID3v1 tag, only the APE * tag will be converted to the PropertyMap. */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. * Affects only the APEv2 tag which will be created if necessary. * If an ID3v1 tag exists, it will be updated as well. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the MPC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this returns a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the MPC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the APE tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. If * there already is an ID3v1 tag, the new APE tag will be placed before it. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag <b>is still</b> owned by the MPC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasAPETag() */ APE::Tag *APETag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \warning This will also invalidate pointers to the tags * as their memory will be freed. * * \note In order to make the removal permanent save() still needs to be called. */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the file on disk actually has an APE tag. * * \see APETag() */ bool hasAPETag() const; /*! * Returns whether or not the given \a stream can be opened as an MPC * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace MPC } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpc/mpcproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000022462�14662262111�0020405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mpcproperties.h" #include <array> #include <cmath> #include "tdebug.h" #include "tstring.h" #include "mpcfile.h" using namespace TagLib; class MPC::Properties::PropertiesPrivate { public: int version { 0 }; int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; unsigned int totalFrames { 0 }; unsigned long sampleFrames { 0 }; int trackGain { 0 }; int trackPeak { 0 }; int albumGain { 0 }; int albumPeak { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPC::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { ByteVector magic = file->readBlock(4); if(magic == "MPCK") { // Musepack version 8 readSV8(file, streamLength); } else { // Musepack version 7 or older, fixed size header readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength); } } MPC::Properties::~Properties() = default; int MPC::Properties::lengthInMilliseconds() const { return d->length; } int MPC::Properties::bitrate() const { return d->bitrate; } int MPC::Properties::sampleRate() const { return d->sampleRate; } int MPC::Properties::channels() const { return d->channels; } int MPC::Properties::mpcVersion() const { return d->version; } unsigned int MPC::Properties::totalFrames() const { return d->totalFrames; } unsigned long MPC::Properties::sampleFrames() const { return d->sampleFrames; } int MPC::Properties::trackGain() const { return d->trackGain; } int MPC::Properties::trackPeak() const { return d->trackPeak; } int MPC::Properties::albumGain() const { return d->albumGain; } int MPC::Properties::albumPeak() const { return d->albumPeak; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// namespace { unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof) { sizeLength = 0; eof = false; unsigned char tmp; unsigned long size = 0; do { const ByteVector b = file->readBlock(1); if(b.isEmpty()) { eof = true; break; } tmp = b[0]; size = (size << 7) | (tmp & 0x7F); sizeLength++; } while(tmp & 0x80); return size; } unsigned long readSize(const ByteVector &data, unsigned int &pos) { unsigned char tmp; unsigned long size = 0; do { tmp = data[pos++]; size = (size << 7) | (tmp & 0x7F); } while((tmp & 0x80) && pos < data.size()); return size; } // This array looks weird, but the same as original MusePack code found at: // https://www.musepack.net/index.php?pg=src constexpr std::array sftable { 44100, 48000, 37800, 32000, 0, 0, 0, 0 }; } // namespace void MPC::Properties::readSV8(File *file, offset_t streamLength) { bool readSH = false, readRG = false; while(!readSH && !readRG) { const ByteVector packetType = file->readBlock(2); unsigned int packetSizeLength; bool eof; const unsigned long packetSize = readSize(file, packetSizeLength, eof); if(eof) { debug("MPC::Properties::readSV8() - Reached to EOF."); break; } const unsigned long dataSize = packetSize - 2 - packetSizeLength; const ByteVector data = file->readBlock(dataSize); if(data.size() != dataSize) { debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size."); break; } if(packetType == "SH") { // Stream Header // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket if(dataSize <= 5) { debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse."); break; } readSH = true; unsigned int pos = 4; d->version = data[pos]; pos += 1; d->sampleFrames = readSize(data, pos); if(pos > dataSize - 3) { debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); break; } const unsigned long begSilence = readSize(data, pos); if(pos > dataSize - 2) { debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); break; } const unsigned short flags = data.toUShort(pos, true); d->sampleRate = sftable[(flags >> 13) & 0x07]; d->channels = ((flags >> 4) & 0x0F) + 1; if(const auto frameCount = d->sampleFrames - begSilence; frameCount > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(frameCount) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } } else if (packetType == "RG") { // Replay Gain // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket if(dataSize <= 9) { debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse."); break; } readRG = true; if(const int replayGainVersion = data[0]; replayGainVersion == 1) { d->trackGain = data.toShort(1, true); d->trackPeak = data.toShort(3, true); d->albumGain = data.toShort(5, true); d->albumPeak = data.toShort(7, true); } } else if(packetType == "SE") { break; } else { file->seek(dataSize, File::Current); } } } void MPC::Properties::readSV7(const ByteVector &data, offset_t streamLength) { if(data.startsWith("MP+")) { if(data.size() < 4) return; d->version = data[3] & 15; if(d->version < 7) return; d->totalFrames = data.toUInt(4, false); const unsigned int flags = data.toUInt(8, false); d->sampleRate = sftable[(flags >> 16) & 0x03]; d->channels = 2; const unsigned int gapless = data.toUInt(5, false); d->trackGain = data.toShort(14, false); d->trackPeak = data.toUShort(12, false); d->albumGain = data.toShort(18, false); d->albumPeak = data.toUShort(16, false); // convert gain info if(d->trackGain != 0) { auto tmp = static_cast<int>((64.82 - static_cast<short>(d->trackGain) / 100.) * 256. + .5); if(tmp >= (1 << 16) || tmp < 0) tmp = 0; d->trackGain = tmp; } if(d->albumGain != 0) { auto tmp = static_cast<int>((64.82 - d->albumGain / 100.) * 256. + .5); if(tmp >= (1 << 16) || tmp < 0) tmp = 0; d->albumGain = tmp; } if (d->trackPeak != 0) d->trackPeak = static_cast<int>(log10(static_cast<double>(d->trackPeak)) * 20 * 256 + .5); if (d->albumPeak != 0) d->albumPeak = static_cast<int>(log10(static_cast<double>(d->albumPeak)) * 20 * 256 + .5); if((gapless >> 31) & 0x0001) { unsigned int lastFrameSamples = (gapless >> 20) & 0x07FF; d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples; } else d->sampleFrames = d->totalFrames * 1152 - 576; } else { const unsigned int headerData = data.toUInt(0, false); d->bitrate = (headerData >> 23) & 0x01ff; d->version = (headerData >> 11) & 0x03ff; d->sampleRate = 44100; d->channels = 2; if(d->version >= 5) d->totalFrames = data.toUInt(4, false); else d->totalFrames = data.toUShort(6, false); d->sampleFrames = d->totalFrames * 1152 - 576; } if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); if(d->bitrate == 0) d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpc/mpcproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000010600�14662262111�0020041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPCPROPERTIES_H #define TAGLIB_MPCPROPERTIES_H #include "tbytevector.h" #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace MPC { class File; static constexpr unsigned int HeaderSize = 8 * 7; //! An implementation of audio property reading for MPC /*! * This reads the data from an MPC stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of MPC::Properties with the data read directly * from an MPC::File. */ Properties(File *file, offset_t streamLength, ReadStyle style = Average); /*! * Destroys this MPC::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the version of the bitstream (SV4-SV8) */ int mpcVersion() const; unsigned int totalFrames() const; unsigned long sampleFrames() const; /*! * Returns the track gain as an integer value, * to convert to dB: trackGain in dB = 64.82 - (trackGain / 256) */ int trackGain() const; /*! * Returns the track peak as an integer value, * to convert to dB: trackPeak in dB = trackPeak / 256 * to convert to floating [-1..1]: trackPeak = 10^(trackPeak / 256 / 20)/32768 */ int trackPeak() const; /*! * Returns the album gain as an integer value, * to convert to dB: albumGain in dB = 64.82 - (albumGain / 256) */ int albumGain() const; /*! * Returns the album peak as an integer value, * to convert to dB: albumPeak in dB = albumPeak / 256 * to convert to floating [-1..1]: albumPeak = 10^(albumPeak / 256 / 20)/32768 */ int albumPeak() const; private: void readSV7(const ByteVector &data, offset_t streamLength); void readSV8(File *file, offset_t streamLength); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace MPC } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015150�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v1/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016076�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v1/id3v1genres.cpp������������������������������������������������������0000664�0000000�0000000�00000014166�14662262111�0020744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v1genres.h" #include <array> using namespace TagLib; namespace { constexpr std::array genres { L"Blues", L"Classic Rock", L"Country", L"Dance", L"Disco", L"Funk", L"Grunge", L"Hip-Hop", L"Jazz", L"Metal", L"New Age", L"Oldies", L"Other", L"Pop", L"R&B", L"Rap", L"Reggae", L"Rock", L"Techno", L"Industrial", L"Alternative", L"Ska", L"Death Metal", L"Pranks", L"Soundtrack", L"Euro-Techno", L"Ambient", L"Trip-Hop", L"Vocal", L"Jazz-Funk", L"Fusion", L"Trance", L"Classical", L"Instrumental", L"Acid", L"House", L"Game", L"Sound Clip", L"Gospel", L"Noise", L"Alternative Rock", L"Bass", L"Soul", L"Punk", L"Space", L"Meditative", L"Instrumental Pop", L"Instrumental Rock", L"Ethnic", L"Gothic", L"Darkwave", L"Techno-Industrial", L"Electronic", L"Pop-Folk", L"Eurodance", L"Dream", L"Southern Rock", L"Comedy", L"Cult", L"Gangsta", L"Top 40", L"Christian Rap", L"Pop/Funk", L"Jungle", L"Native American", L"Cabaret", L"New Wave", L"Psychedelic", L"Rave", L"Showtunes", L"Trailer", L"Lo-Fi", L"Tribal", L"Acid Punk", L"Acid Jazz", L"Polka", L"Retro", L"Musical", L"Rock & Roll", L"Hard Rock", L"Folk", L"Folk Rock", L"National Folk", L"Swing", L"Fast Fusion", L"Bebop", L"Latin", L"Revival", L"Celtic", L"Bluegrass", L"Avant-garde", L"Gothic Rock", L"Progressive Rock", L"Psychedelic Rock", L"Symphonic Rock", L"Slow Rock", L"Big Band", L"Chorus", L"Easy Listening", L"Acoustic", L"Humour", L"Speech", L"Chanson", L"Opera", L"Chamber Music", L"Sonata", L"Symphony", L"Booty Bass", L"Primus", L"Porn Groove", L"Satire", L"Slow Jam", L"Club", L"Tango", L"Samba", L"Folklore", L"Ballad", L"Power Ballad", L"Rhythmic Soul", L"Freestyle", L"Duet", L"Punk Rock", L"Drum Solo", L"A Cappella", L"Euro-House", L"Dancehall", L"Goa", L"Drum & Bass", L"Club-House", L"Hardcore Techno", L"Terror", L"Indie", L"Britpop", L"Worldbeat", L"Polsk Punk", L"Beat", L"Christian Gangsta Rap", L"Heavy Metal", L"Black Metal", L"Crossover", L"Contemporary Christian", L"Christian Rock", L"Merengue", L"Salsa", L"Thrash Metal", L"Anime", L"Jpop", L"Synthpop", L"Abstract", L"Art Rock", L"Baroque", L"Bhangra", L"Big Beat", L"Breakbeat", L"Chillout", L"Downtempo", L"Dub", L"EBM", L"Eclectic", L"Electro", L"Electroclash", L"Emo", L"Experimental", L"Garage", L"Global", L"IDM", L"Illbient", L"Industro-Goth", L"Jam Band", L"Krautrock", L"Leftfield", L"Lounge", L"Math Rock", L"New Romantic", L"Nu-Breakz", L"Post-Punk", L"Post-Rock", L"Psytrance", L"Shoegaze", L"Space Rock", L"Trop Rock", L"World Music", L"Neoclassical", L"Audiobook", L"Audio Theatre", L"Neue Deutsche Welle", L"Podcast", L"Indie Rock", L"G-Funk", L"Dubstep", L"Garage Rock", L"Psybient" }; } // namespace StringList ID3v1::genreList() { StringList l; for(auto g : genres) { l.append(g); } return l; } ID3v1::GenreMap ID3v1::genreMap() { GenreMap m; for(size_t i = 0; i < genres.size(); i++) { m.insert(genres[i], static_cast<int>(i)); } return m; } String ID3v1::genre(int index) { if(index >= 0 && static_cast<size_t>(index) < genres.size()) return String(genres[index]); // always make a copy return String(); } int ID3v1::genreIndex(const String &name) { for(size_t i = 0; i < genres.size(); ++i) { if(name == genres[i]) return static_cast<int>(i); } // If the name was not found, try the names which have been changed static constexpr std::array fixUpGenres { std::pair(L"Jazz+Funk", 29), std::pair(L"Folk/Rock", 81), std::pair(L"Bebob", 85), std::pair(L"Avantgarde", 90), std::pair(L"Dance Hall", 125), std::pair(L"Hardcore", 129), std::pair(L"BritPop", 132), std::pair(L"Negerpunk", 133), }; for(const auto &[genreName, code] : fixUpGenres) { if(name == genreName) return code; } return 255; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v1/id3v1genres.h��������������������������������������������������������0000664�0000000�0000000�00000005447�14662262111�0020413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V1GENRE_H #define TAGLIB_ID3V1GENRE_H #include "tmap.h" #include "tstringlist.h" #include "taglib_export.h" namespace TagLib { namespace ID3v1 { using GenreMap = Map<String, int>; /*! * Returns the list of canonical ID3v1 genre names in the order that they * are listed in the standard. */ StringList TAGLIB_EXPORT genreList(); /*! * A "reverse mapping" that goes from the canonical ID3v1 genre name to the * respective genre number. genreMap()["Rock"] == */ GenreMap TAGLIB_EXPORT genreMap(); /*! * Returns the name of the genre at \a index in the ID3v1 genre list. If * \a index is out of range -- less than zero or greater than 191 -- a null * string will be returned. */ String TAGLIB_EXPORT genre(int index); /*! * Returns the genre index for the (case sensitive) genre \a name. If the * genre is not in the list 255 (which signifies an unknown genre in ID3v1) * will be returned. */ int TAGLIB_EXPORT genreIndex(const String &name); } // namespace ID3v1 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.cpp���������������������������������������������������������0000664�0000000�0000000�00000015114�14662262111�0020226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v1tag.h" #include "tdebug.h" #include "tfile.h" #include "id3v1genres.h" using namespace TagLib; using namespace ID3v1; namespace { const ID3v1::StringHandler defaultStringHandler; const ID3v1::StringHandler *stringHandler = &defaultStringHandler; } // namespace class ID3v1::Tag::TagPrivate { public: File *file { nullptr }; offset_t tagOffset { 0 }; String title; String artist; String album; String year; String comment; unsigned char track { 0 }; unsigned char genre { 255 }; }; class ID3v1::StringHandler::StringHandlerPrivate { }; //////////////////////////////////////////////////////////////////////////////// // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// StringHandler::StringHandler() = default; StringHandler::~StringHandler() = default; String ID3v1::StringHandler::parse(const ByteVector &data) const { return String(data, String::Latin1).stripWhiteSpace(); } ByteVector ID3v1::StringHandler::render(const String &s) const { if(s.isLatin1()) return s.data(String::Latin1); return ByteVector(); } //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// ID3v1::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } ID3v1::Tag::Tag(File *file, offset_t tagOffset) : d(std::make_unique<TagPrivate>()) { d->file = file; d->tagOffset = tagOffset; read(); } ID3v1::Tag::~Tag() = default; ByteVector ID3v1::Tag::render() const { ByteVector data; data.append(fileIdentifier()); data.append(stringHandler->render(d->title).resize(30)); data.append(stringHandler->render(d->artist).resize(30)); data.append(stringHandler->render(d->album).resize(30)); data.append(stringHandler->render(d->year).resize(4)); data.append(stringHandler->render(d->comment).resize(28)); data.append(static_cast<char>(0)); data.append(static_cast<char>(d->track)); data.append(static_cast<char>(d->genre)); return data; } ByteVector ID3v1::Tag::fileIdentifier() { return ByteVector::fromCString("TAG"); } String ID3v1::Tag::title() const { return d->title; } String ID3v1::Tag::artist() const { return d->artist; } String ID3v1::Tag::album() const { return d->album; } String ID3v1::Tag::comment() const { return d->comment; } String ID3v1::Tag::genre() const { return ID3v1::genre(d->genre); } unsigned int ID3v1::Tag::year() const { return d->year.toInt(); } unsigned int ID3v1::Tag::track() const { return d->track; } void ID3v1::Tag::setTitle(const String &s) { d->title = s; } void ID3v1::Tag::setArtist(const String &s) { d->artist = s; } void ID3v1::Tag::setAlbum(const String &s) { d->album = s; } void ID3v1::Tag::setComment(const String &s) { d->comment = s; } void ID3v1::Tag::setGenre(const String &s) { d->genre = ID3v1::genreIndex(s); } void ID3v1::Tag::setYear(unsigned int i) { d->year = i > 0 ? String::number(i) : String(); } void ID3v1::Tag::setTrack(unsigned int i) { d->track = i < 256 ? i : 0; } unsigned int ID3v1::Tag::genreNumber() const { return d->genre; } void ID3v1::Tag::setGenreNumber(unsigned int i) { d->genre = i < 256 ? i : 255; } void ID3v1::Tag::setStringHandler(const StringHandler *handler) { if(handler) stringHandler = handler; else stringHandler = &defaultStringHandler; } //////////////////////////////////////////////////////////////////////////////// // protected methods //////////////////////////////////////////////////////////////////////////////// void ID3v1::Tag::read() { if(d->file && d->file->isValid()) { d->file->seek(d->tagOffset); // read the tag -- always 128 bytes // some initial sanity checking if(const ByteVector data = d->file->readBlock(128); data.size() == 128 && data.startsWith("TAG")) parse(data); else debug("ID3v1 tag is not valid or could not be read at the specified offset."); } } void ID3v1::Tag::parse(const ByteVector &data) { int offset = 3; d->title = stringHandler->parse(data.mid(offset, 30)); offset += 30; d->artist = stringHandler->parse(data.mid(offset, 30)); offset += 30; d->album = stringHandler->parse(data.mid(offset, 30)); offset += 30; d->year = stringHandler->parse(data.mid(offset, 4)); offset += 4; // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this // is not a bug in TagLib. Since a zeroed byte is what we would expect to // indicate the end of a C-String, specifically the comment string, a value of // zero must be assumed to be just that. if(data[offset + 28] == 0 && data[offset + 29] != 0) { // ID3v1.1 detected d->comment = stringHandler->parse(data.mid(offset, 28)); d->track = static_cast<unsigned char>(data[offset + 29]); } else d->comment = data.mid(offset, 30); offset += 30; d->genre = static_cast<unsigned char>(data[offset]); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v1/id3v1tag.h�����������������������������������������������������������0000664�0000000�0000000�00000016505�14662262111�0017700�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V1TAG_H #define TAGLIB_ID3V1TAG_H #include "tbytevector.h" #include "taglib.h" #include "taglib_export.h" #include "tag.h" namespace TagLib { class File; //! An ID3v1 implementation namespace ID3v1 { //! An abstraction for the string to data encoding in ID3v1 tags. /*! * ID3v1 should in theory always contain ISO-8859-1 (Latin1) data. In * practice it does not. TagLib by default only supports ISO-8859-1 data * in ID3v1 tags. * * However by subclassing this class and reimplementing parse() and render() * and setting your reimplementation as the default with * ID3v1::Tag::setStringHandler() you can define how you would like these * transformations to be done. * * \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1 * tags. Please consider disabling the writing of ID3v1 tags in the case * that the data is not ISO-8859-1. * * \see ID3v1::Tag::setStringHandler() */ class TAGLIB_EXPORT StringHandler { public: StringHandler(); virtual ~StringHandler(); StringHandler(const StringHandler &) = delete; StringHandler &operator=(const StringHandler &) = delete; /*! * Decode a string from \a data. The default implementation assumes that * \a data is an ISO-8859-1 (Latin1) character array. */ virtual String parse(const ByteVector &data) const; /*! * Encode a ByteVector with the data from \a s. The default implementation * assumes that \a s is an ISO-8859-1 (Latin1) string. If the string is * does not conform to ISO-8859-1, no value is written. * * \warning It is recommended that you <b>not</b> override this method, but * instead do not write an ID3v1 tag in the case that the data is not * ISO-8859-1. */ virtual ByteVector render(const String &s) const; private: class StringHandlerPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<StringHandlerPrivate> d; }; //! The main class in the ID3v1 implementation /*! * This is an implementation of the ID3v1 format. ID3v1 is both the simplest * and most common of tag formats but is rather limited. Because of its * pervasiveness and the way that applications have been written around the * fields that it provides, the generic TagLib::Tag API is a mirror of what is * provided by ID3v1. * * ID3v1 tags should generally only contain Latin1 information. However because * many applications do not follow this rule there is now support for overriding * the ID3v1 string handling using the ID3v1::StringHandler class. Please see * the documentation for that class for more information. * * \see StringHandler * * \note Most fields are truncated to a maximum of 28-30 bytes. The * truncation happens automatically when the tag is rendered. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: /*! * Create an ID3v1 tag with default values. */ Tag(); /*! * Create an ID3v1 tag and parse the data in \a file starting at * \a tagOffset. */ Tag(File *file, offset_t tagOffset); /*! * Destroys this Tag instance. */ ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; /*! * Renders the in memory values to a ByteVector suitable for writing to * the file. */ ByteVector render() const; /*! * Returns the string "TAG" suitable for usage in locating the tag in a * file. */ static ByteVector fileIdentifier(); // Reimplementations. String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; /*! * Returns the genre in number. * * \note Normally 255 indicates that this tag contains no genre. */ unsigned int genreNumber() const; /*! * Sets the genre in number to \a i. * * \note Valid value is from 0 up to 255. Normally 255 indicates that * this tag contains no genre. */ void setGenreNumber(unsigned int i); /*! * Sets the string handler that decides how the ID3v1 data will be * converted to and from binary data. * If the parameter \a handler is null, the previous handler is * released and default ISO-8859-1 handler is restored. * * \note The caller is responsible for deleting the previous handler * as needed after it is released. * * \see StringHandler */ static void setStringHandler(const StringHandler *handler); protected: /*! * Reads from the file specified in the constructor. */ void read(); /*! * Parses the body of the tag in \a data. */ void parse(const ByteVector &data); private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace ID3v1 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016077�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/��������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0017354�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp��������������������������������������0000664�0000000�0000000�00000015015�14662262111�0024246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "attachedpictureframe.h" #include "tstringlist.h" #include "tdebug.h" using namespace TagLib; using namespace ID3v2; class AttachedPictureFrame::AttachedPictureFramePrivate { public: String::Type textEncoding { String::Latin1 }; String mimeType; AttachedPictureFrame::Type type { AttachedPictureFrame::Other }; String description; ByteVector data; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC"), d(std::make_unique<AttachedPictureFramePrivate>()) { } AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data), d(std::make_unique<AttachedPictureFramePrivate>()) { setData(data); } AttachedPictureFrame::~AttachedPictureFrame() = default; String AttachedPictureFrame::toString() const { String s = "[" + d->mimeType + "]"; return d->description.isEmpty() ? s : d->description + " " + s; } StringList AttachedPictureFrame::toStringList() const { return {d->description, d->mimeType}; } String::Type AttachedPictureFrame::textEncoding() const { return d->textEncoding; } void AttachedPictureFrame::setTextEncoding(String::Type t) { d->textEncoding = t; } String AttachedPictureFrame::mimeType() const { return d->mimeType; } void AttachedPictureFrame::setMimeType(const String &m) { d->mimeType = m; } AttachedPictureFrame::Type AttachedPictureFrame::type() const { return d->type; } void AttachedPictureFrame::setType(Type t) { d->type = t; } String AttachedPictureFrame::description() const { return d->description; } void AttachedPictureFrame::setDescription(const String &desc) { d->description = desc; } ByteVector AttachedPictureFrame::picture() const { return d->data; } void AttachedPictureFrame::setPicture(const ByteVector &p) { d->data = p; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void AttachedPictureFrame::parseFields(const ByteVector &data) { if(data.size() < 5) { debug("A picture frame must contain at least 5 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); int pos = 1; d->mimeType = readStringField(data, String::Latin1, &pos); /* Now we need at least two more bytes available */ if(static_cast<unsigned int>(pos) + 1 >= data.size()) { debug("Truncated picture frame."); return; } d->type = static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(data[pos++]); d->description = readStringField(data, d->textEncoding, &pos); d->data = data.mid(pos); } ByteVector AttachedPictureFrame::renderFields() const { ByteVector data; String::Type encoding = checkTextEncoding(d->description, d->textEncoding); data.append(static_cast<char>(encoding)); data.append(d->mimeType.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); data.append(static_cast<char>(d->type)); data.append(d->description.data(encoding)); data.append(textDelimiter(encoding)); data.append(d->data); return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<AttachedPictureFramePrivate>()) { parseFields(fieldData(data)); } //////////////////////////////////////////////////////////////////////////////// // support for ID3v2.2 PIC frames //////////////////////////////////////////////////////////////////////////////// void AttachedPictureFrameV22::parseFields(const ByteVector &data) { if(data.size() < 5) { debug("A picture frame must contain at least 5 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); int pos = 1; auto fixedString = String(data.mid(pos, 3), String::Latin1); pos += 3; // convert fixed string image type to mime string if (fixedString.upper() == "JPG") { d->mimeType = "image/jpeg"; } else if (fixedString.upper() == "PNG") { d->mimeType = "image/png"; } else { debug("probably unsupported image type"); d->mimeType = "image/" + fixedString; } d->type = static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(data[pos++]); d->description = readStringField(data, d->textEncoding, &pos); d->data = data.mid(pos); } AttachedPictureFrameV22::AttachedPictureFrameV22(const ByteVector &data, Header *h) { // set v2.2 header to make fieldData work correctly setHeader(h, true); parseFields(fieldData(data)); // now set the v2.4 header auto newHeader = new Frame::Header("APIC"); newHeader->setFrameSize(h->frameSize()); setHeader(newHeader, true); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/attachedpictureframe.h����������������������������������������0000664�0000000�0000000�00000013662�14662262111�0023721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ATTACHEDPICTUREFRAME_H #define TAGLIB_ATTACHEDPICTUREFRAME_H #include "taglib_export.h" #include "tpicturetype.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An ID3v2 attached picture frame implementation /*! * This is an implementation of ID3v2 attached pictures. Pictures may be * included in tags, one per APIC frame (but there may be multiple APIC * frames in a single tag). These pictures are usually in either JPEG or * PNG format. */ class TAGLIB_EXPORT AttachedPictureFrame : public Frame { friend class FrameFactory; public: /* * This describes the function or content of the picture. */ DECLARE_PICTURE_TYPE_ENUM(Type) /*! * Constructs an empty picture frame. The description, content and text * encoding should be set manually. */ AttachedPictureFrame(); /*! * Constructs an AttachedPicture frame based on \a data. */ explicit AttachedPictureFrame(const ByteVector &data); /*! * Destroys the AttachedPictureFrame instance. */ ~AttachedPictureFrame() override; AttachedPictureFrame(const AttachedPictureFrame &) = delete; AttachedPictureFrame &operator=(const AttachedPictureFrame &) = delete; /*! * Returns a string containing the description and mime-type */ String toString() const override; /*! * Returns a string list containing the description and mime-type. */ StringList toStringList() const override; /*! * Returns the text encoding used for the description. * * \see setTextEncoding() * \see description() */ String::Type textEncoding() const; /*! * Set the text encoding used for the description. * * \see description() */ void setTextEncoding(String::Type t); /*! * Returns the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". */ String mimeType() const; /*! * Sets the mime type of the image. This should in most cases be * "image/png" or "image/jpeg". */ void setMimeType(const String &m); /*! * Returns the type of the image. * * \see Type * \see setType() */ Type type() const; /*! * Sets the type for the image. * * \see Type * \see type() */ void setType(Type t); /*! * Returns a text description of the image. * * \see setDescription() * \see textEncoding() * \see setTextEncoding() */ String description() const; /*! * Sets a textual description of the image to \a desc. * * \see description() * \see textEncoding() * \see setTextEncoding() */ void setDescription(const String &desc); /*! * Returns the image data as a ByteVector. * * \note ByteVector has a data() method that returns a <tt>const char *</tt> which * should make it easy to export this data to external programs. * * \see setPicture() * \see mimeType() */ ByteVector picture() const; /*! * Sets the image data to \a p. \a p should be of the type specified in * this frame's mime-type specification. * * \see picture() * \see mimeType() * \see setMimeType() */ void setPicture(const ByteVector &p); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; class AttachedPictureFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<AttachedPictureFramePrivate> d; private: AttachedPictureFrame(const ByteVector &data, Header *h); }; //! support for ID3v2.2 PIC frames class TAGLIB_EXPORT AttachedPictureFrameV22 : public AttachedPictureFrame { protected: void parseFields(const ByteVector &data) override; private: AttachedPictureFrameV22(const ByteVector &data, Header *h); friend class FrameFactory; }; } // namespace ID3v2 } // namespace TagLib #endif ������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/chapterframe.cpp����������������������������������������������0000664�0000000�0000000�00000020153�14662262111�0022522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Lukas Krejci email : krejclu6@fel.cvut.cz ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "chapterframe.h" #include <utility> #include "tbytevectorlist.h" #include "tdebug.h" #include "tpropertymap.h" using namespace TagLib; using namespace ID3v2; class ChapterFrame::ChapterFramePrivate { public: ChapterFramePrivate() { embeddedFrameList.setAutoDelete(true); } const ID3v2::Header *tagHeader { nullptr }; ByteVector elementID; unsigned int startTime { 0 }; unsigned int endTime { 0 }; unsigned int startOffset { 0 }; unsigned int endOffset { 0 }; FrameListMap embeddedFrameListMap; FrameList embeddedFrameList; }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), d(std::make_unique<ChapterFramePrivate>()) { d->tagHeader = tagHeader; setData(data); } ChapterFrame::ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames) : ID3v2::Frame("CHAP"), d(std::make_unique<ChapterFramePrivate>()) { // setElementID has a workaround for a previously silly API where you had to // specifically include the null byte. setElementID(elementID); d->startTime = startTime; d->endTime = endTime; d->startOffset = startOffset; d->endOffset = endOffset; for(const auto &frame : embeddedFrames) addEmbeddedFrame(frame); } ChapterFrame::~ChapterFrame() = default; ByteVector ChapterFrame::elementID() const { return d->elementID; } unsigned int ChapterFrame::startTime() const { return d->startTime; } unsigned int ChapterFrame::endTime() const { return d->endTime; } unsigned int ChapterFrame::startOffset() const { return d->startOffset; } unsigned int ChapterFrame::endOffset() const { return d->endOffset; } void ChapterFrame::setElementID(const ByteVector &eID) { d->elementID = eID; if(d->elementID.endsWith(static_cast<char>(0))) d->elementID = d->elementID.mid(0, d->elementID.size() - 1); } void ChapterFrame::setStartTime(const unsigned int &sT) { d->startTime = sT; } void ChapterFrame::setEndTime(const unsigned int &eT) { d->endTime = eT; } void ChapterFrame::setStartOffset(const unsigned int &sO) { d->startOffset = sO; } void ChapterFrame::setEndOffset(const unsigned int &eO) { d->endOffset = eO; } const FrameListMap &ChapterFrame::embeddedFrameListMap() const { return d->embeddedFrameListMap; } const FrameList &ChapterFrame::embeddedFrameList() const { return d->embeddedFrameList; } const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const { return d->embeddedFrameListMap[frameID]; } void ChapterFrame::addEmbeddedFrame(Frame *frame) { d->embeddedFrameList.append(frame); d->embeddedFrameListMap[frame->frameID()].append(frame); } void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) { // remove the frame from the frame list auto it = d->embeddedFrameList.find(frame); d->embeddedFrameList.erase(it); // ...and from the frame list map it = d->embeddedFrameListMap[frame->frameID()].find(frame); d->embeddedFrameListMap[frame->frameID()].erase(it); // ...and delete as desired if(del) delete frame; } void ChapterFrame::removeEmbeddedFrames(const ByteVector &id) { const FrameList frames = d->embeddedFrameListMap[id]; for(const auto &frame : frames) removeEmbeddedFrame(frame, true); } String ChapterFrame::toString() const { String s = String(d->elementID) + ": start time: " + String::number(d->startTime) + ", end time: " + String::number(d->endTime); if(d->startOffset != 0xFFFFFFFF) s += ", start offset: " + String::number(d->startOffset); if(d->endOffset != 0xFFFFFFFF) s += ", end offset: " + String::number(d->endOffset); if(!d->embeddedFrameList.isEmpty()) { StringList frameIDs; for(const auto &frame : std::as_const(d->embeddedFrameList)) frameIDs.append(frame->frameID()); s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]"; } return s; } PropertyMap ChapterFrame::asProperties() const { PropertyMap map; map.addUnsupportedData(frameID() + String("/") + d->elementID); return map; } ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static { for(const auto &comment : std::as_const(tag->frameList("CHAP"))) { auto frame = dynamic_cast<ChapterFrame *>(comment); if(frame && frame->elementID() == eID) return frame; } return nullptr; } void ChapterFrame::parseFields(const ByteVector &data) { unsigned int size = data.size(); if(size < 18) { debug("A CHAP frame must contain at least 18 bytes (1 byte element ID " "terminated by null and 4x4 bytes for start and end time and offset)."); return; } int pos = 0; unsigned int embPos = 0; d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); d->startTime = data.toUInt(pos, true); pos += 4; d->endTime = data.toUInt(pos, true); pos += 4; d->startOffset = data.toUInt(pos, true); pos += 4; d->endOffset = data.toUInt(pos, true); pos += 4; size -= pos; // Embedded frames are optional if(size < header()->size()) return; while(embPos < size - header()->size()) { Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); if(!frame) return; // Checks to make sure that frame parsed correctly. if(frame->size() <= 0) { delete frame; return; } embPos += frame->size() + header()->size(); addEmbeddedFrame(frame); } } ByteVector ChapterFrame::renderFields() const { ByteVector data; data.append(d->elementID); data.append('\0'); data.append(ByteVector::fromUInt(d->startTime, true)); data.append(ByteVector::fromUInt(d->endTime, true)); data.append(ByteVector::fromUInt(d->startOffset, true)); data.append(ByteVector::fromUInt(d->endOffset, true)); for(const auto &frame : std::as_const(d->embeddedFrameList)) { frame->header()->setVersion(header()->version()); data.append(frame->render()); } return data; } ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<ChapterFramePrivate>()) { d->tagHeader = tagHeader; parseFields(fieldData(data)); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/chapterframe.h������������������������������������������������0000664�0000000�0000000�00000021571�14662262111�0022174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Lukas Krejci email : krejclu6@fel.cvut.cz ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_CHAPTERFRAME #define TAGLIB_CHAPTERFRAME #include "taglib_export.h" #include "id3v2tag.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { /*! * This is an implementation of ID3v2 chapter frames. The purpose of this * frame is to describe a single chapter within an audio file. */ //! An implementation of ID3v2 chapter frames class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame { friend class FrameFactory; public: /*! * Creates a chapter frame based on \a data. \a tagHeader is required as * the internal frames are parsed based on the tag version. */ ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data); /*! * Creates a chapter frame with the element ID \a elementID, start time * \a startTime, end time \a endTime, start offset \a startOffset, * end offset \a endOffset and optionally a list of embedded frames, * whose ownership will then be taken over by this Frame, in * \a embeddedFrames. * * All times are in milliseconds. */ ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames = FrameList()); /*! * Destroys the frame. */ ~ChapterFrame() override; ChapterFrame(const ChapterFrame &) = delete; ChapterFrame &operator=(const ChapterFrame &) = delete; /*! * Returns the element ID of the frame. Element ID * is a null terminated string, however it's not human-readable. * * \see setElementID() */ ByteVector elementID() const; /*! * Returns time of chapter's start (in milliseconds). * * \see setStartTime() */ unsigned int startTime() const; /*! * Returns time of chapter's end (in milliseconds). * * \see setEndTime() */ unsigned int endTime() const; /*! * Returns zero based byte offset (count of bytes from the beginning * of the audio file) of chapter's start. * * \note If the returned value is 0xFFFFFFFF, start time should be used instead. * \see setStartOffset() */ unsigned int startOffset() const; /*! * Returns zero based byte offset (count of bytes from the beginning * of the audio file) of chapter's end. * * \note If the returned value is 0xFFFFFFFF, end time should be used instead. * \see setEndOffset() */ unsigned int endOffset() const; /*! * Sets the element ID of the frame to \a eID. If \a eID isn't * null terminated, a null char is appended automatically. * * \see elementID() */ void setElementID(const ByteVector &eID); /*! * Sets time of chapter's start (in milliseconds) to \a sT. * * \see startTime() */ void setStartTime(const unsigned int &sT); /*! * Sets time of chapter's end (in milliseconds) to \a eT. * * \see endTime() */ void setEndTime(const unsigned int &eT); /*! * Sets zero based byte offset (count of bytes from the beginning * of the audio file) of chapter's start to \a sO. * * \see startOffset() */ void setStartOffset(const unsigned int &sO); /*! * Sets zero based byte offset (count of bytes from the beginning * of the audio file) of chapter's end to \a eO. * * \see endOffset() */ void setEndOffset(const unsigned int &eO); /*! * Returns a reference to the frame list map. This is a FrameListMap of * all of the frames embedded in the CHAP frame. * * This is the most convenient structure for accessing the CHAP frame's * embedded frames. Many frame types allow multiple instances of the same * frame type so this is a map of lists. In most cases however there will * only be a single frame of a certain type. * * \warning You should not modify this data structure directly, instead * use addEmbeddedFrame() and removeEmbeddedFrame(). * * \see embeddedFrameList() */ const FrameListMap &embeddedFrameListMap() const; /*! * Returns a reference to the embedded frame list. This is a FrameList * of all of the frames embedded in the CHAP frame in the order that they * were parsed. * * This can be useful if for example you want to iterate over the CHAP frame's * embedded frames in the order that they occur in the CHAP frame. * * \warning You should not modify this data structure directly, instead * use addEmbeddedFrame() and removeEmbeddedFrame(). */ const FrameList &embeddedFrameList() const; /*! * Returns the embedded frame list for frames with the id \a frameID * or an empty list if there are no embedded frames of that type. This * is just a convenience and is equivalent to: * * \code * embeddedFrameListMap()[frameID]; * \endcode * * \see embeddedFrameListMap() */ const FrameList &embeddedFrameList(const ByteVector &frameID) const; /*! * Add an embedded frame to the CHAP frame. At this point the CHAP frame * takes ownership of the embedded frame and will handle freeing its memory. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void addEmbeddedFrame(Frame *frame); /*! * Remove an embedded frame from the CHAP frame. If \a del is \c true the frame's * memory will be freed; if it is \c false, it must be deleted by the user. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void removeEmbeddedFrame(Frame *frame, bool del = true); /*! * Remove all embedded frames of type \a id from the CHAP frame and free their * memory. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void removeEmbeddedFrames(const ByteVector &id); String toString() const override; PropertyMap asProperties() const override; /*! * CHAP frames each have a unique element ID. This searches for a CHAP * frame with the element ID \a eID and returns a pointer to it. This * can be used to link CTOC and CHAP frames together. * * \see elementID() */ static ChapterFrame *findByElementID(const Tag *tag, const ByteVector &eID); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); class ChapterFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ChapterFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/commentsframe.cpp���������������������������������������������0000664�0000000�0000000�00000013014�14662262111�0022717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "commentsframe.h" #include <utility> #include "tbytevectorlist.h" #include "tdebug.h" #include "tstringlist.h" #include "tpropertymap.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class CommentsFrame::CommentsFramePrivate { public: String::Type textEncoding { String::Latin1 }; ByteVector language; String description; String text; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM"), d(std::make_unique<CommentsFramePrivate>()) { d->textEncoding = encoding; } CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data), d(std::make_unique<CommentsFramePrivate>()) { setData(data); } CommentsFrame::~CommentsFrame() = default; String CommentsFrame::toString() const { return d->text; } ByteVector CommentsFrame::language() const { return d->language; } String CommentsFrame::description() const { return d->description; } String CommentsFrame::text() const { return d->text; } void CommentsFrame::setLanguage(const ByteVector &languageEncoding) { d->language = languageEncoding.mid(0, 3); } void CommentsFrame::setDescription(const String &s) { d->description = s; } void CommentsFrame::setText(const String &s) { d->text = s; } String::Type CommentsFrame::textEncoding() const { return d->textEncoding; } void CommentsFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } PropertyMap CommentsFrame::asProperties() const { String key = description().upper(); PropertyMap map; if(key.isEmpty() || key == "COMMENT") map.insert("COMMENT", text()); else map.insert("COMMENT:" + key, text()); return map; } CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static { for(const auto &comment : std::as_const(tag->frameList("COMM"))) { auto frame = dynamic_cast<CommentsFrame *>(comment); if(frame && frame->description() == d) return frame; } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void CommentsFrame::parseFields(const ByteVector &data) { if(data.size() < 5) { debug("A comment frame must contain at least 5 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); d->language = data.mid(1, 3); int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; if(ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); l.size() == 2) { if(d->textEncoding == String::Latin1) { d->description = Tag::latin1StringHandler()->parse(l.front()); d->text = Tag::latin1StringHandler()->parse(l.back()); } else { d->description = String(l.front(), d->textEncoding); d->text = String(l.back(), d->textEncoding); } } } ByteVector CommentsFrame::renderFields() const { ByteVector v; String::Type encoding = d->textEncoding; encoding = checkTextEncoding(d->description, encoding); encoding = checkTextEncoding(d->text, encoding); v.append(static_cast<char>(encoding)); v.append(d->language.size() == 3 ? d->language : "XXX"); v.append(d->description.data(encoding)); v.append(textDelimiter(encoding)); v.append(d->text.data(encoding)); return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<CommentsFramePrivate>()) { parseFields(fieldData(data)); } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/commentsframe.h�����������������������������������������������0000664�0000000�0000000�00000013640�14662262111�0022371�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_COMMENTSFRAME_H #define TAGLIB_COMMENTSFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An implementation of ID3v2 comments /*! * This implements the ID3v2 comment format. An ID3v2 comment consists of * a language encoding, a description and a single text field. */ class TAGLIB_EXPORT CommentsFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty comment frame that will use the text encoding * \a encoding. */ explicit CommentsFrame(String::Type encoding = String::Latin1); /*! * Construct a comment based on the data in \a data. */ explicit CommentsFrame(const ByteVector &data); /*! * Destroys this CommentFrame instance. */ ~CommentsFrame() override; CommentsFrame(const CommentsFrame &) = delete; CommentsFrame &operator=(const CommentsFrame &) = delete; /*! * Returns the text of this comment. * * \see text() */ String toString() const override; /*! * Returns the language encoding as a 3 byte encoding as specified by * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>. * * \note Most taggers simply ignore this value. * * \see setLanguage() */ ByteVector language() const; /*! * Returns the description of this comment. * * \note Most taggers simply ignore this value. * * \see setDescription() */ String description() const; /*! * Returns the text of this comment. * * \see setText() */ String text() const; /*! * Set the language using the 3 byte language code from * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to * \a languageEncoding. * * \see language() */ void setLanguage(const ByteVector &languageEncoding); /*! * Sets the description of the comment to \a s. * * \see description() */ void setDescription(const String &s); /*! * Sets the text portion of the comment to \a s. * * \see text() */ void setText(const String &s) override; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! * Parses this frame as PropertyMap with a single key. * - if description() is empty or "COMMENT", the key will be "COMMENT" * - if description() is not a valid PropertyMap key, the frame will be * marked unsupported by an entry "COMM/<description>" in the unsupportedData() * attribute of the returned map. * - otherwise, the key will be "COMMENT:<description>" * - The single value will be the frame's text(). */ PropertyMap asProperties() const override; /*! * Comments each have a unique description. This searches for a comment * frame with the description \a d and returns a pointer to it. If no * frame is found that matches the given description, null is returned. * * \see description() */ static CommentsFrame *findByDescription(const Tag *tag, const String &d); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ CommentsFrame(const ByteVector &data, Header *h); class CommentsFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<CommentsFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp�������������������������������������0000664�0000000�0000000�00000011012�14662262111�0024435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2014 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "eventtimingcodesframe.h" #include <utility> #include "tbytevectorlist.h" #include "tdebug.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class EventTimingCodesFrame::EventTimingCodesFramePrivate { public: EventTimingCodesFrame::TimestampFormat timestampFormat { EventTimingCodesFrame::AbsoluteMilliseconds }; EventTimingCodesFrame::SynchedEventList synchedEvents; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// EventTimingCodesFrame::EventTimingCodesFrame() : Frame("ETCO"), d(std::make_unique<EventTimingCodesFramePrivate>()) { } EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : Frame(data), d(std::make_unique<EventTimingCodesFramePrivate>()) { setData(data); } EventTimingCodesFrame::~EventTimingCodesFrame() = default; String EventTimingCodesFrame::toString() const { return String(); } EventTimingCodesFrame::TimestampFormat EventTimingCodesFrame::timestampFormat() const { return d->timestampFormat; } EventTimingCodesFrame::SynchedEventList EventTimingCodesFrame::synchedEvents() const { return d->synchedEvents; } void EventTimingCodesFrame::setTimestampFormat( EventTimingCodesFrame::TimestampFormat f) { d->timestampFormat = f; } void EventTimingCodesFrame::setSynchedEvents( const EventTimingCodesFrame::SynchedEventList &e) { d->synchedEvents = e; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void EventTimingCodesFrame::parseFields(const ByteVector &data) { const int end = data.size(); if(end < 1) { debug("An event timing codes frame must contain at least 1 byte."); return; } d->timestampFormat = static_cast<TimestampFormat>(data[0]); int pos = 1; d->synchedEvents.clear(); while(pos + 4 < end) { auto type = static_cast<EventType>(static_cast<unsigned char>(data[pos++])); unsigned int time = data.toUInt(pos, true); pos += 4; d->synchedEvents.append(SynchedEvent(time, type)); } } ByteVector EventTimingCodesFrame::renderFields() const { ByteVector v; v.append(static_cast<char>(d->timestampFormat)); for(const auto &entry : std::as_const(d->synchedEvents)) { v.append(static_cast<char>(entry.type)); v.append(ByteVector::fromUInt(entry.time)); } return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<EventTimingCodesFramePrivate>()) { parseFields(fieldData(data)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h���������������������������������������0000664�0000000�0000000�00000014343�14662262111�0024114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2014 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_EVENTTIMINGCODESFRAME_H #define TAGLIB_EVENTTIMINGCODESFRAME_H #include "tlist.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! ID3v2 event timing codes frame /*! * An implementation of ID3v2 event timing codes. */ class TAGLIB_EXPORT EventTimingCodesFrame : public Frame { friend class FrameFactory; public: /*! * Specifies the timestamp format used. */ enum TimestampFormat { //! The timestamp is of unknown format. Unknown = 0x00, //! The timestamp represents the number of MPEG frames since //! the beginning of the audio stream. AbsoluteMpegFrames = 0x01, //! The timestamp represents the number of milliseconds since //! the beginning of the audio stream. AbsoluteMilliseconds = 0x02 }; /*! * Event types defined in id3v2.4.0-frames.txt 4.5. Event timing codes. */ enum EventType { Padding = 0x00, EndOfInitialSilence = 0x01, IntroStart = 0x02, MainPartStart = 0x03, OutroStart = 0x04, OutroEnd = 0x05, VerseStart = 0x06, RefrainStart = 0x07, InterludeStart = 0x08, ThemeStart = 0x09, VariationStart = 0x0a, KeyChange = 0x0b, TimeChange = 0x0c, MomentaryUnwantedNoise = 0x0d, SustainedNoise = 0x0e, SustainedNoiseEnd = 0x0f, IntroEnd = 0x10, MainPartEnd = 0x11, VerseEnd = 0x12, RefrainEnd = 0x13, ThemeEnd = 0x14, Profanity = 0x15, ProfanityEnd = 0x16, NotPredefinedSynch0 = 0xe0, NotPredefinedSynch1 = 0xe1, NotPredefinedSynch2 = 0xe2, NotPredefinedSynch3 = 0xe3, NotPredefinedSynch4 = 0xe4, NotPredefinedSynch5 = 0xe5, NotPredefinedSynch6 = 0xe6, NotPredefinedSynch7 = 0xe7, NotPredefinedSynch8 = 0xe8, NotPredefinedSynch9 = 0xe9, NotPredefinedSynchA = 0xea, NotPredefinedSynchB = 0xeb, NotPredefinedSynchC = 0xec, NotPredefinedSynchD = 0xed, NotPredefinedSynchE = 0xee, NotPredefinedSynchF = 0xef, AudioEnd = 0xfd, AudioFileEnds = 0xfe }; /*! * Single entry of time stamp and event. */ struct SynchedEvent { SynchedEvent(unsigned int ms, EventType t) : time(ms), type(t) {} unsigned int time; EventType type; }; /*! * List of synchronized events. */ using SynchedEventList = TagLib::List<SynchedEvent>; /*! * Construct an empty event timing codes frame. */ explicit EventTimingCodesFrame(); /*! * Construct an event timing codes frame based on the data in \a data. */ explicit EventTimingCodesFrame(const ByteVector &data); /*! * Destroys this EventTimingCodesFrame instance. */ ~EventTimingCodesFrame() override; EventTimingCodesFrame(const EventTimingCodesFrame &) = delete; EventTimingCodesFrame &operator=(const EventTimingCodesFrame &) = delete; /*! * Returns an empty string. */ String toString() const override; /*! * Returns the timestamp format. */ TimestampFormat timestampFormat() const; /*! * Returns the events with the time stamps. */ SynchedEventList synchedEvents() const; /*! * Set the timestamp format. * * \see timestampFormat() */ void setTimestampFormat(TimestampFormat f); /*! * Sets the text with the time stamps. * * \see text() */ void setSynchedEvents(const SynchedEventList &e); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ EventTimingCodesFrame(const ByteVector &data, Header *h); class EventTimingCodesFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<EventTimingCodesFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp����������������������������0000664�0000000�0000000�00000013141�14662262111�0026270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Aaron VonderHaar email : avh4@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "generalencapsulatedobjectframe.h" #include "tdebug.h" #include "tstringlist.h" using namespace TagLib; using namespace ID3v2; class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate { public: String::Type textEncoding { String::Latin1 }; String mimeType; String fileName; String description; ByteVector data; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB"), d(std::make_unique<GeneralEncapsulatedObjectFramePrivate>()) { } GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data), d(std::make_unique<GeneralEncapsulatedObjectFramePrivate>()) { setData(data); } GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame() = default; String GeneralEncapsulatedObjectFrame::toString() const { String text = "[" + d->mimeType + "]"; if(!d->fileName.isEmpty()) text += " " + d->fileName; if(!d->description.isEmpty()) text += " \"" + d->description + "\""; return text; } StringList GeneralEncapsulatedObjectFrame::toStringList() const { return {d->description, d->fileName, d->mimeType}; } String::Type GeneralEncapsulatedObjectFrame::textEncoding() const { return d->textEncoding; } void GeneralEncapsulatedObjectFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } String GeneralEncapsulatedObjectFrame::mimeType() const { return d->mimeType; } void GeneralEncapsulatedObjectFrame::setMimeType(const String &type) { d->mimeType = type; } String GeneralEncapsulatedObjectFrame::fileName() const { return d->fileName; } void GeneralEncapsulatedObjectFrame::setFileName(const String &name) { d->fileName = name; } String GeneralEncapsulatedObjectFrame::description() const { return d->description; } void GeneralEncapsulatedObjectFrame::setDescription(const String &desc) { d->description = desc; } ByteVector GeneralEncapsulatedObjectFrame::object() const { return d->data; } void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data) { d->data = data; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) { if(data.size() < 4) { debug("An object frame must contain at least 4 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); int pos = 1; d->mimeType = readStringField(data, String::Latin1, &pos); d->fileName = readStringField(data, d->textEncoding, &pos); d->description = readStringField(data, d->textEncoding, &pos); d->data = data.mid(pos); } ByteVector GeneralEncapsulatedObjectFrame::renderFields() const { StringList sl; sl.append(d->fileName); sl.append(d->description); const String::Type encoding = checkTextEncoding(sl, d->textEncoding); ByteVector data; data.append(static_cast<char>(encoding)); data.append(d->mimeType.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); data.append(d->fileName.data(encoding)); data.append(textDelimiter(encoding)); data.append(d->description.data(encoding)); data.append(textDelimiter(encoding)); data.append(d->data); return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<GeneralEncapsulatedObjectFramePrivate>()) { parseFields(fieldData(data)); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h������������������������������0000664�0000000�0000000�00000014123�14662262111�0025736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Aaron VonderHaar email : avh4@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_GENERALENCAPSULATEDOBJECT_H #define TAGLIB_GENERALENCAPSULATEDOBJECT_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An ID3v2 general encapsulated object frame implementation /*! * This is an implementation of ID3v2 general encapsulated objects. * Arbitrary binary data may be included in tags, stored in GEOB frames. * There may be multiple GEOB frames in a single tag. Each GEOB is * labelled with a content description (which may be blank), a required * mime-type, and a file name (may be blank). The content description * uniquely identifies the GEOB frame in the tag. */ class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame { friend class FrameFactory; public: /*! * Constructs an empty object frame. The description, file name and text * encoding should be set manually. */ GeneralEncapsulatedObjectFrame(); /*! * Constructs a GeneralEncapsulatedObjectFrame frame based on \a data. * * \warning This is \em not data for the encapsulated object, for that use * setObject(). This constructor is used when reading the frame from the * disk. */ explicit GeneralEncapsulatedObjectFrame(const ByteVector &data); /*! * Destroys the GeneralEncapsulatedObjectFrame instance. */ ~GeneralEncapsulatedObjectFrame() override; GeneralEncapsulatedObjectFrame(const GeneralEncapsulatedObjectFrame &) = delete; GeneralEncapsulatedObjectFrame &operator=(const GeneralEncapsulatedObjectFrame &) = delete; /*! * Returns a string containing the description, file name and mime-type */ String toString() const override; /*! * Returns a string list containing the description, file name and mime-type. */ StringList toStringList() const override; /*! * Returns the text encoding used for the description and file name. * * \see setTextEncoding() * \see description() * \see fileName() */ String::Type textEncoding() const; /*! * Set the text encoding used for the description and file name. * * \see description() * \see fileName() */ void setTextEncoding(String::Type encoding); /*! * Returns the mime type of the object. */ String mimeType() const; /*! * Sets the mime type of the object. */ void setMimeType(const String &type); /*! * Returns the file name of the object. * * \see setFileName() */ String fileName() const; /*! * Sets the file name for the object. * * \see fileName() */ void setFileName(const String &name); /*! * Returns the content description of the object. * * \see setDescription() * \see textEncoding() * \see setTextEncoding() */ String description() const; /*! * Sets the content description of the object to \a desc. * * \see description() * \see textEncoding() * \see setTextEncoding() */ void setDescription(const String &desc); /*! * Returns the object data as a ByteVector. * * \note ByteVector has a data() method that returns a <tt>const char *</tt> which * should make it easy to export this data to external programs. * * \see setObject() * \see mimeType() */ ByteVector object() const; /*! * Sets the object data to \a data. \a data should be of the type specified in * this frame's mime-type specification. * * \see object() * \see mimeType() * \see setMimeType() */ void setObject(const ByteVector &data); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h); class GeneralEncapsulatedObjectFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<GeneralEncapsulatedObjectFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/ownershipframe.cpp��������������������������������������������0000664�0000000�0000000�00000012034�14662262111�0023111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Rupert Daniel email : rupert@cancelmonday.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "ownershipframe.h" #include "tstringlist.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class OwnershipFrame::OwnershipFramePrivate { public: String pricePaid; String datePurchased; String seller; String::Type textEncoding; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE"), d(std::make_unique<OwnershipFramePrivate>()) { d->textEncoding = encoding; } OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data), d(std::make_unique<OwnershipFramePrivate>()) { setData(data); } OwnershipFrame::~OwnershipFrame() = default; String OwnershipFrame::toString() const { return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller; } StringList OwnershipFrame::toStringList() const { return {d->pricePaid, d->datePurchased, d->seller}; } String OwnershipFrame::pricePaid() const { return d->pricePaid; } void OwnershipFrame::setPricePaid(const String &s) { d->pricePaid = s; } String OwnershipFrame::datePurchased() const { return d->datePurchased; } void OwnershipFrame::setDatePurchased(const String &s) { d->datePurchased = s; } String OwnershipFrame::seller() const { return d->seller; } void OwnershipFrame::setSeller(const String &seller) { d->seller = seller; } String::Type OwnershipFrame::textEncoding() const { return d->textEncoding; } void OwnershipFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void OwnershipFrame::parseFields(const ByteVector &data) { int pos = 0; // Need at least 1 byte for the encoding if(data.isEmpty()) { return; } // Get the text encoding d->textEncoding = static_cast<String::Type>(data[0]); pos += 1; // Read the price paid, this is a null terminated string d->pricePaid = readStringField(data, String::Latin1, &pos); // If we don't have at least 8 bytes left then don't parse the rest of the // data if(data.size() - pos < 8) { return; } // Read the date purchased YYYYMMDD d->datePurchased = String(data.mid(pos, 8)); pos += 8; // Read the seller if(d->textEncoding == String::Latin1) d->seller = Tag::latin1StringHandler()->parse(data.mid(pos)); else d->seller = String(data.mid(pos), d->textEncoding); } ByteVector OwnershipFrame::renderFields() const { StringList sl; sl.append(d->seller); const String::Type encoding = checkTextEncoding(sl, d->textEncoding); ByteVector v; v.append(static_cast<char>(encoding)); v.append(d->pricePaid.data(String::Latin1)); v.append(textDelimiter(String::Latin1)); v.append(d->datePurchased.data(String::Latin1)); v.append(d->seller.data(encoding)); return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<OwnershipFramePrivate>()) { parseFields(fieldData(data)); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/ownershipframe.h����������������������������������������������0000664�0000000�0000000�00000011300�14662262111�0022551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Rupert Daniel email : rupert@cancelmonday.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., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OWNERSHIPFRAME_H #define TAGLIB_OWNERSHIPFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An implementation of ID3v2 "ownership" /*! * This implements the ID3v2 ownership (OWNE frame). It consists of * a price paid, a date purchased (YYYYMMDD) and the name of the seller. */ class TAGLIB_EXPORT OwnershipFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty ownership frame. */ explicit OwnershipFrame(String::Type encoding = String::Latin1); /*! * Construct an ownership frame based on the data in \a data. */ explicit OwnershipFrame(const ByteVector &data); /*! * Destroys this OwnershipFrame instance. */ ~OwnershipFrame() override; OwnershipFrame(const OwnershipFrame &) = delete; OwnershipFrame &operator=(const OwnershipFrame &) = delete; /*! * Returns price paid, date purchased and seller. * * \see text() */ String toString() const override; /*! * Returns price paid, date purchased and seller. */ StringList toStringList() const override; /*! * Returns the date purchased. * * \see setDatePurchased() */ String datePurchased() const; /*! * Set the date purchased. * * \see datePurchased() */ void setDatePurchased(const String &s); /*! * Returns the price paid. * * \see setPricePaid() */ String pricePaid() const; /*! * Set the price paid. * * \see pricePaid() */ void setPricePaid(const String &s); /*! * Returns the seller. * * \see setSeller() */ String seller() const; /*! * Set the seller. * * \see seller() */ void setSeller(const String &seller); /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ OwnershipFrame(const ByteVector &data, Header *h); class OwnershipFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<OwnershipFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/podcastframe.cpp����������������������������������������������0000664�0000000�0000000�00000006077�14662262111�0022542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "podcastframe.h" #include "tpropertymap.h" using namespace TagLib; using namespace ID3v2; class PodcastFrame::PodcastFramePrivate { public: ByteVector fieldData; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// PodcastFrame::PodcastFrame() : Frame("PCST"), d(std::make_unique<PodcastFramePrivate>()) { d->fieldData = ByteVector(4, '\0'); } PodcastFrame::~PodcastFrame() = default; String PodcastFrame::toString() const { return String(); } PropertyMap PodcastFrame::asProperties() const { PropertyMap map; map.insert("PODCAST", StringList()); return map; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void PodcastFrame::parseFields(const ByteVector &data) { d->fieldData = data; } ByteVector PodcastFrame::renderFields() const { return d->fieldData; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<PodcastFramePrivate>()) { parseFields(fieldData(data)); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/podcastframe.h������������������������������������������������0000664�0000000�0000000�00000005741�14662262111�0022204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_PODCASTFRAME_H #define TAGLIB_PODCASTFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! ID3v2 podcast frame /*! * An implementation of ID3v2 podcast flag, a frame with four zero bytes. */ class TAGLIB_EXPORT PodcastFrame : public Frame { friend class FrameFactory; public: /*! * Construct a podcast frame. */ PodcastFrame(); /*! * Destroys this PodcastFrame instance. */ ~PodcastFrame() override; PodcastFrame(const PodcastFrame &) = delete; PodcastFrame &operator=(const PodcastFrame &) = delete; /*! * Returns an empty string. */ String toString() const override; PropertyMap asProperties() const override; protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ PodcastFrame(const ByteVector &data, Header *h); class PodcastFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PodcastFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/popularimeterframe.cpp����������������������������������������0000664�0000000�0000000�00000010420�14662262111�0023760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "popularimeterframe.h" #include "tstringlist.h" using namespace TagLib; using namespace ID3v2; class PopularimeterFrame::PopularimeterFramePrivate { public: String email; int rating { 0 }; unsigned int counter { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// PopularimeterFrame::PopularimeterFrame() : Frame("POPM"), d(std::make_unique<PopularimeterFramePrivate>()) { } PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data), d(std::make_unique<PopularimeterFramePrivate>()) { setData(data); } PopularimeterFrame::~PopularimeterFrame() = default; String PopularimeterFrame::toString() const { return d->email + " rating=" + String::number(d->rating) + " counter=" + String::number(d->counter); } StringList PopularimeterFrame::toStringList() const { return {d->email, String::number(d->rating), String::number(d->counter)}; } String PopularimeterFrame::email() const { return d->email; } void PopularimeterFrame::setEmail(const String &s) { d->email = s; } int PopularimeterFrame::rating() const { return d->rating; } void PopularimeterFrame::setRating(int s) { d->rating = s; } unsigned int PopularimeterFrame::counter() const { return d->counter; } void PopularimeterFrame::setCounter(unsigned int s) { d->counter = s; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void PopularimeterFrame::parseFields(const ByteVector &data) { int pos = 0, size = static_cast<int>(data.size()); d->email = readStringField(data, String::Latin1, &pos); d->rating = 0; d->counter = 0; if(pos < size) { d->rating = static_cast<unsigned char>(data[pos++]); if(pos < size) { d->counter = data.toUInt(static_cast<unsigned int>(pos)); } } } ByteVector PopularimeterFrame::renderFields() const { ByteVector data; data.append(d->email.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); data.append(static_cast<char>(d->rating)); data.append(ByteVector::fromUInt(d->counter)); return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<PopularimeterFramePrivate>()) { parseFields(fieldData(data)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/popularimeterframe.h������������������������������������������0000664�0000000�0000000�00000010064�14662262111�0023431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** * copyright : (C) 2008 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_POPULARIMETERFRAME_H #define TAGLIB_POPULARIMETERFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An implementation of ID3v2 "popularimeter" /*! * This implements the ID3v2 popularimeter (POPM frame). It consists of * an email, a rating and an optional counter. */ class TAGLIB_EXPORT PopularimeterFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty popularimeter frame. */ explicit PopularimeterFrame(); /*! * Construct a popularimeter based on the data in \a data. */ explicit PopularimeterFrame(const ByteVector &data); /*! * Destroys this PopularimeterFrame instance. */ ~PopularimeterFrame() override; PopularimeterFrame(const PopularimeterFrame &) = delete; PopularimeterFrame &operator=(const PopularimeterFrame &) = delete; /*! * Returns the text of this popularimeter. * * \see text() */ String toString() const override; /*! * Returns email, rating and counter. */ StringList toStringList() const override; /*! * Returns the email. * * \see setEmail() */ String email() const; /*! * Set the email. * * \see email() */ void setEmail(const String &s); /*! * Returns the rating. * * \see setRating() */ int rating() const; /*! * Set the rating. * * \see rating() */ void setRating(int s); /*! * Returns the counter. * * \see setCounter() */ unsigned int counter() const; /*! * Set the counter. * * \see counter() */ void setCounter(unsigned int s); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ PopularimeterFrame(const ByteVector &data, Header *h); class PopularimeterFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PopularimeterFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/privateframe.cpp����������������������������������������������0000664�0000000�0000000�00000007511�14662262111�0022551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Serkan Kalyoncu copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "privateframe.h" #include "tbytevectorlist.h" #include "tdebug.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class PrivateFrame::PrivateFramePrivate { public: ByteVector data; String owner; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// PrivateFrame::PrivateFrame() : Frame("PRIV"), d(std::make_unique<PrivateFramePrivate>()) { } PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data), d(std::make_unique<PrivateFramePrivate>()) { Frame::setData(data); } PrivateFrame::~PrivateFrame() = default; String PrivateFrame::toString() const { return d->owner; } String PrivateFrame::owner() const { return d->owner; } ByteVector PrivateFrame::data() const { return d->data; } void PrivateFrame::setOwner(const String &s) { d->owner = s; } void PrivateFrame::setData(const ByteVector & data) { d->data = data; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void PrivateFrame::parseFields(const ByteVector &data) { if(data.size() < 2) { debug("A private frame must contain at least 2 bytes."); return; } // Owner identifier is assumed to be Latin1 constexpr int byteAlign = 1; const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign); d->owner = String(data.mid(0, endOfOwner)); d->data = data.mid(endOfOwner + 1); } ByteVector PrivateFrame::renderFields() const { ByteVector v; v.append(d->owner.data(String::Latin1)); v.append(textDelimiter(String::Latin1)); v.append(d->data); return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<PrivateFramePrivate>()) { parseFields(fieldData(data)); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/privateframe.h������������������������������������������������0000664�0000000�0000000�00000007335�14662262111�0022222�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Serkan Kalyoncu copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_PRIVATEFRAME_H #define TAGLIB_PRIVATEFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An implementation of ID3v2 private frame class TAGLIB_EXPORT PrivateFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty private frame. */ PrivateFrame(); /*! * Construct a private frame based on the data in \a data. * * \note This is the constructor used when parsing the frame from a file. */ explicit PrivateFrame(const ByteVector &data); /*! * Destroys this private frame instance. */ ~PrivateFrame() override; PrivateFrame(const PrivateFrame &) = delete; PrivateFrame &operator=(const PrivateFrame &) = delete; /*! * Returns the text of this private frame, currently just the owner. * * \see text() */ String toString() const override; /*! * \return The owner of the private frame. * \note This should contain an email address or link to a website. */ String owner() const; /*! * Returns the private data. */ ByteVector data() const; /*! * Sets the owner of the frame to \a s. * \note This should contain an email address or link to a website. */ void setOwner(const String &s); /*! * Sets the private \a data. */ void setData(const ByteVector &data); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ PrivateFrame(const ByteVector &data, Header *h); class PrivateFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PrivateFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp���������������������������������������0000664�0000000�0000000�00000013274�14662262111�0024145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "relativevolumeframe.h" #include <utility> #include "tmap.h" using namespace TagLib; using namespace ID3v2; struct ChannelData { RelativeVolumeFrame::ChannelType channelType { RelativeVolumeFrame::Other }; short volumeAdjustment { 0 }; RelativeVolumeFrame::PeakVolume peakVolume; }; class RelativeVolumeFrame::RelativeVolumeFramePrivate { public: String identification; Map<ChannelType, ChannelData> channels; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2"), d(std::make_unique<RelativeVolumeFramePrivate>()) { } RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data), d(std::make_unique<RelativeVolumeFramePrivate>()) { setData(data); } RelativeVolumeFrame::~RelativeVolumeFrame() = default; String RelativeVolumeFrame::toString() const { return d->identification; } List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const { List<ChannelType> l; for(const auto &[type, channel] : std::as_const(d->channels)) l.append(type); return l; } short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const { return d->channels.contains(type) ? d->channels[type].volumeAdjustment : 0; } void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index, ChannelType type) { d->channels[type].volumeAdjustment = index; } float RelativeVolumeFrame::volumeAdjustment(ChannelType type) const { return d->channels.contains(type) ? static_cast<float>(d->channels[type].volumeAdjustment) / static_cast<float>(512) : 0; } void RelativeVolumeFrame::setVolumeAdjustment(float adjustment, ChannelType type) { d->channels[type].volumeAdjustment = static_cast<short>(adjustment * static_cast<float>(512)); } RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume(ChannelType type) const { return d->channels.contains(type) ? d->channels[type].peakVolume : PeakVolume(); } void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak, ChannelType type) { d->channels[type].peakVolume = peak; } String RelativeVolumeFrame::identification() const { return d->identification; } void RelativeVolumeFrame::setIdentification(const String &s) { d->identification = s; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void RelativeVolumeFrame::parseFields(const ByteVector &data) { int pos = 0; d->identification = readStringField(data, String::Latin1, &pos); // Each channel is at least 4 bytes. while(pos <= static_cast<int>(data.size()) - 4) { auto type = static_cast<ChannelType>(data[pos]); pos += 1; ChannelData &channel = d->channels[type]; channel.volumeAdjustment = data.toShort(static_cast<unsigned int>(pos)); pos += 2; channel.peakVolume.bitsRepresentingPeak = data[pos]; pos += 1; const int bytes = (channel.peakVolume.bitsRepresentingPeak + 7) / 8; channel.peakVolume.peakVolume = data.mid(pos, bytes); pos += bytes; } } ByteVector RelativeVolumeFrame::renderFields() const { ByteVector data; data.append(d->identification.data(String::Latin1)); data.append(textDelimiter(String::Latin1)); for(const auto &[type, channel] : std::as_const(d->channels)) { data.append(static_cast<char>(type)); data.append(ByteVector::fromShort(channel.volumeAdjustment)); data.append(static_cast<char>(channel.peakVolume.bitsRepresentingPeak)); data.append(channel.peakVolume.peakVolume); } return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<RelativeVolumeFramePrivate>()) { parseFields(fieldData(data)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/relativevolumeframe.h�����������������������������������������0000664�0000000�0000000�00000020133�14662262111�0023602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_RELATIVEVOLUMEFRAME_H #define TAGLIB_RELATIVEVOLUMEFRAME_H #include "tlist.h" #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! An ID3v2 relative volume adjustment frame implementation /*! * This is an implementation of ID3v2 relative volume adjustment. The * presence of this frame makes it possible to specify an increase in volume * for an audio file or specific audio tracks in that file. * * Multiple relative volume adjustment frames may be present in the tag * each with a unique identification and describing volume adjustment for * different channel types. */ class TAGLIB_EXPORT RelativeVolumeFrame : public Frame { friend class FrameFactory; public: /*! * This indicates the type of volume adjustment that should be applied. */ enum ChannelType { //! A type not enumerated below Other = 0x00, //! The master volume for the track MasterVolume = 0x01, //! The front right audio channel FrontRight = 0x02, //! The front left audio channel FrontLeft = 0x03, //! The back right audio channel BackRight = 0x04, //! The back left audio channel BackLeft = 0x05, //! The front center audio channel FrontCentre = 0x06, //! The back center audio channel BackCentre = 0x07, //! The subwoofer audio channel Subwoofer = 0x08 }; //! Struct that stores the relevant values for ID3v2 peak volume /*! * The peak volume is described as a series of bits that is padded to fill * a block of bytes. These two values should always be updated in tandem. */ struct PeakVolume { /*! * The number of bits (in the range of 0 to 255) used to describe the * peak volume. */ unsigned char bitsRepresentingPeak { 0 }; /*! * The array of bits (represented as a series of bytes) used to describe * the peak volume. */ ByteVector peakVolume; }; /*! * Constructs a RelativeVolumeFrame. The relevant data should be set * manually. */ RelativeVolumeFrame(); /*! * Constructs a RelativeVolumeFrame based on the contents of \a data. */ RelativeVolumeFrame(const ByteVector &data); /*! * Destroys the RelativeVolumeFrame instance. */ ~RelativeVolumeFrame() override; RelativeVolumeFrame(const RelativeVolumeFrame &) = delete; RelativeVolumeFrame &operator=(const RelativeVolumeFrame &) = delete; /*! * Returns the frame's identification. * * \see identification() */ String toString() const override; /*! * Returns a list of channels with information currently in the frame. */ List<ChannelType> channels() const; /*! * Returns the relative volume adjustment "index". As indicated by the * ID3v2 standard this is a 16-bit signed integer that reflects the * decibels of adjustment when divided by 512. * * This defaults to returning the value for the master volume channel if * available and returns 0 if the specified channel does not exist. * * \see setVolumeAdjustmentIndex() * \see volumeAdjustment() */ short volumeAdjustmentIndex(ChannelType type = MasterVolume) const; /*! * Set the volume adjustment to \a index. As indicated by the ID3v2 * standard this is a 16-bit signed integer that reflects the decibels of * adjustment when divided by 512. * * By default this sets the value for the master volume. * * \see volumeAdjustmentIndex() * \see setVolumeAdjustment() */ void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume); /*! * Returns the relative volume adjustment in decibels. * * \note Because this is actually stored internally as an "index" to this * value the value returned by this method may not be identical to the * value set using setVolumeAdjustment(). * * This defaults to returning the value for the master volume channel if * available and returns 0 if the specified channel does not exist. * * \see setVolumeAdjustment() * \see volumeAdjustmentIndex() */ float volumeAdjustment(ChannelType type = MasterVolume) const; /*! * Set the relative volume adjustment in decibels to \a adjustment. * * By default this sets the value for the master volume. * * \note Because this is actually stored internally as an "index" to this * value the value set by this method may not be identical to the one * returned by volumeAdjustment(). * * \see setVolumeAdjustment() * \see volumeAdjustmentIndex() */ void setVolumeAdjustment(float adjustment, ChannelType type = MasterVolume); /*! * Returns the peak volume (represented as a length and a string of bits). * * This defaults to returning the value for the master volume channel if * available and returns 0 if the specified channel does not exist. * * \see setPeakVolume() */ PeakVolume peakVolume(ChannelType type = MasterVolume) const; /*! * Sets the peak volume to \a peak. * * By default this sets the value for the master volume. * * \see peakVolume() */ void setPeakVolume(const PeakVolume &peak, ChannelType type = MasterVolume); /*! * Returns the identification for this frame. */ String identification() const; /*! * Sets the identification of the frame to \a s. The string * is used to identify the situation and/or device where this * adjustment should apply. */ void setIdentification(const String &s); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: RelativeVolumeFrame(const ByteVector &data, Header *h); class RelativeVolumeFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<RelativeVolumeFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp�����������������������������������0000664�0000000�0000000�00000016217�14662262111�0025047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2014 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "synchronizedlyricsframe.h" #include <utility> #include "tbytevectorlist.h" #include "tdebug.h" #include "tpropertymap.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class SynchronizedLyricsFrame::SynchronizedLyricsFramePrivate { public: String::Type textEncoding { String::Latin1 }; ByteVector language; SynchronizedLyricsFrame::TimestampFormat timestampFormat { SynchronizedLyricsFrame::AbsoluteMilliseconds }; SynchronizedLyricsFrame::Type type { SynchronizedLyricsFrame::Lyrics }; String description; SynchronizedLyricsFrame::SynchedTextList synchedText; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) : Frame("SYLT"), d(std::make_unique<SynchronizedLyricsFramePrivate>()) { d->textEncoding = encoding; } SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) : Frame(data), d(std::make_unique<SynchronizedLyricsFramePrivate>()) { setData(data); } SynchronizedLyricsFrame::~SynchronizedLyricsFrame() = default; String SynchronizedLyricsFrame::toString() const { return d->description; } String::Type SynchronizedLyricsFrame::textEncoding() const { return d->textEncoding; } ByteVector SynchronizedLyricsFrame::language() const { return d->language; } SynchronizedLyricsFrame::TimestampFormat SynchronizedLyricsFrame::timestampFormat() const { return d->timestampFormat; } SynchronizedLyricsFrame::Type SynchronizedLyricsFrame::type() const { return d->type; } String SynchronizedLyricsFrame::description() const { return d->description; } SynchronizedLyricsFrame::SynchedTextList SynchronizedLyricsFrame::synchedText() const { return d->synchedText; } void SynchronizedLyricsFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } void SynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding) { d->language = languageEncoding.mid(0, 3); } void SynchronizedLyricsFrame::setTimestampFormat(SynchronizedLyricsFrame::TimestampFormat f) { d->timestampFormat = f; } void SynchronizedLyricsFrame::setType(SynchronizedLyricsFrame::Type t) { d->type = t; } void SynchronizedLyricsFrame::setDescription(const String &s) { d->description = s; } void SynchronizedLyricsFrame::setSynchedText( const SynchronizedLyricsFrame::SynchedTextList &t) { d->synchedText = t; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void SynchronizedLyricsFrame::parseFields(const ByteVector &data) { const int end = data.size(); if(end < 7) { debug("A synchronized lyrics frame must contain at least 7 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); d->language = data.mid(1, 3); d->timestampFormat = static_cast<TimestampFormat>(data[4]); d->type = static_cast<Type>(data[5]); int pos = 6; d->description = readStringField(data, d->textEncoding, &pos); if(pos == 6) return; /* * If UTF16 strings are found in SYLT frames, a BOM may only be * present in the first string (content descriptor), and the strings of * the synchronized text have no BOM. Here the BOM is read from * the first string to have a specific encoding with endianness for the * case of strings without BOM so that readStringField() will work. */ String::Type encWithEndianness = d->textEncoding; if(d->textEncoding == String::UTF16) { unsigned short bom = data.toUShort(6, true); if(bom == 0xfffe) { encWithEndianness = String::UTF16LE; } else if(bom == 0xfeff) { encWithEndianness = String::UTF16BE; } } d->synchedText.clear(); while(pos < end) { String::Type enc = d->textEncoding; // If a UTF16 string has no BOM, use the encoding found above. if(enc == String::UTF16 && pos + 1 < end) { unsigned short bom = data.toUShort(pos, true); if(bom != 0xfffe && bom != 0xfeff) { enc = encWithEndianness; } } String text = readStringField(data, enc, &pos); if(pos + 4 > end) return; unsigned int time = data.toUInt(pos, true); pos += 4; d->synchedText.append(SynchedText(time, text)); } } ByteVector SynchronizedLyricsFrame::renderFields() const { ByteVector v; String::Type encoding = d->textEncoding; encoding = checkTextEncoding(d->description, encoding); for(const auto &frame : std::as_const(d->synchedText)) { encoding = checkTextEncoding(frame.text, encoding); } v.append(static_cast<char>(encoding)); v.append(d->language.size() == 3 ? d->language : "XXX"); v.append(static_cast<char>(d->timestampFormat)); v.append(static_cast<char>(d->type)); v.append(d->description.data(encoding)); v.append(textDelimiter(encoding)); for(const auto &entry : std::as_const(d->synchedText)) { v.append(entry.text.data(encoding)); v.append(textDelimiter(encoding)); v.append(ByteVector::fromUInt(entry.time)); } return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<SynchronizedLyricsFramePrivate>()) { parseFields(fieldData(data)); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h�������������������������������������0000664�0000000�0000000�00000016516�14662262111�0024516�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2014 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_SYNCHRONIZEDLYRICSFRAME_H #define TAGLIB_SYNCHRONIZEDLYRICSFRAME_H #include "tlist.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! ID3v2 synchronized lyrics frame /*! * An implementation of ID3v2 synchronized lyrics. */ class TAGLIB_EXPORT SynchronizedLyricsFrame : public Frame { friend class FrameFactory; public: /*! * Specifies the timestamp format used. */ enum TimestampFormat { //! The timestamp is of unknown format. Unknown = 0x00, //! The timestamp represents the number of MPEG frames since //! the beginning of the audio stream. AbsoluteMpegFrames = 0x01, //! The timestamp represents the number of milliseconds since //! the beginning of the audio stream. AbsoluteMilliseconds = 0x02 }; /*! * Specifies the type of text contained. */ enum Type { //! The text is some other type of text. Other = 0x00, //! The text contains lyrical data. Lyrics = 0x01, //! The text contains a transcription. TextTranscription = 0x02, //! The text lists the movements in the piece. Movement = 0x03, //! The text describes events that occur. Events = 0x04, //! The text contains chord changes that occur in the music. Chord = 0x05, //! The text contains trivia or "pop up" information about the media. Trivia = 0x06, //! The text contains URLs for relevant webpages. WebpageUrls = 0x07, //! The text contains URLs for relevant images. ImageUrls = 0x08 }; /*! * Single entry of time stamp and lyrics text. */ struct SynchedText { SynchedText(unsigned int ms, const String &str) : time(ms), text(str) { } unsigned int time; String text; }; /*! * List of synchronized lyrics. */ using SynchedTextList = TagLib::List<SynchedText>; /*! * Construct an empty synchronized lyrics frame that will use the text * encoding \a encoding. */ explicit SynchronizedLyricsFrame(String::Type encoding = String::Latin1); /*! * Construct a synchronized lyrics frame based on the data in \a data. */ explicit SynchronizedLyricsFrame(const ByteVector &data); /*! * Destroys this SynchronizedLyricsFrame instance. */ ~SynchronizedLyricsFrame() override; SynchronizedLyricsFrame(const SynchronizedLyricsFrame &) = delete; SynchronizedLyricsFrame &operator=(const SynchronizedLyricsFrame &) = delete; /*! * Returns the description of this synchronized lyrics frame. * * \see description() */ String toString() const override; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Returns the language encoding as a 3 byte encoding as specified by * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>. * * \note Most taggers simply ignore this value. * * \see setLanguage() */ ByteVector language() const; /*! * Returns the timestamp format. */ TimestampFormat timestampFormat() const; /*! * Returns the type of text contained. */ Type type() const; /*! * Returns the description of this synchronized lyrics frame. * * \note Most taggers simply ignore this value. * * \see setDescription() */ String description() const; /*! * Returns the text with the time stamps. */ SynchedTextList synchedText() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! * Set the language using the 3 byte language code from * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to * \a languageEncoding. * * \see language() */ void setLanguage(const ByteVector &languageEncoding); /*! * Set the timestamp format. * * \see timestampFormat() */ void setTimestampFormat(TimestampFormat f); /*! * Set the type of text contained. * * \see type() */ void setType(Type t); /*! * Sets the description of the synchronized lyrics frame to \a s. * * \see description() */ void setDescription(const String &s); /*! * Sets the text with the time stamps. * * \see text() */ void setSynchedText(const SynchedTextList &t); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ SynchronizedLyricsFrame(const ByteVector &data, Header *h); class SynchronizedLyricsFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<SynchronizedLyricsFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp��������������������������������������0000664�0000000�0000000�00000021544�14662262111�0024273�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Lukas Krejci email : krejclu6@fel.cvut.cz ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tableofcontentsframe.h" #include <utility> #include "tpropertymap.h" #include "tdebug.h" using namespace TagLib; using namespace ID3v2; class TableOfContentsFrame::TableOfContentsFramePrivate { public: TableOfContentsFramePrivate() { embeddedFrameList.setAutoDelete(true); } const ID3v2::Header *tagHeader { nullptr }; ByteVector elementID; bool isTopLevel { false }; bool isOrdered { false }; ByteVectorList childElements; FrameListMap embeddedFrameListMap; FrameList embeddedFrameList; }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data), d(std::make_unique<TableOfContentsFramePrivate>()) { d->tagHeader = tagHeader; setData(data); } TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children, const FrameList &embeddedFrames) : ID3v2::Frame("CTOC"), d(std::make_unique<TableOfContentsFramePrivate>()) { d->elementID = elementID; d->childElements = children; for(const auto &frame : embeddedFrames) addEmbeddedFrame(frame); } TableOfContentsFrame::~TableOfContentsFrame() = default; ByteVector TableOfContentsFrame::elementID() const { return d->elementID; } bool TableOfContentsFrame::isTopLevel() const { return d->isTopLevel; } bool TableOfContentsFrame::isOrdered() const { return d->isOrdered; } unsigned int TableOfContentsFrame::entryCount() const { return d->childElements.size(); } ByteVectorList TableOfContentsFrame::childElements() const { return d->childElements; } void TableOfContentsFrame::setElementID(const ByteVector &eID) { d->elementID = eID; } void TableOfContentsFrame::setIsTopLevel(const bool &t) { d->isTopLevel = t; } void TableOfContentsFrame::setIsOrdered(const bool &o) { d->isOrdered = o; } void TableOfContentsFrame::setChildElements(const ByteVectorList &l) { d->childElements = l; } void TableOfContentsFrame::addChildElement(const ByteVector &cE) { d->childElements.append(cE); } void TableOfContentsFrame::removeChildElement(const ByteVector &cE) { auto it = d->childElements.find(cE); if(it == d->childElements.end()) it = d->childElements.find(cE + ByteVector("\0")); if(it != d->childElements.end()) d->childElements.erase(it); } const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const { return d->embeddedFrameListMap; } const FrameList &TableOfContentsFrame::embeddedFrameList() const { return d->embeddedFrameList; } const FrameList &TableOfContentsFrame::embeddedFrameList(const ByteVector &frameID) const { return d->embeddedFrameListMap[frameID]; } void TableOfContentsFrame::addEmbeddedFrame(Frame *frame) { d->embeddedFrameList.append(frame); d->embeddedFrameListMap[frame->frameID()].append(frame); } void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) { // remove the frame from the frame list auto it = d->embeddedFrameList.find(frame); if(it != d->embeddedFrameList.end()) d->embeddedFrameList.erase(it); // ...and from the frame list map FrameList &mappedList = d->embeddedFrameListMap[frame->frameID()]; it = mappedList.find(frame); if(it != mappedList.end()) mappedList.erase(it); // ...and delete as desired if(del) delete frame; } void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) { const FrameList frames = d->embeddedFrameListMap[id]; for(const auto &frame : frames) removeEmbeddedFrame(frame, true); } String TableOfContentsFrame::toString() const { String s = String(d->elementID) + ": top level: " + (d->isTopLevel ? "true" : "false") + ", ordered: " + (d->isOrdered ? "true" : "false"); if(!d->childElements.isEmpty()) { s+= ", chapters: [ " + String(d->childElements.toByteVector(", ")) + " ]"; } if(!d->embeddedFrameList.isEmpty()) { StringList frameIDs; for(const auto &frame : std::as_const(d->embeddedFrameList)) frameIDs.append(frame->frameID()); s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]"; } return s; } PropertyMap TableOfContentsFrame::asProperties() const { PropertyMap map; map.addUnsupportedData(frameID() + String("/") + d->elementID); return map; } TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static { for(const auto &table : std::as_const(tag->frameList("CTOC"))) { auto frame = dynamic_cast<TableOfContentsFrame *>(table); if(frame && frame->elementID() == eID) return frame; } return nullptr; } TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) // static { for(const auto &table : std::as_const(tag->frameList("CTOC"))) { auto frame = dynamic_cast<TableOfContentsFrame *>(table); if(frame && frame->isTopLevel()) return frame; } return nullptr; } void TableOfContentsFrame::parseFields(const ByteVector &data) { unsigned int size = data.size(); if(size < 6) { debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by " "null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated " "by null."); return; } int pos = 0; unsigned int embPos = 0; d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); d->isTopLevel = (data.at(pos) & 2) != 0; d->isOrdered = (data.at(pos++) & 1) != 0; unsigned int entryCount = static_cast<unsigned char>(data.at(pos++)); for(unsigned int i = 0; i < entryCount; i++) { ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); d->childElements.append(childElementID); } size -= pos; if(size < header()->size()) return; while(embPos < size - header()->size()) { Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); if(!frame) return; // Checks to make sure that frame parsed correctly. if(frame->size() <= 0) { delete frame; return; } embPos += frame->size() + header()->size(); addEmbeddedFrame(frame); } } ByteVector TableOfContentsFrame::renderFields() const { ByteVector data; data.append(d->elementID); data.append('\0'); char flags = 0; if(d->isTopLevel) flags += 2; if(d->isOrdered) flags += 1; data.append(flags); data.append(static_cast<char>(entryCount())); for(const auto &element : std::as_const(d->childElements)) { data.append(element); data.append('\0'); } for(const auto &frame : std::as_const(d->embeddedFrameList)) { frame->header()->setVersion(header()->version()); data.append(frame->render()); } return data; } TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<TableOfContentsFramePrivate>()) { d->tagHeader = tagHeader; parseFields(fieldData(data)); } ������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/tableofcontentsframe.h����������������������������������������0000664�0000000�0000000�00000021757�14662262111�0023746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Lukas Krejci email : krejclu6@fel.cvut.cz ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TABLEOFCONTENTSFRAME #define TAGLIB_TABLEOFCONTENTSFRAME #include "tbytevectorlist.h" #include "id3v2tag.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { /*! * This is an implementation of ID3v2 table of contents frames. Purpose * of this frame is to allow a table of contents to be defined. */ //! An implementation of ID3v2 table of contents frames class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame { friend class FrameFactory; public: /*! * Creates a table of contents frame based on \a data. \a tagHeader is * required as the internal frames are parsed based on the tag version. */ TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data); /*! * Creates a table of contents frame with the element ID \a elementID, * the child elements \a children and embedded frames, which become owned * by this frame, in \a embeddedFrames. */ TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children = ByteVectorList(), const FrameList &embeddedFrames = FrameList()); /*! * Destroys the frame. */ ~TableOfContentsFrame() override; TableOfContentsFrame(const TableOfContentsFrame &) = delete; TableOfContentsFrame &operator=(const TableOfContentsFrame &) = delete; /*! * Returns the elementID of the frame. * Element ID is not intended to be human readable. * * \see setElementID() */ ByteVector elementID() const; /*! * Returns \c true, if the frame is top-level (doesn't have * any parent CTOC frame). * * \see setIsTopLevel() */ bool isTopLevel() const; /*! * Returns \c true, if the child elements list entries * are ordered. * * \see setIsOrdered() */ bool isOrdered() const; /*! * Returns the count of child elements of the frame. It always * corresponds to the size of the child elements list. * * \see childElements() */ unsigned int entryCount() const; /*! * Returns list of child elements of the frame. * * \see setChildElements() */ ByteVectorList childElements() const; /*! * Sets the elementID of the frame to \a eID. * * \see elementID() */ void setElementID(const ByteVector &eID); /*! * Sets, if the frame is top-level (doesn't have * any parent CTOC frame). * * \see isTopLevel() */ void setIsTopLevel(const bool &t); /*! * Sets, if the child elements list entries * are ordered. * * \see isOrdered() */ void setIsOrdered(const bool &o); /*! * Sets list of child elements of the frame to \a l. * * \see childElements() */ void setChildElements(const ByteVectorList &l); /*! * Adds \a cE to the list of child elements of the frame. * * \see childElements() */ void addChildElement(const ByteVector &cE); /*! * Removes \a cE to list of child elements of the frame. * * \see childElements() */ void removeChildElement(const ByteVector &cE); /*! * Returns a reference to the frame list map. This is a FrameListMap of * all of the frames embedded in the CTOC frame. * * This is the most convenient structure for accessing the CTOC frame's * embedded frames. Many frame types allow multiple instances of the same * frame type so this is a map of lists. In most cases however there will * only be a single frame of a certain type. * * \warning You should not modify this data structure directly, instead * use addEmbeddedFrame() and removeEmbeddedFrame(). * * \see embeddedFrameList() */ const FrameListMap &embeddedFrameListMap() const; /*! * Returns a reference to the embedded frame list. This is a FrameList * of all of the frames embedded in the CTOC frame in the order that they * were parsed. * * This can be useful if for example you want to iterate over the CTOC frame's * embedded frames in the order that they occur in the CTOC frame. * * \warning You should not modify this data structure directly, instead * use addEmbeddedFrame() and removeEmbeddedFrame(). */ const FrameList &embeddedFrameList() const; /*! * Returns the embedded frame list for frames with the id \a frameID * or an empty list if there are no embedded frames of that type. This * is just a convenience and is equivalent to: * * \code * embeddedFrameListMap()[frameID]; * \endcode * * \see embeddedFrameListMap() */ const FrameList &embeddedFrameList(const ByteVector &frameID) const; /*! * Add an embedded frame to the CTOC frame. At this point the CTOC frame * takes ownership of the embedded frame and will handle freeing its memory. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void addEmbeddedFrame(Frame *frame); /*! * Remove an embedded frame from the CTOC frame. If \a del is \c true the frame's * memory will be freed; if it is \c false, it must be deleted by the user. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void removeEmbeddedFrame(Frame *frame, bool del = true); /*! * Remove all embedded frames of type \a id from the CTOC frame and free their * memory. * * \note Using this method will invalidate any pointers on the list * returned by embeddedFrameList() */ void removeEmbeddedFrames(const ByteVector &id); String toString() const override; PropertyMap asProperties() const override; /*! * CTOC frames each have a unique element ID. This searches for a CTOC * frame with the element ID \a eID and returns a pointer to it. This * can be used to link together parent and child CTOC frames. * * \see elementID() */ static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); /*! * CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't * any frame, which contains this frame in its child elements list). Only a single frame * within the tag can be top-level. This searches for a top-level CTOC frame. * * \see isTopLevel() */ static TableOfContentsFrame *findTopLevel(const Tag *tag); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); class TableOfContentsFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TableOfContentsFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �����������������taglib-2.0.2/taglib/mpeg/id3v2/frames/textidentificationframe.cpp�����������������������������������0000664�0000000�0000000�00000035126�14662262111�0025000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "textidentificationframe.h" #include <array> #include <utility> #include "tpropertymap.h" #include "id3v1genres.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class TextIdentificationFrame::TextIdentificationFramePrivate { public: String::Type textEncoding { String::Latin1 }; StringList fieldList; }; class UserTextIdentificationFrame::UserTextIdentificationFramePrivate { }; //////////////////////////////////////////////////////////////////////////////// // TextIdentificationFrame public members //////////////////////////////////////////////////////////////////////////////// TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) : Frame(type), d(std::make_unique<TextIdentificationFramePrivate>()) { d->textEncoding = encoding; } TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : Frame(data), d(std::make_unique<TextIdentificationFramePrivate>()) { setData(data); } TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) // static { auto frame = new TextIdentificationFrame("TIPL"); StringList l; for(const auto &[person, list] : properties) { const String role = involvedPeopleMap()[person]; if(role.isEmpty()) // should not happen continue; l.append(role); l.append(list.toString(",")); // comma-separated list of names } frame->setText(l); return frame; } TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) // static { auto frame = new TextIdentificationFrame("TMCL"); StringList l; for(const auto &[instrument, list] : properties) { if(!instrument.startsWith(instrumentPrefix)) // should not happen continue; l.append(instrument.substr(instrumentPrefix.size())); l.append(list.toString(",")); } frame->setText(l); return frame; } TextIdentificationFrame::~TextIdentificationFrame() = default; void TextIdentificationFrame::setText(const StringList &l) { d->fieldList = l; } void TextIdentificationFrame::setText(const String &s) { d->fieldList = s; } String TextIdentificationFrame::toString() const { return d->fieldList.toString(); } StringList TextIdentificationFrame::toStringList() const { return d->fieldList; } StringList TextIdentificationFrame::fieldList() const { return d->fieldList; } String::Type TextIdentificationFrame::textEncoding() const { return d->textEncoding; } void TextIdentificationFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } namespace { // array of allowed TIPL prefixes and their corresponding key value constexpr std::array involvedPeople { std::pair("ARRANGER", "ARRANGER"), std::pair("ENGINEER", "ENGINEER"), std::pair("PRODUCER", "PRODUCER"), std::pair("DJ-MIX", "DJMIXER"), std::pair("MIX", "MIXER"), }; constexpr std::array txxxFrameTranslation { std::pair("MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID"), std::pair("MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID"), std::pair("MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID"), std::pair("MUSICBRAINZ ALBUM RELEASE COUNTRY", "RELEASECOUNTRY"), std::pair("MUSICBRAINZ ALBUM STATUS", "RELEASESTATUS"), std::pair("MUSICBRAINZ ALBUM TYPE", "RELEASETYPE"), std::pair("MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID"), std::pair("MUSICBRAINZ RELEASE TRACK ID", "MUSICBRAINZ_RELEASETRACKID"), std::pair("MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID"), std::pair("ACOUSTID ID", "ACOUSTID_ID"), std::pair("ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT"), std::pair("MUSICIP PUID", "MUSICIP_PUID"), }; } // namespace const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static { static KeyConversionMap m; if(m.isEmpty()) { for(const auto &[o, t] : involvedPeople) m.insert(t, o); } return m; } PropertyMap TextIdentificationFrame::asProperties() const { if(frameID() == "TIPL") return makeTIPLProperties(); if(frameID() == "TMCL") return makeTMCLProperties(); String tagName = frameIDToKey(frameID()); if(tagName.isEmpty()) { PropertyMap map; map.addUnsupportedData(frameID()); return map; } StringList values = fieldList(); if(tagName == "GENRE") { // Special case: Support ID3v1-style genre numbers. They are not officially supported in // ID3v2, however it seems that still a lot of programs use them. for(auto &value : values) { bool ok = false; int test = value.toInt(&ok); // test if the genre value is an integer if(ok) value = ID3v1::genre(test); } } else if(tagName == "DATE") { for(auto &value : values) { // ID3v2 specifies ISO8601 timestamps which contain a 'T' as separator between date and time. // Since this is unusual in other formats, the T is removed. int tpos = value.find("T"); if(tpos != -1) value[tpos] = ' '; } } PropertyMap ret; ret.insert(tagName, values); return ret; } //////////////////////////////////////////////////////////////////////////////// // TextIdentificationFrame protected members //////////////////////////////////////////////////////////////////////////////// void TextIdentificationFrame::parseFields(const ByteVector &data) { // Don't try to parse invalid frames if(data.size() < 2) return; // read the string data type (the first byte of the field data) d->textEncoding = static_cast<String::Type>(data[0]); // split the byte array into chunks based on the string type (two byte delimiter // for unicode encodings) int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; // build a small counter to strip nulls off the end of the field int dataLength = data.size() - 1; while(dataLength > 0 && data[dataLength] == 0) dataLength--; while(dataLength % byteAlign != 0) dataLength++; const ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign); d->fieldList.clear(); // append those split values to the list and make sure that the new string's // type is the same specified for this frame unsigned short firstBom = 0; for(auto it = l.begin(); it != l.end(); ++it) { if(!it->isEmpty() || (it == l.begin() && frameID() == "TXXX")) { if(d->textEncoding == String::Latin1) { d->fieldList.append(Tag::latin1StringHandler()->parse(*it)); } else { String::Type textEncoding = d->textEncoding; if(textEncoding == String::UTF16) { if(it == l.begin()) { firstBom = it->mid(0, 2).toUShort(); } else { unsigned short subsequentBom = it->mid(0, 2).toUShort(); if(subsequentBom != 0xfeff && subsequentBom != 0xfffe) { if(firstBom == 0xfeff) { textEncoding = String::UTF16BE; } else if(firstBom == 0xfffe) { textEncoding = String::UTF16LE; } } } } d->fieldList.append(String(*it, textEncoding)); } } } } ByteVector TextIdentificationFrame::renderFields() const { String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding); ByteVector v; v.append(static_cast<char>(encoding)); for(auto it = d->fieldList.cbegin(); it != d->fieldList.cend(); ++it) { // Since the field list is null delimited, if this is not the first // element in the list, append the appropriate delimiter for this // encoding. if(it != d->fieldList.cbegin()) v.append(textDelimiter(encoding)); v.append(it->data(encoding)); } return v; } //////////////////////////////////////////////////////////////////////////////// // TextIdentificationFrame private members //////////////////////////////////////////////////////////////////////////////// TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<TextIdentificationFramePrivate>()) { parseFields(fieldData(data)); } PropertyMap TextIdentificationFrame::makeTIPLProperties() const { PropertyMap map; if(fieldList().size() % 2 != 0){ // according to the ID3 spec, TIPL must contain an even number of entries map.addUnsupportedData(frameID()); return map; } const StringList l = fieldList(); for(auto it = l.begin(); it != l.end(); ++it) { auto found = std::find_if(involvedPeople.begin(), involvedPeople.end(), [=](const auto &person) { return *it == person.first; }); if(found != involvedPeople.end()) { map.insert(found->second, (++it)->split(",")); } else { // invalid involved role -> mark whole frame as unsupported in order to be consistent with writing map.clear(); map.addUnsupportedData(frameID()); return map; } } return map; } PropertyMap TextIdentificationFrame::makeTMCLProperties() const { PropertyMap map; if(fieldList().size() % 2 != 0){ // according to the ID3 spec, TMCL must contain an even number of entries map.addUnsupportedData(frameID()); return map; } const StringList l = fieldList(); for(auto it = l.begin(); it != l.end(); ++it) { String instrument = it->upper(); // ++it == l.end() is not possible with size check above, // verified to silence cppcheck. if(instrument.isEmpty() || ++it == l.end()) { // instrument is not a valid key -> frame unsupported map.clear(); map.addUnsupportedData(frameID()); return map; } map.insert(L"PERFORMER:" + instrument, it->split(",")); } return map; } //////////////////////////////////////////////////////////////////////////////// // UserTextIdentificationFrame public members //////////////////////////////////////////////////////////////////////////////// UserTextIdentificationFrame::~UserTextIdentificationFrame() = default; UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) : TextIdentificationFrame("TXXX", encoding) { StringList l; l.append(String()); l.append(String()); setText(l); } UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data) : TextIdentificationFrame(data) { checkFields(); } UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) : TextIdentificationFrame("TXXX", encoding) { setDescription(description); setText(values); } String UserTextIdentificationFrame::toString() const { // first entry is the description itself, drop from values list StringList l = fieldList(); if(!l.isEmpty()) l.erase(l.begin()); return "[" + description() + "] " + l.toString(); } String UserTextIdentificationFrame::description() const { return !TextIdentificationFrame::fieldList().isEmpty() ? TextIdentificationFrame::fieldList().front() : String(); } void UserTextIdentificationFrame::setText(const String &text) { if(description().isEmpty()) setDescription(String()); TextIdentificationFrame::setText(StringList(description()).append(text)); } void UserTextIdentificationFrame::setText(const StringList &fields) { if(description().isEmpty()) setDescription(String()); TextIdentificationFrame::setText(StringList(description()).append(fields)); } void UserTextIdentificationFrame::setDescription(const String &s) { StringList l = fieldList(); if(l.isEmpty()) l.append(s); else l[0] = s; TextIdentificationFrame::setText(l); } PropertyMap UserTextIdentificationFrame::asProperties() const { PropertyMap map; String tagName = txxxToKey(description()); const StringList v = fieldList(); for(auto it = std::next(v.begin()); it != v.end(); ++it) map.insert(tagName, *it); return map; } UserTextIdentificationFrame *UserTextIdentificationFrame::find( const ID3v2::Tag *tag, const String &description) // static { for(const auto &frame : std::as_const(tag->frameList("TXXX"))) { auto f = dynamic_cast<UserTextIdentificationFrame *>(frame); if(f && f->description() == description) return f; } return nullptr; } String UserTextIdentificationFrame::txxxToKey(const String &description) { const String d = description.upper(); for(const auto &[o, t] : txxxFrameTranslation) { if(d == o) return t; } return d; } String UserTextIdentificationFrame::keyToTXXX(const String &s) { const String key = s.upper(); for(const auto &[o, t] : txxxFrameTranslation) { if(key == t) return o; } return s; } //////////////////////////////////////////////////////////////////////////////// // UserTextIdentificationFrame private members //////////////////////////////////////////////////////////////////////////////// UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data, Header *h) : TextIdentificationFrame(data, h) { checkFields(); } void UserTextIdentificationFrame::checkFields() { int fields = fieldList().size(); if(fields == 0) setDescription(String()); if(fields <= 1) setText(String()); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/textidentificationframe.h�������������������������������������0000664�0000000�0000000�00000031615�14662262111�0024444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TEXTIDENTIFICATIONFRAME_H #define TAGLIB_TEXTIDENTIFICATIONFRAME_H #include "tstringlist.h" #include "tmap.h" #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { class Tag; using KeyConversionMap = Map<String, String>; //! An ID3v2 text identification frame implementation /*! * This is an implementation of the most common type of ID3v2 frame -- text * identification frames. There are a number of variations on this. Those * enumerated in the ID3v2.4 standard are: * * <ul> * <li><b>TALB</b> Album/Movie/Show title</li> * <li><b>TBPM</b> BPM (beats per minute)</li> * <li><b>TCOM</b> Composer</li> * <li><b>TCON</b> Content type</li> * <li><b>TCOP</b> Copyright message</li> * <li><b>TDEN</b> Encoding time</li> * <li><b>TDLY</b> Playlist delay</li> * <li><b>TDOR</b> Original release time</li> * <li><b>TDRC</b> Recording time</li> * <li><b>TDRL</b> Release time</li> * <li><b>TDTG</b> Tagging time</li> * <li><b>TENC</b> Encoded by</li> * <li><b>TEXT</b> Lyricist/Text writer</li> * <li><b>TFLT</b> %File type</li> * <li><b>TIPL</b> Involved people list</li> * <li><b>TIT1</b> Content group description</li> * <li><b>TIT2</b> Title/songname/content description</li> * <li><b>TIT3</b> Subtitle/Description refinement</li> * <li><b>TKEY</b> Initial key</li> * <li><b>TLAN</b> Language(s)</li> * <li><b>TLEN</b> Length</li> * <li><b>TMCL</b> Musician credits list</li> * <li><b>TMED</b> Media type</li> * <li><b>TMOO</b> Mood</li> * <li><b>TOAL</b> Original album/movie/show title</li> * <li><b>TOFN</b> Original filename</li> * <li><b>TOLY</b> Original lyricist(s)/text writer(s)</li> * <li><b>TOPE</b> Original artist(s)/performer(s)</li> * <li><b>TOWN</b> %File owner/licensee</li> * <li><b>TPE1</b> Lead performer(s)/Soloist(s)</li> * <li><b>TPE2</b> Band/orchestra/accompaniment</li> * <li><b>TPE3</b> Conductor/performer refinement</li> * <li><b>TPE4</b> Interpreted, remixed, or otherwise modified by</li> * <li><b>TPOS</b> Part of a set</li> * <li><b>TPRO</b> Produced notice</li> * <li><b>TPUB</b> Publisher</li> * <li><b>TRCK</b> Track number/Position in set</li> * <li><b>TRSN</b> Internet radio station name</li> * <li><b>TRSO</b> Internet radio station owner</li> * <li><b>TSOA</b> Album sort order</li> * <li><b>TSOP</b> Performer sort order</li> * <li><b>TSOT</b> Title sort order</li> * <li><b>TSRC</b> ISRC (international standard recording code)</li> * <li><b>TSSE</b> Software/Hardware and settings used for encoding</li> * <li><b>TSST</b> Set subtitle</li> * </ul> * * The ID3v2 Frames document gives a description of each of these formats * and the expected order of strings in each. ID3v2::Header::frameID() can * be used to determine the frame type. * * \note If non-Latin1 compatible strings are used with this class, even if * the text encoding is set to Latin1, the frame will be written using UTF8 * (with the encoding flag appropriately set in the output). */ class TAGLIB_EXPORT TextIdentificationFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty frame of type \a type. Uses \a encoding as the * default text encoding. * * \note In this case you must specify the text encoding as it * resolves the ambiguity between constructors. * * \note Please see the note in the class description regarding Latin1. */ TextIdentificationFrame(const ByteVector &type, String::Type encoding); /*! * This is a dual purpose constructor. \a data can either be binary data * that should be parsed or (at a minimum) the frame ID. */ explicit TextIdentificationFrame(const ByteVector &data); /*! * This is a special factory method to create a TIPL (involved people list) * frame from the given \a properties. Will parse key=[list of values] data * into the TIPL format as specified in the ID3 standard. */ static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties); /*! * This is a special factory method to create a TMCL (musician credits list) * frame from the given \a properties. Will parse key=[list of values] data * into the TMCL format as specified in the ID3 standard, where key should be * of the form instrumentPrefix:instrument. */ static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties); /*! * Destroys this TextIdentificationFrame instance. */ ~TextIdentificationFrame() override; TextIdentificationFrame(const TextIdentificationFrame &) = delete; TextIdentificationFrame &operator=(const TextIdentificationFrame &) = delete; /*! * Text identification frames are a list of string fields. * * This function will accept either a StringList or a String (using the * StringList constructor that accepts a single String). * * \note This will not change the text encoding of the frame even if the * strings passed in are not of the same encoding. Please use * setEncoding(s.type()) if you wish to change the encoding of the frame. */ void setText(const StringList &l); // Reimplementations. void setText(const String &s) override; String toString() const override; StringList toStringList() const override; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \note Please see the note in the class description regarding Latin1. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \note Please see the note in the class description regarding Latin1. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! * Returns a list of the strings in this frame. */ StringList fieldList() const; /*! * Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap * to the corresponding key used in a TIPL ID3 frame to describe that role. */ static const KeyConversionMap &involvedPeopleMap(); PropertyMap asProperties() const override; protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; /*! * The constructor used by the FrameFactory. */ TextIdentificationFrame(const ByteVector &data, Header *h); private: /*! * Parses the special structure of a TIPL frame * Only the whitelisted roles "ARRANGER", "ENGINEER", "PRODUCER", * "DJMIXER" (ID3: "DJ-MIX") and "MIXER" (ID3: "MIX") are allowed. */ PropertyMap makeTIPLProperties() const; /*! * Parses the special structure of a TMCL frame. */ PropertyMap makeTMCLProperties() const; class TextIdentificationFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TextIdentificationFramePrivate> d; }; /*! * This is a specialization of text identification frames that allows for * user defined entries. Each entry has a description in addition to the * normal list of fields that a text identification frame has. * * This description identifies the frame and must be unique. */ //! An ID3v2 custom text identification frame implementation class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame { friend class FrameFactory; public: /*! * Constructs an empty user defined text identification frame. For this to be * a useful frame both a description and text must be set. */ explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1); /*! * Creates a frame based on \a data. */ explicit UserTextIdentificationFrame(const ByteVector &data); /*! * Creates a user defined text identification frame with the given \a description * and \a values. */ UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8); ~UserTextIdentificationFrame() override; UserTextIdentificationFrame(const UserTextIdentificationFrame &) = delete; UserTextIdentificationFrame &operator=(const UserTextIdentificationFrame &) = delete; String toString() const override; /*! * Returns the description for this frame. */ String description() const; /*! * Sets the description of the frame to \a s. \a s must be unique. You can * check for the presence of another user defined text frame of the same type * using find() and testing for null. */ void setDescription(const String &s); void setText(const String &text) override; void setText(const StringList &fields); /*! * A UserTextIdentificationFrame is parsed into a PropertyMap as follows: * - the key is the frame's description, uppercased * - if the description contains '::', only the substring after that * separator is considered as key (compatibility with exfalso) * - if the above rules don't yield a valid key (e.g. containing non-ASCII * characters), the returned map will contain an entry "TXXX/<description>" * in its unsupportedData() list. * - The values will be copies of the fieldList(). * - If the description() appears as value in fieldList(), it will be omitted * in the value list, in order to be compatible with TagLib which copies * the description() into the fieldList(). */ PropertyMap asProperties() const override; /*! * Searches for the user defined text frame with the description \a description * in \a tag. This returns null if no matching frames were found. */ static UserTextIdentificationFrame *find(const Tag *tag, const String &description); /*! * Returns an appropriate TXXX frame description for the given free-form tag key. */ static String keyToTXXX(const String &); /*! * Returns a free-form tag name for the given ID3 frame description. */ static String txxxToKey(const String &); private: UserTextIdentificationFrame(const ByteVector &data, Header *h); void checkFields(); class UserTextIdentificationFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UserTextIdentificationFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp���������������������������������0000664�0000000�0000000�00000010513�14662262111�0025304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "uniquefileidentifierframe.h" #include <utility> #include "tbytevectorlist.h" #include "tpropertymap.h" #include "tdebug.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class UniqueFileIdentifierFrame::UniqueFileIdentifierFramePrivate { public: String owner; ByteVector identifier; }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) : ID3v2::Frame(data), d(std::make_unique<UniqueFileIdentifierFramePrivate>()) { setData(data); } UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) : ID3v2::Frame("UFID"), d(std::make_unique<UniqueFileIdentifierFramePrivate>()) { d->owner = owner; d->identifier = id; } UniqueFileIdentifierFrame::~UniqueFileIdentifierFrame() = default; String UniqueFileIdentifierFrame::owner() const { return d->owner; } ByteVector UniqueFileIdentifierFrame::identifier() const { return d->identifier; } void UniqueFileIdentifierFrame::setOwner(const String &s) { d->owner = s; } void UniqueFileIdentifierFrame::setIdentifier(const ByteVector &v) { d->identifier = v; } String UniqueFileIdentifierFrame::toString() const { return String(); } PropertyMap UniqueFileIdentifierFrame::asProperties() const { PropertyMap map; if(d->owner == "http://musicbrainz.org") { map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); } else { map.addUnsupportedData(frameID() + String("/") + d->owner); } return map; } UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static { for(const auto &comment : std::as_const(tag->frameList("UFID"))) { auto frame = dynamic_cast<UniqueFileIdentifierFrame *>(comment); if(frame && frame->owner() == o) return frame; } return nullptr; } void UniqueFileIdentifierFrame::parseFields(const ByteVector &data) { if(data.size() < 1) { debug("An UFID frame must contain at least 1 byte."); return; } int pos = 0; d->owner = readStringField(data, String::Latin1, &pos); d->identifier = data.mid(pos); } ByteVector UniqueFileIdentifierFrame::renderFields() const { ByteVector data; data.append(d->owner.data(String::Latin1)); data.append(static_cast<char>(0)); data.append(d->identifier); return data; } UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<UniqueFileIdentifierFramePrivate>()) { parseFields(fieldData(data)); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h�����������������������������������0000664�0000000�0000000�00000010726�14662262111�0024757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_UNIQUEFILEIDENTIFIERFRAME #define TAGLIB_UNIQUEFILEIDENTIFIERFRAME #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { /*! * This is an implementation of ID3v2 unique file identifier frames. This * frame is used to identify the file in an arbitrary database identified * by the owner field. */ //! An implementation of ID3v2 unique identifier frames class TAGLIB_EXPORT UniqueFileIdentifierFrame : public ID3v2::Frame { friend class FrameFactory; public: /*! * Creates a unique file identifier frame based on \a data. */ UniqueFileIdentifierFrame(const ByteVector &data); /*! * Creates a unique file identifier frame with the owner \a owner and * the identification \a id. */ UniqueFileIdentifierFrame(const String &owner, const ByteVector &id); /*! * Destroys the frame. */ ~UniqueFileIdentifierFrame() override; UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &) = delete; UniqueFileIdentifierFrame &operator=(const UniqueFileIdentifierFrame &) = delete; /*! * Returns the owner for the frame; essentially this is the key for * determining which identification scheme this key belongs to. This * will usually either be an email address or URL for the person or tool * used to create the unique identifier. * * \see setOwner() */ String owner() const; /*! * Returns the unique identifier. Though sometimes this is a text string * it also may be binary data and as much should be assumed when handling * it. */ ByteVector identifier() const; /*! * Sets the owner of the identification scheme to \a s. * * \see owner() */ void setOwner(const String &s); /*! * Sets the unique file identifier to \a v. * * \see identifier() */ void setIdentifier(const ByteVector &v); String toString() const override; PropertyMap asProperties() const override; /*! * UFID frames each have a unique owner. This searches for a UFID * frame with the owner \a o and returns a pointer to it. * * \see owner() */ static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: UniqueFileIdentifierFrame(const ByteVector &data, Header *h); class UniqueFileIdentifierFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UniqueFileIdentifierFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/unknownframe.cpp����������������������������������������������0000664�0000000�0000000�00000005743�14662262111�0022603�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "unknownframe.h" using namespace TagLib; using namespace ID3v2; class UnknownFrame::UnknownFramePrivate { public: ByteVector fieldData; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data), d(std::make_unique<UnknownFramePrivate>()) { setData(data); } UnknownFrame::~UnknownFrame() = default; String UnknownFrame::toString() const { return String(); } ByteVector UnknownFrame::data() const { return d->fieldData; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void UnknownFrame::parseFields(const ByteVector &data) { d->fieldData = data; } ByteVector UnknownFrame::renderFields() const { return d->fieldData; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<UnknownFramePrivate>()) { parseFields(fieldData(data)); } �����������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/unknownframe.h������������������������������������������������0000664�0000000�0000000�00000006351�14662262111�0022244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_UNKNOWNFRAME_H #define TAGLIB_UNKNOWNFRAME_H #include "taglib_export.h" #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! A frame type \e unknown to TagLib. /*! * This class represents a frame type not known (or more often simply * unimplemented) in TagLib. This is here to provide a basic API for * manipulating the binary data of unknown frames and to provide a means * of rendering such \e unknown frames. * * Please note that a cleaner way of handling frame types that TagLib * does not understand is to subclass ID3v2::Frame and ID3v2::FrameFactory * to have your frame type supported through the standard ID3v2 mechanism. */ class TAGLIB_EXPORT UnknownFrame : public Frame { friend class FrameFactory; public: UnknownFrame(const ByteVector &data); ~UnknownFrame() override; UnknownFrame(const UnknownFrame &) = delete; UnknownFrame &operator=(const UnknownFrame &) = delete; String toString() const override; /*! * Returns the field data (everything but the header) for this frame. */ ByteVector data() const; protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: UnknownFrame(const ByteVector &data, Header *h); class UnknownFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UnknownFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp���������������������������������0000664�0000000�0000000�00000013704�14662262111�0025410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "unsynchronizedlyricsframe.h" #include <utility> #include "tbytevectorlist.h" #include "tdebug.h" #include "tpropertymap.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class UnsynchronizedLyricsFrame::UnsynchronizedLyricsFramePrivate { public: String::Type textEncoding { String::Latin1 }; ByteVector language; String description; String text; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) : Frame("USLT"), d(std::make_unique<UnsynchronizedLyricsFramePrivate>()) { d->textEncoding = encoding; } UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) : Frame(data), d(std::make_unique<UnsynchronizedLyricsFramePrivate>()) { setData(data); } UnsynchronizedLyricsFrame::~UnsynchronizedLyricsFrame() = default; String UnsynchronizedLyricsFrame::toString() const { return d->text; } ByteVector UnsynchronizedLyricsFrame::language() const { return d->language; } String UnsynchronizedLyricsFrame::description() const { return d->description; } String UnsynchronizedLyricsFrame::text() const { return d->text; } void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding) { d->language = languageEncoding.mid(0, 3); } void UnsynchronizedLyricsFrame::setDescription(const String &s) { d->description = s; } void UnsynchronizedLyricsFrame::setText(const String &s) { d->text = s; } String::Type UnsynchronizedLyricsFrame::textEncoding() const { return d->textEncoding; } void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } PropertyMap UnsynchronizedLyricsFrame::asProperties() const { PropertyMap map; String key = description().upper(); if(key.isEmpty() || key == "LYRICS") map.insert("LYRICS", text()); else map.insert("LYRICS:" + key, text()); return map; } UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static { for(const auto &lyrics : std::as_const(tag->frameList("USLT"))) { auto frame = dynamic_cast<UnsynchronizedLyricsFrame *>(lyrics); if(frame && frame->description() == d) return frame; } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) { if(data.size() < 5) { debug("An unsynchronized lyrics frame must contain at least 5 bytes."); return; } d->textEncoding = static_cast<String::Type>(data[0]); d->language = data.mid(1, 3); int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; if(ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); l.size() == 2) { if(d->textEncoding == String::Latin1) { d->description = Tag::latin1StringHandler()->parse(l.front()); d->text = Tag::latin1StringHandler()->parse(l.back()); } else { d->description = String(l.front(), d->textEncoding); d->text = String(l.back(), d->textEncoding); } } } ByteVector UnsynchronizedLyricsFrame::renderFields() const { StringList sl; sl.append(d->description); sl.append(d->text); const String::Type encoding = checkTextEncoding(sl, d->textEncoding); ByteVector v; v.append(static_cast<char>(encoding)); v.append(d->language.size() == 3 ? d->language : "XXX"); v.append(d->description.data(encoding)); v.append(textDelimiter(encoding)); v.append(d->text.data(encoding)); return v; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<UnsynchronizedLyricsFramePrivate>()) { parseFields(fieldData(data)); } ������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h�����������������������������������0000664�0000000�0000000�00000014463�14662262111�0025060�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H #define TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! ID3v2 unsynchronized lyrics frame /*! * An implementation of ID3v2 unsynchronized lyrics. */ class TAGLIB_EXPORT UnsynchronizedLyricsFrame : public Frame { friend class FrameFactory; public: /*! * Construct an empty unsynchronized lyrics frame that will use the text encoding * \a encoding. */ explicit UnsynchronizedLyricsFrame(String::Type encoding = String::Latin1); /*! * Construct an unsynchronized lyrics frame based on the data in \a data. */ explicit UnsynchronizedLyricsFrame(const ByteVector &data); /*! * Destroys this UnsynchronizedLyricsFrame instance. */ ~UnsynchronizedLyricsFrame() override; UnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame &) = delete; UnsynchronizedLyricsFrame &operator=(const UnsynchronizedLyricsFrame &) = delete; /*! * Returns the text of this unsynchronized lyrics frame. * * \see text() */ String toString() const override; /*! * Returns the language encoding as a 3 byte encoding as specified by * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>. * * \note Most taggers simply ignore this value. * * \see setLanguage() */ ByteVector language() const; /*! * Returns the description of this unsynchronized lyrics frame. * * \note Most taggers simply ignore this value. * * \see setDescription() */ String description() const; /*! * Returns the text of this unsynchronized lyrics frame. * * \see setText() */ String text() const; /*! * Set the language using the 3 byte language code from * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to * \a languageEncoding. * * \see language() */ void setLanguage(const ByteVector &languageEncoding); /*! * Sets the description of the unsynchronized lyrics frame to \a s. * * \see description() */ void setDescription(const String &s); /*! * Sets the text portion of the unsynchronized lyrics frame to \a s. * * \see text() */ void setText(const String &s) override; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! Parses this frame as PropertyMap with a single key. * - if description() is empty or "LYRICS", the key will be "LYRICS" * - if description() is not a valid PropertyMap key, the frame will be * marked unsupported by an entry "USLT/<description>" in the unsupportedData() * attribute of the returned map. * - otherwise, the key will be "LYRICS:<description>" * - The single value will be the frame's text(). * Note that currently the language() field is not supported by the PropertyMap * interface. */ PropertyMap asProperties() const override; /*! * LyricsFrames each have a unique description. This searches for a lyrics * frame with the description \a d and returns a pointer to it. If no * frame is found that matches the given description, null is returned. * * \see description() */ static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d); protected: // Reimplementations. void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; private: /*! * The constructor used by the FrameFactory. */ UnsynchronizedLyricsFrame(const ByteVector &data, Header *h); class UnsynchronizedLyricsFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UnsynchronizedLyricsFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/urllinkframe.cpp����������������������������������������������0000664�0000000�0000000�00000015011�14662262111�0022551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "urllinkframe.h" #include <utility> #include "tdebug.h" #include "tstringlist.h" #include "tpropertymap.h" #include "id3v2tag.h" using namespace TagLib; using namespace ID3v2; class UrlLinkFrame::UrlLinkFramePrivate { public: String url; }; class UserUrlLinkFrame::UserUrlLinkFramePrivate { public: String::Type textEncoding { String::Latin1 }; String description; }; //////////////////////////////////////////////////////////////////////////////// // UrlLinkFrame public members //////////////////////////////////////////////////////////////////////////////// UrlLinkFrame::UrlLinkFrame(const ByteVector &data) : Frame(data), d(std::make_unique<UrlLinkFramePrivate>()) { setData(data); } UrlLinkFrame::~UrlLinkFrame() = default; void UrlLinkFrame::setUrl(const String &s) { d->url = s; } String UrlLinkFrame::url() const { return d->url; } void UrlLinkFrame::setText(const String &s) { setUrl(s); } String UrlLinkFrame::toString() const { return url(); } PropertyMap UrlLinkFrame::asProperties() const { String key = frameIDToKey(frameID()); PropertyMap map; if(key.isEmpty()) // unknown W*** frame - this normally shouldn't happen map.addUnsupportedData(frameID()); else map.insert(key, url()); return map; } //////////////////////////////////////////////////////////////////////////////// // UrlLinkFrame protected members //////////////////////////////////////////////////////////////////////////////// void UrlLinkFrame::parseFields(const ByteVector &data) { d->url = String(data); } ByteVector UrlLinkFrame::renderFields() const { return d->url.data(String::Latin1); } UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h), d(std::make_unique<UrlLinkFramePrivate>()) { parseFields(fieldData(data)); } //////////////////////////////////////////////////////////////////////////////// // UserUrlLinkFrame public members //////////////////////////////////////////////////////////////////////////////// UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) : UrlLinkFrame("WXXX"), d(std::make_unique<UserUrlLinkFramePrivate>()) { d->textEncoding = encoding; } UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) : UrlLinkFrame(data), d(std::make_unique<UserUrlLinkFramePrivate>()) { setData(data); } UserUrlLinkFrame::~UserUrlLinkFrame() = default; String UserUrlLinkFrame::toString() const { return "[" + description() + "] " + url(); } String::Type UserUrlLinkFrame::textEncoding() const { return d->textEncoding; } void UserUrlLinkFrame::setTextEncoding(String::Type encoding) { d->textEncoding = encoding; } String UserUrlLinkFrame::description() const { return d->description; } void UserUrlLinkFrame::setDescription(const String &s) { d->description = s; } PropertyMap UserUrlLinkFrame::asProperties() const { PropertyMap map; String key = description().upper(); if(key.isEmpty() || key == "URL") map.insert("URL", url()); else map.insert("URL:" + key, url()); return map; } UserUrlLinkFrame *UserUrlLinkFrame::find(const ID3v2::Tag *tag, const String &description) // static { for(const auto &frame : std::as_const(tag->frameList("WXXX"))) { auto f = dynamic_cast<UserUrlLinkFrame *>(frame); if(f && f->description() == description) return f; } return nullptr; } //////////////////////////////////////////////////////////////////////////////// // UserUrlLinkFrame protected members //////////////////////////////////////////////////////////////////////////////// void UserUrlLinkFrame::parseFields(const ByteVector &data) { if(data.size() < 2) { debug("A user URL link frame must contain at least 2 bytes."); return; } int pos = 0; d->textEncoding = static_cast<String::Type>(data[0]); pos += 1; if(d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) { int offset = data.find(textDelimiter(d->textEncoding), pos); if(offset < pos) return; d->description = String(data.mid(pos, offset - pos), d->textEncoding); pos = offset + 1; } else { int len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2); if(len < 0) return; d->description = String(data.mid(pos, len), d->textEncoding); pos += len + 2; } setUrl(String(data.mid(pos))); } ByteVector UserUrlLinkFrame::renderFields() const { ByteVector v; String::Type encoding = checkTextEncoding(d->description, d->textEncoding); v.append(static_cast<char>(encoding)); v.append(d->description.data(encoding)); v.append(textDelimiter(encoding)); v.append(url().data(String::Latin1)); return v; } UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h), d(std::make_unique<UserUrlLinkFramePrivate>()) { parseFields(fieldData(data)); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/frames/urllinkframe.h������������������������������������������������0000664�0000000�0000000�00000014623�14662262111�0022226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org copyright : (C) 2006 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_URLLINKFRAME_H #define TAGLIB_URLLINKFRAME_H #include "id3v2frame.h" namespace TagLib { namespace ID3v2 { //! ID3v2 URL frame /*! * An implementation of ID3v2 URL link frames. */ class TAGLIB_EXPORT UrlLinkFrame : public Frame { friend class FrameFactory; public: /*! * This is a dual purpose constructor. \a data can either be binary data * that should be parsed or (at a minimum) the frame ID. */ explicit UrlLinkFrame(const ByteVector &data); /*! * Destroys this UrlLinkFrame instance. */ ~UrlLinkFrame() override; UrlLinkFrame(const UrlLinkFrame &) = delete; UrlLinkFrame &operator=(const UrlLinkFrame &) = delete; /*! * Returns the URL. */ virtual String url() const; /*! * Sets the URL to \a s. */ virtual void setUrl(const String &s); // Reimplementations. void setText(const String &s) override; String toString() const override; PropertyMap asProperties() const override; protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; /*! * The constructor used by the FrameFactory. */ UrlLinkFrame(const ByteVector &data, Header *h); private: class UrlLinkFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UrlLinkFramePrivate> d; }; //! ID3v2 User defined URL frame /*! * This is a specialization of URL link frames that allows for * user defined entries. Each entry has a description in addition to the * normal list of fields that a URL link frame has. * * This description identifies the frame and must be unique. */ class TAGLIB_EXPORT UserUrlLinkFrame : public UrlLinkFrame { friend class FrameFactory; public: /*! * Constructs an empty user defined URL link frame. For this to be * a useful frame both a description and text must be set. */ explicit UserUrlLinkFrame(String::Type encoding = String::Latin1); /*! * This is a dual purpose constructor. \a data can either be binary data * that should be parsed or (at a minimum) the frame ID. */ explicit UserUrlLinkFrame(const ByteVector &data); /*! * Destroys this UserUrlLinkFrame instance. */ ~UserUrlLinkFrame() override; UserUrlLinkFrame(const UserUrlLinkFrame &) = delete; UserUrlLinkFrame &operator=(const UserUrlLinkFrame &) = delete; // Reimplementations. String toString() const override; /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor * or read from the frame when parsed. * * \see setTextEncoding() * \see render() */ String::Type textEncoding() const; /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. * * \see textEncoding() * \see render() */ void setTextEncoding(String::Type encoding); /*! * Returns the description for this frame. */ String description() const; /*! * Sets the description of the frame to \a s. \a s must be unique. */ void setDescription(const String &s); /*! * Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key, * and the URL as a single value. * - if description() is empty, the key will be "URL". * - otherwise, if description() is not a valid key (e.g. containing non-ASCII * characters), the returned map will contain an entry "WXXX/<description>" * in its unsupportedData() list. */ PropertyMap asProperties() const override; /*! * Searches for the user defined url frame with the description \a description * in \a tag. This returns null if no matching frames were found. */ static UserUrlLinkFrame *find(const Tag *tag, const String &description); protected: void parseFields(const ByteVector &data) override; ByteVector renderFields() const override; /*! * The constructor used by the FrameFactory. */ UserUrlLinkFrame(const ByteVector &data, Header *h); private: class UserUrlLinkFramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<UserUrlLinkFramePrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2.2.0.txt��������������������������������������������������������0000664�0000000�0000000�00000171531�14662262111�0020075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Informal standard M. Nilsson Document: id3v2-00.txt 26th March 1998 ID3 tag version 2 Status of this document This document is an Informal standard and is released so that implementors could have a set standard before the formal standard is set. The formal standard will use another version number if not identical to what is described in this document. The contents in this document may change for clarifications but never for added or altered functionallity. Distribution of this document is unlimited. Abstract The recent gain of popularity for MPEG layer III audio files on the internet forced a standardised way of storing information about an audio file within itself to determinate its origin and contents. Today the most accepted way to do this is with the so called ID3 tag, which is simple but very limited and in some cases very unsuitable. The ID3 tag has very limited space in every field, very limited numbers of fields, not expandable or upgradeable and is placed at the end of a the file, which is unsuitable for streaming audio. This draft is an attempt to answer these issues with a new version of the ID3 tag. 1. Table of contents 2. Conventions in this document 3. ID3v2 overview 3.1. ID3v2 header 3.2. ID3v2 frames overview 4. Declared ID3v2 frames 4.1. Unique file identifier 4.2. Text information frames 4.2.1. Text information frames - details 4.2.2. User defined text information frame 4.3. URL link frames 4.3.1. URL link frames - details 4.3.2. User defined URL link frame 4.4. Involved people list 4.5. Music CD Identifier 4.6. Event timing codes 4.7. MPEG location lookup table 4.8. Synced tempo codes 4.9. Unsychronised lyrics/text transcription 4.10. Synchronised lyrics/text 4.11. Comments 4.12. Relative volume adjustment 4.13. Equalisation 4.14. Reverb 4.15. Attached picture 4.16. General encapsulated object 4.17. Play counter 4.18. Popularimeter 4.19. Recommended buffer size 4.20. Encrypted meta frame 4.21. Audio encryption 4.22. Linked information 5. The 'unsynchronisation scheme' 6. Copyright 7. References 8. Appendix A. Appendix A - ID3-Tag Specification V1.1 A.1. Overview A.2. ID3v1 Implementation A.3. Genre List A.4. Track addition - ID3v1.1 9. Author's Address 2. Conventions in this document In the examples, text within "" is a text string exactly as it appears in a file. Numbers preceded with $ are hexadecimal and numbers preceded with % are binary. $xx is used to indicate a byte with unknown content. %x is used to indicate a bit with unknown content. The most significant bit (MSB) of a byte is called 'bit 7' and the least significant bit (LSB) is called 'bit 0'. A tag is the whole tag described in this document. A frame is a block of information in the tag. The tag consists of a header, frames and optional padding. A field is a piece of information; one value, a string etc. A numeric string is a string that consists of the characters 0-9 only. 3. ID3v2 overview The two biggest design goals were to be able to implement ID3v2 without disturbing old software too much and that ID3v2 should be expandable. The first criterion is met by the simple fact that the MPEG [MPEG] decoding software uses a syncsignal, embedded in the audiostream, to 'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid syncsignal, no software will attempt to play the tag. If, for any reason, coincidence make a syncsignal appear within the tag it will be taken care of by the 'unsynchronisation scheme' described in section 5. The second criterion has made a more noticeable impact on the design of the ID3v2 tag. It is constructed as a container for several information blocks, called frames, whose format need not be known to the software that encounters them. At the start of every frame there is an identifier that explains the frames's format and content, and a size descriptor that allows software to skip unknown frames. If a total revision of the ID3v2 tag should be needed, there is a version number and a size descriptor in the ID3v2 header. The ID3 tag described in this document is mainly targeted to files encoded with MPEG-2 layer I, MPEG-2 layer II, MPEG-2 layer III and MPEG-2.5, but may work with other types of encoded audio. The bitorder in ID3v2 is most significant bit first (MSB). The byteorder in multibyte numbers is most significant byte first (e.g. $12345678 would be encoded $12 34 56 78). It is permitted to include padding after all the final frame (at the end of the ID3 tag), making the size of all the frames together smaller than the size given in the head of the tag. A possible purpose of this padding is to allow for adding a few additional frames or enlarge existing frames within the tag without having to rewrite the entire file. The value of the padding bytes must be $00. 3.1. ID3v2 header The ID3v2 tag header, which should be the first information in the file, is 10 bytes as follows: ID3/file identifier "ID3" ID3 version $02 00 ID3 flags %xx000000 ID3 size 4 * %0xxxxxxx The first three bytes of the tag are always "ID3" to indicate that this is an ID3 tag, directly followed by the two version bytes. The first byte of ID3 version is it's major version, while the second byte is its revision number. All revisions are backwards compatible while major versions are not. If software with ID3v2 and below support should encounter version three or higher it should simply ignore the whole tag. Version and revision will never be $FF. The first bit (bit 7) in the 'ID3 flags' is indicating whether or not unsynchronisation is used (see section 5 for details); a set bit indicates usage. The second bit (bit 6) is indicating whether or not compression is used; a set bit indicates usage. Since no compression scheme has been decided yet, the ID3 decoder (for now) should just ignore the entire tag if the compression bit is set. The ID3 tag size is encoded with four bytes where the first bit (bit 7) is set to zero in every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 02 01. The ID3 tag size is the size of the complete tag after unsychronisation, including padding, excluding the header (total tag size - 10). The reason to use 28 bits (representing up to 256MB) for size description is that we don't want to run out of space here. A ID3v2 tag can be detected with the following pattern: $49 44 33 yy yy xx zz zz zz zz Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80. 3.2. ID3v2 frames overview The headers of the frames are similar in their construction. They consist of one three character identifier (capital A-Z and 0-9) and one three byte size field, making a total of six bytes. The header is excluded from the size. Identifiers beginning with "X", "Y" and "Z" are for experimental use and free for everyone to use. Have in mind that someone else might have used the same identifier as you. All other identifiers are either used or reserved for future use. The three character frame identifier is followed by a three byte size descriptor, making a total header size of six bytes in every frame. The size is calculated as framesize excluding frame identifier and size descriptor (frame size - 6). There is no fixed order of the frames' appearance in the tag, although it is desired that the frames are arranged in order of significance concerning the recognition of the file. An example of such order: UFI, MCI, TT2 ... A tag must contain at least one frame. A frame must be at least 1 byte big, excluding the 6-byte header. If nothing else is said a string is represented as ISO-8859-1 [ISO-8859-1] characters in the range $20 - $FF. All unicode strings [UNICODE] use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). All numeric strings are always encoded as ISO-8859-1. Terminated strings are terminated with $00 if encoded with ISO-8859-1 and $00 00 if encoded as unicode. If nothing else is said newline character is forbidden. In ISO-8859-1 a new line is represented, when allowed, with $0A only. Frames that allow different types of text encoding have a text encoding description byte directly after the frame size. If ISO-8859-1 is used this byte should be $00, if unicode is used it should be $01. The three byte language field is used to describe the language of the frame's content, according to ISO-639-2 [ISO-639-2]. All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt". If a frame is longer than it should be, e.g. having more fields than specified in this document, that indicates that additions to the frame have been made in a later version of the ID3 standard. This is reflected by the revision number in the header of the tag. 4. Declared ID3v2 frames The following frames are declared in this draft. 4.19 BUF Recommended buffer size 4.17 CNT Play counter 4.11 COM Comments 4.21 CRA Audio encryption 4.20 CRM Encrypted meta frame 4.6 ETC Event timing codes 4.13 EQU Equalization 4.16 GEO General encapsulated object 4.4 IPL Involved people list 4.22 LNK Linked information 4.5 MCI Music CD Identifier 4.7 MLL MPEG location lookup table 4.15 PIC Attached picture 4.18 POP Popularimeter 4.14 REV Reverb 4.12 RVA Relative volume adjustment 4.10 SLT Synchronized lyric/text 4.8 STC Synced tempo codes 4.2.1 TAL Album/Movie/Show title 4.2.1 TBP BPM (Beats Per Minute) 4.2.1 TCM Composer 4.2.1 TCO Content type 4.2.1 TCR Copyright message 4.2.1 TDA Date 4.2.1 TDY Playlist delay 4.2.1 TEN Encoded by 4.2.1 TFT File type 4.2.1 TIM Time 4.2.1 TKE Initial key 4.2.1 TLA Language(s) 4.2.1 TLE Length 4.2.1 TMT Media type 4.2.1 TOA Original artist(s)/performer(s) 4.2.1 TOF Original filename 4.2.1 TOL Original Lyricist(s)/text writer(s) 4.2.1 TOR Original release year 4.2.1 TOT Original album/Movie/Show title 4.2.1 TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group 4.2.1 TP2 Band/Orchestra/Accompaniment 4.2.1 TP3 Conductor/Performer refinement 4.2.1 TP4 Interpreted, remixed, or otherwise modified by 4.2.1 TPA Part of a set 4.2.1 TPB Publisher 4.2.1 TRC ISRC (International Standard Recording Code) 4.2.1 TRD Recording dates 4.2.1 TRK Track number/Position in set 4.2.1 TSI Size 4.2.1 TSS Software/hardware and settings used for encoding 4.2.1 TT1 Content group description 4.2.1 TT2 Title/Songname/Content description 4.2.1 TT3 Subtitle/Description refinement 4.2.1 TXT Lyricist/text writer 4.2.2 TXX User defined text information frame 4.2.1 TYE Year 4.1 UFI Unique file identifier 4.9 ULT Unsychronized lyric/text transcription 4.3.1 WAF Official audio file webpage 4.3.1 WAR Official artist/performer webpage 4.3.1 WAS Official audio source webpage 4.3.1 WCM Commercial information 4.3.1 WCP Copyright/Legal information 4.3.1 WPB Publishers official webpage 4.3.2 WXX User defined URL link frame 4.1. Unique file identifier This frame's purpose is to be able to identify the audio file in a database that may contain more information relevant to the content. Since standardisation of such a database is beyond this document, all frames begin with a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific database implementation. Questions regarding the database should be sent to the indicated email address. The URL should not be used for the actual database queries. If a $00 is found directly after the 'Frame size' the whole frame should be ignored, and preferably be removed. The 'Owner identifier' is then followed by the actual identifier, which may be up to 64 bytes. There may be more than one "UFI" frame in a tag, but only one with the same 'Owner identifier'. Unique file identifier "UFI" Frame size $xx xx xx Owner identifier <textstring> $00 Identifier <up to 64 bytes binary data> 4.2. Text information frames The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All the text information frames have the following format: Text information identifier "T00" - "TZZ" , excluding "TXX", described in 4.2.2. Frame size $xx xx xx Text encoding $xx Information <textstring> 4.2.1. Text information frames - details TT1 The 'Content group description' frame is used if the sound belongs to a larger category of sounds/music. For example, classical music is often sorted in different musical sections (e.g. "Piano Concerto", "Weather - Hurricane"). TT2 The 'Title/Songname/Content description' frame is the actual name of the piece (e.g. "Adagio", "Hurricane Donna"). TT3 The 'Subtitle/Description refinement' frame is used for information directly related to the contents title (e.g. "Op. 16" or "Performed live at wembley"). TP1 The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is used for the main artist(s). They are seperated with the "/" character. TP2 The 'Band/Orchestra/Accompaniment' frame is used for additional information about the performers in the recording. TP3 The 'Conductor' frame is used for the name of the conductor. TP4 The 'Interpreted, remixed, or otherwise modified by' frame contains more information about the people behind a remix and similar interpretations of another existing piece. TCM The 'Composer(s)' frame is intended for the name of the composer(s). They are seperated with the "/" character. TXT The 'Lyricist(s)/text writer(s)' frame is intended for the writer(s) of the text or lyrics in the recording. They are seperated with the "/" character. TLA The 'Language(s)' frame should contain the languages of the text or lyrics in the audio file. The language is represented with three characters according to ISO-639-2. If more than one language is used in the text their language codes should follow according to their usage. TCO The content type, which previously (in ID3v1.1, see appendix A) was stored as a one byte numeric value only, is now a numeric string. You may use one or several of the types as ID3v1.1 did or, since the category list would be impossible to maintain with accurate and up to date categories, define your own. References to the ID3v1 genres can be made by, as first byte, enter "(" followed by a number from the genres list (section A.3.) and ended with a ")" character. This is optionally followed by a refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be made in the same frame, e.g. "(51)(39)". If the refinement should begin with a "(" character it should be replaced with "((", e.g. "((I can figure out any genre)" or "(55)((I think...)". The following new content types is defined in ID3v2 and is implemented in the same way as the numerig content types, e.g. "(RX)". RX Remix CR Cover TAL The 'Album/Movie/Show title' frame is intended for the title of the recording(/source of sound) which the audio in the file is taken from. TPA The 'Part of a set' frame is a numeric string that describes which part of a set the audio came from. This frame is used if the source described in the "TAL" frame is divided into several mediums, e.g. a double CD. The value may be extended with a "/" character and a numeric string containing the total number of parts in the set. E.g. "1/2". TRK The 'Track number/Position in set' frame is a numeric string containing the order number of the audio-file on its original recording. This may be extended with a "/" character and a numeric string containing the total numer of tracks/elements on the original recording. E.g. "4/9". TRC The 'ISRC' frame should contian the International Standard Recording Code [ISRC]. TYE The 'Year' frame is a numeric string with a year of the recording. This frames is always four characters long (until the year 10000). TDA The 'Date' frame is a numeric string in the DDMM format containing the date for the recording. This field is always four characters long. TIM The 'Time' frame is a numeric string in the HHMM format containing the time for the recording. This field is always four characters long. TRD The 'Recording dates' frame is a intended to be used as complement to the "TYE", "TDA" and "TIM" frames. E.g. "4th-7th June, 12th June" in combination with the "TYE" frame. TMT The 'Media type' frame describes from which media the sound originated. This may be a textstring or a reference to the predefined media types found in the list below. References are made within "(" and ")" and are optionally followed by a text refinement, e.g. "(MC) with four channels". If a text refinement should begin with a "(" character it should be replaced with "((" in the same way as in the "TCO" frame. Predefined refinements is appended after the media type, e.g. "(CD/S)" or "(VID/PAL/VHS)". DIG Other digital media /A Analog transfer from media ANA Other analog media /WAC Wax cylinder /8CA 8-track tape cassette CD CD /A Analog transfer from media /DD DDD /AD ADD /AA AAD LD Laserdisc /A Analog transfer from media TT Turntable records /33 33.33 rpm /45 45 rpm /71 71.29 rpm /76 76.59 rpm /78 78.26 rpm /80 80 rpm MD MiniDisc /A Analog transfer from media DAT DAT /A Analog transfer from media /1 standard, 48 kHz/16 bits, linear /2 mode 2, 32 kHz/16 bits, linear /3 mode 3, 32 kHz/12 bits, nonlinear, low speed /4 mode 4, 32 kHz/12 bits, 4 channels /5 mode 5, 44.1 kHz/16 bits, linear /6 mode 6, 44.1 kHz/16 bits, 'wide track' play DCC DCC /A Analog transfer from media DVD DVD /A Analog transfer from media TV Television /PAL PAL /NTSC NTSC /SECAM SECAM VID Video /PAL PAL /NTSC NTSC /SECAM SECAM /VHS VHS /SVHS S-VHS /BETA BETAMAX RAD Radio /FM FM /AM AM /LW LW /MW MW TEL Telephone /I ISDN MC MC (normal cassette) /4 4.75 cm/s (normal speed for a two sided cassette) /9 9.5 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) REE Reel /9 9.5 cm/s /19 19 cm/s /38 38 cm/s /76 76 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) TFT The 'File type' frame indicates which type of audio this tag defines. The following type and refinements are defined: MPG MPEG Audio /1 MPEG 2 layer I /2 MPEG 2 layer II /3 MPEG 2 layer III /2.5 MPEG 2.5 /AAC Advanced audio compression but other types may be used, not for these types though. This is used in a similar way to the predefined types in the "TMT" frame, but without parenthesis. If this frame is not present audio type is assumed to be "MPG". TBP BPM is short for beats per minute, and is easily computed by dividing the number of beats in a musical piece with its length. To get a more accurate result, do the BPM calculation on the main-part only. To acquire best result measure the time between each beat and calculate individual BPM for each beat and use the median value as result. BPM is an integer and represented as a numerical string. TCR The 'Copyright message' frame, which must begin with a year and a space character (making five characters), is intended for the copyright holder of the original sound, not the audio file itself. The absence of this frame means only that the copyright information is unavailable or has been removed, and must not be interpreted to mean that the sound is public domain. Every time this field is displayed the field must be preceded with "Copyright " (C) " ", where (C) is one character showing a C in a circle. TPB The 'Publisher' frame simply contains the name of the label or publisher. TEN The 'Encoded by' frame contains the name of the person or organisation that encoded the audio file. This field may contain a copyright message, if the audio file also is copyrighted by the encoder. TSS The 'Software/hardware and settings used for encoding' frame includes the used audio encoder and its settings when the file was encoded. Hardware refers to hardware encoders, not the computer on which a program was run. TOF The 'Original filename' frame contains the preferred filename for the file, since some media doesn't allow the desired length of the filename. The filename is case sensitive and includes its suffix. TLE The 'Length' frame contains the length of the audiofile in milliseconds, represented as a numeric string. TSI The 'Size' frame contains the size of the audiofile in bytes excluding the tag, represented as a numeric string. TDY The 'Playlist delay' defines the numbers of milliseconds of silence between every song in a playlist. The player should use the "ETC" frame, if present, to skip initial silence and silence at the end of the audio to match the 'Playlist delay' time. The time is represented as a numeric string. TKE The 'Initial key' frame contains the musical key in which the sound starts. It is represented as a string with a maximum length of three characters. The ground keys are represented with "A","B","C","D","E", "F" and "G" and halfkeys represented with "b" and "#". Minor is represented as "m". Example "Cbm". Off key is represented with an "o" only. TOT The 'Original album/Movie/Show title' frame is intended for the title of the original recording(/source of sound), if for example the music in the file should be a cover of a previously released song. TOA The 'Original artist(s)/performer(s)' frame is intended for the performer(s) of the original recording, if for example the music in the file should be a cover of a previously released song. The performers are seperated with the "/" character. TOL The 'Original Lyricist(s)/text writer(s)' frame is intended for the text writer(s) of the original recording, if for example the music in the file should be a cover of a previously released song. The text writers are seperated with the "/" character. TOR The 'Original release year' frame is intended for the year when the original recording, if for example the music in the file should be a cover of a previously released song, was released. The field is formatted as in the "TDY" frame. 4.2.2. User defined text information frame This frame is intended for one-string text information concerning the audiofile in a similar way to the other "T"xx frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual string. There may be more than one "TXX" frame in each tag, but only one with the same description. User defined... "TXX" Frame size $xx xx xx Text encoding $xx Description <textstring> $00 (00) Value <textstring> 4.3. URL link frames With these frames dynamic data such as webpages with touring information, price information or plain ordinary news can be added to the tag. There may only be one URL [URL] link frame of its kind in an tag, except when stated otherwise in the frame description. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All URL link frames have the following format: URL link frame "W00" - "WZZ" , excluding "WXX" (described in 4.3.2.) Frame size $xx xx xx URL <textstring> 4.3.1. URL link frames - details WAF The 'Official audio file webpage' frame is a URL pointing at a file specific webpage. WAR The 'Official artist/performer webpage' frame is a URL pointing at the artists official webpage. There may be more than one "WAR" frame in a tag if the audio contains more than one performer. WAS The 'Official audio source webpage' frame is a URL pointing at the official webpage for the source of the audio file, e.g. a movie. WCM The 'Commercial information' frame is a URL pointing at a webpage with information such as where the album can be bought. There may be more than one "WCM" frame in a tag. WCP The 'Copyright/Legal information' frame is a URL pointing at a webpage where the terms of use and ownership of the file is described. WPB The 'Publishers official webpage' frame is a URL pointing at the official wepage for the publisher. 4.3.2. User defined URL link frame This frame is intended for URL [URL] links concerning the audiofile in a similar way to the other "W"xx frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual URL. The URL is always encoded with ISO-8859-1 [ISO-8859-1]. There may be more than one "WXX" frame in each tag, but only one with the same description. User defined... "WXX" Frame size $xx xx xx Text encoding $xx Description <textstring> $00 (00) URL <textstring> 4.4. Involved people list Since there might be a lot of people contributing to an audio file in various ways, such as musicians and technicians, the 'Text information frames' are often insufficient to list everyone involved in a project. The 'Involved people list' is a frame containing the names of those involved, and how they were involved. The body simply contains a terminated string with the involvement directly followed by a terminated string with the involvee followed by a new involvement and so on. There may only be one "IPL" frame in each tag. Involved people list "IPL" Frame size $xx xx xx Text encoding $xx People list strings <textstrings> 4.5. Music CD Identifier This frame is intended for music that comes from a CD, so that the CD can be identified in databases such as the CDDB [CDDB]. The frame consists of a binary dump of the Table Of Contents, TOC, from the CD, which is a header of 4 bytes and then 8 bytes/track on the CD making a maximum of 804 bytes. This frame requires a present and valid "TRK" frame. There may only be one "MCI" frame in each tag. Music CD identifier "MCI" Frame size $xx xx xx CD TOC <binary data> 4.6. Event timing codes This frame allows synchronisation with key events in a song or sound. The head is: Event timing codes "ETC" Frame size $xx xx xx Time stamp format $xx Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. Followed by a list of key events in the following format: Type of event $xx Time stamp $xx (xx ...) The 'Time stamp' is set to zero if directly at the beginning of the sound or after the previous event. All events should be sorted in chronological order. The type of event is as follows: $00 padding (has no meaning) $01 end of initial silence $02 intro start $03 mainpart start $04 outro start $05 outro end $06 verse begins $07 refrain begins $08 interlude $09 theme start $0A variation $0B key change $0C time change $0D unwanted noise (Snap, Crackle & Pop) $0E-$DF reserved for future use $E0-$EF not predefined sync 0-F $F0-$FC reserved for future use $FD audio end (start of silence) $FE audio file ends $FF one more byte of events follows (all the following bytes with the value $FF have the same function) The 'Not predefined sync's ($E0-EF) are for user events. You might want to synchronise your music to something, like setting of an explosion on-stage, turning on your screensaver etc. There may only be one "ETC" frame in each tag. 4.7. MPEG location lookup table To increase performance and accuracy of jumps within a MPEG [MPEG] audio file, frames with timecodes in different locations in the file might be useful. The ID3 frame includes references that the software can use to calculate positions in the file. After the frame header is a descriptor of how much the 'frame counter' should increase for every reference. If this value is two then the first reference points out the second frame, the 2nd reference the 4th frame, the 3rd reference the 6th frame etc. In a similar way the 'bytes between reference' and 'milliseconds between reference' points out bytes and milliseconds respectively. Each reference consists of two parts; a certain number of bits, as defined in 'bits for bytes deviation', that describes the difference between what is said in 'bytes between reference' and the reality and a certain number of bits, as defined in 'bits for milliseconds deviation', that describes the difference between what is said in 'milliseconds between reference' and the reality. The number of bits in every reference, i.e. 'bits for bytes deviation'+'bits for milliseconds deviation', must be a multiple of four. There may only be one "MLL" frame in each tag. Location lookup table "MLL" ID3 frame size $xx xx xx MPEG frames between reference $xx xx Bytes between reference $xx xx xx Milliseconds between reference $xx xx xx Bits for bytes deviation $xx Bits for milliseconds dev. $xx Then for every reference the following data is included; Deviation in bytes %xxx.... Deviation in milliseconds %xxx.... 4.8. Synced tempo codes For a more accurate description of the tempo of a musical piece this frame might be used. After the header follows one byte describing which time stamp format should be used. Then follows one or more tempo codes. Each tempo code consists of one tempo part and one time part. The tempo is in BPM described with one or two bytes. If the first byte has the value $FF, one more byte follows, which is added to the first giving a range from 2 - 510 BPM, since $00 and $01 is reserved. $00 is used to describe a beat-free time period, which is not the same as a music-free time period. $01 is used to indicate one single beat-stroke followed by a beat-free period. The tempo descriptor is followed by a time stamp. Every time the tempo in the music changes, a tempo descriptor may indicate this for the player. All tempo descriptors should be sorted in chronological order. The first beat-stroke in a time-period is at the same time as the beat description occurs. There may only be one "STC" frame in each tag. Synced tempo codes "STC" Frame size $xx xx xx Time stamp format $xx Tempo data <binary data> Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. 4.9. Unsychronised lyrics/text transcription This frame contains the lyrics of the song or a text transcription of other vocal activities. The head includes an encoding descriptor and a content descriptor. The body consists of the actual text. The 'Content descriptor' is a terminated string. If no descriptor is entered, 'Content descriptor' is $00 (00) only. Newline characters are allowed in the text. Maximum length for the descriptor is 64 bytes. There may be more than one lyrics/text frame in each tag, but only one with the same language and content descriptor. Unsynced lyrics/text "ULT" Frame size $xx xx xx Text encoding $xx Language $xx xx xx Content descriptor <textstring> $00 (00) Lyrics/text <textstring> 4.10. Synchronised lyrics/text This is another way of incorporating the words, said or sung lyrics, in the audio file as text, this time, however, in sync with the audio. It might also be used to describing events e.g. occurring on a stage or on the screen in sync with the audio. The header includes a content descriptor, represented with as terminated textstring. If no descriptor is entered, 'Content descriptor' is $00 (00) only. Synced lyrics/text "SLT" Frame size $xx xx xx Text encoding $xx Language $xx xx xx Time stamp format $xx Content type $xx Content descriptor <textstring> $00 (00) Encoding: $00 ISO-8859-1 [ISO-8859-1] character set is used => $00 is sync identifier. $01 Unicode [UNICODE] character set is used => $00 00 is sync identifier. Content type: $00 is other $01 is lyrics $02 is text transcription $03 is movement/part name (e.g. "Adagio") $04 is events (e.g. "Don Quijote enters the stage") $05 is chord (e.g. "Bb F Fsus") Time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. The text that follows the frame header differs from that of the unsynchronised lyrics/text transcription in one major way. Each syllable (or whatever size of text is considered to be convenient by the encoder) is a null terminated string followed by a time stamp denoting where in the sound file it belongs. Each sync thus has the following structure: Terminated text to be synced (typically a syllable) Sync identifier (terminator to above string) $00 (00) Time stamp $xx (xx ...) The 'time stamp' is set to zero or the whole sync is omitted if located directly at the beginning of the sound. All time stamps should be sorted in chronological order. The sync can be considered as a validator of the subsequent string. Newline characters are allowed in all "SLT" frames and should be used after every entry (name, event etc.) in a frame with the content type $03 - $04. A few considerations regarding whitespace characters: Whitespace separating words should mark the beginning of a new word, thus occurring in front of the first syllable of a new word. This is also valid for new line characters. A syllable followed by a comma should not be broken apart with a sync (both the syllable and the comma should be before the sync). An example: The "ULT" passage "Strangers in the night" $0A "Exchanging glances" would be "SLT" encoded as: "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx xx "glan" $00 xx xx "ces" $00 xx xx There may be more than one "SLT" frame in each tag, but only one with the same language and content descriptor. 4.11. Comments This frame replaces the old 30-character comment field in ID3v1. It consists of a frame head followed by encoding, language and content descriptors and is ended with the actual comment as a text string. Newline characters are allowed in the comment text string. There may be more than one comment frame in each tag, but only one with the same language and content descriptor. Comment "COM" Frame size $xx xx xx Text encoding $xx Language $xx xx xx Short content description <textstring> $00 (00) The actual text <textstring> 4.12. Relative volume adjustment This is a more subjective function than the previous ones. It allows the user to say how much he wants to increase/decrease the volume on each channel while the file is played. The purpose is to be able to align all files to a reference volume, so that you don't have to change the volume constantly. This frame may also be used to balance adjust the audio. If the volume peak levels are known then this could be described with the 'Peak volume right' and 'Peak volume left' field. If Peakvolume is not known these fields could be left zeroed or completely omitted. There may only be one "RVA" frame in each tag. Relative volume adjustment "RVA" Frame size $xx xx xx Increment/decrement %000000xx Bits used for volume descr. $xx Relative volume change, right $xx xx (xx ...) Relative volume change, left $xx xx (xx ...) Peak volume right $xx xx (xx ...) Peak volume left $xx xx (xx ...) In the increment/decrement field bit 0 is used to indicate the right channel and bit 1 is used to indicate the left channel. 1 is increment and 0 is decrement. The 'bits used for volume description' field is normally $10 (16 bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be $00. The volume is always represented with whole bytes, padded in the beginning (highest bits) when 'bits used for volume description' is not a multiple of eight. 4.13. Equalisation This is another subjective, alignment frame. It allows the user to predefine an equalisation curve within the audio file. There may only be one "EQU" frame in each tag. Equalisation "EQU" Frame size $xx xx xx Adjustment bits $xx The 'adjustment bits' field defines the number of bits used for representation of the adjustment. This is normally $10 (16 bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be $00. This is followed by 2 bytes + ('adjustment bits' rounded up to the nearest byte) for every equalisation band in the following format, giving a frequency range of 0 - 32767Hz: Increment/decrement %x (MSB of the Frequency) Frequency (lower 15 bits) Adjustment $xx (xx ...) The increment/decrement bit is 1 for increment and 0 for decrement. The equalisation bands should be ordered increasingly with reference to frequency. All frequencies don't have to be declared. Adjustments with the value $00 should be omitted. A frequency should only be described once in the frame. 4.14. Reverb Yet another subjective one. You may here adjust echoes of different kinds. Reverb left/right is the delay between every bounce in ms. Reverb bounces left/right is the number of bounces that should be made. $FF equals an infinite number of bounces. Feedback is the amount of volume that should be returned to the next echo bounce. $00 is 0%, $FF is 100%. If this value were $7F, there would be 50% volume reduction on the first bounce, yet 50% on the second and so on. Left to left means the sound from the left bounce to be played in the left speaker, while left to right means sound from the left bounce to be played in the right speaker. 'Premix left to right' is the amount of left sound to be mixed in the right before any reverb is applied, where $00 id 0% and $FF is 100%. 'Premix right to left' does the same thing, but right to left. Setting both premix to $FF would result in a mono output (if the reverb is applied symmetric). There may only be one "REV" frame in each tag. Reverb settings "REV" Frame size $00 00 0C Reverb left (ms) $xx xx Reverb right (ms) $xx xx Reverb bounces, left $xx Reverb bounces, right $xx Reverb feedback, left to left $xx Reverb feedback, left to right $xx Reverb feedback, right to right $xx Reverb feedback, right to left $xx Premix left to right $xx Premix right to left $xx 4.15. Attached picture This frame contains a picture directly related to the audio file. Image format is preferably "PNG" [PNG] or "JPG" [JFIF]. Description is a short description of the picture, represented as a terminated textstring. The description has a maximum length of 64 characters, but may be empty. There may be several pictures attached to one file, each in their individual "PIC" frame, but only one with the same content descriptor. There may only be one picture with the picture type declared as picture type $01 and $02 respectively. There is a possibility to put only a link to the image file by using the 'image format' "-->" and having a complete URL [URL] instead of picture data. The use of linked files should however be used restrictively since there is the risk of separation of files. Attached picture "PIC" Frame size $xx xx xx Text encoding $xx Image format $xx xx xx Picture type $xx Description <textstring> $00 (00) Picture data <binary data> Picture type: $00 Other $01 32x32 pixels 'file icon' (PNG only) $02 Other file icon $03 Cover (front) $04 Cover (back) $05 Leaflet page $06 Media (e.g. lable side of CD) $07 Lead artist/lead performer/soloist $08 Artist/performer $09 Conductor $0A Band/Orchestra $0B Composer $0C Lyricist/text writer $0D Recording Location $0E During recording $0F During performance $10 Movie/video screen capture $11 A bright coloured fish $12 Illustration $13 Band/artist logotype $14 Publisher/Studio logotype 4.16. General encapsulated object In this frame any type of file can be encapsulated. After the header, 'Frame size' and 'Encoding' follows 'MIME type' [MIME] and 'Filename' for the encapsulated object, both represented as terminated strings encoded with ISO 8859-1 [ISO-8859-1]. The filename is case sensitive. Then follows a content description as terminated string, encoded as 'Encoding'. The last thing in the frame is the actual object. The first two strings may be omitted, leaving only their terminations. MIME type is always an ISO-8859-1 text string. There may be more than one "GEO" frame in each tag, but only one with the same content descriptor. General encapsulated object "GEO" Frame size $xx xx xx Text encoding $xx MIME type <textstring> $00 Filename <textstring> $00 (00) Content description <textstring> $00 (00) Encapsulated object <binary data> 4.17. Play counter This is simply a counter of the number of times a file has been played. The value is increased by one every time the file begins to play. There may only be one "CNT" frame in each tag. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger. The counter must be at least 32-bits long to begin with. Play counter "CNT" Frame size $xx xx xx Counter $xx xx xx xx (xx ...) 4.18. Popularimeter The purpose of this frame is to specify how good an audio file is. Many interesting applications could be found to this frame such as a playlist that features better audiofiles more often than others or it could be used to profile a persons taste and find other 'good' files by comparing people's profiles. The frame is very simple. It contains the email address to the user, one rating byte and a four byte play counter, intended to be increased with one for every time the file is played. The email is a terminated string. The rating is 1-255 where 1 is worst and 255 is best. 0 is unknown. If no personal counter is wanted it may be omitted. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger in the same away as the play counter ("CNT"). There may be more than one "POP" frame in each tag, but only one with the same email address. Popularimeter "POP" Frame size $xx xx xx Email to user <textstring> $00 Rating $xx Counter $xx xx xx xx (xx ...) 4.19. Recommended buffer size Sometimes the server from which a audio file is streamed is aware of transmission or coding problems resulting in interruptions in the audio stream. In these cases, the size of the buffer can be recommended by the server using this frame. If the 'embedded info flag' is true (1) then this indicates that an ID3 tag with the maximum size described in 'Buffer size' may occur in the audiostream. In such case the tag should reside between two MPEG [MPEG] frames, if the audio is MPEG encoded. If the position of the next tag is known, 'offset to next tag' may be used. The offset is calculated from the end of tag in which this frame resides to the first byte of the header in the next. This field may be omitted. Embedded tags is currently not recommended since this could render unpredictable behaviour from present software/hardware. The 'Buffer size' should be kept to a minimum. There may only be one "BUF" frame in each tag. Recommended buffer size "BUF" Frame size $xx xx xx Buffer size $xx xx xx Embedded info flag %0000000x Offset to next tag $xx xx xx xx 4.20. Encrypted meta frame This frame contains one or more encrypted frames. This enables protection of copyrighted information such as pictures and text, that people might want to pay extra for. Since standardisation of such an encryption scheme is beyond this document, all "CRM" frames begin with a terminated string with a URL [URL] containing an email address, or a link to a location where an email adress can be found, that belongs to the organisation responsible for this specific encrypted meta frame. Questions regarding the encrypted frame should be sent to the indicated email address. If a $00 is found directly after the 'Frame size', the whole frame should be ignored, and preferably be removed. The 'Owner identifier' is then followed by a short content description and explanation as to why it's encrypted. After the 'content/explanation' description, the actual encrypted block follows. When an ID3v2 decoder encounters a "CRM" frame, it should send the datablock to the 'plugin' with the corresponding 'owner identifier' and expect to receive either a datablock with one or several ID3v2 frames after each other or an error. There may be more than one "CRM" frames in a tag, but only one with the same 'owner identifier'. Encrypted meta frame "CRM" Frame size $xx xx xx Owner identifier <textstring> $00 (00) Content/explanation <textstring> $00 (00) Encrypted datablock <binary data> 4.21. Audio encryption This frame indicates if the actual audio stream is encrypted, and by whom. Since standardisation of such encrypion scheme is beyond this document, all "CRA" frames begin with a terminated string with a URL containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific encrypted audio file. Questions regarding the encrypted audio should be sent to the email address specified. If a $00 is found directly after the 'Frame size' and the audiofile indeed is encrypted, the whole file may be considered useless. After the 'Owner identifier', a pointer to an unencrypted part of the audio can be specified. The 'Preview start' and 'Preview length' is described in frames. If no part is unencrypted, these fields should be left zeroed. After the 'preview length' field follows optionally a datablock required for decryption of the audio. There may be more than one "CRA" frames in a tag, but only one with the same 'Owner identifier'. Audio encryption "CRA" Frame size $xx xx xx Owner identifier <textstring> $00 (00) Preview start $xx xx Preview length $xx xx Encryption info <binary data> 4.22. Linked information To keep space waste as low as possible this frame may be used to link information from another ID3v2 tag that might reside in another audio file or alone in a binary file. It is recommended that this method is only used when the files are stored on a CD-ROM or other circumstances when the risk of file seperation is low. The frame contains a frame identifier, which is the frame that should be linked into this tag, a URL [URL] field, where a reference to the file where the frame is given, and additional ID data, if needed. Data should be retrieved from the first tag found in the file to which this link points. There may be more than one "LNK" frame in a tag, but only one with the same contents. A linked frame is to be considered as part of the tag and has the same restrictions as if it was a physical part of the tag (i.e. only one "REV" frame allowed, whether it's linked or not). Linked information "LNK" Frame size $xx xx xx Frame identifier $xx xx xx URL <textstring> $00 (00) Additional ID data <textstring(s)> Frames that may be linked and need no additional data are "IPL", "MCI", "ETC", "LLT", "STC", "RVA", "EQU", "REV", "BUF", the text information frames and the URL link frames. The "TXX", "PIC", "GEO", "CRM" and "CRA" frames may be linked with the content descriptor as additional ID data. The "COM", "SLT" and "ULT" frames may be linked with three bytes of language descriptor directly followed by a content descriptor as additional ID data. 5. The 'unsynchronisation scheme' The only purpose of the 'unsychronisation scheme' is to make the ID3v2 tag as compatible as possible with existing software. There is no use in 'unsynchronising' tags if the file is only to be processed by new software. Unsynchronisation may only be made with MPEG 2 layer I, II and III and MPEG 2.5 files. Whenever a false synchronisation is found within the tag, one zeroed byte is inserted after the first false synchronisation byte. The format of a correct sync that should be altered by ID3 encoders is as follows: %11111111 111xxxxx And should be replaced with: %11111111 00000000 111xxxxx This has the side effect that all $FF 00 combinations have to be altered, so they won't be affected by the decoding process. Therefore all the $FF 00 combinations have to be replaced with the $FF 00 00 combination during the unsynchonisation. To indicate usage of the unsynchronisation, the first bit in 'ID3 flags' should be set. This bit should only be set if the tag contained a, now corrected, false synchronisation. The bit should only be clear if the tag does not contain any false synchronisations. Do bear in mind, that if a compression scheme is used by the encoder, the unsyncronisation scheme should be applied *afterwards*. When decoding a compressed, 'unsyncronised' file, the 'unsyncronisation scheme' should be parsed first, compression afterwards. 6. Copyright Copyright (C) Martin Nilsson 1998. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that a reference to this document is included on all such copies and derivative works. However, this document itself may not be modified in any way and reissued as the original document. The limited permissions granted above are perpetual and will not be revoked. This document and the information contained herein is provided on an "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 7. References [CDDB] Compact Disc Data Base <url:http://www.cddb.com> [ISO-639-2] ISO/FDIS 639-2. Codes for the representation of names of languages, Part 2: Alpha-3 code. Technical committee / subcommittee: TC 37 / SC 2 [ISO-8859-1] ISO/IEC DIS 8859-1. 8-bit single-byte coded graphic character sets, Part 1: Latin alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 [ISRC] ISO 3901:1986 International Standard Recording Code (ISRC). Technical committee / subcommittee: TC 46 / SC 9 [JFIF] JPEG File Interchange Format, version 1.02 <url:http://www.w3.org/Graphics/JPEG/jfif.txt> [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC 2045, November 1996. <url:ftp://ftp.isi.edu/in-notes/rfc2045.txt> [MPEG] ISO/IEC 11172-3:1993. Coding of moving pictures and associated audio for digital storage media at up to about 1,5 Mbit/s, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC 13818-3:1995 Generic coding of moving pictures and associated audio information, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC DIS 13818-3 Generic coding of moving pictures and associated audio information, Part 3: Audio (Revision of ISO/IEC 13818-3:1995) [PNG] Portable Network Graphics, version 1.0 <url:http://www.w3.org/TR/REC-png-multi.html> [UNICODE] ISO/IEC 10646-1:1993. Universal Multiple-Octet Coded Character Set (UCS), Part 1: Architecture and Basic Multilingual Plane. Technical committee / subcommittee: JTC 1 / SC 2 <url:http://www.unicode.org> [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource Locators (URL).", RFC 1738, December 1994. <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt> 8. Appendix A. Appendix A - ID3-Tag Specification V1.1 ID3-Tag Specification V1.1 (12 dec 1997) by Michael Mutschler <amiga2@info2.rus.uni-stuttgart.de>, edited for space and clarity reasons. A.1. Overview The ID3-Tag is an information field for MPEG Layer 3 audio files. Since a standalone MP3 doesn't provide a method of storing other information than those directly needed for replay reasons, the ID3-tag was invented by Eric Kemp in 1996. A revision from ID3v1 to ID3v1.1 was made by Michael Mutschler to support track number information is described in A.4. A.2. ID3v1 Implementation The Information is stored in the last 128 bytes of an MP3. The Tag has got the following fields, and the offsets given here, are from 0-127. Field Length Offsets Tag 3 0-2 Songname 30 3-32 Artist 30 33-62 Album 30 63-92 Year 4 93-96 Comment 30 97-126 Genre 1 127 The string-fields contain ASCII-data, coded in ISO-Latin 1 codepage. Strings which are smaller than the field length are padded with zero- bytes. Tag: The tag is valid if this field contains the string "TAG". This has to be uppercase! Songname: This field contains the title of the MP3 (string as above). Artist: This field contains the artist of the MP3 (string as above). Album: this field contains the album where the MP3 comes from (string as above). Year: this field contains the year when this song has originally been released (string as above). Comment: this field contains a comment for the MP3 (string as above). Revision to this field has been made in ID3v1.1. See A.4. Genre: this byte contains the offset of a genre in a predefined list the byte is treated as an unsigned byte. The offset is starting from 0. See A.3. A.3. Genre List The following genres is defined in ID3v1 0.Blues 1.Classic Rock 2.Country 3.Dance 4.Disco 5.Funk 6.Grunge 7.Hip-Hop 8.Jazz 9.Metal 10.New Age 11.Oldies 12.Other 13.Pop 14.R&B 15.Rap 16.Reggae 17.Rock 18.Techno 19.Industrial 20.Alternative 21.Ska 22.Death Metal 23.Pranks 24.Soundtrack 25.Euro-Techno 26.Ambient 27.Trip-Hop 28.Vocal 29.Jazz+Funk 30.Fusion 31.Trance 32.Classical 33.Instrumental 34.Acid 35.House 36.Game 37.Sound Clip 38.Gospel 39.Noise 40.AlternRock 41.Bass 42.Soul 43.Punk 44.Space 45.Meditative 46.Instrumental Pop 47.Instrumental Rock 48.Ethnic 49.Gothic 50.Darkwave 51.Techno-Industrial 52.Electronic 53.Pop-Folk 54.Eurodance 55.Dream 56.Southern Rock 57.Comedy 58.Cult 59.Gangsta 60.Top 40 61.Christian Rap 62.Pop/Funk 63.Jungle 64.Native American 65.Cabaret 66.New Wave 67.Psychadelic 68.Rave 69.Showtunes 70.Trailer 71.Lo-Fi 72.Tribal 73.Acid Punk 74.Acid Jazz 75.Polka 76.Retro 77.Musical 78.Rock & Roll 79.Hard Rock The following genres are Winamp extensions 80.Folk 81.Folk-Rock 82.National Folk 83.Swing 84.Fast Fusion 85.Bebob 86.Latin 87.Revival 88.Celtic 89.Bluegrass 90.Avantgarde 91.Gothic Rock 92.Progressive Rock 93.Psychedelic Rock 94.Symphonic Rock 95.Slow Rock 96.Big Band 97.Chorus 98.Easy Listening 99.Acoustic 100.Humour 101.Speech 102.Chanson 103.Opera 104.Chamber Music 105.Sonata 106.Symphony 107.Booty Bass 108.Primus 109.Porn Groove 110.Satire 111.Slow Jam 112.Club 113.Tango 114.Samba 115.Folklore 116.Ballad 117.Power Ballad 118.Rhythmic Soul 119.Freestyle 120.Duet 121.Punk Rock 122.Drum Solo 123.A capella 124.Euro-House 125.Dance Hall A.4. Track addition - ID3v1.1 In ID3v1.1, Michael Mutschler revised the specification of the comment field in order to implement the track number. The new format of the comment field is a 28 character string followed by a mandatory null ($00) character and the original album tracknumber stored as an unsigned byte-size integer. In such cases where the 29th byte is not the null character or when the 30th is a null character, the tracknumber is to be considered undefined. 9. Author's Address Martin Nilsson Rydsvgen 246 C. 30 S-584 34 Linkping Sweden Email: nilsson@id3.org Co-authors: Johan Sundstrm Email: johan@id3.org �����������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2.3.0.txt��������������������������������������������������������0000664�0000000�0000000�00000226714�14662262111�0020102�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Informal standard M. Nilsson Document: id3v2.3.0.txt 3rd February 1999 ID3 tag version 2.3.0 Status of this document This document is an informal standard and replaces the ID3v2.2.0 standard [ID3v2]. The informal standard is released so that implementors could have a set standard before a formal standard is set. The formal standard will use another version or revision number if not identical to what is described in this document. The contents in this document may change for clarifications but never for added or altered functionallity. Distribution of this document is unlimited. Abstract This document describes the ID3v2.3.0, which is a more developed version of the ID3v2 informal standard [ID3v2] (version 2.2.0), evolved from the ID3 tagging system. The ID3v2 offers a flexible way of storing information about an audio file within itself to determine its origin and contents. The information may be technical information, such as equalisation curves, as well as related meta information, such as title, performer, copyright etc. 1. Table of contents 2. Conventions in this document 3. ID3v2 overview 3.1. ID3v2 header 3.2. ID3v2 extended header 3.3. ID3v2 frames overview 3.3.1. Frame header flags 3.3.2. Default flags 4. Declared ID3v2 frames 4.1. Unique file identifier 4.2. Text information frames 4.2.1. Text information frames - details 4.2.2. User defined text information frame 4.3. URL link frames 4.3.1. URL link frames - details 4.3.2. User defined URL link frame 4.4. Involved people list 4.5. Music CD Identifier 4.6. Event timing codes 4.7. MPEG location lookup table 4.8. Synced tempo codes 4.9. Unsychronised lyrics/text transcription 4.10. Synchronised lyrics/text 4.11. Comments 4.12. Relative volume adjustment 4.13. Equalisation 4.14. Reverb 4.15. Attached picture 4.16. General encapsulated object 4.17. Play counter 4.18. Popularimeter 4.19. Recommended buffer size 4.20. Audio encryption 4.21. Linked information 4.22. Position synchronisation frame 4.23. Terms of use 4.24. Ownership frame 4.25. Commercial frame 4.26. Encryption method registration 4.27. Group identification registration 4.28. Private frame 5. The 'unsynchronisation scheme' 6. Copyright 7. References 8. Appendix A. Appendix A - Genre List from ID3v1 9. Author's Address 2. Conventions in this document In the examples, text within "" is a text string exactly as it appears in a file. Numbers preceded with $ are hexadecimal and numbers preceded with % are binary. $xx is used to indicate a byte with unknown content. %x is used to indicate a bit with unknown content. The most significant bit (MSB) of a byte is called 'bit 7' and the least significant bit (LSB) is called 'bit 0'. A tag is the whole tag described in this document. A frame is a block of information in the tag. The tag consists of a header, frames and optional padding. A field is a piece of information; one value, a string etc. A numeric string is a string that consists of the characters 0-9 only. 3. ID3v2 overview The two biggest design goals were to be able to implement ID3v2 without disturbing old software too much and that ID3v2 should be as flexible and expandable as possible. The first criterion is met by the simple fact that the MPEG [MPEG] decoding software uses a syncsignal, embedded in the audiostream, to 'lock on to' the audio. Since the ID3v2 tag doesn't contain a valid syncsignal, no software will attempt to play the tag. If, for any reason, coincidence make a syncsignal appear within the tag it will be taken care of by the 'unsynchronisation scheme' described in section 5. The second criterion has made a more noticeable impact on the design of the ID3v2 tag. It is constructed as a container for several information blocks, called frames, whose format need not be known to the software that encounters them. At the start of every frame there is an identifier that explains the frames' format and content, and a size descriptor that allows software to skip unknown frames. If a total revision of the ID3v2 tag should be needed, there is a version number and a size descriptor in the ID3v2 header. The ID3 tag described in this document is mainly targeted at files encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III and MPEG-2.5, but may work with other types of encoded audio. The bitorder in ID3v2 is most significant bit first (MSB). The byteorder in multibyte numbers is most significant byte first (e.g. $12345678 would be encoded $12 34 56 78). It is permitted to include padding after all the final frame (at the end of the ID3 tag), making the size of all the frames together smaller than the size given in the head of the tag. A possible purpose of this padding is to allow for adding a few additional frames or enlarge existing frames within the tag without having to rewrite the entire file. The value of the padding bytes must be $00. 3.1. ID3v2 header The ID3v2 tag header, which should be the first information in the file, is 10 bytes as follows: ID3v2/file identifier "ID3" ID3v2 version $03 00 ID3v2 flags %abc00000 ID3v2 size 4 * %0xxxxxxx The first three bytes of the tag are always "ID3" to indicate that this is an ID3v2 tag, directly followed by the two version bytes. The first byte of ID3v2 version is it's major version, while the second byte is its revision number. In this case this is ID3v2.3.0. All revisions are backwards compatible while major versions are not. If software with ID3v2.2.0 and below support should encounter version three or higher it should simply ignore the whole tag. Version and revision will never be $FF. The version is followed by one the ID3v2 flags field, of which currently only three flags are used. a - Unsynchronisation Bit 7 in the 'ID3v2 flags' indicates whether or not unsynchronisation is used (see section 5 for details); a set bit indicates usage. b - Extended header The second bit (bit 6) indicates whether or not the header is followed by an extended header. The extended header is described in section 3.2. c - Experimental indicator The third bit (bit 5) should be used as an 'experimental indicator'. This flag should always be set when the tag is in an experimental stage. All the other flags should be cleared. If one of these undefined flags are set that might mean that the tag is not readable for a parser that does not know the flags function. The ID3v2 tag size is encoded with four bytes where the most significant bit (bit 7) is set to zero in every byte, making a total of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is represented as $00 00 02 01. The ID3v2 tag size is the size of the complete tag after unsychronisation, including padding, excluding the header but not excluding the extended header (total tag size - 10). Only 28 bits (representing up to 256MB) are used in the size description to avoid the introducuction of 'false syncsignals'. An ID3v2 tag can be detected with the following pattern: $49 44 33 yy yy xx zz zz zz zz Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80. 3.2. ID3v2 extended header The extended header contains information that is not vital to the correct parsing of the tag information, hence the extended header is optional. Extended header size $xx xx xx xx Extended Flags $xx xx Size of padding $xx xx xx xx Where the 'Extended header size', currently 6 or 10 bytes, excludes itself. The 'Size of padding' is simply the total tag size excluding the frames and the headers, in other words the padding. The extended header is considered separate from the header proper, and as such is subject to unsynchronisation. The extended flags are a secondary flag set which describes further attributes of the tag. These attributes are currently defined as follows %x0000000 00000000 x - CRC data present If this flag is set four bytes of CRC-32 data is appended to the extended header. The CRC should be calculated before unsynchronisation on the data between the extended header and the padding, i.e. the frames and only the frames. Total frame CRC $xx xx xx xx 3.3. ID3v2 frame overview As the tag consists of a tag header and a tag body with one or more frames, all the frames consists of a frame header followed by one or more fields containing the actual information. The layout of the frame header: Frame ID $xx xx xx xx (four characters) Size $xx xx xx xx Flags $xx xx The frame ID made out of the characters capital A-Z and 0-9. Identifiers beginning with "X", "Y" and "Z" are for experimental use and free for everyone to use, without the need to set the experimental bit in the tag header. Have in mind that someone else might have used the same identifier as you. All other identifiers are either used or reserved for future use. The frame ID is followed by a size descriptor, making a total header size of ten bytes in every frame. The size is calculated as frame size excluding frame header (frame size - 10). In the frame header the size descriptor is followed by two flags bytes. These flags are described in section 3.3.1. There is no fixed order of the frames' appearance in the tag, although it is desired that the frames are arranged in order of significance concerning the recognition of the file. An example of such order: UFID, TIT2, MCDI, TRCK ... A tag must contain at least one frame. A frame must be at least 1 byte big, excluding the header. If nothing else is said a string is represented as ISO-8859-1 [ISO-8859-1] characters in the range $20 - $FF. Such strings are represented as <text string>, or <full text string> if newlines are allowed, in the frame descriptions. All Unicode strings [UNICODE] use 16-bit unicode 2.0 (ISO/IEC 10646-1:1993, UCS-2). Unicode strings must begin with the Unicode BOM ($FF FE or $FE FF) to identify the byte order. All numeric strings and URLs [URL] are always encoded as ISO-8859-1. Terminated strings are terminated with $00 if encoded with ISO-8859-1 and $00 00 if encoded as unicode. If nothing else is said newline character is forbidden. In ISO-8859-1 a new line is represented, when allowed, with $0A only. Frames that allow different types of text encoding have a text encoding description byte directly after the frame size. If ISO-8859-1 is used this byte should be $00, if Unicode is used it should be $01. Strings dependent on encoding is represented as <text string according to encoding>, or <full text string according to encoding> if newlines are allowed. Any empty Unicode strings which are NULL-terminated may have the Unicode BOM followed by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00). The three byte language field is used to describe the language of the frame's content, according to ISO-639-2 [ISO-639-2]. All URLs [URL] may be relative, e.g. "picture.png", "../doc.txt". If a frame is longer than it should be, e.g. having more fields than specified in this document, that indicates that additions to the frame have been made in a later version of the ID3v2 standard. This is reflected by the revision number in the header of the tag. 3.3.1. Frame header flags In the frame header the size descriptor is followed by two flags bytes. All unused flags must be cleared. The first byte is for 'status messages' and the second byte is for encoding purposes. If an unknown flag is set in the first byte the frame may not be changed without the bit cleared. If an unknown flag is set in the second byte it is likely to not be readable. The flags field is defined as follows. %abc00000 %ijk00000 a - Tag alter preservation This flag tells the software what to do with this frame if it is unknown and the tag is altered in any way. This applies to all kinds of alterations, including adding more padding and reordering the frames. 0 Frame should be preserved. 1 Frame should be discarded. b - File alter preservation This flag tells the software what to do with this frame if it is unknown and the file, excluding the tag, is altered. This does not apply when the audio is completely replaced with other audio data. 0 Frame should be preserved. 1 Frame should be discarded. c - Read only This flag, if set, tells the software that the contents of this frame is intended to be read only. Changing the contents might break something, e.g. a signature. If the contents are changed, without knowledge in why the frame was flagged read only and without taking the proper means to compensate, e.g. recalculating the signature, the bit should be cleared. i - Compression This flag indicates whether or not the frame is compressed. 0 Frame is not compressed. 1 Frame is compressed using zlib [zlib] with 4 bytes for 'decompressed size' appended to the frame header. j - Encryption This flag indicates wether or not the frame is enrypted. If set one byte indicating with which method it was encrypted will be appended to the frame header. See section 4.26. for more information about encryption method registration. 0 Frame is not encrypted. 1 Frame is encrypted. k - Grouping identity This flag indicates whether or not this frame belongs in a group with other frames. If set a group identifier byte is added to the frame header. Every frame with the same group identifier belongs to the same group. 0 Frame does not contain group information 1 Frame contains group information Some flags indicates that the frame header is extended with additional information. This information will be added to the frame header in the same order as the flags indicating the additions. I.e. the four bytes of decompressed size will preceed the encryption method byte. These additions to the frame header, while not included in the frame header size but are included in the 'frame size' field, are not subject to encryption or compression. 3.3.2. Default flags The default settings for the frames described in this document can be divided into the following classes. The flags may be set differently if found more suitable by the software. 1. Discarded if tag is altered, discarded if file is altered. None. 2. Discarded if tag is altered, preserved if file is altered. None. 3. Preserved if tag is altered, discarded if file is altered. AENC, ETCO, EQUA, MLLT, POSS, SYLT, SYTC, RVAD, TENC, TLEN, TSIZ 4. Preserved if tag is altered, preserved if file is altered. The rest of the frames. 4. Declared ID3v2 frames The following frames are declared in this draft. 4.21 AENC Audio encryption 4.15 APIC Attached picture 4.11 COMM Comments 4.25 COMR Commercial frame 4.26 ENCR Encryption method registration 4.13 EQUA Equalization 4.6 ETCO Event timing codes 4.16 GEOB General encapsulated object 4.27 GRID Group identification registration 4.4 IPLS Involved people list 4.21 LINK Linked information 4.5 MCDI Music CD identifier 4.7 MLLT MPEG location lookup table 4.24 OWNE Ownership frame 4.28. PRIV Private frame 4.17 PCNT Play counter 4.18 POPM Popularimeter 4.22 POSS Position synchronisation frame 4.19 RBUF Recommended buffer size 4.12 RVAD Relative volume adjustment 4.14 RVRB Reverb 4.10 SYLT Synchronized lyric/text 4.8 SYTC Synchronized tempo codes 4.2.1 TALB Album/Movie/Show title 4.2.1 TBPM BPM (beats per minute) 4.2.1 TCOM Composer 4.2.1 TCON Content type 4.2.1 TCOP Copyright message 4.2.1 TDAT Date 4.2.1 TDLY Playlist delay 4.2.1 TENC Encoded by 4.2.1 TEXT Lyricist/Text writer 4.2.1 TFLT File type 4.2.1 TIME Time 4.2.1 TIT1 Content group description 4.2.1 TIT2 Title/songname/content description 4.2.1 TIT3 Subtitle/Description refinement 4.2.1 TKEY Initial key 4.2.1 TLAN Language(s) 4.2.1 TLEN Length 4.2.1 TMED Media type 4.2.1 TOAL Original album/movie/show title 4.2.1 TOFN Original filename 4.2.1 TOLY Original lyricist(s)/text writer(s) 4.2.1 TOPE Original artist(s)/performer(s) 4.2.1 TORY Original release year 4.2.1 TOWN File owner/licensee 4.2.1 TPE1 Lead performer(s)/Soloist(s) 4.2.1 TPE2 Band/orchestra/accompaniment 4.2.1 TPE3 Conductor/performer refinement 4.2.1 TPE4 Interpreted, remixed, or otherwise modified by 4.2.1 TPOS Part of a set 4.2.1 TPUB Publisher 4.2.1 TRCK Track number/Position in set 4.2.1 TRDA Recording dates 4.2.1 TRSN Internet radio station name 4.2.1 TRSO Internet radio station owner 4.2.1 TSIZ Size 4.2.1 TSRC ISRC (international standard recording code) 4.2.1 TSSE Software/Hardware and settings used for encoding 4.2.1 TYER Year 4.2.2 TXXX User defined text information frame 4.1 UFID Unique file identifier 4.23 USER Terms of use 4.9 USLT Unsychronized lyric/text transcription 4.3.1 WCOM Commercial information 4.3.1 WCOP Copyright/Legal information 4.3.1 WOAF Official audio file webpage 4.3.1 WOAR Official artist/performer webpage 4.3.1 WOAS Official audio source webpage 4.3.1 WORS Official internet radio station homepage 4.3.1 WPAY Payment 4.3.1 WPUB Publishers official webpage 4.3.2 WXXX User defined URL link frame 4.1. Unique file identifier This frame's purpose is to be able to identify the audio file in a database that may contain more information relevant to the content. Since standardisation of such a database is beyond this document, all frames begin with a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific database implementation. Questions regarding the database should be sent to the indicated email address. The URL should not be used for the actual database queries. The string "<a href="http://www.id3.org/dummy/ufid.html">http://www.id3.org/dummy/ufid.html</a>" should be used for tests. Software that isn't told otherwise may safely remove such frames. The 'Owner identifier' must be non-empty (more than just a termination). The 'Owner identifier' is then followed by the actual identifier, which may be up to 64 bytes. There may be more than one "UFID" frame in a tag, but only one with the same 'Owner identifier'. <Header for 'Unique file identifier', ID: "UFID"> Owner identifier <text string> $00 Identifier <up to 64 bytes binary data> 4.2. Text information frames The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All text frame identifiers begin with "T". Only text frame identifiers begin with "T", with the exception of the "TXXX" frame. All the text information frames have the following format: <Header for 'Text information frame', ID: "T000" - "TZZZ", excluding "TXXX" described in 4.2.2.> Text encoding $xx Information <text string according to encoding> 4.2.1. Text information frames - details TALB The 'Album/Movie/Show title' frame is intended for the title of the recording(/source of sound) which the audio in the file is taken from. TBPM The 'BPM' frame contains the number of beats per minute in the mainpart of the audio. The BPM is an integer and represented as a numerical string. TCOM The 'Composer(s)' frame is intended for the name of the composer(s). They are seperated with the "/" character. TCON The 'Content type', which previously was stored as a one byte numeric value only, is now a numeric string. You may use one or several of the types as ID3v1.1 did or, since the category list would be impossible to maintain with accurate and up to date categories, define your own. References to the ID3v1 genres can be made by, as first byte, enter "(" followed by a number from the genres list (appendix A.) and ended with a ")" character. This is optionally followed by a refinement, e.g. "(21)" or "(4)Eurodisco". Several references can be made in the same frame, e.g. "(51)(39)". If the refinement should begin with a "(" character it should be replaced with "((", e.g. "((I can figure out any genre)" or "(55)((I think...)". The following new content types is defined in ID3v2 and is implemented in the same way as the numerig content types, e.g. "(RX)". RX Remix CR Cover TCOP The 'Copyright message' frame, which must begin with a year and a space character (making five characters), is intended for the copyright holder of the original sound, not the audio file itself. The absence of this frame means only that the copyright information is unavailable or has been removed, and must not be interpreted to mean that the sound is public domain. Every time this field is displayed the field must be preceded with "Copyright " (C) " ", where (C) is one character showing a C in a circle. TDAT The 'Date' frame is a numeric string in the DDMM format containing the date for the recording. This field is always four characters long. TDLY The 'Playlist delay' defines the numbers of milliseconds of silence between every song in a playlist. The player should use the "ETC" frame, if present, to skip initial silence and silence at the end of the audio to match the 'Playlist delay' time. The time is represented as a numeric string. TENC The 'Encoded by' frame contains the name of the person or organisation that encoded the audio file. This field may contain a copyright message, if the audio file also is copyrighted by the encoder. TEXT The 'Lyricist(s)/Text writer(s)' frame is intended for the writer(s) of the text or lyrics in the recording. They are seperated with the "/" character. TFLT The 'File type' frame indicates which type of audio this tag defines. The following type and refinements are defined: MPG MPEG Audio /1 MPEG 1/2 layer I /2 MPEG 1/2 layer II /3 MPEG 1/2 layer III /2.5 MPEG 2.5 /AAC Advanced audio compression VQF Transform-domain Weighted Interleave Vector Quantization PCM Pulse Code Modulated audio but other types may be used, not for these types though. This is used in a similar way to the predefined types in the "TMED" frame, but without parentheses. If this frame is not present audio type is assumed to be "MPG". TIME The 'Time' frame is a numeric string in the HHMM format containing the time for the recording. This field is always four characters long. TIT1 The 'Content group description' frame is used if the sound belongs to a larger category of sounds/music. For example, classical music is often sorted in different musical sections (e.g. "Piano Concerto", "Weather - Hurricane"). TIT2 The 'Title/Songname/Content description' frame is the actual name of the piece (e.g. "Adagio", "Hurricane Donna"). TIT3 The 'Subtitle/Description refinement' frame is used for information directly related to the contents title (e.g. "Op. 16" or "Performed live at Wembley"). TKEY The 'Initial key' frame contains the musical key in which the sound starts. It is represented as a string with a maximum length of three characters. The ground keys are represented with "A","B","C","D","E", "F" and "G" and halfkeys represented with "b" and "#". Minor is represented as "m". Example "Cbm". Off key is represented with an "o" only. TLAN The 'Language(s)' frame should contain the languages of the text or lyrics spoken or sung in the audio. The language is represented with three characters according to ISO-639-2. If more than one language is used in the text their language codes should follow according to their usage. TLEN The 'Length' frame contains the length of the audiofile in milliseconds, represented as a numeric string. TMED The 'Media type' frame describes from which media the sound originated. This may be a text string or a reference to the predefined media types found in the list below. References are made within "(" and ")" and are optionally followed by a text refinement, e.g. "(MC) with four channels". If a text refinement should begin with a "(" character it should be replaced with "((" in the same way as in the "TCO" frame. Predefined refinements is appended after the media type, e.g. "(CD/A)" or "(VID/PAL/VHS)". DIG Other digital media /A Analog transfer from media ANA Other analog media /WAC Wax cylinder /8CA 8-track tape cassette CD CD /A Analog transfer from media /DD DDD /AD ADD /AA AAD LD Laserdisc /A Analog transfer from media TT Turntable records /33 33.33 rpm /45 45 rpm /71 71.29 rpm /76 76.59 rpm /78 78.26 rpm /80 80 rpm MD MiniDisc /A Analog transfer from media DAT DAT /A Analog transfer from media /1 standard, 48 kHz/16 bits, linear /2 mode 2, 32 kHz/16 bits, linear /3 mode 3, 32 kHz/12 bits, nonlinear, low speed /4 mode 4, 32 kHz/12 bits, 4 channels /5 mode 5, 44.1 kHz/16 bits, linear /6 mode 6, 44.1 kHz/16 bits, 'wide track' play DCC DCC /A Analog transfer from media DVD DVD /A Analog transfer from media TV Television /PAL PAL /NTSC NTSC /SECAM SECAM VID Video /PAL PAL /NTSC NTSC /SECAM SECAM /VHS VHS /SVHS S-VHS /BETA BETAMAX RAD Radio /FM FM /AM AM /LW LW /MW MW TEL Telephone /I ISDN MC MC (normal cassette) /4 4.75 cm/s (normal speed for a two sided cassette) /9 9.5 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) REE Reel /9 9.5 cm/s /19 19 cm/s /38 38 cm/s /76 76 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) TOAL The 'Original album/movie/show title' frame is intended for the title of the original recording (or source of sound), if for example the music in the file should be a cover of a previously released song. TOFN The 'Original filename' frame contains the preferred filename for the file, since some media doesn't allow the desired length of the filename. The filename is case sensitive and includes its suffix. TOLY The 'Original lyricist(s)/text writer(s)' frame is intended for the text writer(s) of the original recording, if for example the music in the file should be a cover of a previously released song. The text writers are seperated with the "/" character. TOPE The 'Original artist(s)/performer(s)' frame is intended for the performer(s) of the original recording, if for example the music in the file should be a cover of a previously released song. The performers are seperated with the "/" character. TORY The 'Original release year' frame is intended for the year when the original recording, if for example the music in the file should be a cover of a previously released song, was released. The field is formatted as in the "TYER" frame. TOWN The 'File owner/licensee' frame contains the name of the owner or licensee of the file and it's contents. TPE1 The 'Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group' is used for the main artist(s). They are seperated with the "/" character. TPE2 The 'Band/Orchestra/Accompaniment' frame is used for additional information about the performers in the recording. TPE3 The 'Conductor' frame is used for the name of the conductor. TPE4 The 'Interpreted, remixed, or otherwise modified by' frame contains more information about the people behind a remix and similar interpretations of another existing piece. TPOS The 'Part of a set' frame is a numeric string that describes which part of a set the audio came from. This frame is used if the source described in the "TALB" frame is divided into several mediums, e.g. a double CD. The value may be extended with a "/" character and a numeric string containing the total number of parts in the set. E.g. "1/2". TPUB The 'Publisher' frame simply contains the name of the label or publisher. TRCK The 'Track number/Position in set' frame is a numeric string containing the order number of the audio-file on its original recording. This may be extended with a "/" character and a numeric string containing the total numer of tracks/elements on the original recording. E.g. "4/9". TRDA The 'Recording dates' frame is a intended to be used as complement to the "TYER", "TDAT" and "TIME" frames. E.g. "4th-7th June, 12th June" in combination with the "TYER" frame. TRSN The 'Internet radio station name' frame contains the name of the internet radio station from which the audio is streamed. TRSO The 'Internet radio station owner' frame contains the name of the owner of the internet radio station from which the audio is streamed. TSIZ The 'Size' frame contains the size of the audiofile in bytes, excluding the ID3v2 tag, represented as a numeric string. TSRC The 'ISRC' frame should contain the International Standard Recording Code [ISRC] (12 characters). TSSE The 'Software/Hardware and settings used for encoding' frame includes the used audio encoder and its settings when the file was encoded. Hardware refers to hardware encoders, not the computer on which a program was run. TYER The 'Year' frame is a numeric string with a year of the recording. This frames is always four characters long (until the year 10000). 4.2.2. User defined text information frame This frame is intended for one-string text information concerning the audiofile in a similar way to the other "T"-frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual string. There may be more than one "TXXX" frame in each tag, but only one with the same description. <Header for 'User defined text information frame', ID: "TXXX"> Text encoding $xx Description <text string according to encoding> $00 (00) Value <text string according to encoding> 4.3. URL link frames With these frames dynamic data such as webpages with touring information, price information or plain ordinary news can be added to the tag. There may only be one URL [URL] link frame of its kind in an tag, except when stated otherwise in the frame description. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All URL link frame identifiers begins with "W". Only URL link frame identifiers begins with "W". All URL link frames have the following format: <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX" described in 4.3.2.> URL <text string> 4.3.1. URL link frames - details WCOM The 'Commercial information' frame is a URL pointing at a webpage with information such as where the album can be bought. There may be more than one "WCOM" frame in a tag, but not with the same content. WCOP The 'Copyright/Legal information' frame is a URL pointing at a webpage where the terms of use and ownership of the file is described. WOAF The 'Official audio file webpage' frame is a URL pointing at a file specific webpage. WOAR The 'Official artist/performer webpage' frame is a URL pointing at the artists official webpage. There may be more than one "WOAR" frame in a tag if the audio contains more than one performer, but not with the same content. WOAS The 'Official audio source webpage' frame is a URL pointing at the official webpage for the source of the audio file, e.g. a movie. WORS The 'Official internet radio station homepage' contains a URL pointing at the homepage of the internet radio station. WPAY The 'Payment' frame is a URL pointing at a webpage that will handle the process of paying for this file. WPUB The 'Publishers official webpage' frame is a URL pointing at the official wepage for the publisher. 4.3.2. User defined URL link frame This frame is intended for URL [URL] links concerning the audiofile in a similar way to the other "W"-frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual URL. The URL is always encoded with ISO-8859-1 [ISO-8859-1]. There may be more than one "WXXX" frame in each tag, but only one with the same description. <Header for 'User defined URL link frame', ID: "WXXX"> Text encoding $xx Description <text string according to encoding> $00 (00) URL <text string> 4.4. Involved people list Since there might be a lot of people contributing to an audio file in various ways, such as musicians and technicians, the 'Text information frames' are often insufficient to list everyone involved in a project. The 'Involved people list' is a frame containing the names of those involved, and how they were involved. The body simply contains a terminated string with the involvement directly followed by a terminated string with the involvee followed by a new involvement and so on. There may only be one "IPLS" frame in each tag. <Header for 'Involved people list', ID: "IPLS"> Text encoding $xx People list strings <text strings according to encoding> 4.5. Music CD identifier This frame is intended for music that comes from a CD, so that the CD can be identified in databases such as the CDDB [CDDB]. The frame consists of a binary dump of the Table Of Contents, TOC, from the CD, which is a header of 4 bytes and then 8 bytes/track on the CD plus 8 bytes for the 'lead out' making a maximum of 804 bytes. The offset to the beginning of every track on the CD should be described with a four bytes absolute CD-frame address per track, and not with absolute time. This frame requires a present and valid "TRCK" frame, even if the CD's only got one track. There may only be one "MCDI" frame in each tag. <Header for 'Music CD identifier', ID: "MCDI"> CD TOC <binary data> 4.6. Event timing codes This frame allows synchronisation with key events in a song or sound. The header is: <Header for 'Event timing codes', ID: "ETCO"> Time stamp format $xx Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. Followed by a list of key events in the following format: Type of event $xx Time stamp $xx (xx ...) The 'Time stamp' is set to zero if directly at the beginning of the sound or after the previous event. All events should be sorted in chronological order. The type of event is as follows: $00 padding (has no meaning) $01 end of initial silence $02 intro start $03 mainpart start $04 outro start $05 outro end $06 verse start $07 refrain start $08 interlude start $09 theme start $0A variation start $0B key change $0C time change $0D momentary unwanted noise (Snap, Crackle & Pop) $0E sustained noise $0F sustained noise end $10 intro end $11 mainpart end $12 verse end $13 refrain end $14 theme end $15-$DF reserved for future use $E0-$EF not predefined sync 0-F $F0-$FC reserved for future use $FD audio end (start of silence) $FE audio file ends $FF one more byte of events follows (all the following bytes with the value $FF have the same function) Terminating the start events such as "intro start" is not required. The 'Not predefined sync's ($E0-EF) are for user events. You might want to synchronise your music to something, like setting of an explosion on-stage, turning on your screensaver etc. There may only be one "ETCO" frame in each tag. 4.7. MPEG location lookup table To increase performance and accuracy of jumps within a MPEG [MPEG] audio file, frames with timecodes in different locations in the file might be useful. The ID3v2 frame includes references that the software can use to calculate positions in the file. After the frame header is a descriptor of how much the 'frame counter' should increase for every reference. If this value is two then the first reference points out the second frame, the 2nd reference the 4th frame, the 3rd reference the 6th frame etc. In a similar way the 'bytes between reference' and 'milliseconds between reference' points out bytes and milliseconds respectively. Each reference consists of two parts; a certain number of bits, as defined in 'bits for bytes deviation', that describes the difference between what is said in 'bytes between reference' and the reality and a certain number of bits, as defined in 'bits for milliseconds deviation', that describes the difference between what is said in 'milliseconds between reference' and the reality. The number of bits in every reference, i.e. 'bits for bytes deviation'+'bits for milliseconds deviation', must be a multiple of four. There may only be one "MLLT" frame in each tag. <Header for 'Location lookup table', ID: "MLLT"> MPEG frames between reference $xx xx Bytes between reference $xx xx xx Milliseconds between reference $xx xx xx Bits for bytes deviation $xx Bits for milliseconds dev. $xx Then for every reference the following data is included; Deviation in bytes %xxx.... Deviation in milliseconds %xxx.... 4.8. Synchronised tempo codes For a more accurate description of the tempo of a musical piece this frame might be used. After the header follows one byte describing which time stamp format should be used. Then follows one or more tempo codes. Each tempo code consists of one tempo part and one time part. The tempo is in BPM described with one or two bytes. If the first byte has the value $FF, one more byte follows, which is added to the first giving a range from 2 - 510 BPM, since $00 and $01 is reserved. $00 is used to describe a beat-free time period, which is not the same as a music-free time period. $01 is used to indicate one single beat-stroke followed by a beat-free period. The tempo descriptor is followed by a time stamp. Every time the tempo in the music changes, a tempo descriptor may indicate this for the player. All tempo descriptors should be sorted in chronological order. The first beat-stroke in a time-period is at the same time as the beat description occurs. There may only be one "SYTC" frame in each tag. <Header for 'Synchronised tempo codes', ID: "SYTC"> Time stamp format $xx Tempo data <binary data> Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. 4.9. Unsychronised lyrics/text transcription This frame contains the lyrics of the song or a text transcription of other vocal activities. The head includes an encoding descriptor and a content descriptor. The body consists of the actual text. The 'Content descriptor' is a terminated string. If no descriptor is entered, 'Content descriptor' is $00 (00) only. Newline characters are allowed in the text. There may be more than one 'Unsynchronised lyrics/text transcription' frame in each tag, but only one with the same language and content descriptor. <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT"> Text encoding $xx Language $xx xx xx Content descriptor <text string according to encoding> $00 (00) Lyrics/text <full text string according to encoding> 4.10. Synchronised lyrics/text This is another way of incorporating the words, said or sung lyrics, in the audio file as text, this time, however, in sync with the audio. It might also be used to describing events e.g. occurring on a stage or on the screen in sync with the audio. The header includes a content descriptor, represented with as terminated textstring. If no descriptor is entered, 'Content descriptor' is $00 (00) only. <Header for 'Synchronised lyrics/text', ID: "SYLT"> Text encoding $xx Language $xx xx xx Time stamp format $xx Content type $xx Content descriptor <text string according to encoding> $00 (00) Encoding: $00 ISO-8859-1 [ISO-8859-1] character set is used => $00 is sync identifier. $01 Unicode [UNICODE] character set is used => $00 00 is sync identifier. Content type: $00 is other $01 is lyrics $02 is text transcription $03 is movement/part name (e.g. "Adagio") $04 is events (e.g. "Don Quijote enters the stage") $05 is chord (e.g. "Bb F Fsus") $06 is trivia/'pop up' information Time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Abolute time means that every stamp contains the time from the beginning of the file. The text that follows the frame header differs from that of the unsynchronised lyrics/text transcription in one major way. Each syllable (or whatever size of text is considered to be convenient by the encoder) is a null terminated string followed by a time stamp denoting where in the sound file it belongs. Each sync thus has the following structure: Terminated text to be synced (typically a syllable) Sync identifier (terminator to above string) $00 (00) Time stamp $xx (xx ...) The 'time stamp' is set to zero or the whole sync is omitted if located directly at the beginning of the sound. All time stamps should be sorted in chronological order. The sync can be considered as a validator of the subsequent string. Newline ($0A) characters are allowed in all "SYLT" frames and should be used after every entry (name, event etc.) in a frame with the content type $03 - $04. A few considerations regarding whitespace characters: Whitespace separating words should mark the beginning of a new word, thus occurring in front of the first syllable of a new word. This is also valid for new line characters. A syllable followed by a comma should not be broken apart with a sync (both the syllable and the comma should be before the sync). An example: The "USLT" passage "Strangers in the night" $0A "Exchanging glances" would be "SYLT" encoded as: "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx xx "glan" $00 xx xx "ces" $00 xx xx There may be more than one "SYLT" frame in each tag, but only one with the same language and content descriptor. 4.11. Comments This frame is indended for any kind of full text information that does not fit in any other frame. It consists of a frame header followed by encoding, language and content descriptors and is ended with the actual comment as a text string. Newline characters are allowed in the comment text string. There may be more than one comment frame in each tag, but only one with the same language and content descriptor. <Header for 'Comment', ID: "COMM"> Text encoding $xx Language $xx xx xx Short content descrip. <text string according to encoding> $00 (00) The actual text <full text string according to encoding> 4.12. Relative volume adjustment This is a more subjective function than the previous ones. It allows the user to say how much he wants to increase/decrease the volume on each channel while the file is played. The purpose is to be able to align all files to a reference volume, so that you don't have to change the volume constantly. This frame may also be used to balance adjust the audio. If the volume peak levels are known then this could be described with the 'Peak volume right' and 'Peak volume left' field. If Peakvolume is not known these fields could be left zeroed or, if no other data follows, be completely omitted. There may only be one "RVAD" frame in each tag. <Header for 'Relative volume adjustment', ID: "RVAD"> Increment/decrement %00xxxxxx Bits used for volume descr. $xx Relative volume change, right $xx xx (xx ...) Relative volume change, left $xx xx (xx ...) Peak volume right $xx xx (xx ...) Peak volume left $xx xx (xx ...) In the increment/decrement field bit 0 is used to indicate the right channel and bit 1 is used to indicate the left channel. 1 is increment and 0 is decrement. The 'bits used for volume description' field is normally $10 (16 bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be $00. The volume is always represented with whole bytes, padded in the beginning (highest bits) when 'bits used for volume description' is not a multiple of eight. This datablock is then optionally followed by a volume definition for the left and right back channels. If this information is appended to the frame the first two channels will be treated as front channels. In the increment/decrement field bit 2 is used to indicate the right back channel and bit 3 for the left back channel. Relative volume change, right back $xx xx (xx ...) Relative volume change, left back $xx xx (xx ...) Peak volume right back $xx xx (xx ...) Peak volume left back $xx xx (xx ...) If the center channel adjustment is present the following is appended to the existing frame, after the left and right back channels. The center channel is represented by bit 4 in the increase/decrease field. Relative volume change, center $xx xx (xx ...) Peak volume center $xx xx (xx ...) If the bass channel adjustment is present the following is appended to the existing frame, after the center channel. The bass channel is represented by bit 5 in the increase/decrease field. Relative volume change, bass $xx xx (xx ...) Peak volume bass $xx xx (xx ...) 4.13. Equalisation This is another subjective, alignment frame. It allows the user to predefine an equalisation curve within the audio file. There may only be one "EQUA" frame in each tag. <Header of 'Equalisation', ID: "EQUA"> Adjustment bits $xx The 'adjustment bits' field defines the number of bits used for representation of the adjustment. This is normally $10 (16 bits) for MPEG 2 layer I, II and III [MPEG] and MPEG 2.5. This value may not be $00. This is followed by 2 bytes + ('adjustment bits' rounded up to the nearest byte) for every equalisation band in the following format, giving a frequency range of 0 - 32767Hz: Increment/decrement %x (MSB of the Frequency) Frequency (lower 15 bits) Adjustment $xx (xx ...) The increment/decrement bit is 1 for increment and 0 for decrement. The equalisation bands should be ordered increasingly with reference to frequency. All frequencies don't have to be declared. The equalisation curve in the reading software should be interpolated between the values in this frame. Three equal adjustments for three subsequent frequencies. A frequency should only be described once in the frame. 4.14. Reverb Yet another subjective one. You may here adjust echoes of different kinds. Reverb left/right is the delay between every bounce in ms. Reverb bounces left/right is the number of bounces that should be made. $FF equals an infinite number of bounces. Feedback is the amount of volume that should be returned to the next echo bounce. $00 is 0%, $FF is 100%. If this value were $7F, there would be 50% volume reduction on the first bounce, 50% of that on the second and so on. Left to left means the sound from the left bounce to be played in the left speaker, while left to right means sound from the left bounce to be played in the right speaker. 'Premix left to right' is the amount of left sound to be mixed in the right before any reverb is applied, where $00 id 0% and $FF is 100%. 'Premix right to left' does the same thing, but right to left. Setting both premix to $FF would result in a mono output (if the reverb is applied symmetric). There may only be one "RVRB" frame in each tag. <Header for 'Reverb', ID: "RVRB"> Reverb left (ms) $xx xx Reverb right (ms) $xx xx Reverb bounces, left $xx Reverb bounces, right $xx Reverb feedback, left to left $xx Reverb feedback, left to right $xx Reverb feedback, right to right $xx Reverb feedback, right to left $xx Premix left to right $xx Premix right to left $xx 4.15. Attached picture This frame contains a picture directly related to the audio file. Image format is the MIME type and subtype [MIME] for the image. In the event that the MIME media type name is omitted, "image/" will be implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format should be used when interoperability is wanted. Description is a short description of the picture, represented as a terminated textstring. The description has a maximum length of 64 characters, but may be empty. There may be several pictures attached to one file, each in their individual "APIC" frame, but only one with the same content descriptor. There may only be one picture with the picture type declared as picture type $01 and $02 respectively. There is the possibility to put only a link to the image file by using the 'MIME type' "-->" and having a complete URL [URL] instead of picture data. The use of linked files should however be used sparingly since there is the risk of separation of files. <Header for 'Attached picture', ID: "APIC"> Text encoding $xx MIME type <text string> $00 Picture type $xx Description <text string according to encoding> $00 (00) Picture data <binary data> Picture type: $00 Other $01 32x32 pixels 'file icon' (PNG only) $02 Other file icon $03 Cover (front) $04 Cover (back) $05 Leaflet page $06 Media (e.g. lable side of CD) $07 Lead artist/lead performer/soloist $08 Artist/performer $09 Conductor $0A Band/Orchestra $0B Composer $0C Lyricist/text writer $0D Recording Location $0E During recording $0F During performance $10 Movie/video screen capture $11 A bright coloured fish $12 Illustration $13 Band/artist logotype $14 Publisher/Studio logotype 4.16. General encapsulated object In this frame any type of file can be encapsulated. After the header, 'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The filename is case sensitive and is encoded as 'Encoding'. Then follows a content description as terminated string, encoded as 'Encoding'. The last thing in the frame is the actual object. The first two strings may be omitted, leaving only their terminations. MIME type is always an ISO-8859-1 text string. There may be more than one "GEOB" frame in each tag, but only one with the same content descriptor. <Header for 'General encapsulated object', ID: "GEOB"> Text encoding $xx MIME type <text string> $00 Filename <text string according to encoding> $00 (00) Content description <text string according to encding> $00 (00) Encapsulated object <binary data> 4.17. Play counter This is simply a counter of the number of times a file has been played. The value is increased by one every time the file begins to play. There may only be one "PCNT" frame in each tag. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger. The counter must be at least 32-bits long to begin with. <Header for 'Play counter', ID: "PCNT"> Counter $xx xx xx xx (xx ...) 4.18. Popularimeter The purpose of this frame is to specify how good an audio file is. Many interesting applications could be found to this frame such as a playlist that features better audiofiles more often than others or it could be used to profile a person's taste and find other 'good' files by comparing people's profiles. The frame is very simple. It contains the email address to the user, one rating byte and a four byte play counter, intended to be increased with one for every time the file is played. The email is a terminated string. The rating is 1-255 where 1 is worst and 255 is best. 0 is unknown. If no personal counter is wanted it may be omitted. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger in the same away as the play counter ("PCNT"). There may be more than one "POPM" frame in each tag, but only one with the same email address. <Header for 'Popularimeter', ID: "POPM"> Email to user <text string> $00 Rating $xx Counter $xx xx xx xx (xx ...) 4.19. Recommended buffer size Sometimes the server from which a audio file is streamed is aware of transmission or coding problems resulting in interruptions in the audio stream. In these cases, the size of the buffer can be recommended by the server using this frame. If the 'embedded info flag' is true (1) then this indicates that an ID3 tag with the maximum size described in 'Buffer size' may occur in the audiostream. In such case the tag should reside between two MPEG [MPEG] frames, if the audio is MPEG encoded. If the position of the next tag is known, 'offset to next tag' may be used. The offset is calculated from the end of tag in which this frame resides to the first byte of the header in the next. This field may be omitted. Embedded tags are generally not recommended since this could render unpredictable behaviour from present software/hardware. For applications like streaming audio it might be an idea to embed tags into the audio stream though. If the clients connects to individual connections like HTTP and there is a possibility to begin every transmission with a tag, then this tag should include a 'recommended buffer size' frame. If the client is connected to a arbitrary point in the stream, such as radio or multicast, then the 'recommended buffer size' frame should be included in every tag. Every tag that is picked up after the initial/first tag is to be considered as an update of the previous one. E.g. if there is a "TIT2" frame in the first received tag and one in the second tag, then the first should be 'replaced' with the second. The 'Buffer size' should be kept to a minimum. There may only be one "RBUF" frame in each tag. <Header for 'Recommended buffer size', ID: "RBUF"> Buffer size $xx xx xx Embedded info flag %0000000x Offset to next tag $xx xx xx xx 4.20. Audio encryption This frame indicates if the actual audio stream is encrypted, and by whom. Since standardisation of such encrypion scheme is beyond this document, all "AENC" frames begin with a terminated string with a URL containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific encrypted audio file. Questions regarding the encrypted audio should be sent to the email address specified. If a $00 is found directly after the 'Frame size' and the audiofile indeed is encrypted, the whole file may be considered useless. After the 'Owner identifier', a pointer to an unencrypted part of the audio can be specified. The 'Preview start' and 'Preview length' is described in frames. If no part is unencrypted, these fields should be left zeroed. After the 'preview length' field follows optionally a datablock required for decryption of the audio. There may be more than one "AENC" frames in a tag, but only one with the same 'Owner identifier'. <Header for 'Audio encryption', ID: "AENC"> Owner identifier <text string> $00 Preview start $xx xx Preview length $xx xx Encryption info <binary data> 4.21. Linked information To keep space waste as low as possible this frame may be used to link information from another ID3v2 tag that might reside in another audio file or alone in a binary file. It is recommended that this method is only used when the files are stored on a CD-ROM or other circumstances when the risk of file seperation is low. The frame contains a frame identifier, which is the frame that should be linked into this tag, a URL [URL] field, where a reference to the file where the frame is given, and additional ID data, if needed. Data should be retrieved from the first tag found in the file to which this link points. There may be more than one "LINK" frame in a tag, but only one with the same contents. A linked frame is to be considered as part of the tag and has the same restrictions as if it was a physical part of the tag (i.e. only one "RVRB" frame allowed, whether it's linked or not). <Header for 'Linked information', ID: "LINK"> Frame identifier $xx xx xx URL <text string> $00 ID and additional data <text string(s)> Frames that may be linked and need no additional data are "IPLS", "MCID", "ETCO", "MLLT", "SYTC", "RVAD", "EQUA", "RVRB", "RBUF", the text information frames and the URL link frames. The "TXXX", "APIC", "GEOB" and "AENC" frames may be linked with the content descriptor as additional ID data. The "COMM", "SYLT" and "USLT" frames may be linked with three bytes of language descriptor directly followed by a content descriptor as additional ID data. 4.22. Position synchronisation frame This frame delivers information to the listener of how far into the audio stream he picked up; in effect, it states the time offset of the first frame in the stream. The frame layout is: <Head for 'Position synchronisation', ID: "POSS"> Time stamp format $xx Position $xx (xx ...) Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit and position is where in the audio the listener starts to receive, i.e. the beginning of the next frame. If this frame is used in the beginning of a file the value is always 0. There may only be one "POSS" frame in each tag. 4.23. Terms of use frame This frame contains a brief description of the terms of use and ownership of the file. More detailed information concerning the legal terms might be available through the "WCOP" frame. Newlines are allowed in the text. There may only be one "USER" frame in a tag. <Header for 'Terms of use frame', ID: "USER"> Text encoding $xx Language $xx xx xx The actual text <text string according to encoding> 4.24. Ownership frame The ownership frame might be used as a reminder of a made transaction or, if signed, as proof. Note that the "USER" and "TOWN" frames are good to use in conjunction with this one. The frame begins, after the frame ID, size and encoding fields, with a 'price payed' field. The first three characters of this field contains the currency used for the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic currency code. Concatenated to this is the actual price payed, as a numerical string using "." as the decimal separator. Next is an 8 character date string (YYYYMMDD) followed by a string with the name of the seller as the last field in the frame. There may only be one "OWNE" frame in a tag. <Header for 'Ownership frame', ID: "OWNE"> Text encoding $xx Price payed <text string> $00 Date of purch. <text string> Seller <text string according to encoding> 4.25. Commercial frame This frame enables several competing offers in the same tag by bundling all needed information. That makes this frame rather complex but it's an easier solution than if one tries to achieve the same result with several frames. The frame begins, after the frame ID, size and encoding fields, with a price string field. A price is constructed by one three character currency code, encoded according to ISO 4217 [ISO-4217] alphabetic currency code, followed by a numerical value where "." is used as decimal seperator. In the price string several prices may be concatenated, seperated by a "/" character, but there may only be one currency of each type. The price string is followed by an 8 character date string in the format YYYYMMDD, describing for how long the price is valid. After that is a contact URL, with which the user can contact the seller, followed by a one byte 'received as' field. It describes how the audio is delivered when bought according to the following list: $00 Other $01 Standard CD album with other songs $02 Compressed audio on CD $03 File over the Internet $04 Stream over the Internet $05 As note sheets $06 As note sheets in a book with other sheets $07 Music on other media $08 Non-musical merchandise Next follows a terminated string with the name of the seller followed by a terminated string with a short description of the product. The last thing is the ability to include a company logotype. The first of them is the 'Picture MIME type' field containing information about which picture format is used. In the event that the MIME media type name is omitted, "image/" will be implied. Currently only "image/png" and "image/jpeg" are allowed. This format string is followed by the binary picture data. This two last fields may be omitted if no picture is to attach. <Header for 'Commercial frame', ID: "COMR"> Text encoding $xx Price string <text string> $00 Valid until <text string> Contact URL <text string> $00 Received as $xx Name of seller <text string according to encoding> $00 (00) Description <text string according to encoding> $00 (00) Picture MIME type <string> $00 Seller logo <binary data> 4.26. Encryption method registration To identify with which method a frame has been encrypted the encryption method must be registered in the tag with this frame. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific encryption method. Questions regarding the encryption method should be sent to the indicated email address. The 'Method symbol' contains a value that is associated with this method throughout the whole tag. Values below $80 are reserved. The 'Method symbol' may optionally be followed by encryption specific data. There may be several "ENCR" frames in a tag but only one containing the same symbol and only one containing the same owner identifier. The method must be used somewhere in the tag. See section 3.3.1, flag j for more information. <Header for 'Encryption method registration', ID: "ENCR"> Owner identifier <text string> $00 Method symbol $xx Encryption data <binary data> 4.27. Group identification registration This frame enables grouping of otherwise unrelated frames. This can be used when some frames are to be signed. To identify which frames belongs to a set of frames a group identifier must be registered in the tag with this frame. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this grouping. Questions regarding the grouping should be sent to the indicated email address. The 'Group symbol' contains a value that associates the frame with this group throughout the whole tag. Values below $80 are reserved. The 'Group symbol' may optionally be followed by some group specific data, e.g. a digital signature. There may be several "GRID" frames in a tag but only one containing the same symbol and only one containing the same owner identifier. The group symbol must be used somewhere in the tag. See section 3.3.1, flag j for more information. <Header for 'Group ID registration', ID: "GRID"> Owner identifier <text string> $00 Group symbol $xx Group dependent data <binary data> 4.28. Private frame This frame is used to contain information from a software producer that its program uses and does not fit into the other frames. The frame consists of an 'Owner identifier' string and the binary data. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for the frame. Questions regarding the frame should be sent to the indicated email address. The tag may contain more than one "PRIV" frame but only with different contents. It is recommended to keep the number of "PRIV" frames as low as possible. <Header for 'Private frame', ID: "PRIV"> Owner identifier <text string> $00 The private data <binary data> 5. The 'unsynchronisation scheme' The only purpose of the 'unsynchronisation scheme' is to make the ID3v2 tag as compatible as possible with existing software. There is no use in 'unsynchronising' tags if the file is only to be processed by new software. Unsynchronisation may only be made with MPEG 2 layer I, II and III and MPEG 2.5 files. Whenever a false synchronisation is found within the tag, one zeroed byte is inserted after the first false synchronisation byte. The format of a correct sync that should be altered by ID3 encoders is as follows: %11111111 111xxxxx And should be replaced with: %11111111 00000000 111xxxxx This has the side effect that all $FF 00 combinations have to be altered, so they won't be affected by the decoding process. Therefore all the $FF 00 combinations have to be replaced with the $FF 00 00 combination during the unsynchronisation. To indicate usage of the unsynchronisation, the first bit in 'ID3 flags' should be set. This bit should only be set if the tag contains a, now corrected, false synchronisation. The bit should only be clear if the tag does not contain any false synchronisations. Do bear in mind, that if a compression scheme is used by the encoder, the unsynchronisation scheme should be applied *afterwards*. When decoding a compressed, 'unsynchronised' file, the 'unsynchronisation scheme' should be parsed first, decompression afterwards. If the last byte in the tag is $FF, and there is a need to eliminate false synchronisations in the tag, at least one byte of padding should be added. 6. Copyright Copyright (C) Martin Nilsson 1998. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that a reference to this document is included on all such copies and derivative works. However, this document itself may not be modified in any way and reissued as the original document. The limited permissions granted above are perpetual and will not be revoked. This document and the information contained herein is provided on an "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 7. References [CDDB] Compact Disc Data Base http://www.cddb.com [ID3v2] Martin Nilsson, "ID3v2 informal standard". http://www.id3lib.org/id3/id3v2-00.txt [ISO-639-2] ISO/FDIS 639-2. Codes for the representation of names of languages, Part 2: Alpha-3 code. Technical committee / subcommittee: TC 37 / SC 2 [ISO-4217] ISO 4217:1995. Codes for the representation of currencies and funds. Technical committee / subcommittee: TC 68 [ISO-8859-1] ISO/IEC DIS 8859-1. 8-bit single-byte coded graphic character sets, Part 1: Latin alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 [ISRC] ISO 3901:1986 International Standard Recording Code (ISRC). Technical committee / subcommittee: TC 46 / SC 9 [JFIF] JPEG File Interchange Format, version 1.02 http://www.w3.org/Graphics/JPEG/jfif.txt">http://www.w3.org/Graphics/JPEG/jfif.txt [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC 2045, November 1996. ftp://ftp.isi.edu/in-notes/rfc2045.txt">ftp://ftp.isi.edu/in-notes/rfc2045.txt [MPEG] ISO/IEC 11172-3:1993. Coding of moving pictures and associated audio for digital storage media at up to about 1,5 Mbit/s, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC 13818-3:1995 Generic coding of moving pictures and associated audio information, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC DIS 13818-3 Generic coding of moving pictures and associated audio information, Part 3: Audio (Revision of ISO/IEC 13818-3:1995) [PNG] Portable Network Graphics, version 1.0 http://www.w3.org/TR/REC-png-multi.html [UNICODE] ISO/IEC 10646-1:1993. Universal Multiple-Octet Coded Character Set (UCS), Part 1: Architecture and Basic Multilingual Plane. Technical committee / subcommittee: JTC 1 / SC 2 http://www.unicode.org/ [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource Locators (URL).", RFC 1738, December 1994. ftp://ftp.isi.edu/in-notes/rfc1738.txt [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB Compressed Data Format Specification version 3.3", RFC 1950, May 1996. ftp://ftp.isi.edu/in-notes/rfc1950.txt 8. Appendix A. Appendix A - Genre List from ID3v1 The following genres is defined in ID3v1 0.Blues 1.Classic Rock 2.Country 3.Dance 4.Disco 5.Funk 6.Grunge 7.Hip-Hop 8.Jazz 9.Metal 10.New Age 11.Oldies 12.Other 13.Pop 14.R&B 15.Rap 16.Reggae 17.Rock 18.Techno 19.Industrial 20.Alternative 21.Ska 22.Death Metal 23.Pranks 24.Soundtrack 25.Euro-Techno 26.Ambient 27.Trip-Hop 28.Vocal 29.Jazz+Funk 30.Fusion 31.Trance 32.Classical 33.Instrumental 34.Acid 35.House 36.Game 37.Sound Clip 38.Gospel 39.Noise 40.AlternRock 41.Bass 42.Soul 43.Punk 44.Space 45.Meditative 46.Instrumental Pop 47.Instrumental Rock 48.Ethnic 49.Gothic 50.Darkwave 51.Techno-Industrial 52.Electronic 53.Pop-Folk 54.Eurodance 55.Dream 56.Southern Rock 57.Comedy 58.Cult 59.Gangsta 60.Top 40 61.Christian Rap 62.Pop/Funk 63.Jungle 64.Native American 65.Cabaret 66.New Wave 67.Psychadelic 68.Rave 69.Showtunes 70.Trailer 71.Lo-Fi 72.Tribal 73.Acid Punk 74.Acid Jazz 75.Polka 76.Retro 77.Musical 78.Rock & Roll 79.Hard Rock The following genres are Winamp extensions 80.Folk 81.Folk-Rock 82.National Folk 83.Swing 84.Fast Fusion 85.Bebob 86.Latin 87.Revival 88.Celtic 89.Bluegrass 90.Avantgarde 91.Gothic Rock 92.Progressive Rock 93.Psychedelic Rock 94.Symphonic Rock 95.Slow Rock 96.Big Band 97.Chorus 98.Easy Listening 99.Acoustic 100.Humour 101.Speech 102.Chanson 103.Opera 104.Chamber Music 105.Sonata 106.Symphony 107.Booty Bass 108.Primus 109.Porn Groove 110.Satire 111.Slow Jam 112.Club 113.Tango 114.Samba 115.Folklore 116.Ballad 117.Power Ballad 118.Rhythmic Soul 119.Freestyle 120.Duet 121.Punk Rock 122.Drum Solo 123.Acapella 124.Euro-House 125.Dance Hall 9. Author's Address Written by Martin Nilsson Rydsvgen 246 C. 30 S-584 34 Linkping Sweden Email: nilsson@id3.org Edited by Dirk Mahoney 57 Pechey Street Chermside Q Australia 4032 Email: dirk@id3.org Johan Sundstrm Alsttersgatan 5 A. 34 S-584 35 Linkping Sweden Email: johan@id3.org ����������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2.4.0-frames.txt�������������������������������������������������0000664�0000000�0000000�00000200067�14662262111�0021347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������$Id$ Informal standard M. Nilsson Document: id3v2.4.0-frames.txt 1st November 2000 ID3 tag version 2.4.0 - Native Frames Status of this document This document is an informal standard and replaces the ID3v2.3.0 standard [ID3v2]. A formal standard will use another revision number even if the content is identical to document. The contents in this document may change for clarifications but never for added or altered functionallity. Distribution of this document is unlimited. Abstract This document describes the frames natively supported by ID3v2.4.0, which is a revised version of the ID3v2 informal standard [ID3v2.3.0] version 2.3.0. The ID3v2 offers a flexible way of storing audio meta information within audio file itself. The information may be technical information, such as equalisation curves, as well as title, performer, copyright etc. ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order to allow for implementations to be revised as easily as possible. 1. Table of contents 2. Conventions in this document 3. Default flags 4. Declared ID3v2 frames 4.1. Unique file identifier 4.2. Text information frames 4.2.1. Identification frames 4.2.2. Involved persons frames 4.2.3. Derived and subjective properties frames 4.2.4. Rights and license frames 4.2.5. Other text frames 4.2.6. User defined text information frame 4.3. URL link frames 4.3.1. URL link frames - details 4.3.2. User defined URL link frame 4.4. Music CD Identifier 4.5. Event timing codes 4.6. MPEG location lookup table 4.7. Synced tempo codes 4.8. Unsynchronised lyrics/text transcription 4.9. Synchronised lyrics/text 4.10. Comments 4.11. Relative volume adjustment (2) 4.12. Equalisation (2) 4.13. Reverb 4.14. Attached picture 4.15. General encapsulated object 4.16. Play counter 4.17. Popularimeter 4.18. Recommended buffer size 4.19. Audio encryption 4.20. Linked information 4.21. Position synchronisation frame 4.22. Terms of use 4.23. Ownership frame 4.24. Commercial frame 4.25. Encryption method registration 4.26. Group identification registration 4.27. Private frame 4.28. Signature frame 4.29. Seek frame 4.30. Audio seek point index 5. Copyright 6. References 7. Appendix A. Appendix A - Genre List from ID3v1 8. Author's Address 2. Conventions in this document Text within "" is a text string exactly as it appears in a tag. Numbers preceded with $ are hexadecimal and numbers preceded with % are binary. $xx is used to indicate a byte with unknown content. %x is used to indicate a bit with unknown content. The most significant bit (MSB) of a byte is called 'bit 7' and the least significant bit (LSB) is called 'bit 0'. A tag is the whole tag described the ID3v2 main structure document [ID3v2-strct]. A frame is a block of information in the tag. The tag consists of a header, frames and optional padding. A field is a piece of information; one value, a string etc. A numeric string is a string that consists of the characters "0123456789" only. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [KEYWORDS]. 3. Default flags The default settings for the frames described in this document can be divided into the following classes. The flags may be set differently if found more suitable by the software. 1. Discarded if tag is altered, discarded if file is altered. None. 2. Discarded if tag is altered, preserved if file is altered. None. 3. Preserved if tag is altered, discarded if file is altered. ASPI, AENC, ETCO, EQU2, MLLT, POSS, SEEK, SYLT, SYTC, RVA2, TENC, TLEN 4. Preserved if tag is altered, preserved if file is altered. The rest of the frames. 4. Declared ID3v2 frames The following frames are declared in this draft. 4.19 AENC Audio encryption 4.14 APIC Attached picture 4.30 ASPI Audio seek point index 4.10 COMM Comments 4.24 COMR Commercial frame 4.25 ENCR Encryption method registration 4.12 EQU2 Equalisation (2) 4.5 ETCO Event timing codes 4.15 GEOB General encapsulated object 4.26 GRID Group identification registration 4.20 LINK Linked information 4.4 MCDI Music CD identifier 4.6 MLLT MPEG location lookup table 4.23 OWNE Ownership frame 4.27 PRIV Private frame 4.16 PCNT Play counter 4.17 POPM Popularimeter 4.21 POSS Position synchronisation frame 4.18 RBUF Recommended buffer size 4.11 RVA2 Relative volume adjustment (2) 4.13 RVRB Reverb 4.29 SEEK Seek frame 4.28 SIGN Signature frame 4.9 SYLT Synchronised lyric/text 4.7 SYTC Synchronised tempo codes 4.2.1 TALB Album/Movie/Show title 4.2.3 TBPM BPM (beats per minute) 4.2.2 TCOM Composer 4.2.3 TCON Content type 4.2.4 TCOP Copyright message 4.2.5 TDEN Encoding time 4.2.5 TDLY Playlist delay 4.2.5 TDOR Original release time 4.2.5 TDRC Recording time 4.2.5 TDRL Release time 4.2.5 TDTG Tagging time 4.2.2 TENC Encoded by 4.2.2 TEXT Lyricist/Text writer 4.2.3 TFLT File type 4.2.2 TIPL Involved people list 4.2.1 TIT1 Content group description 4.2.1 TIT2 Title/songname/content description 4.2.1 TIT3 Subtitle/Description refinement 4.2.3 TKEY Initial key 4.2.3 TLAN Language(s) 4.2.3 TLEN Length 4.2.2 TMCL Musician credits list 4.2.3 TMED Media type 4.2.3 TMOO Mood 4.2.1 TOAL Original album/movie/show title 4.2.5 TOFN Original filename 4.2.2 TOLY Original lyricist(s)/text writer(s) 4.2.2 TOPE Original artist(s)/performer(s) 4.2.4 TOWN File owner/licensee 4.2.2 TPE1 Lead performer(s)/Soloist(s) 4.2.2 TPE2 Band/orchestra/accompaniment 4.2.2 TPE3 Conductor/performer refinement 4.2.2 TPE4 Interpreted, remixed, or otherwise modified by 4.2.1 TPOS Part of a set 4.2.4 TPRO Produced notice 4.2.4 TPUB Publisher 4.2.1 TRCK Track number/Position in set 4.2.4 TRSN Internet radio station name 4.2.4 TRSO Internet radio station owner 4.2.5 TSOA Album sort order 4.2.5 TSOP Performer sort order 4.2.5 TSOT Title sort order 4.2.1 TSRC ISRC (international standard recording code) 4.2.5 TSSE Software/Hardware and settings used for encoding 4.2.1 TSST Set subtitle 4.2.2 TXXX User defined text information frame 4.1 UFID Unique file identifier 4.22 USER Terms of use 4.8 USLT Unsynchronised lyric/text transcription 4.3.1 WCOM Commercial information 4.3.1 WCOP Copyright/Legal information 4.3.1 WOAF Official audio file webpage 4.3.1 WOAR Official artist/performer webpage 4.3.1 WOAS Official audio source webpage 4.3.1 WORS Official Internet radio station homepage 4.3.1 WPAY Payment 4.3.1 WPUB Publishers official webpage 4.3.2 WXXX User defined URL link frame 4.1. Unique file identifier This frame's purpose is to be able to identify the audio file in a database, that may provide more information relevant to the content. Since standardisation of such a database is beyond this document, all UFID frames begin with an 'owner identifier' field. It is a null- terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific database implementation. Questions regarding the database should be sent to the indicated email address. The URL should not be used for the actual database queries. The string "http://www.id3.org/dummy/ufid.html" should be used for tests. The 'Owner identifier' must be non-empty (more than just a termination). The 'Owner identifier' is then followed by the actual identifier, which may be up to 64 bytes. There may be more than one "UFID" frame in a tag, but only one with the same 'Owner identifier'. <Header for 'Unique file identifier', ID: "UFID"> Owner identifier <text string> $00 Identifier <up to 64 bytes binary data> 4.2. Text information frames The text information frames are often the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. All text information frames supports multiple strings, stored as a null separated list, where null is reperesented by the termination code for the charater encoding. All text frame identifiers begin with "T". Only text frame identifiers begin with "T", with the exception of the "TXXX" frame. All the text information frames have the following format: <Header for 'Text information frame', ID: "T000" - "TZZZ", excluding "TXXX" described in 4.2.6.> Text encoding $xx Information <text string(s) according to encoding> 4.2.1. Identification frames TIT1 The 'Content group description' frame is used if the sound belongs to a larger category of sounds/music. For example, classical music is often sorted in different musical sections (e.g. "Piano Concerto", "Weather - Hurricane"). TIT2 The 'Title/Songname/Content description' frame is the actual name of the piece (e.g. "Adagio", "Hurricane Donna"). TIT3 The 'Subtitle/Description refinement' frame is used for information directly related to the contents title (e.g. "Op. 16" or "Performed live at Wembley"). TALB The 'Album/Movie/Show title' frame is intended for the title of the recording (or source of sound) from which the audio in the file is taken. TOAL The 'Original album/movie/show title' frame is intended for the title of the original recording (or source of sound), if for example the music in the file should be a cover of a previously released song. TRCK The 'Track number/Position in set' frame is a numeric string containing the order number of the audio-file on its original recording. This MAY be extended with a "/" character and a numeric string containing the total number of tracks/elements on the original recording. E.g. "4/9". TPOS The 'Part of a set' frame is a numeric string that describes which part of a set the audio came from. This frame is used if the source described in the "TALB" frame is divided into several mediums, e.g. a double CD. The value MAY be extended with a "/" character and a numeric string containing the total number of parts in the set. E.g. "1/2". TSST The 'Set subtitle' frame is intended for the subtitle of the part of a set this track belongs to. TSRC The 'ISRC' frame should contain the International Standard Recording Code [ISRC] (12 characters). 4.2.2. Involved persons frames TPE1 The 'Lead artist/Lead performer/Soloist/Performing group' is used for the main artist. TPE2 The 'Band/Orchestra/Accompaniment' frame is used for additional information about the performers in the recording. TPE3 The 'Conductor' frame is used for the name of the conductor. TPE4 The 'Interpreted, remixed, or otherwise modified by' frame contains more information about the people behind a remix and similar interpretations of another existing piece. TOPE The 'Original artist/performer' frame is intended for the performer of the original recording, if for example the music in the file should be a cover of a previously released song. TEXT The 'Lyricist/Text writer' frame is intended for the writer of the text or lyrics in the recording. TOLY The 'Original lyricist/text writer' frame is intended for the text writer of the original recording, if for example the music in the file should be a cover of a previously released song. TCOM The 'Composer' frame is intended for the name of the composer. TMCL The 'Musician credits list' is intended as a mapping between instruments and the musician that played it. Every odd field is an instrument and every even is an artist or a comma delimited list of artists. TIPL The 'Involved people list' is very similar to the musician credits list, but maps between functions, like producer, and names. TENC The 'Encoded by' frame contains the name of the person or organisation that encoded the audio file. This field may contain a copyright message, if the audio file also is copyrighted by the encoder. 4.2.3. Derived and subjective properties frames TBPM The 'BPM' frame contains the number of beats per minute in the main part of the audio. The BPM is an integer and represented as a numerical string. TLEN The 'Length' frame contains the length of the audio file in milliseconds, represented as a numeric string. TKEY The 'Initial key' frame contains the musical key in which the sound starts. It is represented as a string with a maximum length of three characters. The ground keys are represented with "A","B","C","D","E", "F" and "G" and halfkeys represented with "b" and "#". Minor is represented as "m", e.g. "Dbm" $00. Off key is represented with an "o" only. TLAN The 'Language' frame should contain the languages of the text or lyrics spoken or sung in the audio. The language is represented with three characters according to ISO-639-2 [ISO-639-2]. If more than one language is used in the text their language codes should follow according to the amount of their usage, e.g. "eng" $00 "sve" $00. TCON The 'Content type', which ID3v1 was stored as a one byte numeric value only, is now a string. You may use one or several of the ID3v1 types as numerical strings, or, since the category list would be impossible to maintain with accurate and up to date categories, define your own. Example: "21" $00 "Eurodisco" $00 You may also use any of the following keywords: RX Remix CR Cover TFLT The 'File type' frame indicates which type of audio this tag defines. The following types and refinements are defined: MIME MIME type follows MPG MPEG Audio /1 MPEG 1/2 layer I /2 MPEG 1/2 layer II /3 MPEG 1/2 layer III /2.5 MPEG 2.5 /AAC Advanced audio compression VQF Transform-domain Weighted Interleave Vector Quantisation PCM Pulse Code Modulated audio but other types may be used, but not for these types though. This is used in a similar way to the predefined types in the "TMED" frame, but without parentheses. If this frame is not present audio type is assumed to be "MPG". TMED The 'Media type' frame describes from which media the sound originated. This may be a text string or a reference to the predefined media types found in the list below. Example: "VID/PAL/VHS" $00. DIG Other digital media /A Analogue transfer from media ANA Other analogue media /WAC Wax cylinder /8CA 8-track tape cassette CD CD /A Analogue transfer from media /DD DDD /AD ADD /AA AAD LD Laserdisc TT Turntable records /33 33.33 rpm /45 45 rpm /71 71.29 rpm /76 76.59 rpm /78 78.26 rpm /80 80 rpm MD MiniDisc /A Analogue transfer from media DAT DAT /A Analogue transfer from media /1 standard, 48 kHz/16 bits, linear /2 mode 2, 32 kHz/16 bits, linear /3 mode 3, 32 kHz/12 bits, non-linear, low speed /4 mode 4, 32 kHz/12 bits, 4 channels /5 mode 5, 44.1 kHz/16 bits, linear /6 mode 6, 44.1 kHz/16 bits, 'wide track' play DCC DCC /A Analogue transfer from media DVD DVD /A Analogue transfer from media TV Television /PAL PAL /NTSC NTSC /SECAM SECAM VID Video /PAL PAL /NTSC NTSC /SECAM SECAM /VHS VHS /SVHS S-VHS /BETA BETAMAX RAD Radio /FM FM /AM AM /LW LW /MW MW TEL Telephone /I ISDN MC MC (normal cassette) /4 4.75 cm/s (normal speed for a two sided cassette) /9 9.5 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) REE Reel /9 9.5 cm/s /19 19 cm/s /38 38 cm/s /76 76 cm/s /I Type I cassette (ferric/normal) /II Type II cassette (chrome) /III Type III cassette (ferric chrome) /IV Type IV cassette (metal) TMOO The 'Mood' frame is intended to reflect the mood of the audio with a few keywords, e.g. "Romantic" or "Sad". 4.2.4. Rights and license frames TCOP The 'Copyright message' frame, in which the string must begin with a year and a space character (making five characters), is intended for the copyright holder of the original sound, not the audio file itself. The absence of this frame means only that the copyright information is unavailable or has been removed, and must not be interpreted to mean that the audio is public domain. Every time this field is displayed the field must be preceded with "Copyright " (C) " ", where (C) is one character showing a C in a circle. TPRO The 'Produced notice' frame, in which the string must begin with a year and a space character (making five characters), is intended for the production copyright holder of the original sound, not the audio file itself. The absence of this frame means only that the production copyright information is unavailable or has been removed, and must not be interpreted to mean that the audio is public domain. Every time this field is displayed the field must be preceded with "Produced " (P) " ", where (P) is one character showing a P in a circle. TPUB The 'Publisher' frame simply contains the name of the label or publisher. TOWN The 'File owner/licensee' frame contains the name of the owner or licensee of the file and it's contents. TRSN The 'Internet radio station name' frame contains the name of the internet radio station from which the audio is streamed. TRSO The 'Internet radio station owner' frame contains the name of the owner of the internet radio station from which the audio is streamed. 4.2.5. Other text frames TOFN The 'Original filename' frame contains the preferred filename for the file, since some media doesn't allow the desired length of the filename. The filename is case sensitive and includes its suffix. TDLY The 'Playlist delay' defines the numbers of milliseconds of silence that should be inserted before this audio. The value zero indicates that this is a part of a multifile audio track that should be played continuously. TDEN The 'Encoding time' frame contains a timestamp describing when the audio was encoded. Timestamp format is described in the ID3v2 structure document [ID3v2-strct]. TDOR The 'Original release time' frame contains a timestamp describing when the original recording of the audio was released. Timestamp format is described in the ID3v2 structure document [ID3v2-strct]. TDRC The 'Recording time' frame contains a timestamp describing when the audio was recorded. Timestamp format is described in the ID3v2 structure document [ID3v2-strct]. TDRL The 'Release time' frame contains a timestamp describing when the audio was first released. Timestamp format is described in the ID3v2 structure document [ID3v2-strct]. TDTG The 'Tagging time' frame contains a timestamp describing then the audio was tagged. Timestamp format is described in the ID3v2 structure document [ID3v2-strct]. TSSE The 'Software/Hardware and settings used for encoding' frame includes the used audio encoder and its settings when the file was encoded. Hardware refers to hardware encoders, not the computer on which a program was run. TSOA The 'Album sort order' frame defines a string which should be used instead of the album name (TALB) for sorting purposes. E.g. an album named "A Soundtrack" might preferably be sorted as "Soundtrack". TSOP The 'Performer sort order' frame defines a string which should be used instead of the performer (TPE2) for sorting purposes. TSOT The 'Title sort order' frame defines a string which should be used instead of the title (TIT2) for sorting purposes. 4.2.6. User defined text information frame This frame is intended for one-string text information concerning the audio file in a similar way to the other "T"-frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual string. There may be more than one "TXXX" frame in each tag, but only one with the same description. <Header for 'User defined text information frame', ID: "TXXX"> Text encoding $xx Description <text string according to encoding> $00 (00) Value <text string according to encoding> 4.3. URL link frames With these frames dynamic data such as webpages with touring information, price information or plain ordinary news can be added to the tag. There may only be one URL [URL] link frame of its kind in an tag, except when stated otherwise in the frame description. If the text string is followed by a string termination, all the following information should be ignored and not be displayed. All URL link frame identifiers begins with "W". Only URL link frame identifiers begins with "W", except for "WXXX". All URL link frames have the following format: <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX" described in 4.3.2.> URL <text string> 4.3.1. URL link frames - details WCOM The 'Commercial information' frame is a URL pointing at a webpage with information such as where the album can be bought. There may be more than one "WCOM" frame in a tag, but not with the same content. WCOP The 'Copyright/Legal information' frame is a URL pointing at a webpage where the terms of use and ownership of the file is described. WOAF The 'Official audio file webpage' frame is a URL pointing at a file specific webpage. WOAR The 'Official artist/performer webpage' frame is a URL pointing at the artists official webpage. There may be more than one "WOAR" frame in a tag if the audio contains more than one performer, but not with the same content. WOAS The 'Official audio source webpage' frame is a URL pointing at the official webpage for the source of the audio file, e.g. a movie. WORS The 'Official Internet radio station homepage' contains a URL pointing at the homepage of the internet radio station. WPAY The 'Payment' frame is a URL pointing at a webpage that will handle the process of paying for this file. WPUB The 'Publishers official webpage' frame is a URL pointing at the official webpage for the publisher. 4.3.2. User defined URL link frame This frame is intended for URL [URL] links concerning the audio file in a similar way to the other "W"-frames. The frame body consists of a description of the string, represented as a terminated string, followed by the actual URL. The URL is always encoded with ISO-8859-1 [ISO-8859-1]. There may be more than one "WXXX" frame in each tag, but only one with the same description. <Header for 'User defined URL link frame', ID: "WXXX"> Text encoding $xx Description <text string according to encoding> $00 (00) URL <text string> 4.4. Music CD identifier This frame is intended for music that comes from a CD, so that the CD can be identified in databases such as the CDDB [CDDB]. The frame consists of a binary dump of the Table Of Contents, TOC, from the CD, which is a header of 4 bytes and then 8 bytes/track on the CD plus 8 bytes for the 'lead out', making a maximum of 804 bytes. The offset to the beginning of every track on the CD should be described with a four bytes absolute CD-frame address per track, and not with absolute time. When this frame is used the presence of a valid "TRCK" frame is REQUIRED, even if the CD's only got one track. It is recommended that this frame is always added to tags originating from CDs. There may only be one "MCDI" frame in each tag. <Header for 'Music CD identifier', ID: "MCDI"> CD TOC <binary data> 4.5. Event timing codes This frame allows synchronisation with key events in the audio. The header is: <Header for 'Event timing codes', ID: "ETCO"> Time stamp format $xx Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Absolute time means that every stamp contains the time from the beginning of the file. Followed by a list of key events in the following format: Type of event $xx Time stamp $xx (xx ...) The 'Time stamp' is set to zero if directly at the beginning of the sound or after the previous event. All events MUST be sorted in chronological order. The type of event is as follows: $00 padding (has no meaning) $01 end of initial silence $02 intro start $03 main part start $04 outro start $05 outro end $06 verse start $07 refrain start $08 interlude start $09 theme start $0A variation start $0B key change $0C time change $0D momentary unwanted noise (Snap, Crackle & Pop) $0E sustained noise $0F sustained noise end $10 intro end $11 main part end $12 verse end $13 refrain end $14 theme end $15 profanity $16 profanity end $17-$DF reserved for future use $E0-$EF not predefined synch 0-F $F0-$FC reserved for future use $FD audio end (start of silence) $FE audio file ends $FF one more byte of events follows (all the following bytes with the value $FF have the same function) Terminating the start events such as "intro start" is OPTIONAL. The 'Not predefined synch's ($E0-EF) are for user events. You might want to synchronise your music to something, like setting off an explosion on-stage, activating a screensaver etc. There may only be one "ETCO" frame in each tag. 4.6. MPEG location lookup table To increase performance and accuracy of jumps within a MPEG [MPEG] audio file, frames with time codes in different locations in the file might be useful. This ID3v2 frame includes references that the software can use to calculate positions in the file. After the frame header follows a descriptor of how much the 'frame counter' should be increased for every reference. If this value is two then the first reference points out the second frame, the 2nd reference the 4th frame, the 3rd reference the 6th frame etc. In a similar way the 'bytes between reference' and 'milliseconds between reference' points out bytes and milliseconds respectively. Each reference consists of two parts; a certain number of bits, as defined in 'bits for bytes deviation', that describes the difference between what is said in 'bytes between reference' and the reality and a certain number of bits, as defined in 'bits for milliseconds deviation', that describes the difference between what is said in 'milliseconds between reference' and the reality. The number of bits in every reference, i.e. 'bits for bytes deviation'+'bits for milliseconds deviation', must be a multiple of four. There may only be one "MLLT" frame in each tag. <Header for 'Location lookup table', ID: "MLLT"> MPEG frames between reference $xx xx Bytes between reference $xx xx xx Milliseconds between reference $xx xx xx Bits for bytes deviation $xx Bits for milliseconds dev. $xx Then for every reference the following data is included; Deviation in bytes %xxx.... Deviation in milliseconds %xxx.... 4.7. Synchronised tempo codes For a more accurate description of the tempo of a musical piece, this frame might be used. After the header follows one byte describing which time stamp format should be used. Then follows one or more tempo codes. Each tempo code consists of one tempo part and one time part. The tempo is in BPM described with one or two bytes. If the first byte has the value $FF, one more byte follows, which is added to the first giving a range from 2 - 510 BPM, since $00 and $01 is reserved. $00 is used to describe a beat-free time period, which is not the same as a music-free time period. $01 is used to indicate one single beat-stroke followed by a beat-free period. The tempo descriptor is followed by a time stamp. Every time the tempo in the music changes, a tempo descriptor may indicate this for the player. All tempo descriptors MUST be sorted in chronological order. The first beat-stroke in a time-period is at the same time as the beat description occurs. There may only be one "SYTC" frame in each tag. <Header for 'Synchronised tempo codes', ID: "SYTC"> Time stamp format $xx Tempo data <binary data> Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Absolute time means that every stamp contains the time from the beginning of the file. 4.8. Unsynchronised lyrics/text transcription This frame contains the lyrics of the song or a text transcription of other vocal activities. The head includes an encoding descriptor and a content descriptor. The body consists of the actual text. The 'Content descriptor' is a terminated string. If no descriptor is entered, 'Content descriptor' is $00 (00) only. Newline characters are allowed in the text. There may be more than one 'Unsynchronised lyrics/text transcription' frame in each tag, but only one with the same language and content descriptor. <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT"> Text encoding $xx Language $xx xx xx Content descriptor <text string according to encoding> $00 (00) Lyrics/text <full text string according to encoding> 4.9. Synchronised lyrics/text This is another way of incorporating the words, said or sung lyrics, in the audio file as text, this time, however, in sync with the audio. It might also be used to describing events e.g. occurring on a stage or on the screen in sync with the audio. The header includes a content descriptor, represented with as terminated text string. If no descriptor is entered, 'Content descriptor' is $00 (00) only. <Header for 'Synchronised lyrics/text', ID: "SYLT"> Text encoding $xx Language $xx xx xx Time stamp format $xx Content type $xx Content descriptor <text string according to encoding> $00 (00) Content type: $00 is other $01 is lyrics $02 is text transcription $03 is movement/part name (e.g. "Adagio") $04 is events (e.g. "Don Quijote enters the stage") $05 is chord (e.g. "Bb F Fsus") $06 is trivia/'pop up' information $07 is URLs to webpages $08 is URLs to images Time stamp format: $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit Absolute time means that every stamp contains the time from the beginning of the file. The text that follows the frame header differs from that of the unsynchronised lyrics/text transcription in one major way. Each syllable (or whatever size of text is considered to be convenient by the encoder) is a null terminated string followed by a time stamp denoting where in the sound file it belongs. Each sync thus has the following structure: Terminated text to be synced (typically a syllable) Sync identifier (terminator to above string) $00 (00) Time stamp $xx (xx ...) The 'time stamp' is set to zero or the whole sync is omitted if located directly at the beginning of the sound. All time stamps should be sorted in chronological order. The sync can be considered as a validator of the subsequent string. Newline characters are allowed in all "SYLT" frames and MUST be used after every entry (name, event etc.) in a frame with the content type $03 - $04. A few considerations regarding whitespace characters: Whitespace separating words should mark the beginning of a new word, thus occurring in front of the first syllable of a new word. This is also valid for new line characters. A syllable followed by a comma should not be broken apart with a sync (both the syllable and the comma should be before the sync). An example: The "USLT" passage "Strangers in the night" $0A "Exchanging glances" would be "SYLT" encoded as: "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx xx "glan" $00 xx xx "ces" $00 xx xx There may be more than one "SYLT" frame in each tag, but only one with the same language and content descriptor. 4.10. Comments This frame is intended for any kind of full text information that does not fit in any other frame. It consists of a frame header followed by encoding, language and content descriptors and is ended with the actual comment as a text string. Newline characters are allowed in the comment text string. There may be more than one comment frame in each tag, but only one with the same language and content descriptor. <Header for 'Comment', ID: "COMM"> Text encoding $xx Language $xx xx xx Short content descrip. <text string according to encoding> $00 (00) The actual text <full text string according to encoding> 4.11. Relative volume adjustment (2) This is a more subjective frame than the previous ones. It allows the user to say how much he wants to increase/decrease the volume on each channel when the file is played. The purpose is to be able to align all files to a reference volume, so that you don't have to change the volume constantly. This frame may also be used to balance adjust the audio. The volume adjustment is encoded as a fixed point decibel value, 16 bit signed integer representing (adjustment*512), giving +/- 64 dB with a precision of 0.001953125 dB. E.g. +2 dB is stored as $04 00 and -2 dB is $FC 00. There may be more than one "RVA2" frame in each tag, but only one with the same identification string. <Header for 'Relative volume adjustment (2)', ID: "RVA2"> Identification <text string> $00 The 'identification' string is used to identify the situation and/or device where this adjustment should apply. The following is then repeated for every channel Type of channel $xx Volume adjustment $xx xx Bits representing peak $xx Peak volume $xx (xx ...) Type of channel: $00 Other $01 Master volume $02 Front right $03 Front left $04 Back right $05 Back left $06 Front centre $07 Back centre $08 Subwoofer Bits representing peak can be any number between 0 and 255. 0 means that there is no peak volume field. The peak volume field is always padded to whole bytes, setting the most significant bits to zero. 4.12. Equalisation (2) This is another subjective, alignment frame. It allows the user to predefine an equalisation curve within the audio file. There may be more than one "EQU2" frame in each tag, but only one with the same identification string. <Header of 'Equalisation (2)', ID: "EQU2"> Interpolation method $xx Identification <text string> $00 The 'interpolation method' describes which method is preferred when an interpolation between the adjustment point that follows. The following methods are currently defined: $00 Band No interpolation is made. A jump from one adjustment level to another occurs in the middle between two adjustment points. $01 Linear Interpolation between adjustment points is linear. The 'identification' string is used to identify the situation and/or device where this adjustment should apply. The following is then repeated for every adjustment point Frequency $xx xx Volume adjustment $xx xx The frequency is stored in units of 1/2 Hz, giving it a range from 0 to 32767 Hz. The volume adjustment is encoded as a fixed point decibel value, 16 bit signed integer representing (adjustment*512), giving +/- 64 dB with a precision of 0.001953125 dB. E.g. +2 dB is stored as $04 00 and -2 dB is $FC 00. Adjustment points should be ordered by frequency and one frequency should only be described once in the frame. 4.13. Reverb Yet another subjective frame, with which you can adjust echoes of different kinds. Reverb left/right is the delay between every bounce in ms. Reverb bounces left/right is the number of bounces that should be made. $FF equals an infinite number of bounces. Feedback is the amount of volume that should be returned to the next echo bounce. $00 is 0%, $FF is 100%. If this value were $7F, there would be 50% volume reduction on the first bounce, 50% of that on the second and so on. Left to left means the sound from the left bounce to be played in the left speaker, while left to right means sound from the left bounce to be played in the right speaker. 'Premix left to right' is the amount of left sound to be mixed in the right before any reverb is applied, where $00 id 0% and $FF is 100%. 'Premix right to left' does the same thing, but right to left. Setting both premix to $FF would result in a mono output (if the reverb is applied symmetric). There may only be one "RVRB" frame in each tag. <Header for 'Reverb', ID: "RVRB"> Reverb left (ms) $xx xx Reverb right (ms) $xx xx Reverb bounces, left $xx Reverb bounces, right $xx Reverb feedback, left to left $xx Reverb feedback, left to right $xx Reverb feedback, right to right $xx Reverb feedback, right to left $xx Premix left to right $xx Premix right to left $xx 4.14. Attached picture This frame contains a picture directly related to the audio file. Image format is the MIME type and subtype [MIME] for the image. In the event that the MIME media type name is omitted, "image/" will be implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format should be used when interoperability is wanted. Description is a short description of the picture, represented as a terminated text string. There may be several pictures attached to one file, each in their individual "APIC" frame, but only one with the same content descriptor. There may only be one picture with the picture type declared as picture type $01 and $02 respectively. There is the possibility to put only a link to the image file by using the 'MIME type' "-->" and having a complete URL [URL] instead of picture data. The use of linked files should however be used sparingly since there is the risk of separation of files. <Header for 'Attached picture', ID: "APIC"> Text encoding $xx MIME type <text string> $00 Picture type $xx Description <text string according to encoding> $00 (00) Picture data <binary data> Picture type: $00 Other $01 32x32 pixels 'file icon' (PNG only) $02 Other file icon $03 Cover (front) $04 Cover (back) $05 Leaflet page $06 Media (e.g. label side of CD) $07 Lead artist/lead performer/soloist $08 Artist/performer $09 Conductor $0A Band/Orchestra $0B Composer $0C Lyricist/text writer $0D Recording Location $0E During recording $0F During performance $10 Movie/video screen capture $11 A bright coloured fish $12 Illustration $13 Band/artist logotype $14 Publisher/Studio logotype 4.15. General encapsulated object In this frame any type of file can be encapsulated. After the header, 'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The filename is case sensitive and is encoded as 'Encoding'. Then follows a content description as terminated string, encoded as 'Encoding'. The last thing in the frame is the actual object. The first two strings may be omitted, leaving only their terminations. MIME type is always an ISO-8859-1 text string. There may be more than one "GEOB" frame in each tag, but only one with the same content descriptor. <Header for 'General encapsulated object', ID: "GEOB"> Text encoding $xx MIME type <text string> $00 Filename <text string according to encoding> $00 (00) Content description <text string according to encoding> $00 (00) Encapsulated object <binary data> 4.16. Play counter This is simply a counter of the number of times a file has been played. The value is increased by one every time the file begins to play. There may only be one "PCNT" frame in each tag. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger. The counter must be at least 32-bits long to begin with. <Header for 'Play counter', ID: "PCNT"> Counter $xx xx xx xx (xx ...) 4.17. Popularimeter The purpose of this frame is to specify how good an audio file is. Many interesting applications could be found to this frame such as a playlist that features better audio files more often than others or it could be used to profile a person's taste and find other 'good' files by comparing people's profiles. The frame contains the email address to the user, one rating byte and a four byte play counter, intended to be increased with one for every time the file is played. The email is a terminated string. The rating is 1-255 where 1 is worst and 255 is best. 0 is unknown. If no personal counter is wanted it may be omitted. When the counter reaches all one's, one byte is inserted in front of the counter thus making the counter eight bits bigger in the same away as the play counter ("PCNT"). There may be more than one "POPM" frame in each tag, but only one with the same email address. <Header for 'Popularimeter', ID: "POPM"> Email to user <text string> $00 Rating $xx Counter $xx xx xx xx (xx ...) 4.18. Recommended buffer size Sometimes the server from which an audio file is streamed is aware of transmission or coding problems resulting in interruptions in the audio stream. In these cases, the size of the buffer can be recommended by the server using this frame. If the 'embedded info flag' is true (1) then this indicates that an ID3 tag with the maximum size described in 'Buffer size' may occur in the audio stream. In such case the tag should reside between two MPEG [MPEG] frames, if the audio is MPEG encoded. If the position of the next tag is known, 'offset to next tag' may be used. The offset is calculated from the end of tag in which this frame resides to the first byte of the header in the next. This field may be omitted. Embedded tags are generally not recommended since this could render unpredictable behaviour from present software/hardware. For applications like streaming audio it might be an idea to embed tags into the audio stream though. If the clients connects to individual connections like HTTP and there is a possibility to begin every transmission with a tag, then this tag should include a 'recommended buffer size' frame. If the client is connected to a arbitrary point in the stream, such as radio or multicast, then the 'recommended buffer size' frame SHOULD be included in every tag. The 'Buffer size' should be kept to a minimum. There may only be one "RBUF" frame in each tag. <Header for 'Recommended buffer size', ID: "RBUF"> Buffer size $xx xx xx Embedded info flag %0000000x Offset to next tag $xx xx xx xx 4.19. Audio encryption This frame indicates if the actual audio stream is encrypted, and by whom. Since standardisation of such encryption scheme is beyond this document, all "AENC" frames begin with a terminated string with a URL containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific encrypted audio file. Questions regarding the encrypted audio should be sent to the email address specified. If a $00 is found directly after the 'Frame size' and the audio file indeed is encrypted, the whole file may be considered useless. After the 'Owner identifier', a pointer to an unencrypted part of the audio can be specified. The 'Preview start' and 'Preview length' is described in frames. If no part is unencrypted, these fields should be left zeroed. After the 'preview length' field follows optionally a data block required for decryption of the audio. There may be more than one "AENC" frames in a tag, but only one with the same 'Owner identifier'. <Header for 'Audio encryption', ID: "AENC"> Owner identifier <text string> $00 Preview start $xx xx Preview length $xx xx Encryption info <binary data> 4.20. Linked information To keep information duplication as low as possible this frame may be used to link information from another ID3v2 tag that might reside in another audio file or alone in a binary file. It is RECOMMENDED that this method is only used when the files are stored on a CD-ROM or other circumstances when the risk of file separation is low. The frame contains a frame identifier, which is the frame that should be linked into this tag, a URL [URL] field, where a reference to the file where the frame is given, and additional ID data, if needed. Data should be retrieved from the first tag found in the file to which this link points. There may be more than one "LINK" frame in a tag, but only one with the same contents. A linked frame is to be considered as part of the tag and has the same restrictions as if it was a physical part of the tag (i.e. only one "RVRB" frame allowed, whether it's linked or not). <Header for 'Linked information', ID: "LINK"> Frame identifier $xx xx xx xx URL <text string> $00 ID and additional data <text string(s)> Frames that may be linked and need no additional data are "ASPI", "ETCO", "EQU2", "MCID", "MLLT", "OWNE", "RVA2", "RVRB", "SYTC", the text information frames and the URL link frames. The "AENC", "APIC", "GEOB" and "TXXX" frames may be linked with the content descriptor as additional ID data. The "USER" frame may be linked with the language field as additional ID data. The "PRIV" frame may be linked with the owner identifier as additional ID data. The "COMM", "SYLT" and "USLT" frames may be linked with three bytes of language descriptor directly followed by a content descriptor as additional ID data. 4.21. Position synchronisation frame This frame delivers information to the listener of how far into the audio stream he picked up; in effect, it states the time offset from the first frame in the stream. The frame layout is: <Head for 'Position synchronisation', ID: "POSS"> Time stamp format $xx Position $xx (xx ...) Where time stamp format is: $01 Absolute time, 32 bit sized, using MPEG frames as unit $02 Absolute time, 32 bit sized, using milliseconds as unit and position is where in the audio the listener starts to receive, i.e. the beginning of the next frame. If this frame is used in the beginning of a file the value is always 0. There may only be one "POSS" frame in each tag. 4.22. Terms of use frame This frame contains a brief description of the terms of use and ownership of the file. More detailed information concerning the legal terms might be available through the "WCOP" frame. Newlines are allowed in the text. There may be more than one 'Terms of use' frame in a tag, but only one with the same 'Language'. <Header for 'Terms of use frame', ID: "USER"> Text encoding $xx Language $xx xx xx The actual text <text string according to encoding> 4.23. Ownership frame The ownership frame might be used as a reminder of a made transaction or, if signed, as proof. Note that the "USER" and "TOWN" frames are good to use in conjunction with this one. The frame begins, after the frame ID, size and encoding fields, with a 'price paid' field. The first three characters of this field contains the currency used for the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic currency code. Concatenated to this is the actual price paid, as a numerical string using "." as the decimal separator. Next is an 8 character date string (YYYYMMDD) followed by a string with the name of the seller as the last field in the frame. There may only be one "OWNE" frame in a tag. <Header for 'Ownership frame', ID: "OWNE"> Text encoding $xx Price paid <text string> $00 Date of purch. <text string> Seller <text string according to encoding> 4.24. Commercial frame This frame enables several competing offers in the same tag by bundling all needed information. That makes this frame rather complex but it's an easier solution than if one tries to achieve the same result with several frames. The frame begins, after the frame ID, size and encoding fields, with a price string field. A price is constructed by one three character currency code, encoded according to ISO 4217 [ISO-4217] alphabetic currency code, followed by a numerical value where "." is used as decimal separator. In the price string several prices may be concatenated, separated by a "/" character, but there may only be one currency of each type. The price string is followed by an 8 character date string in the format YYYYMMDD, describing for how long the price is valid. After that is a contact URL, with which the user can contact the seller, followed by a one byte 'received as' field. It describes how the audio is delivered when bought according to the following list: $00 Other $01 Standard CD album with other songs $02 Compressed audio on CD $03 File over the Internet $04 Stream over the Internet $05 As note sheets $06 As note sheets in a book with other sheets $07 Music on other media $08 Non-musical merchandise Next follows a terminated string with the name of the seller followed by a terminated string with a short description of the product. The last thing is the ability to include a company logotype. The first of them is the 'Picture MIME type' field containing information about which picture format is used. In the event that the MIME media type name is omitted, "image/" will be implied. Currently only "image/png" and "image/jpeg" are allowed. This format string is followed by the binary picture data. This two last fields may be omitted if no picture is attached. There may be more than one 'commercial frame' in a tag, but no two may be identical. <Header for 'Commercial frame', ID: "COMR"> Text encoding $xx Price string <text string> $00 Valid until <text string> Contact URL <text string> $00 Received as $xx Name of seller <text string according to encoding> $00 (00) Description <text string according to encoding> $00 (00) Picture MIME type <string> $00 Seller logo <binary data> 4.25. Encryption method registration To identify with which method a frame has been encrypted the encryption method must be registered in the tag with this frame. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this specific encryption method. Questions regarding the encryption method should be sent to the indicated email address. The 'Method symbol' contains a value that is associated with this method throughout the whole tag, in the range $80-F0. All other values are reserved. The 'Method symbol' may optionally be followed by encryption specific data. There may be several "ENCR" frames in a tag but only one containing the same symbol and only one containing the same owner identifier. The method must be used somewhere in the tag. See the description of the frame encryption flag in the ID3v2 structure document [ID3v2-strct] for more information. <Header for 'Encryption method registration', ID: "ENCR"> Owner identifier <text string> $00 Method symbol $xx Encryption data <binary data> 4.26. Group identification registration This frame enables grouping of otherwise unrelated frames. This can be used when some frames are to be signed. To identify which frames belongs to a set of frames a group identifier must be registered in the tag with this frame. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for this grouping. Questions regarding the grouping should be sent to the indicated email address. The 'Group symbol' contains a value that associates the frame with this group throughout the whole tag, in the range $80-F0. All other values are reserved. The 'Group symbol' may optionally be followed by some group specific data, e.g. a digital signature. There may be several "GRID" frames in a tag but only one containing the same symbol and only one containing the same owner identifier. The group symbol must be used somewhere in the tag. See the description of the frame grouping flag in the ID3v2 structure document [ID3v2-strct] for more information. <Header for 'Group ID registration', ID: "GRID"> Owner identifier <text string> $00 Group symbol $xx Group dependent data <binary data> 4.27. Private frame This frame is used to contain information from a software producer that its program uses and does not fit into the other frames. The frame consists of an 'Owner identifier' string and the binary data. The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email address, or a link to a location where an email address can be found, that belongs to the organisation responsible for the frame. Questions regarding the frame should be sent to the indicated email address. The tag may contain more than one "PRIV" frame but only with different contents. <Header for 'Private frame', ID: "PRIV"> Owner identifier <text string> $00 The private data <binary data> 4.28. Signature frame This frame enables a group of frames, grouped with the 'Group identification registration', to be signed. Although signatures can reside inside the registration frame, it might be desired to store the signature elsewhere, e.g. in watermarks. There may be more than one 'signature frame' in a tag, but no two may be identical. <Header for 'Signature frame', ID: "SIGN"> Group symbol $xx Signature <binary data> 4.29. Seek frame This frame indicates where other tags in a file/stream can be found. The 'minimum offset to next tag' is calculated from the end of this tag to the beginning of the next. There may only be one 'seek frame' in a tag. <Header for 'Seek frame', ID: "SEEK"> Minimum offset to next tag $xx xx xx xx 4.30. Audio seek point index Audio files with variable bit rates are intrinsically difficult to deal with in the case of seeking within the file. The ASPI frame makes seeking easier by providing a list a seek points within the audio file. The seek points are a fractional offset within the audio data, providing a starting point from which to find an appropriate point to start decoding. The presence of an ASPI frame requires the existence of a TLEN frame, indicating the duration of the file in milliseconds. There may only be one 'audio seek point index' frame in a tag. <Header for 'Seek Point Index', ID: "ASPI"> Indexed data start (S) $xx xx xx xx Indexed data length (L) $xx xx xx xx Number of index points (N) $xx xx Bits per index point (b) $xx Then for every index point the following data is included; Fraction at index (Fi) $xx (xx) 'Indexed data start' is a byte offset from the beginning of the file. 'Indexed data length' is the byte length of the audio data being indexed. 'Number of index points' is the number of index points, as the name implies. The recommended number is 100. 'Bits per index point' is 8 or 16, depending on the chosen precision. 8 bits works well for short files (less than 5 minutes of audio), while 16 bits is advantageous for long files. 'Fraction at index' is the numerator of the fraction representing a relative position in the data. The denominator is 2 to the power of b. Here are the algorithms to be used in the calculation. The known data must be the offset of the start of the indexed data (S), the offset of the end of the indexed data (E), the number of index points (N), the offset at index i (Oi). We calculate the fraction at index i (Fi). Oi is the offset of the frame whose start is soonest after the point for which the time offset is (i/N * duration). The frame data should be calculated as follows: Fi = Oi/L * 2^b (rounded down to the nearest integer) Offset calculation should be calculated as follows from data in the frame: Oi = (Fi/2^b)*L (rounded up to the nearest integer) 5. Copyright Copyright (C) Martin Nilsson 2000. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that a reference to this document is included on all such copies and derivative works. However, this document itself may not be modified in any way and reissued as the original document. The limited permissions granted above are perpetual and will not be revoked. This document and the information contained herein is provided on an "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 6. References [CDDB] Compact Disc Data Base <url:http://www.cddb.com> [ID3v2.3.0] Martin Nilsson, "ID3v2 informal standard". <url:http://www.id3.org/id3v2.3.0.txt> [ID3v2-strct] Martin Nilsson, "ID3 tag version 2.4.0 - Main Structure" <url:http//www.id3.org/id3v2.4.0-structure.txt> [ISO-639-2] ISO/FDIS 639-2. Codes for the representation of names of languages, Part 2: Alpha-3 code. Technical committee / subcommittee: TC 37 / SC 2 [ISO-4217] ISO 4217:1995. Codes for the representation of currencies and funds. Technical committee / subcommittee: TC 68 [ISO-8859-1] ISO/IEC DIS 8859-1. 8-bit single-byte coded graphic character sets, Part 1: Latin alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2 [ISRC] ISO 3901:1986 International Standard Recording Code (ISRC). Technical committee / subcommittee: TC 46 / SC 9 [JFIF] JPEG File Interchange Format, version 1.02 <url:http://www.w3.org/Graphics/JPEG/jfif.txt> [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate Requirement Levels', RFC 2119, March 1997. <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt> [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC 2045, November 1996. <url:ftp://ftp.isi.edu/in-notes/rfc2045.txt> [MPEG] ISO/IEC 11172-3:1993. Coding of moving pictures and associated audio for digital storage media at up to about 1,5 Mbit/s, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC 13818-3:1995 Generic coding of moving pictures and associated audio information, Part 3: Audio. Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC DIS 13818-3 Generic coding of moving pictures and associated audio information, Part 3: Audio (Revision of ISO/IEC 13818-3:1995) [PNG] Portable Network Graphics, version 1.0 <url:http://www.w3.org/TR/REC-png-multi.html> [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource Locators (URL).", RFC 1738, December 1994. <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt> [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB Compressed Data Format Specification version 3.3", RFC 1950, May 1996. <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt> 7. Appendix A. Appendix A - Genre List from ID3v1 The following genres is defined in ID3v1 0.Blues 1.Classic Rock 2.Country 3.Dance 4.Disco 5.Funk 6.Grunge 7.Hip-Hop 8.Jazz 9.Metal 10.New Age 11.Oldies 12.Other 13.Pop 14.R&B 15.Rap 16.Reggae 17.Rock 18.Techno 19.Industrial 20.Alternative 21.Ska 22.Death Metal 23.Pranks 24.Soundtrack 25.Euro-Techno 26.Ambient 27.Trip-Hop 28.Vocal 29.Jazz+Funk 30.Fusion 31.Trance 32.Classical 33.Instrumental 34.Acid 35.House 36.Game 37.Sound Clip 38.Gospel 39.Noise 40.AlternRock 41.Bass 42.Soul 43.Punk 44.Space 45.Meditative 46.Instrumental Pop 47.Instrumental Rock 48.Ethnic 49.Gothic 50.Darkwave 51.Techno-Industrial 52.Electronic 53.Pop-Folk 54.Eurodance 55.Dream 56.Southern Rock 57.Comedy 58.Cult 59.Gangsta 60.Top 40 61.Christian Rap 62.Pop/Funk 63.Jungle 64.Native American 65.Cabaret 66.New Wave 67.Psychadelic 68.Rave 69.Showtunes 70.Trailer 71.Lo-Fi 72.Tribal 73.Acid Punk 74.Acid Jazz 75.Polka 76.Retro 77.Musical 78.Rock & Roll 79.Hard Rock 8. Author's Address Written by Martin Nilsson Rydsvgen 246 C. 30 SE-584 34 Linkping Sweden Email: nilsson@id3.org �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2.4.0-structure.txt����������������������������������������������0000664�0000000�0000000�00000067445�14662262111�0022145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Informal standard M. Nilsson Document: id3v2.4.0-structure.txt 16 September 2001 ID3 tag version 2.4.0 - Main Structure Status of this document This document is an informal standard and replaces the ID3v2.3.0 standard [ID3v2]. A formal standard will use another revision number even if the content is identical to document. The contents in this document may change for clarifications but never for added or altered functionallity. Distribution of this document is unlimited. Abstract This document describes the main structure of ID3v2.4.0, which is a revised version of the ID3v2 informal standard [ID3v2] version 2.3.0. The ID3v2 offers a flexible way of storing audio meta information within the audio file itself. The information may be technical information, such as equalisation curves, as well as title, performer, copyright etc. ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order to allow for implementations to be revised as easily as possible. 1. Table of contents Status of this document Abstract 1. Table of contents 2. Conventions in this document 2. Standard overview 3. ID3v2 overview 3.1. ID3v2 header 3.2. ID3v2 extended header 3.3. Padding 3.4. ID3v2 footer 4. ID3v2 frames overview 4.1. Frame header flags 4.1.1. Frame status flags 4.1.2. Frame format flags 5. Tag location 6. Unsynchronisation 6.1. The unsynchronisation scheme 6.2. Synchsafe integers 7. Copyright 8. References 9. Author's Address 2. Conventions in this document Text within "" is a text string exactly as it appears in a tag. Numbers preceded with $ are hexadecimal and numbers preceded with % are binary. $xx is used to indicate a byte with unknown content. %x is used to indicate a bit with unknown content. The most significant bit (MSB) of a byte is called 'bit 7' and the least significant bit (LSB) is called 'bit 0'. A tag is the whole tag described in this document. A frame is a block of information in the tag. The tag consists of a header, frames and optional padding. A field is a piece of information; one value, a string etc. A numeric string is a string that consists of the characters "0123456789" only. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [KEYWORDS]. 3. ID3v2 overview ID3v2 is a general tagging format for audio, which makes it possible to store meta data about the audio inside the audio file itself. The ID3 tag described in this document is mainly targeted at files encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III and MPEG-2.5, but may work with other types of encoded audio or as a stand alone format for audio meta data. ID3v2 is designed to be as flexible and expandable as possible to meet new meta information needs that might arise. To achieve that ID3v2 is constructed as a container for several information blocks, called frames, whose format need not be known to the software that encounters them. At the start of every frame is an unique and predefined identifier, a size descriptor that allows software to skip unknown frames and a flags field. The flags describes encoding details and if the frame should remain in the tag, should it be unknown to the software, if the file is altered. The bitorder in ID3v2 is most significant bit first (MSB). The byteorder in multibyte numbers is most significant byte first (e.g. $12345678 would be encoded $12 34 56 78), also known as big endian and network byte order. Overall tag structure: +-----------------------------+ | Header (10 bytes) | +-----------------------------+ | Extended Header | | (variable length, OPTIONAL) | +-----------------------------+ | Frames (variable length) | +-----------------------------+ | Padding | | (variable length, OPTIONAL) | +-----------------------------+ | Footer (10 bytes, OPTIONAL) | +-----------------------------+ In general, padding and footer are mutually exclusive. See details in sections 3.3, 3.4 and 5. 3.1. ID3v2 header The first part of the ID3v2 tag is the 10 byte tag header, laid out as follows: ID3v2/file identifier "ID3" ID3v2 version $04 00 ID3v2 flags %abcd0000 ID3v2 size 4 * %0xxxxxxx The first three bytes of the tag are always "ID3", to indicate that this is an ID3v2 tag, directly followed by the two version bytes. The first byte of ID3v2 version is its major version, while the second byte is its revision number. In this case this is ID3v2.4.0. All revisions are backwards compatible while major versions are not. If software with ID3v2.4.0 and below support should encounter version five or higher it should simply ignore the whole tag. Version or revision will never be $FF. The version is followed by the ID3v2 flags field, of which currently four flags are used. a - Unsynchronisation Bit 7 in the 'ID3v2 flags' indicates whether or not unsynchronisation is applied on all frames (see section 6.1 for details); a set bit indicates usage. b - Extended header The second bit (bit 6) indicates whether or not the header is followed by an extended header. The extended header is described in section 3.2. A set bit indicates the presence of an extended header. c - Experimental indicator The third bit (bit 5) is used as an 'experimental indicator'. This flag SHALL always be set when the tag is in an experimental stage. d - Footer present Bit 4 indicates that a footer (section 3.4) is present at the very end of the tag. A set bit indicates the presence of a footer. All the other flags MUST be cleared. If one of these undefined flags are set, the tag might not be readable for a parser that does not know the flags function. The ID3v2 tag size is stored as a 32 bit synchsafe integer (section 6.2), making a total of 28 effective bits (representing up to 256MB). The ID3v2 tag size is the sum of the byte length of the extended header, the padding and the frames after unsynchronisation. If a footer is present this equals to ('total size' - 20) bytes, otherwise ('total size' - 10) bytes. An ID3v2 tag can be detected with the following pattern: $49 44 33 yy yy xx zz zz zz zz Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80. 3.2. Extended header The extended header contains information that can provide further insight in the structure of the tag, but is not vital to the correct parsing of the tag information; hence the extended header is optional. Extended header size 4 * %0xxxxxxx Number of flag bytes $01 Extended Flags $xx Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. An extended header can thus never have a size of fewer than six bytes. The extended flags field, with its size described by 'number of flag bytes', is defined as: %0bcd0000 Each flag that is set in the extended header has data attached, which comes in the order in which the flags are encountered (i.e. the data for flag 'b' comes before the data for flag 'c'). Unset flags cannot have any attached data. All unknown flags MUST be unset and their corresponding data removed when a tag is modified. Every set flag's data starts with a length byte, which contains a value between 0 and 127 ($00 - $7f), followed by data that has the field length indicated by the length byte. If a flag has no attached data, the value $00 is used as length byte. b - Tag is an update If this flag is set, the present tag is an update of a tag found earlier in the present file or stream. If frames defined as unique are found in the present tag, they are to override any corresponding ones found in the earlier tag. This flag has no corresponding data. Flag data length $00 c - CRC data present If this flag is set, a CRC-32 [ISO-3309] data is included in the extended header. The CRC is calculated on all the data between the header and footer as indicated by the header's tag length field, minus the extended header. Note that this includes the padding (if there is any), but excludes the footer. The CRC-32 is stored as an 35 bit synchsafe integer, leaving the upper four bits always zeroed. Flag data length $05 Total frame CRC 5 * %0xxxxxxx d - Tag restrictions For some applications it might be desired to restrict a tag in more ways than imposed by the ID3v2 specification. Note that the presence of these restrictions does not affect how the tag is decoded, merely how it was restricted before encoding. If this flag is set the tag is restricted as follows: Flag data length $01 Restrictions %ppqrrstt p - Tag size restrictions 00 No more than 128 frames and 1 MB total tag size. 01 No more than 64 frames and 128 KB total tag size. 10 No more than 32 frames and 40 KB total tag size. 11 No more than 32 frames and 4 KB total tag size. q - Text encoding restrictions 0 No restrictions 1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or UTF-8 [UTF-8]. r - Text fields size restrictions 00 No restrictions 01 No string is longer than 1024 characters. 10 No string is longer than 128 characters. 11 No string is longer than 30 characters. Note that nothing is said about how many bytes is used to represent those characters, since it is encoding dependent. If a text frame consists of more than one string, the sum of the strungs is restricted as stated. s - Image encoding restrictions 0 No restrictions 1 Images are encoded only with PNG [PNG] or JPEG [JFIF]. t - Image size restrictions 00 No restrictions 01 All images are 256x256 pixels or smaller. 10 All images are 64x64 pixels or smaller. 11 All images are exactly 64x64 pixels, unless required otherwise. 3.3. Padding It is OPTIONAL to include padding after the final frame (at the end of the ID3 tag), making the size of all the frames together smaller than the size given in the tag header. A possible purpose of this padding is to allow for adding a few additional frames or enlarge existing frames within the tag without having to rewrite the entire file. The value of the padding bytes must be $00. A tag MUST NOT have any padding between the frames or between the tag header and the frames. Furthermore it MUST NOT have any padding when a tag footer is added to the tag. 3.4. ID3v2 footer To speed up the process of locating an ID3v2 tag when searching from the end of a file, a footer can be added to the tag. It is REQUIRED to add a footer to an appended tag, i.e. a tag located after all audio data. The footer is a copy of the header, but with a different identifier. ID3v2 identifier "3DI" ID3v2 version $04 00 ID3v2 flags %abcd0000 ID3v2 size 4 * %0xxxxxxx 4. ID3v2 frame overview All ID3v2 frames consists of one frame header followed by one or more fields containing the actual information. The header is always 10 bytes and laid out as follows: Frame ID $xx xx xx xx (four characters) Size 4 * %0xxxxxxx Flags $xx xx The frame ID is made out of the characters capital A-Z and 0-9. Identifiers beginning with "X", "Y" and "Z" are for experimental frames and free for everyone to use, without the need to set the experimental bit in the tag header. Bear in mind that someone else might have used the same identifier as you. All other identifiers are either used or reserved for future use. The frame ID is followed by a size descriptor containing the size of the data in the final frame, after encryption, compression and unsynchronisation. The size is excluding the frame header ('total frame size' - 10 bytes) and stored as a 32 bit synchsafe integer. In the frame header the size descriptor is followed by two flag bytes. These flags are described in section 4.1. There is no fixed order of the frames' appearance in the tag, although it is desired that the frames are arranged in order of significance concerning the recognition of the file. An example of such order: UFID, TIT2, MCDI, TRCK ... A tag MUST contain at least one frame. A frame must be at least 1 byte big, excluding the header. If nothing else is said, strings, including numeric strings and URLs [URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the range $20 - $FF. Such strings are represented in frame descriptions as <text string>, or <full text string> if newlines are allowed. If nothing else is said newline character is forbidden. In ISO-8859-1 a newline is represented, when allowed, with $0A only. Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. Terminated with $00 00. $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. Strings dependent on encoding are represented in frame descriptions as <text string according to encoding>, or <full text string according to encoding> if newlines are allowed. Any empty strings of type $01 which are NULL-terminated may have the Unicode BOM followed by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00). The timestamp fields are based on a subset of ISO 8601. When being as precise as possible the format of a time string is yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of 24), ":", minutes, ":", seconds), but the precision may be reduced by removing as many time indicators as wanted. Hence valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use the slash character as described in 8601, and for multiple non- contiguous dates, use multiple strings, if allowed by the frame definition. The three byte language field, present in several frames, is used to describe the language of the frame's content, according to ISO-639-2 [ISO-639-2]. The language should be represented in lower case. If the language is not known the string "XXX" should be used. All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt". If a frame is longer than it should be, e.g. having more fields than specified in this document, that indicates that additions to the frame have been made in a later version of the ID3v2 standard. This is reflected by the revision number in the header of the tag. 4.1. Frame header flags In the frame header the size descriptor is followed by two flag bytes. All unused flags MUST be cleared. The first byte is for 'status messages' and the second byte is a format description. If an unknown flag is set in the first byte the frame MUST NOT be changed without that bit cleared. If an unknown flag is set in the second byte the frame is likely to not be readable. Some flags in the second byte indicates that extra information is added to the header. These fields of extra information is ordered as the flags that indicates them. The flags field is defined as follows (l and o left out because ther resemblence to one and zero): %0abc0000 %0h00kmnp Some frame format flags indicate that additional information fields are added to the frame. This information is added after the frame header and before the frame data in the same order as the flags that indicates them. I.e. the four bytes of decompressed size will precede the encryption method byte. These additions affects the 'frame size' field, but are not subject to encryption or compression. The default status flags setting for a frame is, unless stated otherwise, 'preserved if tag is altered' and 'preserved if file is altered', i.e. %00000000. 4.1.1. Frame status flags a - Tag alter preservation This flag tells the tag parser what to do with this frame if it is unknown and the tag is altered in any way. This applies to all kinds of alterations, including adding more padding and reordering the frames. 0 Frame should be preserved. 1 Frame should be discarded. b - File alter preservation This flag tells the tag parser what to do with this frame if it is unknown and the file, excluding the tag, is altered. This does not apply when the audio is completely replaced with other audio data. 0 Frame should be preserved. 1 Frame should be discarded. c - Read only This flag, if set, tells the software that the contents of this frame are intended to be read only. Changing the contents might break something, e.g. a signature. If the contents are changed, without knowledge of why the frame was flagged read only and without taking the proper means to compensate, e.g. recalculating the signature, the bit MUST be cleared. 4.1.2. Frame format flags h - Grouping identity This flag indicates whether or not this frame belongs in a group with other frames. If set, a group identifier byte is added to the frame. Every frame with the same group identifier belongs to the same group. 0 Frame does not contain group information 1 Frame contains group information k - Compression This flag indicates whether or not the frame is compressed. A 'Data Length Indicator' byte MUST be included in the frame. 0 Frame is not compressed. 1 Frame is compressed using zlib [zlib] deflate method. If set, this requires the 'Data Length Indicator' bit to be set as well. m - Encryption This flag indicates whether or not the frame is encrypted. If set, one byte indicating with which method it was encrypted will be added to the frame. See description of the ENCR frame for more information about encryption method registration. Encryption should be done after compression. Whether or not setting this flag requires the presence of a 'Data Length Indicator' depends on the specific algorithm used. 0 Frame is not encrypted. 1 Frame is encrypted. n - Unsynchronisation This flag indicates whether or not unsynchronisation was applied to this frame. See section 6 for details on unsynchronisation. If this flag is set all data from the end of this header to the end of this frame has been unsynchronised. Although desirable, the presence of a 'Data Length Indicator' is not made mandatory by unsynchronisation. 0 Frame has not been unsynchronised. 1 Frame has been unsyrchronised. p - Data length indicator This flag indicates that a data length indicator has been added to the frame. The data length indicator is the value one would write as the 'Frame length' if all of the frame format flags were zeroed, represented as a 32 bit synchsafe integer. 0 There is no Data Length Indicator. 1 A data length Indicator has been added to the frame. 5. Tag location The default location of an ID3v2 tag is prepended to the audio so that players can benefit from the information when the data is streamed. It is however possible to append the tag, or make a prepend/append combination. When deciding upon where an unembedded tag should be located, the following order of preference SHOULD be considered. 1. Prepend the tag. 2. Prepend a tag with all vital information and add a second tag at the end of the file, before tags from other tagging systems. The first tag is required to have a SEEK frame. 3. Add a tag at the end of the file, before tags from other tagging systems. In case 2 and 3 the tag can simply be appended if no other known tags are present. The suggested method to find ID3v2 tags are: 1. Look for a prepended tag using the pattern found in section 3.1. 2. If a SEEK frame was found, use its values to guide further searching. 3. Look for a tag footer, scanning from the back of the file. For every new tag that is found, the old tag should be discarded unless the update flag in the extended header (section 3.2) is set. 6. Unsynchronisation The only purpose of unsynchronisation is to make the ID3v2 tag as compatible as possible with existing software and hardware. There is no use in 'unsynchronising' tags if the file is only to be processed only by ID3v2 aware software and hardware. Unsynchronisation is only useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC files. 6.1. The unsynchronisation scheme Whenever a false synchronisation is found within the tag, one zeroed byte is inserted after the first false synchronisation byte. The format of synchronisations that should be altered by ID3 encoders is as follows: %11111111 111xxxxx and should be replaced with: %11111111 00000000 111xxxxx This has the side effect that all $FF 00 combinations have to be altered, so they will not be affected by the decoding process. Therefore all the $FF 00 combinations have to be replaced with the $FF 00 00 combination during the unsynchronisation. To indicate usage of the unsynchronisation, the unsynchronisation flag in the frame header should be set. This bit MUST be set if the frame was altered by the unsynchronisation and SHOULD NOT be set if unaltered. If all frames in the tag are unsynchronised the unsynchronisation flag in the tag header SHOULD be set. It MUST NOT be set if the tag has a frame which is not unsynchronised. Assume the first byte of the audio to be $FF. The special case when the last byte of the last frame is $FF and no padding nor footer is used will then introduce a false synchronisation. This can be solved by adding a footer, adding padding or unsynchronising the frame and add $00 to the end of the frame data, thus adding more byte to the frame size than a normal unsynchronisation would. Although not preferred, it is allowed to apply the last method on all frames ending with $FF. It is preferred that the tag is either completely unsynchronised or not unsynchronised at all. A completely unsynchronised tag has no false synchonisations in it, as defined above, and does not end with $FF. A completely non-unsynchronised tag contains no unsynchronised frames, and thus the unsynchronisation flag in the header is cleared. Do bear in mind, that if compression or encryption is used, the unsynchronisation scheme MUST be applied afterwards. When decoding an unsynchronised frame, the unsynchronisation scheme MUST be reversed first, encryption and decompression afterwards. 6.2. Synchsafe integers In some parts of the tag it is inconvenient to use the unsychronisation scheme because the size of unsynchronised data is not known in advance, which is particularly problematic with size descriptors. The solution in ID3v2 is to use synchsafe integers, in which there can never be any false synchs. Synchsafe integers are integers that keep its highest bit (bit 7) zeroed, making seven bits out of eight available. Thus a 32 bit synchsafe integer can store 28 bits of information. Example: 255 (%11111111) encoded as a 16 bit synchsafe integer is 383 (%00000001 01111111). 7. Copyright Copyright (C) Martin Nilsson 2000. All Rights Reserved. This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that a reference to this document is included on all such copies and derivative works. However, this document itself may not be modified in any way and reissued as the original document. The limited permissions granted above are perpetual and will not be revoked. This document and the information contained herein is provided on an 'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 8. References [ID3v2] Martin Nilsson, 'ID3v2 informal standard'. <url:http://www.id3.org/id3v2.3.0.txt> [ISO-639-2] ISO/FDIS 639-2. 'Codes for the representation of names of languages, Part 2: Alpha-3 code.' Technical committee / subcommittee: TC 37 / SC 2 [ISO-3309] ISO 3309 'Information Processing Systems--Data Communication High-Level Data Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd Edition. [ISO-8859-1] ISO/IEC DIS 8859-1. '8-bit single-byte coded graphic character sets, Part 1: Latin alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2 [JFIF] 'JPEG File Interchange Format, version 1.02' <url:http://www.w3.org/Graphics/JPEG/jfif.txt> [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate Requirement Levels', RFC 2119, March 1997. <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt> [MPEG] ISO/IEC 11172-3:1993. 'Coding of moving pictures and associated audio for digital storage media at up to about 1,5 Mbit/s, Part 3: Audio.' Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC 13818-3:1995 'Generic coding of moving pictures and associated audio information, Part 3: Audio.' Technical committee / subcommittee: JTC 1 / SC 29 and ISO/IEC DIS 13818-3 'Generic coding of moving pictures and associated audio information, Part 3: Audio (Revision of ISO/IEC 13818-3:1995)' [PNG] 'Portable Network Graphics, version 1.0' <url:http://www.w3.org/TR/REC-png-multi.html> [UNICODE] The Unicode Consortium, 'The Unicode Standard Version 3.0', ISBN 0-201-61633-5. <url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm> [URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource Locators (URL)', RFC 1738, December 1994. <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt> [UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646', RFC 2279, January 1998. <url:ftp://ftp.isi.edu/in-notes/rfc2279.txt> [UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781, February 2000. <url:ftp://ftp.isi.edu/in-notes/rfc2781.txt> [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB Compressed Data Format Specification version 3.3', RFC 1950, May 1996. <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt> 9. Author's Address Written by Martin Nilsson Rydsvgen 246 C. 30 SE-584 34 Linkping Sweden Email: nilsson@id3.org ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2.h��������������������������������������������������������������0000664�0000000�0000000�00000002035�14662262111�0017177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#ifndef TAGLIB_ID3V2_H #define TAGLIB_ID3V2_H namespace TagLib { //! An ID3v2 implementation /*! * This is a relatively complete and flexible framework for working with ID3v2 * tags. * * More information about ID3v2 tags can be found at * - <a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.2.0.txt"> * id3v2.2.0.txt</a> * - <a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.3.0.txt"> * id3v2.3.0.txt</a> * - <a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a> * - <a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-frames.txt"> * id3v2.4.0-frames.txt</a> * * \see ID3v2::Tag */ namespace ID3v2 { /*! * Used to specify which version of the ID3 standard to use when saving tags. */ enum Version { v3 = 3, //!< ID3v2.3 v4 = 4 //!< ID3v2.4 }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2extendedheader.cpp����������������������������������������������0000664�0000000�0000000�00000005214�14662262111�0022426�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2extendedheader.h" #include "id3v2synchdata.h" using namespace TagLib; using namespace ID3v2; class ExtendedHeader::ExtendedHeaderPrivate { public: unsigned int size { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// ExtendedHeader::ExtendedHeader() : d(std::make_unique<ExtendedHeaderPrivate>()) { } ExtendedHeader::~ExtendedHeader() = default; unsigned int ExtendedHeader::size() const { return d->size; } void ExtendedHeader::setData(const ByteVector &data) { parse(data); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void ExtendedHeader::parse(const ByteVector &data) { d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size") } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2extendedheader.h������������������������������������������������0000664�0000000�0000000�00000007370�14662262111�0022100�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2EXTENDEDHEADER_H #define TAGLIB_ID3V2EXTENDEDHEADER_H #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { namespace ID3v2 { //! ID3v2 extended header implementation /*! * This class implements ID3v2 extended headers. It attempts to follow, * both semantically and programmatically, the structure specified in * the ID3v2 standard. The API is based on the properties of ID3v2 extended * headers specified there. If any of the terms used in this documentation * are unclear please check the specification in the linked section. * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 3.2) */ class TAGLIB_EXPORT ExtendedHeader { public: /*! * Constructs an empty ID3v2 extended header. */ ExtendedHeader(); /*! * Destroys the extended header. */ ~ExtendedHeader(); ExtendedHeader(const ExtendedHeader &) = delete; ExtendedHeader &operator=(const ExtendedHeader &) = delete; /*! * Returns the size of the extended header. This is variable for the * extended header. */ unsigned int size() const; /*! * Sets the data that will be used as the extended header. Since the * length is not known before the extended header has been parsed, this * should just be a pointer to the first byte of the extended header. It * will determine the length internally and make that available through * size(). */ void setData(const ByteVector &data); protected: /*! * Called by setData() to parse the extended header data. It makes this * information available through the public API. */ void parse(const ByteVector &data); private: class ExtendedHeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ExtendedHeaderPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2footer.cpp������������������������������������������������������0000664�0000000�0000000�00000004153�14662262111�0020754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2footer.h" #include "id3v2header.h" using namespace TagLib; using namespace ID3v2; class Footer::FooterPrivate { }; Footer::Footer() = default; Footer::~Footer() = default; unsigned int Footer::size() { return 10; } ByteVector Footer::render(const Header *header) const { ByteVector headerData = header->render(); headerData[0] = '3'; headerData[1] = 'D'; headerData[2] = 'I'; return headerData; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2footer.h��������������������������������������������������������0000664�0000000�0000000�00000006131�14662262111�0020417�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2FOOTER_H #define TAGLIB_ID3V2FOOTER_H #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { namespace ID3v2 { class Header; //! ID3v2 footer implementation /*! * Per the ID3v2 specification, the tag's footer is just a copy of the * information in the header. As such there is no API for reading the * data from the header, it can just as easily be done from the header. * * In fact, at this point, TagLib does not even parse the footer since * it is not useful internally. However, if the flag to include a footer * has been set in the ID3v2::Tag, TagLib will render a footer. */ class TAGLIB_EXPORT Footer { public: /*! * Constructs an empty ID3v2 footer. */ Footer(); /*! * Destroys the footer. */ ~Footer(); Footer(const Footer &) = delete; Footer &operator=(const Footer &) = delete; /*! * Returns the size of the footer. Presently this is always 10 bytes. */ static unsigned int size(); /*! * Renders the footer based on the data in \a header. */ ByteVector render(const Header *header) const; private: class FooterPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FooterPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2frame.cpp�������������������������������������������������������0000664�0000000�0000000�00000042365�14662262111�0020557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2frame.h" #include <array> #include <bitset> #include "tdebug.h" #include "tstringlist.h" #include "tzlib.h" #include "tpropertymap.h" #include "id3v2tag.h" #include "id3v2synchdata.h" #include "frames/textidentificationframe.h" #include "frames/unknownframe.h" using namespace TagLib; using namespace ID3v2; class Frame::FramePrivate { public: FramePrivate() = default; ~FramePrivate() { delete header; } FramePrivate(const FramePrivate &) = delete; FramePrivate &operator=(const FramePrivate &) = delete; Frame::Header *header { nullptr }; }; namespace { bool isValidFrameID(const ByteVector &frameID) { if(frameID.size() != 4) return false; return std::none_of(frameID.begin(), frameID.end(), [](auto c) { return (c < 'A' || c > 'Z') && (c < '0' || c > '9'); }); } } // namespace //////////////////////////////////////////////////////////////////////////////// // static methods //////////////////////////////////////////////////////////////////////////////// ByteVector Frame::textDelimiter(String::Type t) { if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE) return ByteVector(2, '\0'); return ByteVector(1, '\0'); } const String Frame::instrumentPrefix("PERFORMER:"); const String Frame::commentPrefix("COMMENT:"); const String Frame::lyricsPrefix("LYRICS:"); const String Frame::urlPrefix("URL:"); //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// unsigned int Frame::headerSize() const { return d->header->size(); } Frame::~Frame() = default; ByteVector Frame::frameID() const { if(d->header) return d->header->frameID(); return ByteVector(); } unsigned int Frame::size() const { if(d->header) return d->header->frameSize(); return 0; } void Frame::setData(const ByteVector &data) { parse(data); } void Frame::setText(const String &) { } StringList Frame::toStringList() const { return toString(); } ByteVector Frame::render() const { ByteVector fieldData = renderFields(); d->header->setFrameSize(fieldData.size()); ByteVector headerData = d->header->render(); return headerData + fieldData; } Frame::Header *Frame::header() const { return d->header; } namespace { constexpr std::array frameTranslation { // Text information frames std::pair("TALB", "ALBUM"), std::pair("TBPM", "BPM"), std::pair("TCOM", "COMPOSER"), std::pair("TCON", "GENRE"), std::pair("TCOP", "COPYRIGHT"), std::pair("TDEN", "ENCODINGTIME"), std::pair("TDLY", "PLAYLISTDELAY"), std::pair("TDOR", "ORIGINALDATE"), std::pair("TDRC", "DATE"), // std::pair("TRDA", "DATE"), // id3 v2.3, replaced by TDRC in v2.4 // std::pair("TDAT", "DATE"), // id3 v2.3, replaced by TDRC in v2.4 // std::pair("TYER", "DATE"), // id3 v2.3, replaced by TDRC in v2.4 // std::pair("TIME", "DATE"), // id3 v2.3, replaced by TDRC in v2.4 std::pair("TDRL", "RELEASEDATE"), std::pair("TDTG", "TAGGINGDATE"), std::pair("TENC", "ENCODEDBY"), std::pair("TEXT", "LYRICIST"), std::pair("TFLT", "FILETYPE"), // std::pair("TIPL", "INVOLVEDPEOPLE"), handled separately std::pair("TIT1", "WORK"), // 'Work' in iTunes std::pair("TIT2", "TITLE"), std::pair("TIT3", "SUBTITLE"), std::pair("TKEY", "INITIALKEY"), std::pair("TLAN", "LANGUAGE"), std::pair("TLEN", "LENGTH"), // std::pair("TMCL", "MUSICIANCREDITS"), handled separately std::pair("TMED", "MEDIA"), std::pair("TMOO", "MOOD"), std::pair("TOAL", "ORIGINALALBUM"), std::pair("TOFN", "ORIGINALFILENAME"), std::pair("TOLY", "ORIGINALLYRICIST"), std::pair("TOPE", "ORIGINALARTIST"), std::pair("TOWN", "OWNER"), std::pair("TPE1", "ARTIST"), std::pair("TPE2", "ALBUMARTIST"), // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST' std::pair("TPE3", "CONDUCTOR"), std::pair("TPE4", "REMIXER"), // could also be ARRANGER std::pair("TPOS", "DISCNUMBER"), std::pair("TPRO", "PRODUCEDNOTICE"), std::pair("TPUB", "LABEL"), std::pair("TRCK", "TRACKNUMBER"), std::pair("TRSN", "RADIOSTATION"), std::pair("TRSO", "RADIOSTATIONOWNER"), std::pair("TSOA", "ALBUMSORT"), std::pair("TSOC", "COMPOSERSORT"), std::pair("TSOP", "ARTISTSORT"), std::pair("TSOT", "TITLESORT"), std::pair("TSO2", "ALBUMARTISTSORT"), // non-standard, used by iTunes std::pair("TSRC", "ISRC"), std::pair("TSSE", "ENCODING"), std::pair("TSST", "DISCSUBTITLE"), // URL frames std::pair("WCOP", "COPYRIGHTURL"), std::pair("WOAF", "FILEWEBPAGE"), std::pair("WOAR", "ARTISTWEBPAGE"), std::pair("WOAS", "AUDIOSOURCEWEBPAGE"), std::pair("WORS", "RADIOSTATIONWEBPAGE"), std::pair("WPAY", "PAYMENTWEBPAGE"), std::pair("WPUB", "PUBLISHERWEBPAGE"), // std::pair("WXXX", "URL"), handled specially // Other frames std::pair("COMM", "COMMENT"), // std::pair("USLT", "LYRICS"), handled specially // Apple iTunes proprietary frames std::pair("PCST", "PODCAST"), std::pair("TCAT", "PODCASTCATEGORY"), std::pair("TDES", "PODCASTDESC"), std::pair("TGID", "PODCASTID"), std::pair("WFED", "PODCASTURL"), std::pair("MVNM", "MOVEMENTNAME"), std::pair("MVIN", "MOVEMENTNUMBER"), std::pair("GRP1", "GROUPING"), std::pair("TCMP", "COMPILATION"), }; // list of deprecated frames and their successors constexpr std::array deprecatedFrames { std::pair("TRDA", "TDRC"), // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3) std::pair("TDAT", "TDRC"), // 2.3 -> 2.4 std::pair("TYER", "TDRC"), // 2.3 -> 2.4 std::pair("TIME", "TDRC"), // 2.3 -> 2.4 }; } // namespace String Frame::frameIDToKey(const ByteVector &id) { ByteVector id24 = id; for(const auto &[o, t] : deprecatedFrames) { if(id24 == o) { id24 = t; break; } } for(const auto &[o, t] : frameTranslation) { if(id24 == o) return t; } return String(); } ByteVector Frame::keyToFrameID(const String &s) { const String key = s.upper(); for(const auto &[o, t] : frameTranslation) { if(key == t) return o; } return ByteVector(); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// Frame::Frame(const ByteVector &data) : d(std::make_unique<FramePrivate>()) { d->header = new Header(data); } Frame::Frame(Header *h) : d(std::make_unique<FramePrivate>()) { d->header = h; } void Frame::setHeader(Header *h, bool deleteCurrent) { if(deleteCurrent) delete d->header; d->header = h; } void Frame::parse(const ByteVector &data) { if(d->header) d->header->setData(data); else d->header = new Header(data); parseFields(fieldData(data)); } ByteVector Frame::fieldData(const ByteVector &frameData) const { unsigned int headerSize = d->header->size(); unsigned int frameDataOffset = headerSize; unsigned int frameDataLength = size(); if(d->header->compression() || d->header->dataLengthIndicator()) { frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4)); frameDataOffset += 4; } if(zlib::isAvailable() && d->header->compression() && !d->header->encryption()) { if(frameData.size() <= frameDataOffset) { debug("Compressed frame doesn't have enough data to decode"); return ByteVector(); } const ByteVector outData = zlib::decompress(frameData.mid(frameDataOffset)); if(!outData.isEmpty() && frameDataLength != outData.size()) { debug("frameDataLength does not match the data length returned by zlib"); } return outData; } return frameData.mid(frameDataOffset, frameDataLength); } String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position) { int start = 0; if(!position) position = &start; ByteVector delimiter = textDelimiter(encoding); int end = data.find(delimiter, *position, delimiter.size()); if(end < *position) return String(); String str; if(encoding == String::Latin1) str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position)); else str = String(data.mid(*position, end - *position), encoding); *position = end + delimiter.size(); return str; } String::Type Frame::checkTextEncoding(const StringList &fields, String::Type encoding) const { if((encoding == String::UTF8 || encoding == String::UTF16BE) && header()->version() != 4) return String::UTF16; if(encoding != String::Latin1) return encoding; for(const auto &field : fields) { if(!field.isLatin1()) { if(header()->version() == 4) { debug("Frame::checkEncoding() -- Rendering using UTF8."); return String::UTF8; } debug("Frame::checkEncoding() -- Rendering using UTF16."); return String::UTF16; } } return String::Latin1; } PropertyMap Frame::asProperties() const { if(dynamic_cast< const UnknownFrame *>(this)) { PropertyMap m; m.addUnsupportedData("UNKNOWN/" + frameID()); return m; } const ByteVector &id = frameID(); PropertyMap m; m.addUnsupportedData(id); return m; } void Frame::splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties, PropertyMap &tiplProperties, PropertyMap &tmclProperties) { singleFrameProperties.clear(); tiplProperties.clear(); tmclProperties.clear(); for(const auto &[person, tag] : original) { if(TextIdentificationFrame::involvedPeopleMap().contains(person)) tiplProperties.insert(person, tag); else if(person.startsWith(TextIdentificationFrame::instrumentPrefix)) tmclProperties.insert(person, tag); else singleFrameProperties.insert(person, tag); } } //////////////////////////////////////////////////////////////////////////////// // Frame::Header class //////////////////////////////////////////////////////////////////////////////// class Frame::Header::HeaderPrivate { public: ByteVector frameID; unsigned int frameSize { 0 }; unsigned int version { 4 }; // flags bool tagAlterPreservation { false }; bool fileAlterPreservation { false }; bool readOnly { false }; bool groupingIdentity { false }; bool compression { false }; bool encryption { false }; bool unsynchronisation { false }; bool dataLengthIndicator { false }; }; //////////////////////////////////////////////////////////////////////////////// // public members (Frame::Header) //////////////////////////////////////////////////////////////////////////////// unsigned int Frame::Header::size() const { switch(d->version) { case 0: case 1: case 2: return 6; case 3: case 4: default: return 10; } } Frame::Header::Header(const ByteVector &data, unsigned int version) : d(std::make_unique<HeaderPrivate>()) { setData(data, version); } Frame::Header::~Header() = default; void Frame::Header::setData(const ByteVector &data, unsigned int version) { d->version = version; switch(version) { case 0: case 1: case 2: { // ID3v2.2 if(data.size() < 3) { debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first three bytes d->frameID = data.mid(0, 3); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if(data.size() < 6) { d->frameSize = 0; return; } d->frameSize = data.toUInt(3, 3, true); break; } case 3: { // ID3v2.3 if(data.size() < 4) { debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first four bytes d->frameID = data.mid(0, 4); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if(data.size() < 10) { d->frameSize = 0; return; } // Set the size -- the frame size is the four bytes starting at byte four in // the frame header (structure 4) d->frameSize = data.toUInt(4U); { // read the first byte of flags std::bitset<8> flags(data[8]); d->tagAlterPreservation = flags[7]; // (structure 3.3.1.a) d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b) d->readOnly = flags[5]; // (structure 3.3.1.c) } { // read the second byte of flags std::bitset<8> flags(data[9]); d->compression = flags[7]; // (structure 3.3.1.i) d->encryption = flags[6]; // (structure 3.3.1.j) d->groupingIdentity = flags[5]; // (structure 3.3.1.k) } break; } case 4: default: { // ID3v2.4 if(data.size() < 4) { debug("You must at least specify a frame ID."); return; } // Set the frame ID -- the first four bytes d->frameID = data.mid(0, 4); // If the full header information was not passed in, do not continue to the // steps to parse the frame size and flags. if(data.size() < 10) { d->frameSize = 0; return; } // Set the size -- the frame size is the four bytes starting at byte four in // the frame header (structure 4) d->frameSize = SynchData::toUInt(data.mid(4, 4)); #ifndef NO_ITUNES_HACKS // iTunes writes v2.4 tags with v2.3-like frame sizes if(d->frameSize > 127) { if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) { unsigned int uintSize = data.toUInt(4U); if(isValidFrameID(data.mid(uintSize + 10, 4))) { d->frameSize = uintSize; } } } #endif { // read the first byte of flags std::bitset<8> flags(data[8]); d->tagAlterPreservation = flags[6]; // (structure 4.1.1.a) d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b) d->readOnly = flags[4]; // (structure 4.1.1.c) } { // read the second byte of flags std::bitset<8> flags(data[9]); d->groupingIdentity = flags[6]; // (structure 4.1.2.h) d->compression = flags[3]; // (structure 4.1.2.k) d->encryption = flags[2]; // (structure 4.1.2.m) d->unsynchronisation = flags[1]; // (structure 4.1.2.n) d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p) } break; } } } ByteVector Frame::Header::frameID() const { return d->frameID; } void Frame::Header::setFrameID(const ByteVector &id) { d->frameID = id.mid(0, 4); } unsigned int Frame::Header::frameSize() const { return d->frameSize; } void Frame::Header::setFrameSize(unsigned int size) { d->frameSize = size; } unsigned int Frame::Header::version() const { return d->version; } void Frame::Header::setVersion(unsigned int version) { d->version = version; } bool Frame::Header::tagAlterPreservation() const { return d->tagAlterPreservation; } void Frame::Header::setTagAlterPreservation(bool preserve) { d->tagAlterPreservation = preserve; } bool Frame::Header::fileAlterPreservation() const { return d->fileAlterPreservation; } bool Frame::Header::readOnly() const { return d->readOnly; } bool Frame::Header::groupingIdentity() const { return d->groupingIdentity; } bool Frame::Header::compression() const { return d->compression; } bool Frame::Header::encryption() const { return d->encryption; } bool Frame::Header::unsynchronisation() const { return d->unsynchronisation; } bool Frame::Header::dataLengthIndicator() const { return d->dataLengthIndicator; } ByteVector Frame::Header::render() const { ByteVector flags(2, static_cast<char>(0)); // just blank for the moment ByteVector v = d->frameID + (d->version == 3 ? ByteVector::fromUInt(d->frameSize) : SynchData::fromUInt(d->frameSize)) + flags; return v; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2frame.h���������������������������������������������������������0000664�0000000�0000000�00000040357�14662262111�0020223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2FRAME_H #define TAGLIB_ID3V2FRAME_H #include "tstring.h" #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { class StringList; class PropertyMap; namespace ID3v2 { class Tag; class FrameFactory; //! ID3v2 frame implementation /*! * This class is the main ID3v2 frame implementation. In ID3v2, a tag is * split between a collection of frames (which are in turn split into fields * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 4) * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-frames.txt"> * id3v2.4.0-frames.txt</a>). This class provides an API for * gathering information about and modifying ID3v2 frames. Functionality * specific to a given frame type is handled in one of the many subclasses. */ class TAGLIB_EXPORT Frame { friend class Tag; public: class Header; /*! * Destroys this Frame instance. */ virtual ~Frame(); Frame(const Frame &) = delete; Frame &operator=(const Frame &) = delete; /*! * Returns the Frame ID * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 4) * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-frames.txt"> * id3v2.4.0-frames.txt</a>, 4) */ ByteVector frameID() const; /*! * Returns the size of the frame. */ unsigned int size() const; /*! * Returns the size of the frame header */ unsigned int headerSize() const; /*! * Sets the data that will be used as the frame. Since the length is not * known before the frame has been parsed, this should just be a pointer to * the first byte of the frame. It will determine the length internally * and make that available through size(). */ void setData(const ByteVector &data); /*! * Set the text of frame in the sanest way possible. This should only be * reimplemented in frames where there is some logical mapping to text. * * \note If the frame type supports multiple text encodings, this will not * change the text encoding of the frame; the string will be converted to * that frame's encoding. Please use the specific APIs of the frame types * to set the encoding if that is desired. */ virtual void setText(const String &text); /*! * This returns the textual representation of the data in the frame. * Subclasses must reimplement this method to provide a string * representation of the frame's data. */ virtual String toString() const = 0; /*! * This returns the textual representation of the data in the frame. * Subclasses can reimplement this method to provide a string list * representation of the frame's data. The default implementation * returns the single string representation from toString(). */ virtual StringList toStringList() const; /*! * Render the frame back to its binary format in a ByteVector. */ ByteVector render() const; /*! * Returns a pointer to the frame header. */ Header *header() const; /*! * Returns the text delimiter that is used between fields for the string * type \a t. */ static ByteVector textDelimiter(String::Type t); /*! * Returns an appropriate ID3 frame ID for the given free-form tag key. This method * will return an empty ByteVector if no specialized translation is found. */ static ByteVector keyToFrameID(const String &); /*! * Returns a free-form tag name for the given ID3 frame ID. Note that this does not work * for general frame IDs such as TXXX or WXXX; in such a case an empty string is returned. */ static String frameIDToKey(const ByteVector &); /*! * The string with which an instrument name is prefixed to build a key in a PropertyMap; * used to translate PropertyMaps to TMCL frames. In the current implementation, this * is "PERFORMER:". */ static const String instrumentPrefix; /*! * The PropertyMap key prefix which triggers the use of a COMM frame instead of a TXXX * frame for a non-standard key. In the current implementation, this is "COMMENT:". */ static const String commentPrefix; /*! * The PropertyMap key prefix which triggers the use of a USLT frame instead of a TXXX * frame for a non-standard key. In the current implementation, this is "LYRICS:". */ static const String lyricsPrefix; /*! * The PropertyMap key prefix which triggers the use of a WXXX frame instead of a TXX * frame for a non-standard key. In the current implementation, this is "URL:". */ static const String urlPrefix; protected: /*! * Constructs an ID3v2 frame using \a data to read the header information. * All other processing of \a data should be handled in a subclass. * * \note This need not contain anything more than a frame ID, but * \e must contain at least that. */ explicit Frame(const ByteVector &data); /*! * This creates a Frame using the header \a h. * * The ownership of this header will be assigned to the frame and the * header will be deleted when the frame is destroyed. */ Frame(Header *h); /*! * Sets the header to \a h. If \a deleteCurrent is \c true, this will free * the memory of the current header. * * The ownership of this header will be assigned to the frame and the * header will be deleted when the frame is destroyed. */ void setHeader(Header *h, bool deleteCurrent = true); /*! * Called by setData() to parse the frame data. It makes this information * available through the public API. */ void parse(const ByteVector &data); /*! * Called by parse() to parse the field data. It makes this information * available through the public API. This must be overridden by the * subclasses. */ virtual void parseFields(const ByteVector &data) = 0; /*! * Render the field data back to a binary format in a ByteVector. This * must be overridden by subclasses. */ virtual ByteVector renderFields() const = 0; /*! * Returns a ByteVector containing the field data given the frame data. * This correctly adjusts for the header size plus any additional frame * data that's specified in the frame header flags. */ ByteVector fieldData(const ByteVector &frameData) const; /*! * Reads a String of type \a encoding from the ByteVector \a data. If \a * position is passed in it is used both as the starting point and is * updated to return the position just after the string that has been read. * This is useful for reading strings sequentially. */ String readStringField(const ByteVector &data, String::Type encoding, int *position = nullptr); /*! * Checks the list of string values to see if they can be used with the * specified encoding and returns the recommended encoding. This method * also checks the ID3v2 version and makes sure the encoding can be used * in the version specified by the frame's header. */ String::Type checkTextEncoding(const StringList &fields, String::Type encoding) const; /*! * Parses the contents of this frame as PropertyMap. If that fails, the returned * PropertyMap will be empty, and its unsupportedData() will contain this frame's * ID. */ virtual PropertyMap asProperties() const; /*! * This helper function splits the PropertyMap \a original into three ProperytMaps * \a singleFrameProperties, \a tiplProperties, and \a tmclProperties, such that: * - \a singleFrameProperties contains only of keys which can be represented with * exactly one ID3 frame per key. In the current implementation * this is everything except for the fixed "involved people" keys and keys of the * form "TextIdentificationFrame::instrumentPrefix" + "instrument", which are * mapped to a TMCL frame. * - \a tiplProperties will consist of those keys that are present in * TextIdentificationFrame::involvedPeopleMap() * - \a tmclProperties contains the "musician credits" keys which should be mapped * to a TMCL frame */ static void splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties, PropertyMap &tiplProperties, PropertyMap &tmclProperties); private: class FramePrivate; friend class FramePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FramePrivate> d; }; //! ID3v2 frame header implementation /*! * The ID3v2 Frame Header * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 4) * * Every ID3v2::Frame has an associated header that gives some general * properties of the frame and also makes it possible to identify the frame * type. * * As such when reading an ID3v2 tag ID3v2::FrameFactory first creates the * frame headers and then creates the appropriate Frame subclass based on * the type and attaches the header. */ class TAGLIB_EXPORT Frame::Header { public: /*! * Construct a Frame Header based on \a data. \a data must at least * contain a 4 byte frame ID, and optionally can contain flag data and the * frame size. i.e. Just the frame id -- "TALB" -- is a valid value. * * \a version should be the ID3v2 version of the tag. */ explicit Header(const ByteVector &data, unsigned int version = 4); /*! * Destroys this Header instance. */ virtual ~Header(); Header(const Header &) = delete; Header &operator=(const Header &) = delete; /*! * Sets the data for the Header. \a version should indicate the ID3v2 * version number of the tag that this frame is contained in. */ void setData(const ByteVector &data, unsigned int version = 4); /*! * Returns the Frame ID * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 4) * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-frames.txt"> * id3v2.4.0-frames.txt</a>, 4) */ ByteVector frameID() const; /*! * Sets the frame's ID to \a id. Only the first four bytes of \a id will * be used. * * \warning This method should in general be avoided. It exists simply to * provide a mechanism for transforming frames from a deprecated frame type * to a newer one -- i.e. TYER to TDRC from ID3v2.3 to ID3v2.4. */ void setFrameID(const ByteVector &id); /*! * Returns the size of the frame data portion, as set when setData() was * called or set explicitly via setFrameSize(). */ unsigned int frameSize() const; /*! * Sets the size of the frame data portion. */ void setFrameSize(unsigned int size); /*! * Returns the ID3v2 version of the header, as passed in from the * construction of the header or set via setVersion(). */ unsigned int version() const; /*! * Sets the ID3v2 version of the header, changing has impact on the * correct parsing/rendering of frame data. */ void setVersion(unsigned int version); /*! * Returns the size of the frame header in bytes. */ unsigned int size() const; /*! * Returns \c true if the flag for tag alter preservation is set. * * The semantics are a little backwards from what would seem natural * (setting the preservation flag to throw away the frame), but this * follows the ID3v2 standard. * * \see setTagAlterPreservation() */ bool tagAlterPreservation() const; /*! * Sets the flag for preservation of this frame if the tag is set. If * this is set to \c true the frame will not be written when the tag is * saved. * * The semantics are a little backwards from what would seem natural * (setting the preservation flag to throw away the frame), but this * follows the ID3v2 standard. * * \see tagAlterPreservation() */ void setTagAlterPreservation(bool preserve); /*! * Returns \c true if the flag for file alter preservation is set. * * \note This flag is currently ignored internally in TagLib. */ bool fileAlterPreservation() const; /*! * Returns \c true if the frame is meant to be read only. * * \note This flag is currently ignored internally in TagLib. */ bool readOnly() const; /*! * Returns \c true if the flag for the grouping identity is set. * * \note This flag is currently ignored internally in TagLib. */ bool groupingIdentity() const; /*! * Returns \c true if compression is enabled for this frame. * * \note This flag is currently ignored internally in TagLib. */ bool compression() const; /*! * Returns \c true if encryption is enabled for this frame. * * \note This flag is currently ignored internally in TagLib. */ bool encryption() const; /*! * Returns \c true if unsynchronisation is enabled for this frame. */ bool unsynchronisation() const; /*! * Returns \c true if the flag for a data length indicator is set. */ bool dataLengthIndicator() const; /*! * Render the Header back to binary format in a ByteVector. */ ByteVector render() const; private: class HeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<HeaderPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2framefactory.cpp������������������������������������������������0000664�0000000�0000000�00000043530�14662262111�0022142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2framefactory.h" #include <array> #include <utility> #include "tdebug.h" #include "tzlib.h" #include "id3v2synchdata.h" #include "id3v1genres.h" #include "frames/attachedpictureframe.h" #include "frames/commentsframe.h" #include "frames/relativevolumeframe.h" #include "frames/textidentificationframe.h" #include "frames/uniquefileidentifierframe.h" #include "frames/unknownframe.h" #include "frames/generalencapsulatedobjectframe.h" #include "frames/urllinkframe.h" #include "frames/unsynchronizedlyricsframe.h" #include "frames/popularimeterframe.h" #include "frames/privateframe.h" #include "frames/ownershipframe.h" #include "frames/synchronizedlyricsframe.h" #include "frames/eventtimingcodesframe.h" #include "frames/chapterframe.h" #include "frames/tableofcontentsframe.h" #include "frames/podcastframe.h" using namespace TagLib; using namespace ID3v2; namespace { void updateGenre(TextIdentificationFrame *frame) { StringList fields = frame->fieldList(); StringList newfields; for(auto s : std::as_const(fields)) { int offset = 0; int end = 0; while(static_cast<int>(s.length()) > offset && s[offset] == '(' && (end = s.find(")", offset + 1)) > offset) { // "(12)Genre" const String genreCode = s.substr(offset + 1, end - 1); s = s.substr(end + 1); bool ok; int number = genreCode.toInt(&ok); if((ok && number >= 0 && number <= 255 && ID3v1::genre(number) != s) || genreCode == "RX" || genreCode == "CR") newfields.append(genreCode); } if(!s.isEmpty()) // "Genre" or "12" newfields.append(s); } if(newfields.isEmpty()) fields.append(String()); frame->setText(newfields); } } // namespace class FrameFactory::FrameFactoryPrivate { public: String::Type defaultEncoding { String::Latin1 }; bool useDefaultEncoding { false }; template <class T> void setTextEncoding(T *frame) { if(useDefaultEncoding) frame->setTextEncoding(defaultEncoding); } }; FrameFactory FrameFactory::factory; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FrameFactory *FrameFactory::instance() { return &factory; } std::pair<Frame::Header *, bool> FrameFactory::prepareFrameHeader( ByteVector &data, const Header *tagHeader) const { unsigned int version = tagHeader->majorVersion(); auto header = new Frame::Header(data, version); ByteVector frameID = header->frameID(); // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1 // characters. Also make sure that there is data in the frame. if(frameID.size() != (version < 3U ? 3U : 4U) || header->frameSize() <= static_cast<unsigned int>(header->dataLengthIndicator() ? 4 : 0) || header->frameSize() > data.size()) { delete header; return {nullptr, false}; } #ifndef NO_ITUNES_HACKS if(version == 3 && frameID[3] == '\0') { // iTunes v2.3 tags store v2.2 frames - convert now frameID = frameID.mid(0, 3); header->setFrameID(frameID); header->setVersion(2); updateFrame(header); header->setVersion(3); } #endif if(std::any_of(frameID.cbegin(), frameID.cend(), [](auto c) { return (c < 'A' || c > 'Z') && (c < '0' || c > '9'); })) { delete header; return { nullptr, false }; } if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) { // Data lengths are not part of the encoded data, but since they are synch-safe // integers they will be never actually encoded. ByteVector frameData = data.mid(header->size(), header->frameSize()); frameData = SynchData::decode(frameData); data = data.mid(0, header->size()) + frameData; } // TagLib doesn't mess with encrypted frames, so just treat them // as unknown frames. if(!zlib::isAvailable() && header->compression()) { debug("Compressed frames are currently not supported."); return {header, false}; } if(header->encryption()) { debug("Encrypted frames are currently not supported."); return {header, false}; } if(!updateFrame(header)) { header->setTagAlterPreservation(true); return {header, false}; } return {header, true}; } Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const { ByteVector data = origData; auto [header, ok] = prepareFrameHeader(data, tagHeader); if(!ok) { // check if frame is valid and return as UnknownFrame return header ? new UnknownFrame(data, header) : nullptr; } return createFrame(data, header, tagHeader); } Frame *FrameFactory::createFrame(const ByteVector &data, Frame::Header *header, const Header *tagHeader) const { ByteVector frameID = header->frameID(); // This is where things get necessarily nasty. Here we determine which // Frame subclass (or if none is found simply a 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) // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames. if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1") { TextIdentificationFrame *f = frameID != "TXXX" ? new TextIdentificationFrame(data, header) : new UserTextIdentificationFrame(data, header); d->setTextEncoding(f); if(frameID == "TCON") updateGenre(f); return f; } // Comments (frames 4.10) if(frameID == "COMM") { auto f = new CommentsFrame(data, header); d->setTextEncoding(f); return f; } // Attached Picture (frames 4.14) if(frameID == "APIC") { auto f = new AttachedPictureFrame(data, header); d->setTextEncoding(f); return f; } // ID3v2.2 Attached Picture if(frameID == "PIC") { AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header); d->setTextEncoding(f); return f; } // Relative Volume Adjustment (frames 4.11) if(frameID == "RVA2") return new RelativeVolumeFrame(data, header); // Unique File Identifier (frames 4.1) if(frameID == "UFID") return new UniqueFileIdentifierFrame(data, header); // General Encapsulated Object (frames 4.15) if(frameID == "GEOB") { auto f = new GeneralEncapsulatedObjectFrame(data, header); d->setTextEncoding(f); return f; } // URL link (frames 4.3) if(frameID.startsWith("W")) { if(frameID != "WXXX") { return new UrlLinkFrame(data, header); } auto f = new UserUrlLinkFrame(data, header); d->setTextEncoding(f); return f; } // Unsynchronized lyric/text transcription (frames 4.8) if(frameID == "USLT") { auto f = new UnsynchronizedLyricsFrame(data, header); if(d->useDefaultEncoding) f->setTextEncoding(d->defaultEncoding); return f; } // Synchronized lyrics/text (frames 4.9) if(frameID == "SYLT") { auto f = new SynchronizedLyricsFrame(data, header); if(d->useDefaultEncoding) f->setTextEncoding(d->defaultEncoding); return f; } // Event timing codes (frames 4.5) if(frameID == "ETCO") return new EventTimingCodesFrame(data, header); // Popularimeter (frames 4.17) if(frameID == "POPM") return new PopularimeterFrame(data, header); // Private (frames 4.27) if(frameID == "PRIV") return new PrivateFrame(data, header); // Ownership (frames 4.22) if(frameID == "OWNE") { auto f = new OwnershipFrame(data, header); d->setTextEncoding(f); return f; } // Chapter (ID3v2 chapters 1.0) if(frameID == "CHAP") return new ChapterFrame(tagHeader, data, header); // Table of contents (ID3v2 chapters 1.0) if(frameID == "CTOC") return new TableOfContentsFrame(tagHeader, data, header); // Apple proprietary PCST (Podcast) if(frameID == "PCST") return new PodcastFrame(data, header); return new UnknownFrame(data, header); } void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const { if(tag->header()->majorVersion() < 4 && tag->frameList("TDRC").size() == 1 && tag->frameList("TDAT").size() == 1) { auto tdrc = dynamic_cast<TextIdentificationFrame *>(tag->frameList("TDRC").front()); auto tdat = dynamic_cast<UnknownFrame *>(tag->frameList("TDAT").front()); if(tdrc && tdrc->fieldList().size() == 1 && tdrc->fieldList().front().size() == 4 && tdat && tdat->data().size() >= 5) { String date(tdat->data().mid(1), static_cast<String::Type>(tdat->data()[0])); if(date.length() == 4) { tdrc->setText(tdrc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2)); if(tag->frameList("TIME").size() == 1) { auto timeframe = dynamic_cast<UnknownFrame *>(tag->frameList("TIME").front()); if(timeframe && timeframe->data().size() >= 5) { String time(timeframe->data().mid(1), static_cast<String::Type>(timeframe->data()[0])); if(time.length() == 4) { tdrc->setText(tdrc->toString() + 'T' + time.substr(0, 2) + ':' + time.substr(2, 2)); } } } } } } } String::Type FrameFactory::defaultTextEncoding() const { return d->defaultEncoding; } void FrameFactory::setDefaultTextEncoding(String::Type encoding) { d->useDefaultEncoding = true; d->defaultEncoding = encoding; } bool FrameFactory::isUsingDefaultTextEncoding() const { return d->useDefaultEncoding; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// FrameFactory::FrameFactory() : d(std::make_unique<FrameFactoryPrivate>()) { } FrameFactory::~FrameFactory() = default; namespace { // Frame conversion table ID3v2.2 -> 2.4 constexpr std::array frameConversion2 { std::pair("BUF", "RBUF"), std::pair("CNT", "PCNT"), std::pair("COM", "COMM"), std::pair("CRA", "AENC"), std::pair("ETC", "ETCO"), std::pair("GEO", "GEOB"), std::pair("IPL", "TIPL"), std::pair("MCI", "MCDI"), std::pair("MLL", "MLLT"), std::pair("POP", "POPM"), std::pair("REV", "RVRB"), std::pair("SLT", "SYLT"), std::pair("STC", "SYTC"), std::pair("TAL", "TALB"), std::pair("TBP", "TBPM"), std::pair("TCM", "TCOM"), std::pair("TCO", "TCON"), std::pair("TCP", "TCMP"), std::pair("TCR", "TCOP"), std::pair("TDY", "TDLY"), std::pair("TEN", "TENC"), std::pair("TFT", "TFLT"), std::pair("TKE", "TKEY"), std::pair("TLA", "TLAN"), std::pair("TLE", "TLEN"), std::pair("TMT", "TMED"), std::pair("TOA", "TOAL"), std::pair("TOF", "TOFN"), std::pair("TOL", "TOLY"), std::pair("TOR", "TDOR"), std::pair("TOT", "TOAL"), std::pair("TP1", "TPE1"), std::pair("TP2", "TPE2"), std::pair("TP3", "TPE3"), std::pair("TP4", "TPE4"), std::pair("TPA", "TPOS"), std::pair("TPB", "TPUB"), std::pair("TRC", "TSRC"), std::pair("TRD", "TDRC"), std::pair("TRK", "TRCK"), std::pair("TS2", "TSO2"), std::pair("TSA", "TSOA"), std::pair("TSC", "TSOC"), std::pair("TSP", "TSOP"), std::pair("TSS", "TSSE"), std::pair("TST", "TSOT"), std::pair("TT1", "TIT1"), std::pair("TT2", "TIT2"), std::pair("TT3", "TIT3"), std::pair("TXT", "TOLY"), std::pair("TXX", "TXXX"), std::pair("TYE", "TDRC"), std::pair("UFI", "UFID"), std::pair("ULT", "USLT"), std::pair("WAF", "WOAF"), std::pair("WAR", "WOAR"), std::pair("WAS", "WOAS"), std::pair("WCM", "WCOM"), std::pair("WCP", "WCOP"), std::pair("WPB", "WPUB"), std::pair("WXX", "WXXX"), // Apple iTunes nonstandard frames std::pair("PCS", "PCST"), std::pair("TCT", "TCAT"), std::pair("TDR", "TDRL"), std::pair("TDS", "TDES"), std::pair("TID", "TGID"), std::pair("WFD", "WFED"), std::pair("MVN", "MVNM"), std::pair("MVI", "MVIN"), std::pair("GP1", "GRP1"), }; // Frame conversion table ID3v2.3 -> 2.4 constexpr std::array frameConversion3 { std::pair("TORY", "TDOR"), std::pair("TYER", "TDRC"), std::pair("IPLS", "TIPL"), }; } // namespace bool FrameFactory::updateFrame(Frame::Header *header) const { const ByteVector frameID = header->frameID(); switch(header->version()) { case 2: // ID3v2.2 { if(frameID == "CRM" || frameID == "EQU" || frameID == "LNK" || frameID == "RVA" || frameID == "TIM" || frameID == "TSI" || frameID == "TDA") { debug("ID3v2.4 no longer supports the frame type " + String(frameID) + ". It will be discarded from the tag."); return false; } // ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all // the frames to their 4 byte ID3v2.4 equivalent. for(const auto &[o, t] : frameConversion2) { if(frameID == o) { header->setFrameID(t); break; } } break; } case 3: // ID3v2.3 { if(frameID == "EQUA" || frameID == "RVAD" || frameID == "TIME" || frameID == "TRDA" || frameID == "TSIZ" || frameID == "TDAT") { debug("ID3v2.4 no longer supports the frame type " + String(frameID) + ". It will be discarded from the tag."); return false; } for(const auto &[o, t] : frameConversion3) { if(frameID == o) { header->setFrameID(t); break; } } break; } default: // This should catch a typo that existed in TagLib up to and including // version 1.1 where TRDC was used for the year rather than TDRC. if(frameID == "TRDC") header->setFrameID("TDRC"); break; } return true; } Frame *FrameFactory::createFrameForProperty(const String &key, const StringList &values) const { // check if the key is contained in the key<=>frameID mapping if(ByteVector frameID = Frame::keyToFrameID(key); !frameID.isEmpty()) { // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames. if(frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN" || frameID == "GRP1"){ // text frame auto frame = new TextIdentificationFrame(frameID, String::UTF8); frame->setText(values); return frame; } if(frameID[0] == 'W' && values.size() == 1){ // URL frame (not WXXX); support only one value auto frame = new UrlLinkFrame(frameID); frame->setUrl(values.front()); return frame; } if(frameID == "PCST") { return new PodcastFrame(); } } if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) { auto frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8)); return frame; } // now we check if it's one of the "special" cases: // -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS) if((key == "LYRICS" || key.startsWith(Frame::lyricsPrefix)) && values.size() == 1){ auto frame = new UnsynchronizedLyricsFrame(String::UTF8); frame->setDescription(key == "LYRICS" ? key : key.substr(Frame::lyricsPrefix.size())); frame->setText(values.front()); return frame; } // -URL: depending on the number of values, use WXXX or TXXX (with description=URL) if((key == "URL" || key.startsWith(Frame::urlPrefix)) && values.size() == 1){ auto frame = new UserUrlLinkFrame(String::UTF8); frame->setDescription(key == "URL" ? key : key.substr(Frame::urlPrefix.size())); frame->setUrl(values.front()); return frame; } // -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT) if((key == "COMMENT" || key.startsWith(Frame::commentPrefix)) && values.size() == 1){ auto frame = new CommentsFrame(String::UTF8); if (key != "COMMENT"){ frame->setDescription(key.substr(Frame::commentPrefix.size())); } frame->setText(values.front()); return frame; } // if none of the above cases apply, we use a TXXX frame with the key as description return new UserTextIdentificationFrame( UserTextIdentificationFrame::keyToTXXX(key), values, String::UTF8); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2framefactory.h��������������������������������������������������0000664�0000000�0000000�00000017503�14662262111�0021610�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ /*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2FRAMEFACTORY_H #define TAGLIB_ID3V2FRAMEFACTORY_H #include "tbytevector.h" #include "taglib_export.h" #include "id3v2frame.h" #include "id3v2header.h" namespace TagLib { namespace ID3v2 { class TextIdentificationFrame; //! A factory for creating ID3v2 frames during parsing /*! * This factory abstracts away the frame creation process and instantiates * the appropriate ID3v2::Frame subclasses based on the contents of the * data. * * Reimplementing this factory is the key to adding support for frame types * not directly supported by TagLib to your application. To do so you would * subclass this factory and reimplement createFrame(). Then by setting your * factory to be the default factory in ID3v2::Tag constructor you can * implement behavior that will allow for new ID3v2::Frame subclasses (also * provided by you) to be used. * See <a href="https://github.com/taglib/taglib/blob/master/tests/test_id3v2framefactory.cpp"> * tests/test_id3v2framefactory.cpp</a> for an example. * * This implements both <i>abstract factory</i> and <i>singleton</i> patterns * of which more information is available on the web and in software design * textbooks (notably <i>Design Patterns</i>). * * \note You do not need to use this factory to create new frames to add to * an ID3v2::Tag. You can instantiate frame subclasses directly (with \c new) * and add them to a tag using ID3v2::Tag::addFrame() * * \see ID3v2::Tag::addFrame() */ class TAGLIB_EXPORT FrameFactory { public: FrameFactory(const FrameFactory &) = delete; FrameFactory &operator=(const FrameFactory &) = delete; static FrameFactory *instance(); /*! * Create a frame based on \a origData. \a tagHeader should be a valid * ID3v2::Header instance. */ virtual Frame *createFrame(const ByteVector &origData, const Header *tagHeader) const; /*! * Creates a textual frame which corresponds to a single key in the * PropertyMap interface. TIPL and TMCL do not belong to this category * and are thus handled explicitly in the Frame class. */ virtual Frame *createFrameForProperty( const String &key, const StringList &values) const; /*! * After a tag has been read, this tries to rebuild some of them * information, most notably the recording date, from frames that * have been deprecated and can't be upgraded directly. */ virtual void rebuildAggregateFrames(ID3v2::Tag *tag) const; /*! * Returns the default text encoding for text frames. If setTextEncoding() * has not been explicitly called this will only be used for new text * frames. However, if this value has been set explicitly all frames will be * converted to this type (unless it's explicitly set differently for the * individual frame) when being rendered. * * \see setDefaultTextEncoding() */ String::Type defaultTextEncoding() const; /*! * Set the default text encoding for all text frames that are created to * \a encoding. If no value is set the frames with either default to the * encoding type that was parsed and new frames default to Latin1. * * Valid string types for ID3v2 tags are Latin1, UTF8, UTF16 and UTF16BE. * * \see defaultTextEncoding() */ void setDefaultTextEncoding(String::Type encoding); /*! * Returns \c true if defaultTextEncoding() is used. * The default text encoding is used when setDefaultTextEncoding() has * been called. In this case, reimplementations of FrameFactory should * use defaultTextEncoding() on the frames (having a text encoding field) * they create. * * \see defaultTextEncoding() * \see setDefaultTextEncoding() */ bool isUsingDefaultTextEncoding() const; protected: /*! * Constructs a frame factory. Because this is a singleton this method is * protected, but may be used for subclasses. */ FrameFactory(); /*! * Destroys the frame factory. */ ~FrameFactory(); /*! * This method checks for compliance to the current ID3v2 standard (2.4) * and does nothing in the common case. However if a frame is found that * is not compatible with the current standard, this method either updates * the frame or indicates that it should be discarded. * * This method with return \c true (with or without changes to the frame) if * this frame should be kept or \c false if it should be discarded. * * See the id3v2.4.0-changes.txt document for further information. */ virtual bool updateFrame(Frame::Header *header) const; /*! * Creates and prepares the frame header for createFrame(). * * \param data data of the frame (might be modified) * \param tagHeader the tag header * \return {header, ok}: header is a created frame header or nullptr * if the frame is invalid; ok is \c true if the frame is supported. */ std::pair<Frame::Header *, bool> prepareFrameHeader( ByteVector &data, const Header *tagHeader) const; /*! * Create a frame based on \a data. \a header should be a valid frame * header and \a tagHeader a valid ID3v2::Header instance. * * This method is called by the public overloaded method * createFrame(const ByteVector &, const Header *) after creating * \a header from verified \a data using prepareFrameHeader(), so * this method is provided to be reimplemented in derived classes. */ virtual Frame *createFrame(const ByteVector &data, Frame::Header *header, const Header *tagHeader) const; private: static FrameFactory factory; class FrameFactoryPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FrameFactoryPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2header.cpp������������������������������������������������������0000664�0000000�0000000�00000014512�14662262111�0020706�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2header.h" #include <algorithm> #include <bitset> #include "tstring.h" #include "tdebug.h" #include "id3v2footer.h" #include "id3v2synchdata.h" using namespace TagLib; using namespace ID3v2; class Header::HeaderPrivate { public: unsigned int majorVersion { 4 }; unsigned int revisionNumber { 0 }; bool unsynchronisation { false }; bool extendedHeader { false }; bool experimentalIndicator { false }; bool footerPresent { false }; unsigned int tagSize { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// unsigned int Header::size() { return 10; } ByteVector Header::fileIdentifier() { return ByteVector::fromCString("ID3"); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Header::Header() : d(std::make_unique<HeaderPrivate>()) { } Header::Header(const ByteVector &data) : d(std::make_unique<HeaderPrivate>()) { parse(data); } Header::~Header() = default; unsigned int Header::majorVersion() const { return d->majorVersion; } void Header::setMajorVersion(unsigned int version) { d->majorVersion = version; } unsigned int Header::revisionNumber() const { return d->revisionNumber; } bool Header::unsynchronisation() const { return d->unsynchronisation; } bool Header::extendedHeader() const { return d->extendedHeader; } bool Header::experimentalIndicator() const { return d->experimentalIndicator; } bool Header::footerPresent() const { return d->footerPresent; } unsigned int Header::tagSize() const { return d->tagSize; } unsigned int Header::completeTagSize() const { if(d->footerPresent) return d->tagSize + size() + Footer::size(); return d->tagSize + size(); } void Header::setTagSize(unsigned int s) { d->tagSize = s; } void Header::setData(const ByteVector &data) { parse(data); } ByteVector Header::render() const { ByteVector v; // add the file identifier -- "ID3" v.append(fileIdentifier()); // add the version number -- we always render a 2.4.0 tag regardless of what // the tag originally was. v.append(static_cast<char>(majorVersion())); v.append(static_cast<char>(0)); // Currently we don't actually support writing extended headers, footers or // unsynchronized tags, make sure that the flags are set accordingly. d->extendedHeader = false; d->footerPresent = false; d->unsynchronisation = false; // render and add the flags std::bitset<8> flags; flags[7] = d->unsynchronisation; flags[6] = d->extendedHeader; flags[5] = d->experimentalIndicator; flags[4] = d->footerPresent; v.append(static_cast<char>(flags.to_ulong())); // add the size v.append(SynchData::fromUInt(d->tagSize)); return v; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void Header::parse(const ByteVector &data) { if(data.size() < size()) return; // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a // synch-safe integer, so all bytes must be less than 128. If this is not // true then this is an invalid tag. // note that we're doing things a little out of order here -- the size is // later in the bytestream than the version const ByteVector sizeData = data.mid(6, 4); if(sizeData.size() != 4) { d->tagSize = 0; debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!"); return; } if(std::any_of(sizeData.cbegin(), sizeData.cend(), [](unsigned char size) { return size >= 128; })) { d->tagSize = 0; debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128."); return; } // The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier") // Read the version number from the fourth and fifth bytes. d->majorVersion = data[3]; // (structure 3.1 "major version") d->revisionNumber = data[4]; // (structure 3.1 "revision number") // Read the flags, the first four bits of the sixth byte. std::bitset<8> flags(data[5]); d->unsynchronisation = flags[7]; // (structure 3.1.a) d->extendedHeader = flags[6]; // (structure 3.1.b) d->experimentalIndicator = flags[5]; // (structure 3.1.c) d->footerPresent = flags[4]; // (structure 3.1.d) // Get the size from the remaining four bytes (read above) d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size") } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2header.h��������������������������������������������������������0000664�0000000�0000000�00000013677�14662262111�0020366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2HEADER_H #define TAGLIB_ID3V2HEADER_H #include "tbytevector.h" #include "taglib_export.h" namespace TagLib { namespace ID3v2 { //! An implementation of ID3v2 headers /*! * This class implements ID3v2 headers. It attempts to follow, both * semantically and programmatically, the structure specified in * the ID3v2 standard. The API is based on the properties of ID3v2 headers * specified there. If any of the terms used in this documentation are * unclear please check the specification in the linked section. * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 3.1) */ class TAGLIB_EXPORT Header { public: /*! * Constructs an empty ID3v2 header. */ Header(); /*! * Constructs an ID3v2 header based on \a data. parse() is called * immediately. */ Header(const ByteVector &data); /*! * Destroys the header. */ ~Header(); Header(const Header &) = delete; Header &operator=(const Header &) = delete; /*! * Returns the major version number. (Note: This is the 4, not the 2 in * ID3v2.4.0. The 2 is implied.) */ unsigned int majorVersion() const; /*! * Set the major version number to \a version. (Note: This is * the 4, not the 2 in ID3v2.4.0. The 2 is implied.) * \see majorVersion() * * \note This is used by the internal parser; this will not change the * version which is written and in general should not be called by API * users. */ void setMajorVersion(unsigned int version); /*! * Returns the revision number. (Note: This is the 0, not the 4 in * ID3v2.4.0. The 2 is implied.) */ unsigned int revisionNumber() const; /*! * Returns \c true if unsynchronisation has been applied to all frames. */ bool unsynchronisation() const; /*! * Returns \c true if an extended header is present in the tag. */ bool extendedHeader() const; /*! * Returns \c true if the experimental indicator flag is set. */ bool experimentalIndicator() const; /*! * Returns \c true if a footer is present in the tag. */ bool footerPresent() const; /*! * Returns the tag size in bytes. This is the size of the frame content. * The size of the \e entire tag will be this plus the header size (10 * bytes) and, if present, the footer size (potentially another 10 bytes). * * \note This is the value as read from the header to which TagLib attempts * to provide an API to; it was not a design decision on the part of TagLib * to not include the mentioned portions of the tag in the \e size. * * \see completeTagSize() */ unsigned int tagSize() const; /*! * Returns the tag size, including the header and, if present, the footer * size. * * \see tagSize() */ unsigned int completeTagSize() const; /*! * Set the tag size to \a s. * \see tagSize() */ void setTagSize(unsigned int s); /*! * Returns the size of the header. Presently this is always 10 bytes. */ static unsigned int size(); /*! * Returns the string used to identify an ID3v2 tag inside of a file. * Presently this is always "ID3". */ static ByteVector fileIdentifier(); /*! * Sets the data that will be used as the header. 10 bytes, starting from * the beginning of \a data are used. */ void setData(const ByteVector &data); /*! * Renders the Header back to binary format. */ ByteVector render() const; protected: /*! * Called by setData() to parse the header data. It makes this information * available through the public API. */ void parse(const ByteVector &data); private: class HeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<HeaderPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif �����������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2synchdata.cpp���������������������������������������������������0000664�0000000�0000000�00000006341�14662262111�0021435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2synchdata.h" using namespace TagLib; using namespace ID3v2; unsigned int SynchData::toUInt(const ByteVector &data) { unsigned int sum = 0; bool notSynchSafe = false; int last = data.size() > 4 ? 3 : data.size() - 1; for(int i = 0; i <= last; i++) { if(data[i] & 0x80) { notSynchSafe = true; break; } sum |= (data[i] & 0x7f) << ((last - i) * 7); } if(notSynchSafe) { // Invalid data; assume this was created by some buggy software that just // put normal integers here rather than syncsafe ones, and try it that // way. if(data.size() >= 4) { sum = data.toUInt(0, true); } else { ByteVector tmp(data); tmp.resize(4); sum = tmp.toUInt(0, true); } } return sum; } ByteVector SynchData::fromUInt(unsigned int value) { ByteVector v(4, 0); for(int i = 0; i < 4; i++) v[i] = static_cast<unsigned char>(value >> ((3 - i) * 7) & 0x7f); return v; } ByteVector SynchData::decode(const ByteVector &data) { if(data.isEmpty()) { return ByteVector(); } // We have this optimized method instead of using ByteVector::replace(), // since it makes a great difference when decoding huge unsynchronized frames. ByteVector result(data.size()); auto src = data.begin(); auto dst = result.begin(); while(src < data.end() - 1) { *dst++ = *src++; if(*(src - 1) == '\xff' && *src == '\x00') ++src; } if(src < data.end()) *dst++ = *src++; result.resize(static_cast<unsigned int>(dst - result.begin())); return result; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2synchdata.h�����������������������������������������������������0000664�0000000�0000000�00000006146�14662262111�0021105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2SYNCHDATA_H #define TAGLIB_ID3V2SYNCHDATA_H #include "tbytevector.h" namespace TagLib { namespace ID3v2 { //! A few functions for ID3v2 synch safe integer conversion /*! * In the ID3v2.4 standard most integer values are encoded as "synch safe" * integers which are encoded in such a way that they will not give false * MPEG syncs and confuse MPEG decoders. This namespace provides some * methods for converting to and from these values to ByteVectors for * things rendering and parsing ID3v2 data. */ namespace SynchData { /*! * This returns the unsigned integer value of \a data where \a data is a * ByteVector that contains a \e synchsafe integer * (<a href="https://github.com/taglib/taglib/blob/master/taglib/mpeg/id3v2/id3v2.4.0-structure.txt"> * id3v2.4.0-structure.txt</a>, 6.2). The default \a length of * 4 is used if another value is not specified. */ TAGLIB_EXPORT unsigned int toUInt(const ByteVector &data); /*! * Returns a 4 byte (32 bit) synchsafe integer based on \a value. */ TAGLIB_EXPORT ByteVector fromUInt(unsigned int value); /*! * Convert the data from unsynchronized data to its original format. */ TAGLIB_EXPORT ByteVector decode(const ByteVector &data); } // namespace SynchData } // namespace ID3v2 } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.cpp���������������������������������������������������������0000664�0000000�0000000�00000064066�14662262111�0020242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2tag.h" #include <algorithm> #include <array> #include <utility> #include "tdebug.h" #include "tfile.h" #include "tpropertymap.h" #include "id3v2header.h" #include "id3v2extendedheader.h" #include "id3v2footer.h" #include "id3v2synchdata.h" #include "id3v1genres.h" #include "frames/attachedpictureframe.h" #include "frames/generalencapsulatedobjectframe.h" #include "frames/textidentificationframe.h" #include "frames/commentsframe.h" #include "frames/urllinkframe.h" #include "frames/uniquefileidentifierframe.h" #include "frames/unsynchronizedlyricsframe.h" #include "frames/unknownframe.h" using namespace TagLib; using namespace ID3v2; namespace { const ID3v2::Latin1StringHandler defaultStringHandler; const ID3v2::Latin1StringHandler *stringHandler = &defaultStringHandler; constexpr long MinPaddingSize = 1024; constexpr long MaxPaddingSize = 1024 * 1024; } // namespace class ID3v2::Tag::TagPrivate { public: TagPrivate() { frameList.setAutoDelete(true); } const FrameFactory *factory { nullptr }; File *file { nullptr }; offset_t tagOffset { 0 }; Header header; std::unique_ptr<ExtendedHeader> extendedHeader; std::unique_ptr<Footer> footer; FrameListMap frameListMap; FrameList frameList; }; class ID3v2::Latin1StringHandler::Latin1StringHandlerPrivate { }; //////////////////////////////////////////////////////////////////////////////// // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// Latin1StringHandler::Latin1StringHandler() = default; Latin1StringHandler::~Latin1StringHandler() = default; String Latin1StringHandler::parse(const ByteVector &data) const { return String(data, String::Latin1); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ID3v2::Tag::Tag() : d(std::make_unique<TagPrivate>()) { d->factory = FrameFactory::instance(); } ID3v2::Tag::Tag(File *file, offset_t tagOffset, const FrameFactory *factory) : d(std::make_unique<TagPrivate>()) { d->factory = factory; d->file = file; d->tagOffset = tagOffset; read(); } ID3v2::Tag::~Tag() = default; String ID3v2::Tag::title() const { if(!d->frameListMap["TIT2"].isEmpty()) return joinTagValues(d->frameListMap["TIT2"].front()->toStringList()); return String(); } String ID3v2::Tag::artist() const { if(!d->frameListMap["TPE1"].isEmpty()) return joinTagValues(d->frameListMap["TPE1"].front()->toStringList()); return String(); } String ID3v2::Tag::album() const { if(!d->frameListMap["TALB"].isEmpty()) return joinTagValues(d->frameListMap["TALB"].front()->toStringList()); return String(); } String ID3v2::Tag::comment() const { const FrameList &comments = d->frameListMap["COMM"]; if(comments.isEmpty()) return String(); for(const auto &commFrame : comments) { auto frame = dynamic_cast<CommentsFrame *>(commFrame); if(frame && frame->description().isEmpty()) return commFrame->toString(); } return comments.front()->toString(); } String ID3v2::Tag::genre() const { const FrameList &tconFrames = d->frameListMap["TCON"]; if(tconFrames.isEmpty()) { return String(); } auto f = dynamic_cast<TextIdentificationFrame *>(tconFrames.front()); if(!f) { return String(); } // ID3v2.4 lists genres as the fields in its frames field list. If the field // is simply a number it can be assumed that it is an ID3v1 genre number. // Here was assumed that if an ID3v1 string is present then it should be // appended to the genre string. Multiple fields will be appended as the // string is built. StringList genres; for(auto &field : f->fieldList()) { if(field.isEmpty()) continue; bool ok; int number = field.toInt(&ok); if(ok && number >= 0 && number <= 255) { field = ID3v1::genre(number); } if(std::find(genres.begin(), genres.end(), field) == genres.end()) genres.append(field); } return joinTagValues(genres); } unsigned int ID3v2::Tag::year() const { if(!d->frameListMap["TDRC"].isEmpty()) return d->frameListMap["TDRC"].front()->toString().substr(0, 4).toInt(); return 0; } unsigned int ID3v2::Tag::track() const { if(!d->frameListMap["TRCK"].isEmpty()) return d->frameListMap["TRCK"].front()->toString().toInt(); return 0; } void ID3v2::Tag::setTitle(const String &s) { setTextFrame("TIT2", s); } void ID3v2::Tag::setArtist(const String &s) { setTextFrame("TPE1", s); } void ID3v2::Tag::setAlbum(const String &s) { setTextFrame("TALB", s); } void ID3v2::Tag::setComment(const String &s) { if(s.isEmpty()) { removeFrames("COMM"); return; } if(const FrameList &comments = d->frameListMap["COMM"]; !comments.isEmpty()) { for(const auto &commFrame : comments) { auto frame = dynamic_cast<CommentsFrame *>(commFrame); if(frame && frame->description().isEmpty()) { commFrame->setText(s); return; } } comments.front()->setText(s); return; } auto f = new CommentsFrame(d->factory->defaultTextEncoding()); addFrame(f); f->setText(s); } void ID3v2::Tag::setGenre(const String &s) { if(s.isEmpty()) { removeFrames("TCON"); return; } // iTunes can't handle correctly encoded ID3v2.4 numerical genres. Just use // strings until iTunes sucks less. #ifdef NO_ITUNES_HACKS int index = ID3v1::genreIndex(s); if(index != 255) setTextFrame("TCON", String::number(index)); else setTextFrame("TCON", s); #else setTextFrame("TCON", s); #endif } void ID3v2::Tag::setYear(unsigned int i) { if(i == 0) { removeFrames("TDRC"); return; } setTextFrame("TDRC", String::number(i)); } void ID3v2::Tag::setTrack(unsigned int i) { if(i == 0) { removeFrames("TRCK"); return; } setTextFrame("TRCK", String::number(i)); } bool ID3v2::Tag::isEmpty() const { return d->frameList.isEmpty(); } Header *ID3v2::Tag::header() const { return &d->header; } ExtendedHeader *ID3v2::Tag::extendedHeader() const { return d->extendedHeader.get(); } const FrameListMap &ID3v2::Tag::frameListMap() const { return d->frameListMap; } const FrameList &ID3v2::Tag::frameList() const { return d->frameList; } const FrameList &ID3v2::Tag::frameList(const ByteVector &frameID) const { return d->frameListMap[frameID]; } void ID3v2::Tag::addFrame(Frame *frame) { d->frameList.append(frame); d->frameListMap[frame->frameID()].append(frame); } void ID3v2::Tag::removeFrame(Frame *frame, bool del) { // remove the frame from the frame list auto it = d->frameList.find(frame); d->frameList.erase(it); // ...and from the frame list map it = d->frameListMap[frame->frameID()].find(frame); d->frameListMap[frame->frameID()].erase(it); // ...and delete as desired if(del) delete frame; } void ID3v2::Tag::removeFrames(const ByteVector &id) { const FrameList frames = d->frameListMap[id]; for(const auto &frame : frames) removeFrame(frame, true); } PropertyMap ID3v2::Tag::properties() const { PropertyMap properties; for(const auto &frame : std::as_const(frameList())) { PropertyMap props = frame->asProperties(); properties.merge(props); } return properties; } void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) { for(const auto &property : properties) { if(property.startsWith("UNKNOWN/")) { String frameID = property.substr(String("UNKNOWN/").size()); if(frameID.size() != 4) continue; // invalid specification ByteVector id = frameID.data(String::Latin1); // delete all unknown frames of given type const FrameList frames = frameList(id); for(const auto &frame : frames) if(dynamic_cast<const UnknownFrame *>(frame) != nullptr) removeFrame(frame); } else if(property.size() == 4) { ByteVector id = property.data(String::Latin1); removeFrames(id); } else { ByteVector id = property.substr(0, 4).data(String::Latin1); if(property.size() <= 5) continue; // invalid specification String description = property.substr(5); Frame *frame = nullptr; if(id == "TXXX") frame = UserTextIdentificationFrame::find(this, description); else if(id == "WXXX") frame = UserUrlLinkFrame::find(this, description); else if(id == "COMM") frame = CommentsFrame::findByDescription(this, description); else if(id == "USLT") frame = UnsynchronizedLyricsFrame::findByDescription(this, description); else if(id == "UFID") frame = UniqueFileIdentifierFrame::findByOwner(this, description); if(frame) removeFrame(frame); } } } PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps) { FrameList framesToDelete; // we split up the PropertyMap into the "normal" keys and the "complicated" ones, // which are those according to TIPL or TMCL frames. PropertyMap singleFrameProperties; PropertyMap tiplProperties; PropertyMap tmclProperties; Frame::splitProperties(origProps, singleFrameProperties, tiplProperties, tmclProperties); for(const auto &[tag, frames] : std::as_const(frameListMap())) { for(const auto &frame : frames) { PropertyMap frameProperties = frame->asProperties(); if(tag == "TIPL") { if (tiplProperties != frameProperties) framesToDelete.append(frame); else tiplProperties.erase(frameProperties); } else if(tag == "TMCL") { if (tmclProperties != frameProperties) framesToDelete.append(frame); else tmclProperties.erase(frameProperties); } else if(!singleFrameProperties.contains(frameProperties)) framesToDelete.append(frame); else singleFrameProperties.erase(frameProperties); } } for(const auto &frame : std::as_const(framesToDelete)) removeFrame(frame); // now create remaining frames: // start with the involved people list (TIPL) if(!tiplProperties.isEmpty()) addFrame(TextIdentificationFrame::createTIPLFrame(tiplProperties)); // proceed with the musician credit list (TMCL) if(!tmclProperties.isEmpty()) addFrame(TextIdentificationFrame::createTMCLFrame(tmclProperties)); // now create the "one key per frame" frames for(const auto &[tag, frames] : std::as_const(singleFrameProperties)) addFrame(d->factory->createFrameForProperty(tag, frames)); return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned } StringList ID3v2::Tag::complexPropertyKeys() const { StringList keys; if(d->frameListMap.contains("APIC")) { keys.append("PICTURE"); } if(d->frameListMap.contains("GEOB")) { keys.append("GENERALOBJECT"); } return keys; } List<VariantMap> ID3v2::Tag::complexProperties(const String &key) const { List<VariantMap> props; if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { const FrameList pictures = d->frameListMap.value("APIC"); for(const Frame *frame : pictures) { if(auto picture = dynamic_cast<const AttachedPictureFrame *>(frame)) { VariantMap property; property.insert("data", picture->picture()); property.insert("mimeType", picture->mimeType()); property.insert("description", picture->description()); property.insert("pictureType", AttachedPictureFrame::typeToString(picture->type())); props.append(property); } } } else if(uppercaseKey == "GENERALOBJECT") { const FrameList geobs = d->frameListMap.value("GEOB"); for(const Frame *frame : geobs) { if(auto geob = dynamic_cast<const GeneralEncapsulatedObjectFrame *>(frame)) { VariantMap property; property.insert("data", geob->object()); property.insert("mimeType", geob->mimeType()); property.insert("description", geob->description()); property.insert("fileName", geob->fileName()); props.append(property); } } } return props; } bool ID3v2::Tag::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { removeFrames("APIC"); for(const auto &property : value) { auto picture = new AttachedPictureFrame; picture->setPicture(property.value("data").value<ByteVector>()); picture->setMimeType(property.value("mimeType").value<String>()); picture->setDescription(property.value("description").value<String>()); picture->setType(AttachedPictureFrame::typeFromString( property.value("pictureType").value<String>())); addFrame(picture); } } else if(uppercaseKey == "GENERALOBJECT") { removeFrames("GEOB"); for(const auto &property : value) { auto geob = new GeneralEncapsulatedObjectFrame; geob->setObject(property.value("data").value<ByteVector>()); geob->setMimeType(property.value("mimeType").value<String>()); geob->setDescription(property.value("description").value<String>()); geob->setFileName(property.value("fileName").value<String>()); addFrame(geob); } } else { return false; } return true; } ByteVector ID3v2::Tag::render() const { return render(ID3v2::v4); } void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const { #ifdef NO_ITUNES_HACKS static constexpr std::array unsupportedFrames { "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", "TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP" }; #else // iTunes writes and reads TSOA, TSOT, TSOP to ID3v2.3. static constexpr std::array unsupportedFrames { "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", "TMOO", "TPRO", "TSST" }; #endif ID3v2::TextIdentificationFrame *frameTDOR = nullptr; ID3v2::TextIdentificationFrame *frameTDRC = nullptr; ID3v2::TextIdentificationFrame *frameTIPL = nullptr; ID3v2::TextIdentificationFrame *frameTMCL = nullptr; ID3v2::TextIdentificationFrame *frameTCON = nullptr; for(const auto &frame : std::as_const(d->frameList)) { ByteVector frameID = frame->header()->frameID(); if(std::any_of(unsupportedFrames.begin(), unsupportedFrames.end(), [&frameID](auto m){ return frameID == m; })) { debug("A frame that is not supported in ID3v2.3 \'" + String(frameID) + "\' has been discarded"); continue; } if(frameID == "TDOR") frameTDOR = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame); else if(frameID == "TDRC") frameTDRC = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame); else if(frameID == "TIPL") frameTIPL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame); else if(frameID == "TMCL") frameTMCL = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame); else if(frame && frameID == "TCON") frameTCON = dynamic_cast<ID3v2::TextIdentificationFrame *>(frame); else frames->append(frame); } if(frameTDOR) { String content = frameTDOR->toString(); if(content.size() >= 4) { auto frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1); frameTORY->setText(content.substr(0, 4)); frames->append(frameTORY); newFrames->append(frameTORY); } } if(frameTDRC) { if(String content = frameTDRC->toString(); content.size() >= 4) { auto frameTYER = new ID3v2::TextIdentificationFrame("TYER", String::Latin1); frameTYER->setText(content.substr(0, 4)); frames->append(frameTYER); newFrames->append(frameTYER); if(content.size() >= 10 && content[4] == '-' && content[7] == '-') { auto frameTDAT = new ID3v2::TextIdentificationFrame("TDAT", String::Latin1); frameTDAT->setText(content.substr(8, 2) + content.substr(5, 2)); frames->append(frameTDAT); newFrames->append(frameTDAT); if(content.size() >= 16 && content[10] == 'T' && content[13] == ':') { auto frameTIME = new ID3v2::TextIdentificationFrame("TIME", String::Latin1); frameTIME->setText(content.substr(11, 2) + content.substr(14, 2)); frames->append(frameTIME); newFrames->append(frameTIME); } } } } if(frameTIPL || frameTMCL) { auto frameIPLS = new ID3v2::TextIdentificationFrame("IPLS", String::Latin1); StringList people; if(frameTMCL) { StringList v24People = frameTMCL->fieldList(); for(unsigned int i = 0; i + 1 < v24People.size(); i += 2) { people.append(v24People[i]); people.append(v24People[i+1]); } } if(frameTIPL) { StringList v24People = frameTIPL->fieldList(); for(unsigned int i = 0; i + 1 < v24People.size(); i += 2) { people.append(v24People[i]); people.append(v24People[i+1]); } } frameIPLS->setText(people); frames->append(frameIPLS); newFrames->append(frameIPLS); } if(frameTCON) { const StringList genres = frameTCON->fieldList(); String combined; String genreText; const bool hasMultipleGenres = genres.size() > 1; // If there are multiple genres, add them as multiple references to ID3v1 // genres if such a reference exists. The first genre for which no ID3v1 // genre number exists can be finally added as a refinement. for(const auto &genre : genres) { bool ok = false; if(int number = genre.toInt(&ok); (ok && number >= 0 && number <= 255) || genre == "RX" || genre == "CR") combined += '(' + genre + ')'; else if(hasMultipleGenres && (number = ID3v1::genreIndex(genre)) != 255) combined += '(' + String::number(number) + ')'; else if(genreText.isEmpty()) genreText = genre; } if(!genreText.isEmpty()) combined += genreText; frameTCON = new ID3v2::TextIdentificationFrame("TCON", String::Latin1); frameTCON->setText(combined); frames->append(frameTCON); newFrames->append(frameTCON); } } ByteVector ID3v2::Tag::render(Version version) const { // We need to render the "tag data" first so that we have to correct size to // render in the tag's header. The "tag data" -- everything that is included // in ID3v2::Header::tagSize() -- includes the extended header, frames and // padding, but does not include the tag's header or footer. // TODO: Render the extended header. // Downgrade the frames that ID3v2.3 doesn't support. FrameList newFrames; newFrames.setAutoDelete(true); FrameList frames; if(version == v4) { frames = d->frameList; } else { downgradeFrames(&frames, &newFrames); } // Reserve a 10-byte blank space for an ID3v2 tag header. ByteVector tagData(Header::size(), '\0'); // Loop through the frames rendering them and adding them to the tagData. for(const auto &frame : std::as_const(frames)) { frame->header()->setVersion(version == v3 ? 3 : 4); if(frame->header()->frameID().size() != 4) { debug("An ID3v2 frame of unsupported or unknown type \'" + String(frame->header()->frameID()) + "\' has been discarded"); continue; } if(!frame->header()->tagAlterPreservation()) { const ByteVector frameData = frame->render(); if(frameData.size() == frame->headerSize()) { debug("An empty ID3v2 frame \'" + String(frame->header()->frameID()) + "\' has been discarded"); continue; } tagData.append(frameData); } } // Compute the amount of padding, and append that to tagData. long originalSize = d->header.tagSize(); long paddingSize = originalSize - (tagData.size() - Header::size()); if(paddingSize <= 0) { paddingSize = MinPaddingSize; } else { // Padding won't increase beyond 1% of the file size or 1MB. offset_t threshold = d->file ? d->file->length() / 100 : 0; threshold = std::max<offset_t>(threshold, MinPaddingSize); threshold = std::min<offset_t>(threshold, MaxPaddingSize); if(paddingSize > threshold) paddingSize = MinPaddingSize; } tagData.resize(static_cast<unsigned int>(tagData.size() + paddingSize), '\0'); // Set the version and data size. d->header.setMajorVersion(version); d->header.setTagSize(tagData.size() - Header::size()); // TODO: This should eventually include d->footer->render(). const ByteVector headerData = d->header.render(); std::copy(headerData.begin(), headerData.end(), tagData.begin()); return tagData; } Latin1StringHandler const *ID3v2::Tag::latin1StringHandler() { return stringHandler; } void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler) { if(handler) stringHandler = handler; else stringHandler = &defaultStringHandler; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void ID3v2::Tag::read() { if(!d->file) return; if(!d->file->isOpen()) return; d->file->seek(d->tagOffset); d->header.setData(d->file->readBlock(Header::size())); // If the tag size is 0, then this is an invalid tag (tags must contain at // least one frame) if(d->header.tagSize() != 0) parse(d->file->readBlock(d->header.tagSize())); // Look for duplicate ID3v2 tags and treat them as an extra blank of this one. // It leads to overwriting them with zero when saving the tag. // This is a workaround for some faulty files that have duplicate ID3v2 tags. // Unfortunately, TagLib itself may write such duplicate tags until v1.10. unsigned int extraSize = 0; while(true) { d->file->seek(d->tagOffset + d->header.completeTagSize() + extraSize); const ByteVector data = d->file->readBlock(Header::size()); if(data.size() < Header::size() || !data.startsWith(Header::fileIdentifier())) break; extraSize += Header(data).completeTagSize(); } if(extraSize != 0) { debug("ID3v2::Tag::read() - Duplicate ID3v2 tags found."); d->header.setTagSize(d->header.tagSize() + extraSize); } } void ID3v2::Tag::parse(const ByteVector &origData) { ByteVector data = origData; if(d->header.unsynchronisation() && d->header.majorVersion() <= 3) data = SynchData::decode(data); unsigned int frameDataPosition = 0; unsigned int frameDataLength = data.size(); // check for extended header if(d->header.extendedHeader()) { if(!d->extendedHeader) d->extendedHeader = std::make_unique<ExtendedHeader>(); d->extendedHeader->setData(data); if(d->extendedHeader->size() <= data.size()) { frameDataPosition += d->extendedHeader->size(); } } // check for footer -- we don't actually need to parse it, as it *must* // contain the same data as the header, but we do need to account for its // size. if(d->header.footerPresent() && Footer::size() <= frameDataLength) frameDataLength -= Footer::size(); // parse frames // Make sure that there is at least enough room in the remaining frame data for // a frame header. while(frameDataPosition < frameDataLength - TagLib::ID3v2::Header::size()) { // If the next data is position is 0, assume that we've hit the padding // portion of the frame data. if(data.at(frameDataPosition) == 0) { if(d->header.footerPresent()) { debug("Padding *and* a footer found. This is not allowed by the spec."); } break; } const ByteVector origData = data.mid(frameDataPosition); const Header *tagHeader = &d->header; unsigned int headerVersion = tagHeader->majorVersion(); Frame *frame = d->factory->createFrame(origData, tagHeader); if(!frame) return; // Checks to make sure that frame parsed correctly. if(frame->size() <= 0) { delete frame; return; } if(frame->header()->version() == headerVersion) { frameDataPosition += frame->size() + frame->headerSize(); } else { // The frame was converted to another version, e.g. from 2.2 to 2.4. // We must advance the frame data position according to the original // frame, not the converted frame because its header size might differ. Frame::Header origHeader(origData, headerVersion); frameDataPosition += origHeader.frameSize() + origHeader.size(); } addFrame(frame); } d->factory->rebuildAggregateFrames(this); } void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value) { if(value.isEmpty()) { removeFrames(id); return; } if(!d->frameListMap[id].isEmpty()) d->frameListMap[id].front()->setText(value); else { const String::Type encoding = d->factory->defaultTextEncoding(); auto f = new TextIdentificationFrame(id, encoding); addFrame(f); f->setText(value); } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/id3v2/id3v2tag.h�����������������������������������������������������������0000664�0000000�0000000�00000040013�14662262111�0017671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_ID3V2TAG_H #define TAGLIB_ID3V2TAG_H #include "tbytevector.h" #include "tstring.h" #include "tlist.h" #include "tmap.h" #include "taglib.h" #include "taglib_export.h" #include "tag.h" #include "id3v2.h" #include "id3v2framefactory.h" namespace TagLib { class File; namespace ID3v2 { class Header; class ExtendedHeader; using FrameList = List<Frame *>; using FrameListMap = Map<ByteVector, FrameList>; //! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags. /*! * ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only * supports genuine ISO-8859-1 by default. However, in practice, non * ISO-8859-1 encodings are often used instead of ISO-8859-1, such as * Windows-1252 for western languages, Shift_JIS for Japanese and so on. * * Here is an option to read such tags by subclassing this class, * reimplementing parse() and setting your reimplementation as the default * with ID3v2::Tag::setStringHandler(). * * \note Writing non-ISO-8859-1 tags is not implemented intentionally. * Use UTF-16 or UTF-8 instead. * * \see ID3v2::Tag::setStringHandler() */ class TAGLIB_EXPORT Latin1StringHandler { public: Latin1StringHandler(); virtual ~Latin1StringHandler(); Latin1StringHandler(const Latin1StringHandler &) = delete; Latin1StringHandler &operator=(const Latin1StringHandler &) = delete; /*! * Decode a string from \a data. The default implementation assumes that * \a data is an ISO-8859-1 (Latin1) character array. */ virtual String parse(const ByteVector &data) const; private: class Latin1StringHandlerPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<Latin1StringHandlerPrivate> d; }; //! The main class in the ID3v2 implementation /*! * This is the main class in the ID3v2 implementation. It serves two * functions. The first, as is obvious from the public API, is to provide a * container for the other ID3v2 related classes. In addition, through the * read() and parse() protected methods, it provides the most basic level of * parsing. In these methods the ID3v2 tag is extracted from the file and * split into data components. * * ID3v2 tags have several parts, TagLib attempts to provide an interface * for them all. header() and extendedHeader() correspond to those * data structures in the ID3v2 standard and the APIs for the classes that * they return attempt to reflect this. * * Also ID3v2 tags are built up from a list of frames, which * have a header and a list of fields. TagLib provides two ways of accessing * the list of frames that are in a given ID3v2 tag. The first is simply * via the frameList() method. This is just a list of pointers to the frames. * The second is a map from the frame type -- i.e. "COMM" for comments -- and * a list of frames of that type. (In some cases ID3v2 allows for multiple * frames of the same type, hence this being a map to a list rather than just * a map to an individual frame.) * * More information on the structure of frames can be found in the ID3v2::Frame * class. * * read() and parse() pass binary data to the other ID3v2 class structures, * they do not handle parsing of flags or fields, for instance. Those are * handled by similar functions within those classes. * * \note All pointers to data structures within the tag will become invalid * when the tag is destroyed. * * \warning Dealing with the nasty details of ID3v2 is not for the faint of * heart and should not be done without much meditation on the spec. It's * rather long, but if you're planning on messing with this class and others * that deal with the details of ID3v2 (rather than the nice, safe, abstract * TagLib::Tag and friends), it's worth your time to familiarize yourself * with said spec (which is distributed with the TagLib sources). TagLib * tries to do most of the work, but with a little luck, you can still * convince it to generate invalid ID3v2 tags. The APIs for ID3v2 assume a * working knowledge of ID3v2 structure. You've been warned. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: /*! * Constructs an empty ID3v2 tag. * * \note You must create at least one frame for this tag to be valid. */ Tag(); /*! * Constructs an ID3v2 tag read from \a file starting at \a tagOffset. * \a factory specifies which FrameFactory will be used for the * construction of new frames. * * \note You should be able to ignore the \a factory parameter in almost * all situations. You would want to specify your own FrameFactory * subclass in the case that you are extending TagLib to support additional * frame types, which would be incorporated into your factory. * * \see FrameFactory */ Tag(File *file, offset_t tagOffset, const FrameFactory *factory = FrameFactory::instance()); /*! * Destroys this Tag instance. */ ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; // Reimplementations. String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; bool isEmpty() const override; /*! * Returns a pointer to the tag's header. */ Header *header() const; /*! * Returns a pointer to the tag's extended header or null if there is no * extended header. */ ExtendedHeader *extendedHeader() const; /*! * Returns a reference to the frame list map. This is a FrameListMap of * all of the frames in the tag. * * This is the most convenient structure for accessing the tag's frames. * Many frame types allow multiple instances of the same frame type so this * is a map of lists. In most cases however there will only be a single * frame of a certain type. * * Let's say for instance that you wanted to access the frame for total * beats per minute -- the TBPM frame. * * \code * TagLib::MPEG::File f("foo.mp3"); * * // Check to make sure that it has an ID3v2 tag * * if(f.ID3v2Tag()) { * * // Get the list of frames for a specific frame type * * TagLib::ID3v2::FrameList l = f.ID3v2Tag()->frameListMap()["TBPM"]; * * if(!l.isEmpty()) * std::cout << l.front()->toString() << std::endl; * } * * \endcode * * \warning You should not modify this data structure directly, instead * use addFrame() and removeFrame(). * * \see frameList() */ const FrameListMap &frameListMap() const; /*! * Returns a reference to the frame list. This is a FrameList of all of * the frames in the tag in the order that they were parsed. * * This can be useful if for example you want to iterate over the tag's frames * in the order that they occur in the tag. * * \warning You should not modify this data structure directly, instead * use addFrame() and removeFrame(). */ const FrameList &frameList() const; /*! * Returns the frame list for frames with the id \a frameID or an empty * list if there are no frames of that type. This is just a convenience * and is equivalent to: * * \code * frameListMap()[frameID]; * \endcode * * \see frameListMap() */ const FrameList &frameList(const ByteVector &frameID) const; /*! * Add a frame to the tag. At this point the tag takes ownership of * the frame and will handle freeing its memory. * * \note Using this method will invalidate any pointers on the list * returned by frameList() */ void addFrame(Frame *frame); /*! * Remove a frame from the tag. If \a del is \c true the frame's memory * will be freed; if it is \c false, it must be deleted by the user. * * \note Using this method will invalidate any pointers on the list * returned by frameList() */ void removeFrame(Frame *frame, bool del = true); /*! * Remove all frames of type \a id from the tag and free their memory. * * \note Using this method will invalidate any pointers on the list * returned by frameList() */ void removeFrames(const ByteVector &id); /*! * Implements the unified property interface -- export function. * This function does some work to translate the hard-specified ID3v2 * frame types into a free-form string-to-stringlist PropertyMap: * - if ID3v2 frame ID is known by Frame::frameIDToKey(), the returned * key is used * - if the frame ID is "TXXX" (user text frame), the description() is * used as key * - if the frame ID is "WXXX" (user url frame), * - if the description is empty or "URL", the key "URL" is used * - otherwise, the key "URL:<description>" is used; * - if the frame ID is "COMM" (comments frame), * - if the description is empty or "COMMENT", the key "COMMENT" * is used * - otherwise, the key "COMMENT:<description>" is used; * - if the frame ID is "USLT" (unsynchronized lyrics), * - if the description is empty or "LYRICS", the key "LYRICS" is used * - otherwise, the key "LYRICS:<description>" is used; * - if the frame ID is "TIPL" (involved peoples list), and if all the * roles defined in the frame are known in TextIdentificationFrame::involvedPeopleMap(), * then "<role>=<name>" will be contained in the returned object for each * - if the frame ID is "TMCL" (musician credit list), then * "PERFORMER:<instrument>=<name>" will be contained in the returned * PropertyMap for each defined musician * In any other case, the unsupportedData() of the returned object will contain * the frame's ID and, in case of a frame ID which is allowed to appear more than * once, the description, separated by a "/". * */ PropertyMap properties() const override; /*! * Removes unsupported frames given by \a properties. The elements of * \a properties must be taken from properties().unsupportedData(); they * are of one of the following forms: * - a four-character frame ID, if the ID3 specification allows only one * frame with that ID (thus, the frame is uniquely determined) * - frameID + "/" + description(), when the ID is one of "TXXX", "WXXX", * "COMM", or "USLT", * - "UNKNOWN/" + frameID, for frames that could not be parsed by TagLib. * In that case, *all* unknown frames with the given ID will be removed. */ void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the unified property interface -- import function. * See the comments in properties(). */ PropertyMap setProperties(const PropertyMap &) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; /*! * Render the tag back to binary data, suitable to be written to disk. */ ByteVector render() const; /*! * Render the tag back to binary data, suitable to be written to disk. * * The \a version parameter specifies whether ID3v2.4 (default) or ID3v2.3 * should be used. */ ByteVector render(Version version) const; /*! * Gets the current string handler that decides how the "Latin-1" data * will be converted to and from binary data. * * \see Latin1StringHandler */ static Latin1StringHandler const *latin1StringHandler(); /*! * Sets the string handler that decides how the "Latin-1" data will be * converted to and from binary data. * If the parameter \a handler is null, the previous handler is * released and default ISO-8859-1 handler is restored. * * \note The caller is responsible for deleting the previous handler * as needed after it is released. * * \see Latin1StringHandler */ static void setLatin1StringHandler(const Latin1StringHandler *handler); protected: /*! * Reads data from the file specified in the constructor. It does basic * parsing of the data in the largest chunks. It partitions the tag into * the Header, the body of the tag (which contains the ExtendedHeader and * frames) and Footer. */ void read(); /*! * This is called by read to parse the body of the tag. It determines if an * extended header exists and adds frames to the FrameListMap. */ void parse(const ByteVector &origData); /*! * Sets the value of the text frame with the Frame ID \a id to \a value. * If the frame does not exist, it is created. */ void setTextFrame(const ByteVector &id, const String &value); /*! * Downgrade frames from ID3v2.4 (used internally and by default) to ID3v2.3. */ void downgradeFrames(FrameList *frames, FrameList *newFrames) const; private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace ID3v2 } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegfile.cpp���������������������������������������������������������������0000664�0000000�0000000�00000034175�14662262111�0017456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mpegfile.h" #include "id3v2framefactory.h" #include "tdebug.h" #include "tpropertymap.h" #include "apefooter.h" #include "apetag.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "tagunion.h" #include "tagutils.h" #include "mpegheader.h" #include "mpegutils.h" using namespace TagLib; namespace { enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 }; } // namespace class MPEG::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory) { } const ID3v2::FrameFactory *ID3v2FrameFactory; offset_t ID3v2Location { -1 }; long ID3v2OriginalSize { 0 }; offset_t APELocation { -1 }; long APEOriginalSize { 0 }; offset_t ID3v1Location { -1 }; TagUnion tag; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// namespace { // Dummy file class to make a stream work with MPEG::Header. class AdapterFile : public TagLib::File { public: AdapterFile(IOStream *stream) : File(stream) {} Tag *tag() const override { return nullptr; } AudioProperties *audioProperties() const override { return nullptr; } bool save() override { return false; } }; } // namespace bool MPEG::File::isSupported(IOStream *stream) { if(!stream || !stream->isOpen()) return false; // An MPEG file has MPEG frame headers. An ID3v2 tag may precede. // MPEG frame headers are really confusing with irrelevant binary data. // So we check if a frame header is really valid. offset_t headerOffset; const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset); if(buffer.isEmpty()) return false; const offset_t originalPosition = stream->tell(); AdapterFile file(stream); for(unsigned int i = 0; i < buffer.size() - 1; ++i) { if(isFrameSync(buffer, i)) { if(const Header header(&file, headerOffset + i, true); header.isValid()) { stream->seek(originalPosition); return true; } } } stream->seek(originalPosition); return false; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPEG::File::File(FileName file, bool readProperties, Properties::ReadStyle readStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties, readStyle); } MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle readStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties, readStyle); } MPEG::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle readStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties, readStyle); } MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle readStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties, readStyle); } MPEG::File::~File() = default; TagLib::Tag *MPEG::File::tag() const { return &d->tag; } PropertyMap MPEG::File::properties() const { return d->tag.properties(); } void MPEG::File::removeUnsupportedProperties(const StringList &properties) { d->tag.removeUnsupportedProperties(properties); } PropertyMap MPEG::File::setProperties(const PropertyMap &properties) { // update ID3v1 tag if it exists, but ignore the return value if(ID3v1Tag()) ID3v1Tag()->setProperties(properties); return ID3v2Tag(true)->setProperties(properties); } MPEG::Properties *MPEG::File::audioProperties() const { return d->properties.get(); } bool MPEG::File::save() { return save(AllTags); } bool MPEG::File::save(int tags, StripTags strip, ID3v2::Version version, DuplicateTags duplicate) { if(readOnly()) { debug("MPEG::File::save() -- File is read only."); return false; } // Create the tags if we've been asked to. if(duplicate == Duplicate) { // Copy the values from the tag that does exist into the new tag, // except if the existing tag is to be stripped. if((tags & ID3v2) && ID3v1Tag() && (strip != StripOthers || (tags & ID3v1))) Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); if((tags & ID3v1) && d->tag[ID3v2Index] && (strip != StripOthers || (tags & ID3v2))) Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false); } // Remove all the tags not going to be saved. if(strip == StripOthers) File::strip(~tags, false); if(ID3v2 & tags) { if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) { // ID3v2 tag is not empty. Update the old one or create a new one. if(d->ID3v2Location < 0) d->ID3v2Location = 0; const ByteVector data = ID3v2Tag()->render(version); insert(data, d->ID3v2Location, d->ID3v2OriginalSize); if(d->APELocation >= 0) d->APELocation += static_cast<long>(data.size()) - d->ID3v2OriginalSize; if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->ID3v2OriginalSize; d->ID3v2OriginalSize = data.size(); } else { // ID3v2 tag is empty. Remove the old one. File::strip(ID3v2, false); } } if(ID3v1 & tags) { if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. File::strip(ID3v1, false); } } if(APE & tags) { if(APETag() && !APETag()->isEmpty()) { // APE tag is not empty. Update the old one or create a new one. if(d->APELocation < 0) { if(d->ID3v1Location >= 0) d->APELocation = d->ID3v1Location; else d->APELocation = length(); } const ByteVector data = APETag()->render(); insert(data, d->APELocation, d->APEOriginalSize); if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->APEOriginalSize; d->APEOriginalSize = data.size(); } else { // APE tag is empty. Remove the old one. File::strip(APE, false); } } return true; } ID3v2::Tag *MPEG::File::ID3v2Tag(bool create) { return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory); } ID3v1::Tag *MPEG::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(ID3v1Index, create); } APE::Tag *MPEG::File::APETag(bool create) { return d->tag.access<APE::Tag>(APEIndex, create); } bool MPEG::File::strip(int tags, bool freeMemory) { if(readOnly()) { debug("MPEG::File::strip() - Cannot strip tags from a read only file."); return false; } if((tags & ID3v2) && d->ID3v2Location >= 0) { removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); if(d->APELocation >= 0) d->APELocation -= d->ID3v2OriginalSize; if(d->ID3v1Location >= 0) d->ID3v1Location -= d->ID3v2OriginalSize; d->ID3v2Location = -1; d->ID3v2OriginalSize = 0; if(freeMemory) d->tag.set(ID3v2Index, nullptr); } if((tags & ID3v1) && d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; if(freeMemory) d->tag.set(ID3v1Index, nullptr); } if((tags & APE) && d->APELocation >= 0) { removeBlock(d->APELocation, d->APEOriginalSize); if(d->ID3v1Location >= 0) d->ID3v1Location -= d->APEOriginalSize; d->APELocation = -1; d->APEOriginalSize = 0; if(freeMemory) d->tag.set(APEIndex, nullptr); } return true; } offset_t MPEG::File::nextFrameOffset(offset_t position) { ByteVector frameSyncBytes(2, '\0'); while(true) { seek(position); const ByteVector buffer = readBlock(bufferSize()); if(buffer.isEmpty()) return -1; for(unsigned int i = 0; i < buffer.size(); ++i) { frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; if(isFrameSync(frameSyncBytes)) { if(const Header header(this, position + i - 1, true); header.isValid()) return position + i - 1; } } position += bufferSize(); } } offset_t MPEG::File::previousFrameOffset(offset_t position) { ByteVector frameSyncBytes(2, '\0'); while(position > 0) { const offset_t bufferLength = std::min<offset_t>(position, bufferSize()); position -= bufferLength; seek(position); const ByteVector buffer = readBlock(bufferLength); for(int i = buffer.size() - 1; i >= 0; --i) { frameSyncBytes[1] = frameSyncBytes[0]; frameSyncBytes[0] = buffer[i]; if(isFrameSync(frameSyncBytes)) { if(const Header header(this, position + i, true); header.isValid()) return position + i + header.frameLength(); } } } return -1; } offset_t MPEG::File::firstFrameOffset() { offset_t position = 0; if(hasID3v2Tag()) position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); return nextFrameOffset(position); } offset_t MPEG::File::lastFrameOffset() { offset_t position; if(hasAPETag()) position = d->APELocation - 1; else if(hasID3v1Tag()) position = d->ID3v1Location - 1; else position = length(); return previousFrameOffset(position); } bool MPEG::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } bool MPEG::File::hasID3v2Tag() const { return d->ID3v2Location >= 0; } bool MPEG::File::hasAPETag() const { return d->APELocation >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MPEG::File::read(bool readProperties, Properties::ReadStyle readStyle) { // Look for an ID3v2 tag d->ID3v2Location = findID3v2(readStyle); if(d->ID3v2Location >= 0) { d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); } // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); // Look for an APE tag d->APELocation = Utils::findAPE(this, d->ID3v1Location); if(d->APELocation >= 0) { d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); d->APEOriginalSize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APE::Footer::size() - d->APEOriginalSize; } if(readProperties) d->properties = std::make_unique<Properties>(this, readStyle); // Make sure that we have our default tag types available. ID3v2Tag(true); ID3v1Tag(true); } offset_t MPEG::File::findID3v2(Properties::ReadStyle readStyle) { if(!isValid()) return -1; // An ID3v2 tag or MPEG frame is most likely be at the beginning of the file. const ByteVector headerID = ID3v2::Header::fileIdentifier(); seek(0); if(readBlock(headerID.size()) == headerID) return 0; if(readStyle == Properties::Fast) return -1; if(const Header firstHeader(this, 0, true); firstHeader.isValid()) return -1; // Look for an ID3v2 tag until reaching the first valid MPEG frame. ByteVector frameSyncBytes(2, '\0'); ByteVector tagHeaderBytes(3, '\0'); long position = 0; while(true) { seek(position); const ByteVector buffer = readBlock(bufferSize()); if(buffer.isEmpty()) return -1; for(unsigned int i = 0; i < buffer.size(); ++i) { frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; if(isFrameSync(frameSyncBytes)) { if(const Header header(this, position + i - 1, true); header.isValid()) return -1; } tagHeaderBytes[0] = tagHeaderBytes[1]; tagHeaderBytes[1] = tagHeaderBytes[2]; tagHeaderBytes[2] = buffer[i]; if(tagHeaderBytes == headerID) return position + i - 2; } position += bufferSize(); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegfile.h�����������������������������������������������������������������0000664�0000000�0000000�00000033123�14662262111�0017113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPEGFILE_H #define TAGLIB_MPEGFILE_H #include "tfile.h" #include "taglib_export.h" #include "tag.h" #include "mpegproperties.h" #include "id3v2.h" namespace TagLib { namespace ID3v2 { class Tag; class FrameFactory; } namespace ID3v1 { class Tag; } namespace APE { class Tag; } //! An implementation of TagLib::File with MPEG (MP3) specific methods namespace MPEG { //! An MPEG file class with some useful methods specific to MPEG /*! * This implements the generic TagLib::File API and additionally provides * access to properties that are distinct to MPEG files, notably access * to the different ID3 tags. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, //! Matches APE tags. APE = 0x0004, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs an MPEG file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * If \a readStyle is not Fast, the file will be scanned * completely if no ID3v2 tag or MPEG sync code is found at the start. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle readStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs an MPEG file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * If \a readStyle is not Fast, the file will be scanned * completely if no ID3v2 tag or MPEG sync code is found at the start. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle readStyle = Properties::Average); /*! * Constructs an MPEG file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * If \a readStyle is not Fast, the file will be scanned * completely if no ID3v2 tag or MPEG sync code is found at the start. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle readStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs an MPEG file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * If \a readStyle is not Fast, the file will be scanned * completely if no ID3v2 tag or MPEG sync code is found at the start. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle readStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns a pointer to a tag that is the union of the ID3v2 and ID3v1 * tags. The ID3v2 tag is given priority in reading the information -- if * requested information exists in both the ID3v2 tag and the ID3v1 tag, * the information from the ID3v2 tag will be returned. * * If you would like more granular control over the content of the tags, * with the concession of generality, use the tag-type specific calls. * * \note As this tag is not implemented as an ID3v2 tag or an ID3v1 tag, * but a union of the two this pointer may not be cast to the specific * tag types. * * \see ID3v1Tag() * \see ID3v2Tag() * \see APETag() */ Tag *tag() const override; /*! * Implements the reading part of the unified property interface. * If the file contains more than one tag, only the * first one (in the order ID3v2, APE, ID3v1) will be converted to the * PropertyMap. */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &properties) override; /*! * Implements the writing part of the unified tag dictionary interface. * In order to avoid problems with deprecated tag formats, this method * always creates an ID3v2 tag if necessary. * If an ID3v1 tag exists, it will be updated as well, within the * limitations of that format. * The returned PropertyMap refers to the ID3v2 tag only. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the MPEG::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. If at least one tag -- ID3v1 or ID3v2 -- exists this * will duplicate its content into the other tag. This returns \c true * if saving was successful. * * If neither exists or if both tags are empty, this will strip the tags * from the file. * * This is the same as calling save(AllTags); * * If you would like more granular control over the content of the tags, * with the concession of generality, use parameterized save call below. * * \see save(int tags) */ bool save() override; /*! * Save the file. This will attempt to save all of the tag types that are * specified by OR-ing together TagTypes values. * * \a strip can be set to strip all tags except those in \a tags. Those * tags will not be modified in memory, and thus remain valid. * * \a version specifies the ID3v2 version to be used for writing tags. By * default, the latest standard, ID3v2.4 is used. * * If \a duplicate is set to Duplicate and at least one tag -- ID3v1 * or ID3v2 -- exists this will duplicate its content into the other tag. */ bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4, DuplicateTags duplicate = Duplicate); /*! * Returns a pointer to the ID3v2 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v2 tag. If \a create is \c true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag <b>is still</b> owned by the MPEG::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v2Tag() */ ID3v2::Tag *ID3v2Tag(bool create = false); /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is \c true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the MPEG::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the APE tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag <b>is still</b> owned by the MPEG::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasAPETag() */ APE::Tag *APETag(bool create = false); /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns \c true if the tags are * successfully stripped. * * If \a freeMemory is \c true the ID3 and APE tags will be deleted and * pointers to them will be invalidated. * * \note This will update the file immediately. */ bool strip(int tags = AllTags, bool freeMemory = true); /*! * Returns the position in the file of the first MPEG frame. */ offset_t firstFrameOffset(); /*! * Returns the position in the file of the next MPEG frame, * using the current position as start. */ offset_t nextFrameOffset(offset_t position); /*! * Returns the position in the file of the previous MPEG frame, * using the current position as start. */ offset_t previousFrameOffset(offset_t position); /*! * Returns the position in the file of the last MPEG frame. */ offset_t lastFrameOffset(); /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Returns whether or not the file on disk actually has an APE tag. * * \see APETag() */ bool hasAPETag() const; /*! * Returns whether or not the given \a stream can be opened as an MPEG * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties, Properties::ReadStyle readStyle); offset_t findID3v2(Properties::ReadStyle readStyle); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace MPEG } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegheader.cpp�������������������������������������������������������������0000664�0000000�0000000�00000025347�14662262111�0017770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mpegheader.h" #include <array> #include "tbytevector.h" #include "tdebug.h" #include "tfile.h" #include "mpegutils.h" using namespace TagLib; class MPEG::Header::HeaderPrivate { public: bool isValid { false }; Version version { Version1 }; int layer { 0 }; bool protectionEnabled { false }; int bitrate { 0 }; int sampleRate { 0 }; bool isPadded { false }; ChannelMode channelMode { Stereo }; ChannelConfiguration channelConfiguration { Custom }; bool isCopyrighted { false }; bool isOriginal { false }; int frameLength { 0 }; int samplesPerFrame { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPEG::Header::Header(File *file, offset_t offset, bool checkLength) : d(std::make_shared<HeaderPrivate>()) { parse(file, offset, checkLength); } MPEG::Header::Header(const Header &) = default; MPEG::Header::~Header() = default; bool MPEG::Header::isValid() const { return d->isValid; } MPEG::Header::Version MPEG::Header::version() const { return d->version; } int MPEG::Header::layer() const { return d->layer; } bool MPEG::Header::protectionEnabled() const { return d->protectionEnabled; } int MPEG::Header::bitrate() const { return d->bitrate; } int MPEG::Header::sampleRate() const { return d->sampleRate; } bool MPEG::Header::isPadded() const { return d->isPadded; } MPEG::Header::ChannelMode MPEG::Header::channelMode() const { return d->channelMode; } MPEG::Header::ChannelConfiguration MPEG::Header::channelConfiguration() const { return d->channelConfiguration; } bool MPEG::Header::isADTS() const { // See detection in parse(). return d->layer == 0 && (d->version == Version2 || d->version == Version4); } bool MPEG::Header::isCopyrighted() const { return d->isCopyrighted; } bool MPEG::Header::isOriginal() const { return d->isOriginal; } int MPEG::Header::frameLength() const { return d->frameLength; } int MPEG::Header::samplesPerFrame() const { return d->samplesPerFrame; } MPEG::Header &MPEG::Header::operator=(const Header &h) { if(&h == this) return *this; d = h.d; return *this; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MPEG::Header::parse(File *file, offset_t offset, bool checkLength) { file->seek(offset); const ByteVector data = file->readBlock(4); if(data.size() < 4) { debug("MPEG::Header::parse() -- data is too short for an MPEG frame header."); return; } // Check for the MPEG synch bytes. if(!isFrameSync(data)) { debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch."); return; } // Set the MPEG version const int versionBits = (static_cast<unsigned char>(data[1]) >> 3) & 0x03; if(versionBits == 0) d->version = Version2_5; else if(versionBits == 2) d->version = Version2; else if(versionBits == 3) d->version = Version1; else return; // Set the MPEG layer if(const int layerBits = (static_cast<unsigned char>(data[1]) >> 1) & 0x03; layerBits == 1) d->layer = 3; else if(layerBits == 2) d->layer = 2; else if(layerBits == 3) d->layer = 1; else { // layerBits == 0 is reserved in the // <a href="http://www.mp3-tech.org/programmer/frame_header.html"> // MPEG Audio Layer I/II/III frame header</a>, for // <a href="https://wiki.multimedia.cx/index.php/ADTS">ADTS</a> // they are always set to 0. // Bit 1 of versionBits is bit 4 of the 2nd header word. For ADTS // it must be set to 1, therefore these three bits are used to detect // that this header is from an ADTS file. if(versionBits == 2) { d->version = Version4; d->layer = 0; } else if(versionBits == 3) { d->version = Version2; d->layer = 0; } else { return; } } d->protectionEnabled = static_cast<unsigned char>(data[1] & 0x01) == 0; if(isADTS()) { static constexpr std::array sampleRates { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; const int sampleRateIndex = (static_cast<unsigned char>(data[2]) >> 2) & 0x0F; d->sampleRate = sampleRates[sampleRateIndex]; d->samplesPerFrame = 1024; d->channelConfiguration = static_cast<ChannelConfiguration>( ((static_cast<unsigned char>(data[3]) >> 6) & 0x03) | ((static_cast<unsigned char>(data[2]) << 2) & 0x04)); d->channelMode = d->channelConfiguration == FrontCenter ? SingleChannel : Stereo; // TODO: Add mode extension for completeness d->isOriginal = (static_cast<unsigned char>(data[3]) & 0x20) != 0; d->isCopyrighted = (static_cast<unsigned char>(data[3]) & 0x04) != 0; // Calculate the frame length if(const ByteVector frameLengthData = file->readBlock(2); frameLengthData.size() >= 2) { d->frameLength = (static_cast<unsigned char>(data[3]) & 0x3) << 11 | (static_cast<unsigned char>(frameLengthData[0]) << 3) | (static_cast<unsigned char>(frameLengthData[1]) >> 5); d->bitrate = static_cast<int>(d->frameLength * d->sampleRate / 1024.0 + 0.5) * 8 / 1024; } } else { // Not ADTS // Set the bitrate static constexpr std::array bitrates { std::array { // Version 1 std::array { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1 std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2 std::array { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3 }, std::array { // Version 2 or 2.5 std::array { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1 std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2 std::array { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3 }, }; const int versionIndex = d->version == Version1 ? 0 : 1; const int layerIndex = d->layer > 0 ? d->layer - 1 : 0; // The bitrate index is encoded as the first 4 bits of the 3rd byte, // i.e. 1111xxxx const int bitrateIndex = (static_cast<unsigned char>(data[2]) >> 4) & 0x0F; d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex]; if(d->bitrate == 0) return; // Set the sample rate static constexpr std::array sampleRates { std::array { 44100, 48000, 32000, 0 }, // Version 1 std::array { 22050, 24000, 16000, 0 }, // Version 2 std::array { 11025, 12000, 8000, 0 } // Version 2.5 }; // The sample rate index is encoded as two bits in the 3rd byte, i.e. xxxx11xx const int samplerateIndex = (static_cast<unsigned char>(data[2]) >> 2) & 0x03; d->sampleRate = sampleRates[d->version][samplerateIndex]; if(d->sampleRate == 0) { return; } // The channel mode is encoded as a 2 bit value at the end of the 3rd byte, // i.e. xxxxxx11 d->channelMode = static_cast<ChannelMode>((static_cast<unsigned char>(data[3]) >> 6) & 0x03); // TODO: Add mode extension for completeness d->isOriginal = (static_cast<unsigned char>(data[3]) & 0x04) != 0; d->isCopyrighted = (static_cast<unsigned char>(data[3]) & 0x08) != 0; d->isPadded = (static_cast<unsigned char>(data[2]) & 0x02) != 0; // Samples per frame static constexpr std::array samplesPerFrameForLayer { // MPEG1, 2/2.5 std::pair(384, 384), // Layer I std::pair(1152, 1152), // Layer II std::pair(1152, 576), // Layer III }; d->samplesPerFrame = versionIndex ? samplesPerFrameForLayer[layerIndex].second : samplesPerFrameForLayer[layerIndex].first; // Calculate the frame length static constexpr std::array paddingSize { 4, 1, 1 }; d->frameLength = d->samplesPerFrame * d->bitrate * 125 / d->sampleRate; if(d->isPadded) d->frameLength += paddingSize[layerIndex]; } if(checkLength) { // Check if the frame length has been calculated correctly, or the next frame // header is right next to the end of this frame. // The MPEG versions, layers and sample rates of the two frames should be // consistent. Otherwise, we assume that either or both of the frames are // broken. // A frame length of 0 is probably invalid and would pass the test below // because nextData would be the same as data. if(d->frameLength == 0) return; file->seek(offset + d->frameLength); const ByteVector nextData = file->readBlock(4); if(nextData.size() < 4) return; constexpr unsigned int HeaderMask = 0xfffe0c00; const unsigned int header = data.toUInt(0, true) & HeaderMask; if(const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask; header != nextHeader) return; } // Now that we're done parsing, set this to be a valid frame. d->isValid = true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegheader.h���������������������������������������������������������������0000664�0000000�0000000�00000014704�14662262111�0017430�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPEGHEADER_H #define TAGLIB_MPEGHEADER_H #include <memory> #include "taglib.h" #include "taglib_export.h" namespace TagLib { class ByteVector; class File; namespace MPEG { //! An implementation of MP3 frame headers /*! * This is an implementation of MPEG Layer III headers. The API follows more * or less the binary format of these headers. * * See these documents as a reference * - <a href="http://www.mp3-tech.org/programmer/frame_header.html"> * MPEG Audio Layer I/II/III frame header</a> * - <a href="https://wiki.multimedia.cx/index.php/ADTS">ADTS</a> */ class TAGLIB_EXPORT Header { public: /*! * Parses an MPEG header based on \a file and \a offset. * * \note If \a checkLength is \c true, this requires the next MPEG frame to * check if the frame length is parsed and calculated correctly. So it's * suitable for seeking for the first valid frame. */ Header(File *file, offset_t offset, bool checkLength = true); /*! * Does a shallow copy of \a h. */ Header(const Header &h); /*! * Destroys this Header instance. */ ~Header(); /*! * Returns \c true if the frame is at least an appropriate size and has * legal values. */ bool isValid() const; /*! * The MPEG Version. */ enum Version { //! MPEG Version 1 Version1 = 0, //! MPEG Version 2 Version2 = 1, //! MPEG Version 2.5 Version2_5 = 2, //! MPEG Version 4 Version4 = 3 }; /*! * Returns the MPEG Version of the header. */ Version version() const; /*! * Returns the layer version. This will be between the values 1-3. */ int layer() const; /*! * Returns \c true if the MPEG protection bit is enabled. */ bool protectionEnabled() const; /*! * Returns the bitrate encoded in the header. */ int bitrate() const; /*! * Returns the sample rate in Hz. */ int sampleRate() const; /*! * Returns \c true if the frame is padded. */ bool isPadded() const; /*! * There are a few combinations or one or two channel audio that are * possible: */ enum ChannelMode { //! Stereo Stereo = 0, //! Stereo JointStereo = 1, //! Dual Mono DualChannel = 2, //! Mono SingleChannel = 3 }; /*! * Returns the channel mode for this frame. */ ChannelMode channelMode() const; /*! * MPEG-4 channel configuration. */ enum ChannelConfiguration { //! Defined in audio object type (AOT) specific configuration Custom = 0, //! 1 channel: front-center FrontCenter = 1, //! 2 channels: front-left, front-right FrontLeftRight = 2, //! 3 channels: front-center, front-left, front-right FrontCenterLeftRight = 3, //! 4 channels: front-center, front-left, front-right, back-center FrontCenterLeftRightBackCenter = 4, //! 5 channels: front-center, front-left, front-right, back-left, //! back-right FrontCenterLeftRightBackLeftRight = 5, //! 6 channels: front-center, front-left, front-right, back-left, //! back-right, LFE-channel FrontCenterLeftRightBackLeftRightLFE = 6, //! 8 channels: front-center, front-left, front-right, side-left, //! side-right, back-left, back-right, LFE-channel FrontCenterLeftRightSideLeftRightBackLeftRightLFE = 7 }; /*! * Returns the MPEG-4 channel configuration. */ ChannelConfiguration channelConfiguration() const; /*! * Returns \c true if this is the header of an Audio Data Transport Stream * (ADTS), usually AAC. */ bool isADTS() const; /*! * Returns \c true if the copyrighted bit is set. */ bool isCopyrighted() const; /*! * Returns \c true if the "original" bit is set. */ bool isOriginal() const; /*! * Returns the frame length in bytes. */ int frameLength() const; /*! * Returns the number of frames per sample. */ int samplesPerFrame() const; /*! * Makes a shallow copy of the header. */ Header &operator=(const Header &h); private: void parse(File *file, offset_t offset, bool checkLength); class HeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<HeaderPrivate> d; }; } // namespace MPEG } // namespace TagLib #endif ������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegproperties.cpp���������������������������������������������������������0000664�0000000�0000000�00000022231�14662262111�0020721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "mpegproperties.h" #include "tdebug.h" #include "mpegfile.h" #include "xingheader.h" #include "apetag.h" using namespace TagLib; class MPEG::Properties::PropertiesPrivate { public: std::unique_ptr<XingHeader> xingHeader; int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int layer { 0 }; Header::Version version { Header::Version1 }; Header::ChannelMode channelMode { Header::Stereo }; Header::ChannelConfiguration channelConfiguration { Header::Custom }; bool protectionEnabled { false }; bool isCopyrighted { false }; bool isOriginal { false }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file, style); } MPEG::Properties::~Properties() = default; int MPEG::Properties::lengthInMilliseconds() const { return d->length; } int MPEG::Properties::bitrate() const { return d->bitrate; } int MPEG::Properties::sampleRate() const { return d->sampleRate; } int MPEG::Properties::channels() const { return d->channels; } const MPEG::XingHeader *MPEG::Properties::xingHeader() const { return d->xingHeader.get(); } MPEG::Header::Version MPEG::Properties::version() const { return d->version; } int MPEG::Properties::layer() const { return d->layer; } bool MPEG::Properties::protectionEnabled() const { return d->protectionEnabled; } MPEG::Header::ChannelMode MPEG::Properties::channelMode() const { return d->channelMode; } MPEG::Header::ChannelConfiguration MPEG::Properties::channelConfiguration() const { return d->channelConfiguration; } bool MPEG::Properties::isADTS() const { return d->layer == 0 && (d->version == Header::Version2 || d->version == Header::Version4); } bool MPEG::Properties::isCopyrighted() const { return d->isCopyrighted; } bool MPEG::Properties::isOriginal() const { return d->isOriginal; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MPEG::Properties::read(File *file, ReadStyle readStyle) { // Only the first valid frame is required if we have a VBR header. const offset_t firstFrameOffset = file->firstFrameOffset(); if(firstFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); return; } const Header firstHeader(file, firstFrameOffset, false); // Check for a VBR header that will help us in gathering information about a // VBR stream. file->seek(firstFrameOffset); d->xingHeader = std::make_unique<XingHeader>(file->readBlock(firstHeader.frameLength())); if(!d->xingHeader->isValid()) { d->xingHeader = nullptr; } if(d->xingHeader && firstHeader.samplesPerFrame() > 0 && firstHeader.sampleRate() > 0) { // Read the length and the bitrate from the VBR header. const double timePerFrame = firstHeader.samplesPerFrame() * 1000.0 / firstHeader.sampleRate(); const double length = timePerFrame * d->xingHeader->totalFrames(); d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(d->xingHeader->totalSize() * 8.0 / length + 0.5); } else { int bitRate = firstHeader.bitrate(); if(firstHeader.isADTS()) { // ADTS is probably VBR, so to get the real length, we would have to go // through all frames, count the frames in numFrames and sum their // header.frameLength() to totalFrameSize, and finally calculate // d->length = 1000LL * numFrames * firstHeader.samplesPerFrame() / firstHeader.sampleRate(); // d->bitrate = d->length > 0 ? totalFrameSize * 8 / d->length : 0; // // With Fast read style, we do not try to estimate the length and just set // it and the bitrate to zero. // With Average read style, in order to come faster to an estimate which // is accurate enough, we stop when the average bytes/frame rate is stable // for 10 frames and then calculate the length from the estimated bitrate // and the stream length. if(readStyle == Fast) { bitRate = 0; d->length = 0; } else { Header header(firstHeader); unsigned long long totalFrameSize = header.frameLength(); unsigned long long bytesPerFrame = 0; unsigned long long lastBytesPerFrame = 0; offset_t offset = firstFrameOffset; offset_t nextOffset; int numFrames = 1; int sameBytesPerFrameCount = 0; while((nextOffset = file->nextFrameOffset(offset + header.frameLength())) > offset) { offset = nextOffset; header = Header(file, offset, false); totalFrameSize += header.frameLength(); ++numFrames; bytesPerFrame = totalFrameSize / numFrames; if(readStyle != Accurate) { if(bytesPerFrame == lastBytesPerFrame) { if(++sameBytesPerFrameCount >= 10) { break; } } else { sameBytesPerFrameCount = 0; } lastBytesPerFrame = bytesPerFrame; } } bitRate = firstHeader.samplesPerFrame() != 0 ? static_cast<int>(bytesPerFrame * 8 * firstHeader.sampleRate() / 1000 / firstHeader.samplesPerFrame()) : 0; } } else if(firstHeader.bitrate() > 0) { // Since there was no valid VBR header found, we hope that we're in a constant // bitrate file. // TODO: Make this more robust with audio property detection for VBR without a // Xing header. bitRate = firstHeader.bitrate(); } if(bitRate > 0) { d->bitrate = bitRate; // Look for the last MPEG audio frame to calculate the stream length. if(const offset_t lastFrameOffset = file->lastFrameOffset(); lastFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); } else { const Header lastHeader(file, lastFrameOffset, false); if(const offset_t streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); streamLength > 0) d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5); } } } d->sampleRate = firstHeader.sampleRate(); d->channelConfiguration = firstHeader.channelConfiguration(); switch(d->channelConfiguration) { case Header::FrontCenter: d->channels = 1; break; case Header::FrontLeftRight: d->channels = 2; break; case Header::FrontCenterLeftRight: d->channels = 3; break; case Header::FrontCenterLeftRightBackCenter: d->channels = 4; break; case Header::FrontCenterLeftRightBackLeftRight: d->channels = 5; break; case Header::FrontCenterLeftRightBackLeftRightLFE: d->channels = 6; break; case Header::FrontCenterLeftRightSideLeftRightBackLeftRightLFE: d->channels = 8; break; case Header::Custom: default: d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; } d->version = firstHeader.version(); d->layer = firstHeader.layer(); d->protectionEnabled = firstHeader.protectionEnabled(); d->channelMode = firstHeader.channelMode(); d->isCopyrighted = firstHeader.isCopyrighted(); d->isOriginal = firstHeader.isOriginal(); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegproperties.h�����������������������������������������������������������0000664�0000000�0000000�00000010575�14662262111�0020376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPEGPROPERTIES_H #define TAGLIB_MPEGPROPERTIES_H #include "taglib_export.h" #include "audioproperties.h" #include "mpegheader.h" namespace TagLib { namespace MPEG { class File; class XingHeader; //! An implementation of audio property reading for MP3 /*! * This reads the data from an MPEG Layer III stream found in the * AudioProperties API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of MPEG::Properties with the data read from the * MPEG::File \a file. */ Properties(File *file, ReadStyle style = Average); /*! * Destroys this MPEG Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns a pointer to the Xing/VBRI header if one exists or null if no * Xing/VBRI header was found. */ const XingHeader *xingHeader() const; /*! * Returns the MPEG Version of the file. */ Header::Version version() const; /*! * Returns the layer version. This will be between the values 1-3. */ int layer() const; /*! * Returns \c true if the MPEG protection bit is enabled. */ bool protectionEnabled() const; /*! * Returns the channel mode for this frame. */ Header::ChannelMode channelMode() const; /*! * Returns the MPEG-4 channel configuration. */ Header::ChannelConfiguration channelConfiguration() const; /*! * Returns \c true for an Audio Data Transport Stream (ADTS), usually AAC. */ bool isADTS() const; /*! * Returns \c true if the copyrighted bit is set. */ bool isCopyrighted() const; /*! * Returns \c true if the "original" bit is set. */ bool isOriginal() const; private: void read(File *file, ReadStyle readStyle); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace MPEG } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/mpegutils.h����������������������������������������������������������������0000664�0000000�0000000�00000005317�14662262111�0017340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MPEGUTILS_H #define TAGLIB_MPEGUTILS_H // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header namespace TagLib { namespace MPEG { namespace { /*! * MPEG frames can be recognized by the bit pattern 11111111 111, so the * first byte is easy to check for, however checking to see if the second byte * starts with \e 111 is a bit more tricky, hence these functions. * * \note This does not check the length of the vector, since this is an * internal utility function. */ inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0) { // 0xFF in the second byte is possible in theory, but it's very unlikely. const unsigned char b1 = bytes[offset + 0]; const unsigned char b2 = bytes[offset + 1]; return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0); } } // namespace } // namespace MPEG } // namespace TagLib #endif #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/xingheader.cpp�������������������������������������������������������������0000664�0000000�0000000�00000007651�14662262111�0020003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003 by Ismael Orenstein email : orenstein@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "xingheader.h" #include "tbytevector.h" #include "tstring.h" #include "tdebug.h" using namespace TagLib; class MPEG::XingHeader::XingHeaderPrivate { public: unsigned int frames { 0 }; unsigned int size { 0 }; MPEG::XingHeader::HeaderType type { MPEG::XingHeader::Invalid }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// MPEG::XingHeader::XingHeader(const ByteVector &data) : d(std::make_unique<XingHeaderPrivate>()) { parse(data); } MPEG::XingHeader::~XingHeader() = default; bool MPEG::XingHeader::isValid() const { return d->type != Invalid && d->frames > 0 && d->size > 0; } unsigned int MPEG::XingHeader::totalFrames() const { return d->frames; } unsigned int MPEG::XingHeader::totalSize() const { return d->size; } MPEG::XingHeader::HeaderType MPEG::XingHeader::type() const { return d->type; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void MPEG::XingHeader::parse(const ByteVector &data) { // Look for a Xing header. auto offset = data.find("Xing"); if(offset < 0) offset = data.find("Info"); if(offset >= 0) { // Xing header found. if(data.size() < static_cast<unsigned long>(offset) + 16) { debug("MPEG::XingHeader::parse() -- Xing header found but too short."); return; } if((data[offset + 7] & 0x03) != 0x03) { debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the required information."); return; } d->frames = data.toUInt(offset + 8, true); d->size = data.toUInt(offset + 12, true); d->type = Xing; } else { // Xing header not found. Then look for a VBRI header. offset = data.find("VBRI"); if(offset >= 0) { // VBRI header found. if(data.size() < static_cast<unsigned long>(offset) + 32) { debug("MPEG::XingHeader::parse() -- VBRI header found but too short."); return; } d->frames = data.toUInt(offset + 14, true); d->size = data.toUInt(offset + 10, true); d->type = VBRI; } } } ���������������������������������������������������������������������������������������taglib-2.0.2/taglib/mpeg/xingheader.h���������������������������������������������������������������0000664�0000000�0000000�00000007605�14662262111�0017447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2003 by Ismael Orenstein email : orenstein@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_XINGHEADER_H #define TAGLIB_XINGHEADER_H #include <memory> #include "taglib_export.h" #include "mpegheader.h" namespace TagLib { class ByteVector; namespace MPEG { //! An implementation of the Xing/VBRI headers /*! * This is a minimalistic implementation of the Xing/VBRI VBR headers. * Xing/VBRI headers are often added to VBR (variable bit rate) MP3 streams * to make it easy to compute the length and quality of a VBR stream. Our * implementation is only concerned with the total size of the stream (so * that we can calculate the total playing time and the average bitrate). * It uses <a href="https://multimedia.cx/mp3extensions.txt"> * mp3extensions.txt</a> and the XMMS sources as references. */ class TAGLIB_EXPORT XingHeader { public: /*! * The type of the VBR header. */ enum HeaderType { /*! * Invalid header or no VBR header found. */ Invalid = 0, /*! * Xing header. */ Xing = 1, /*! * VBRI header. */ VBRI = 2, }; /*! * Parses a Xing/VBRI header based on \a data which contains the entire * first MPEG frame. */ XingHeader(const ByteVector &data); /*! * Destroy this XingHeader instance. */ ~XingHeader(); XingHeader(const XingHeader &) = delete; XingHeader &operator=(const XingHeader &) = delete; /*! * Returns \c true if the data was parsed properly and if there is a valid * Xing/VBRI header present. */ bool isValid() const; /*! * Returns the total number of frames. */ unsigned int totalFrames() const; /*! * Returns the total size of stream in bytes. */ unsigned int totalSize() const; /*! * Returns the type of the VBR header. */ HeaderType type() const; private: void parse(const ByteVector &data); class XingHeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<XingHeaderPrivate> d; }; } // namespace MPEG } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014774�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/flac/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015701�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/flac/oggflacfile.cpp��������������������������������������������������������0000664�0000000�0000000�00000017753�14662262111�0020664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004-2005 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "oggflacfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; using TagLib::FLAC::Properties; class Ogg::FLAC::File::FilePrivate { public: std::unique_ptr<Ogg::XiphComment> comment; std::unique_ptr<Properties> properties; ByteVector streamInfoData; ByteVector xiphCommentData; offset_t streamStart { 0 }; offset_t streamLength { 0 }; bool scanned { false }; bool hasXiphComment { false }; int commentPacket { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool Ogg::FLAC::File::isSupported(IOStream *stream) { // An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); return buffer.find("OggS") >= 0 && buffer.find("fLaC") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Ogg::FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties, propertiesStyle); } Ogg::FLAC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties, propertiesStyle); } Ogg::FLAC::File::~File() = default; Ogg::XiphComment *Ogg::FLAC::File::tag() const { return d->comment.get(); } PropertyMap Ogg::FLAC::File::properties() const { return d->comment->properties(); } PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); } Properties *Ogg::FLAC::File::audioProperties() const { return d->properties.get(); } bool Ogg::FLAC::File::save() { d->xiphCommentData = d->comment->render(false); // Create FLAC metadata-block: // Put the size in the first 32 bit (I assume no more than 24 bit are used) ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size()); // Set the type of the metadata-block to be a Xiph / Vorbis comment v[0] = 4; // Append the comment-data after the 32 bit header v.append(d->xiphCommentData); // Save the packet at the old spot // FIXME: Use padding if size is increasing setPacket(d->commentPacket, v); return Ogg::File::save(); } bool Ogg::FLAC::File::hasXiphComment() const { return d->hasXiphComment; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) { // Sanity: Check if we really have an Ogg/FLAC file /* ByteVector oggHeader = packet(0); if (oggHeader.mid(28,4) != "fLaC") { debug("Ogg::FLAC::File::read() -- Not an Ogg/FLAC file"); setValid(false); return; }*/ // Look for FLAC metadata, including vorbis comments scan(); if (!d->scanned) { setValid(false); return; } if(d->hasXiphComment) d->comment = std::make_unique<Ogg::XiphComment>(xiphCommentData()); else d->comment = std::make_unique<Ogg::XiphComment>(); if(readProperties) d->properties = std::make_unique<Properties>(streamInfoData(), streamLength(), propertiesStyle); } ByteVector Ogg::FLAC::File::streamInfoData() { scan(); return d->streamInfoData; } ByteVector Ogg::FLAC::File::xiphCommentData() { scan(); return d->xiphCommentData; } offset_t Ogg::FLAC::File::streamLength() { scan(); return d->streamLength; } void Ogg::FLAC::File::scan() { // Scan the metadata pages if(d->scanned) return; if(!isValid()) return; int ipacket = 0; offset_t overhead = 0; ByteVector metadataHeader = packet(ipacket); if(metadataHeader.isEmpty()) return; if(!metadataHeader.startsWith("fLaC")) { // FLAC 1.1.2+ // See https://xiph.org/flac/ogg_mapping.html for the header specification. if(metadataHeader.size() < 13) return; if(metadataHeader[0] != 0x7f) return; if(metadataHeader.mid(1, 4) != "FLAC") return; if(metadataHeader[5] != 1 && metadataHeader[6] != 0) return; // not version 1.0 if(metadataHeader.mid(9, 4) != "fLaC") return; metadataHeader = metadataHeader.mid(13); } else { // FLAC 1.1.0 & 1.1.1 metadataHeader = packet(++ipacket); } ByteVector header = metadataHeader.mid(0, 4); if(header.size() != 4) { debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); return; } // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE // 0 : STREAMINFO // 1 : PADDING // .. // 4 : VORBIS_COMMENT // .. // <24> Length of metadata to follow char blockType = header[0] & 0x7f; bool lastBlock = (header[0] & 0x80) != 0; unsigned int length = header.toUInt(1, 3, true); overhead += length; // Sanity: First block should be the stream_info metadata if(blockType != 0) { debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream"); return; } d->streamInfoData = metadataHeader.mid(4, length); // Search through the remaining metadata while(!lastBlock) { metadataHeader = packet(++ipacket); header = metadataHeader.mid(0, 4); if(header.size() != 4) { debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); return; } blockType = header[0] & 0x7f; lastBlock = (header[0] & 0x80) != 0; length = header.toUInt(1, 3, true); overhead += length; if(blockType == 1) { // debug("Ogg::FLAC::File::scan() -- Padding found"); } else if(blockType == 4) { // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found"); d->xiphCommentData = metadataHeader.mid(4, length); d->hasXiphComment = true; d->commentPacket = ipacket; } else if(blockType > 5) { debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); } } // End of metadata, now comes the datastream d->streamStart = overhead; d->streamLength = File::length() - d->streamStart; d->scanned = true; } ���������������������taglib-2.0.2/taglib/ogg/flac/oggflacfile.h����������������������������������������������������������0000664�0000000�0000000�00000014071�14662262111�0020317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OGGFLACFILE_H #define TAGLIB_OGGFLACFILE_H #include "taglib_export.h" #include "oggfile.h" #include "xiphcomment.h" #include "flacproperties.h" namespace TagLib { class Tag; namespace Ogg { //! An implementation of Ogg FLAC metadata /*! * This is an implementation of FLAC metadata for Ogg FLAC files. For "pure" * FLAC files look under the FLAC hierarchy. * * Unlike "pure" FLAC-files, Ogg FLAC only supports Xiph-comments, * while the audio-properties are the same. */ namespace FLAC { using TagLib::FLAC::Properties; //! An implementation of TagLib::File with Ogg/FLAC specific methods /*! * This implements and provides an interface for Ogg/FLAC files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to Ogg FLAC files. */ class TAGLIB_EXPORT File : public Ogg::File { public: /*! * Constructs an Ogg/FLAC file from \a file. If \a readProperties is \c true * the file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs an Ogg/FLAC file from \a stream. If \a readProperties is \c true * the file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. This will always be a XiphComment. * * \note This always returns a valid pointer regardless of whether or not * the file on disk has a XiphComment. Use hasXiphComment() to check if * the file on disk actually has a XiphComment. * * \note The Tag <b>is still</b> owned by the Ogg::FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasXiphComment() */ XiphComment *tag() const override; /*! * Returns the FLAC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Implements the unified property interface -- export function. * This forwards directly to XiphComment::properties(). */ PropertyMap properties() const override; /*! * Implements the unified tag dictionary interface -- import function. * Like properties(), this is a forwarder to the file's XiphComment. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Save the file. This will primarily save and update the XiphComment. * Returns \c true if the save is successful. */ bool save() override; /*! * Returns the length of the audio-stream, used by FLAC::Properties for * calculating the bitrate. */ offset_t streamLength(); /*! * Returns whether or not the file on disk actually has a XiphComment. * * \see tag() */ bool hasXiphComment() const; /*! * Check if the given \a stream can be opened as an Ogg FLAC file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties, Properties::ReadStyle propertiesStyle); void scan(); ByteVector streamInfoData(); ByteVector xiphCommentData(); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace FLAC } // namespace Ogg } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/oggfile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000021551�14662262111�0017120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "oggfile.h" #include <utility> #include "tdebug.h" #include "tmap.h" #include "oggpage.h" #include "oggpageheader.h" using namespace TagLib; namespace { // Returns the first packet index of the right next page to the given one. unsigned int nextPacketIndex(const Ogg::Page *page) { if(page->header()->lastPacketCompleted()) return page->firstPacketIndex() + page->packetCount(); return page->firstPacketIndex() + page->packetCount() - 1; } } // namespace class Ogg::File::FilePrivate { public: FilePrivate() { pages.setAutoDelete(true); } List<Page *> pages; std::unique_ptr<PageHeader> firstPageHeader; std::unique_ptr<PageHeader> lastPageHeader; Map<unsigned int, ByteVector> dirtyPackets; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Ogg::File::~File() = default; ByteVector Ogg::File::packet(unsigned int i) { // Check to see if we're called setPacket() for this packet since the last // save: if(d->dirtyPackets.contains(i)) return d->dirtyPackets[i]; // If we haven't indexed the page where the packet we're interested in starts, // begin reading pages until we have. if(!readPages(i)) { debug("Ogg::File::packet() -- Could not find the requested packet."); return ByteVector(); } // Look for the first page in which the requested packet starts. auto it = d->pages.cbegin(); while((*it)->containsPacket(i) == Page::DoesNotContainPacket) ++it; // If the packet is completely contained in the first page that it's in. // If the packet is *not* completely contained in the first page that it's a // part of then that packet trails off the end of the page. Continue appending // the pages' packet data until we hit a page that either does not end with the // packet that we're fetching or where the last packet is complete. ByteVector packet = (*it)->packets()[i - (*it)->firstPacketIndex()]; while(nextPacketIndex(*it) <= i) { ++it; packet.append((*it)->packets().front()); } return packet; } void Ogg::File::setPacket(unsigned int i, const ByteVector &p) { if(!readPages(i)) { debug("Ogg::File::setPacket() -- Could not set the requested packet."); return; } d->dirtyPackets[i] = p; } const Ogg::PageHeader *Ogg::File::firstPageHeader() { if(!d->firstPageHeader) { const offset_t firstPageHeaderOffset = find("OggS"); if(firstPageHeaderOffset < 0) return nullptr; d->firstPageHeader = std::make_unique<PageHeader>(this, firstPageHeaderOffset); } return d->firstPageHeader->isValid() ? d->firstPageHeader.get() : nullptr; } const Ogg::PageHeader *Ogg::File::lastPageHeader() { if(!d->lastPageHeader) { const offset_t lastPageHeaderOffset = rfind("OggS"); if(lastPageHeaderOffset < 0) return nullptr; d->lastPageHeader = std::make_unique<PageHeader>(this, lastPageHeaderOffset); } return d->lastPageHeader->isValid() ? d->lastPageHeader.get() : nullptr; } bool Ogg::File::save() { if(readOnly()) { debug("Ogg::File::save() - Cannot save to a read only file."); return false; } for(const auto &[i, pkt] : std::as_const(d->dirtyPackets)) writePacket(i, pkt); d->dirtyPackets.clear(); return true; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// Ogg::File::File(FileName file) : TagLib::File(file), d(std::make_unique<FilePrivate>()) { } Ogg::File::File(IOStream *stream) : TagLib::File(stream), d(std::make_unique<FilePrivate>()) { } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// bool Ogg::File::readPages(unsigned int i) { while(true) { unsigned int packetIndex; offset_t offset; if(d->pages.isEmpty()) { packetIndex = 0; offset = find("OggS"); if(offset < 0) return false; } else { const Page *page = d->pages.back(); packetIndex = nextPacketIndex(page); offset = page->fileOffset() + page->size(); } // Enough pages have been fetched. if(packetIndex > i) return true; // Read the next page and add it to the page list. auto nextPage = new Page(this, offset); if(!nextPage->header()->isValid()) { delete nextPage; return false; } nextPage->setFirstPacketIndex(packetIndex); d->pages.append(nextPage); if(nextPage->header()->lastPageOfStream()) return false; } } void Ogg::File::writePacket(unsigned int i, const ByteVector &packet) { if(!readPages(i)) { debug("Ogg::File::writePacket() -- Could not find the requested packet."); return; } // Look for the pages where the requested packet should belong to. auto it = d->pages.cbegin(); while((*it)->containsPacket(i) == Page::DoesNotContainPacket) ++it; const Page *firstPage = *it; while(nextPacketIndex(*it) <= i) ++it; const Page *lastPage = *it; // Replace the requested packet and create new pages to replace the located pages. ByteVectorList packets = firstPage->packets(); packets[i - firstPage->firstPacketIndex()] = packet; if(firstPage != lastPage && lastPage->packetCount() > 1) { ByteVectorList lastPagePackets = lastPage->packets(); lastPagePackets.erase(lastPagePackets.begin()); packets.append(lastPagePackets); } // TODO: This pagination method isn't accurate for what's being done here. // This should account for real possibilities like non-aligned packets and such. List<Page *> pages = Page::paginate(packets, Page::SinglePagePerGroup, firstPage->header()->streamSerialNumber(), firstPage->pageSequenceNumber(), firstPage->header()->firstPacketContinued(), lastPage->header()->lastPacketCompleted()); pages.setAutoDelete(true); // Write the pages. ByteVector data; for(const auto &page : pages) data.append(page->render()); const offset_t originalOffset = firstPage->fileOffset(); const offset_t originalLength = lastPage->fileOffset() + lastPage->size() - originalOffset; insert(data, originalOffset, originalLength); // Renumber the following pages if the pages have been split or merged. if(const int numberOfNewPages = pages.back()->pageSequenceNumber() - lastPage->pageSequenceNumber(); numberOfNewPages != 0) { offset_t pageOffset = originalOffset + data.size(); while(true) { Page page(this, pageOffset); if(!page.header()->isValid()) break; page.setPageSequenceNumber(page.pageSequenceNumber() + numberOfNewPages); const ByteVector pageData = page.render(); seek(pageOffset + 18); writeBlock(pageData.mid(18, 8)); if(page.header()->lastPageOfStream()) break; pageOffset += page.size(); } } // Discard all the pages to keep them up-to-date by fetching them again. d->pages.clear(); } �������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/oggfile.h�������������������������������������������������������������������0000664�0000000�0000000�00000011056�14662262111�0016564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OGGFILE_H #define TAGLIB_OGGFILE_H #include "tfile.h" #include "tbytevectorlist.h" #include "taglib_export.h" namespace TagLib { //! A namespace for the classes used by Ogg-based metadata files namespace Ogg { class PageHeader; //! An implementation of TagLib::File with some helpers for Ogg based formats /*! * This is an implementation of Ogg file page and packet rendering and is of * use to Ogg based formats. While the API is small this handles the * non-trivial details of breaking up an Ogg stream into packets and makes * these available (via subclassing) to the codec metadata implementations. */ class TAGLIB_EXPORT File : public TagLib::File { public: ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the packet contents for the i-th packet (starting from zero) * in the Ogg bitstream. * * \warning This requires reading at least the packet header for every page * up to the requested page. */ ByteVector packet(unsigned int i); /*! * Sets the packet with index \a i to the value \a p. */ void setPacket(unsigned int i, const ByteVector &p); /*! * Returns a pointer to the PageHeader for the first page in the stream or * null if the page could not be found. */ const PageHeader *firstPageHeader(); /*! * Returns a pointer to the PageHeader for the last page in the stream or * null if the page could not be found. */ const PageHeader *lastPageHeader(); bool save() override; protected: /*! * Constructs an Ogg file from \a file. * * \note This constructor is protected since Ogg::File shouldn't be * instantiated directly but rather should be used through the codec * specific subclasses. */ File(FileName file); /*! * Constructs an Ogg file from \a stream. * * \note This constructor is protected since Ogg::File shouldn't be * instantiated directly but rather should be used through the codec * specific subclasses. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream); private: /*! * Reads the pages from the beginning of the file until enough to compose * the requested packet. */ bool readPages(unsigned int i); /*! * Writes the requested packet to the file. */ void writePacket(unsigned int i, const ByteVector &packet); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace Ogg } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/oggpage.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000031501�14662262111�0017111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "oggpage.h" #include <algorithm> #include <numeric> #include <array> #include <utility> #include "tstring.h" #include "tdebug.h" #include "oggpageheader.h" #include "oggfile.h" using namespace TagLib; namespace { /*! * Returns a CRC checksum of the byte vector's \a data. * * \note This uses an uncommon variant of CRC32 specializes in Ogg. */ unsigned int pageChecksum(const ByteVector &data) { static constexpr std::array<unsigned int, 256> crcTable { 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 }; return std::accumulate(data.cbegin(), data.cend(), 0U, [](unsigned int sum, unsigned char byte) { return (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ byte]; }); } } // namespace class Ogg::Page::PagePrivate { public: PagePrivate(File *f = nullptr, offset_t pageOffset = -1) : file(f), fileOffset(pageOffset), header(f, pageOffset) { } File *file; offset_t fileOffset; PageHeader header; int firstPacketIndex { -1 }; ByteVectorList packets; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Ogg::Page::Page(Ogg::File *file, offset_t pageOffset) : d(std::make_unique<PagePrivate>(file, pageOffset)) { } Ogg::Page::~Page() = default; offset_t Ogg::Page::fileOffset() const { return d->fileOffset; } const Ogg::PageHeader *Ogg::Page::header() const { return &d->header; } int Ogg::Page::pageSequenceNumber() const { return d->header.pageSequenceNumber(); } void Ogg::Page::setPageSequenceNumber(int sequenceNumber) { d->header.setPageSequenceNumber(sequenceNumber); } int Ogg::Page::firstPacketIndex() const { return d->firstPacketIndex; } void Ogg::Page::setFirstPacketIndex(int index) { d->firstPacketIndex = index; } Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const { const int lastPacketIndex = d->firstPacketIndex + packetCount() - 1; if(index < d->firstPacketIndex || index > lastPacketIndex) return DoesNotContainPacket; ContainsPacketFlags flags = DoesNotContainPacket; if(index == d->firstPacketIndex) flags = static_cast<ContainsPacketFlags>(flags | BeginsWithPacket); if(index == lastPacketIndex) flags = static_cast<ContainsPacketFlags>(flags | EndsWithPacket); // If there's only one page and it's complete: if(packetCount() == 1 && !d->header.firstPacketContinued() && d->header.lastPacketCompleted()) { flags = static_cast<ContainsPacketFlags>(flags | CompletePacket); } // Or if there is more than one page and the page is // (a) the first page and it's complete or // (b) the last page and it's complete or // (c) a page in the middle. else if(packetCount() > 1 && ((flags & BeginsWithPacket && !d->header.firstPacketContinued()) || (flags & EndsWithPacket && d->header.lastPacketCompleted()) || (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket)))) { flags = static_cast<ContainsPacketFlags>(flags | CompletePacket); } return flags; } unsigned int Ogg::Page::packetCount() const { return d->header.packetSizes().size(); } ByteVectorList Ogg::Page::packets() const { if(!d->packets.isEmpty()) return d->packets; ByteVectorList l; if(d->file && d->header.isValid()) { d->file->seek(d->fileOffset + d->header.size()); const List<int> packetSizes = d->header.packetSizes(); for(const auto &sz : packetSizes) l.append(d->file->readBlock(sz)); } else debug("Ogg::Page::packets() -- attempting to read packets from an invalid page."); return l; } int Ogg::Page::size() const { return d->header.size() + d->header.dataSize(); } ByteVector Ogg::Page::render() const { ByteVector data; data.append(d->header.render()); if(d->packets.isEmpty()) { if(d->file) { d->file->seek(d->fileOffset + d->header.size()); data.append(d->file->readBlock(d->header.dataSize())); } else debug("Ogg::Page::render() -- this page is empty!"); } else { for(const auto &packet : std::as_const(d->packets)) data.append(packet); } // 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. const ByteVector checksum = ByteVector::fromUInt(::pageChecksum(data), false); std::copy(checksum.begin(), checksum.end(), data.begin() + 22); return data; } List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets, PaginationStrategy strategy, unsigned int streamSerialNumber, int firstPage, bool firstPacketContinued, bool lastPacketCompleted, bool containsLastPacket) { // SplitSize must be a multiple of 255 in order to get the lacing values right // create pages of about 8KB each static constexpr unsigned int SplitSize = 32 * 255; // Force repagination if the segment table will exceed the size limit. if(strategy != Repaginate) { size_t tableSize = std::accumulate(packets.cbegin(), packets.cend(), static_cast<size_t>(0), [](size_t acc, const ByteVector &packet) { return acc + packet.size() / 255 + 1; }); if(tableSize > 255) strategy = Repaginate; } List<Page *> l; // Handle creation of multiple pages with appropriate pagination. if(strategy == Repaginate) { int pageIndex = firstPage; for(auto it = packets.begin(); it != packets.end(); ++it) { const bool lastPacketInList = it == --packets.end(); // mark very first packet? bool continued = firstPacketContinued && it == packets.begin(); unsigned int pos = 0; while(pos < it->size()) { const bool lastSplit = pos + SplitSize >= it->size(); ByteVectorList packetList; packetList.append(it->mid(pos, SplitSize)); l.append(new Page(packetList, streamSerialNumber, pageIndex, continued, lastSplit && (!lastPacketInList || lastPacketCompleted), lastSplit && (containsLastPacket && lastPacketInList))); pageIndex++; continued = true; pos += SplitSize; } } } else { l.append(new Page(packets, streamSerialNumber, firstPage, firstPacketContinued, lastPacketCompleted, containsLastPacket)); } return l; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// Ogg::Page::Page(const ByteVectorList &packets, unsigned int streamSerialNumber, int pageNumber, bool firstPacketContinued, bool lastPacketCompleted, bool containsLastPacket) : d(std::make_unique<PagePrivate>()) { d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued); d->header.setLastPageOfStream(containsLastPacket); d->header.setFirstPacketContinued(firstPacketContinued); d->header.setLastPacketCompleted(lastPacketCompleted); d->header.setStreamSerialNumber(streamSerialNumber); d->header.setPageSequenceNumber(pageNumber); // Build a page from the list of packets. ByteVector data; List<int> packetSizes; for(const auto &packet : packets) { packetSizes.append(packet.size()); data.append(packet); } d->packets = packets; d->header.setPacketSizes(packetSizes); // https://xiph.org/ogg/doc/framing.html, absolute granule position: // A special value of '-1' (in two's complement) indicates that no packets // finish on this page. if(!lastPacketCompleted && packets.size() <= 1) d->header.setAbsoluteGranularPosition(-1); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/oggpage.h�������������������������������������������������������������������0000664�0000000�0000000�00000017640�14662262111�0016566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OGGPAGE_H #define TAGLIB_OGGPAGE_H #include "tbytevectorlist.h" #include "taglib.h" #include "taglib_export.h" namespace TagLib { namespace Ogg { class File; class PageHeader; //! An implementation of Ogg pages /*! * This is an implementation of the pages that make up an Ogg stream. * This handles parsing pages and breaking them down into packets and handles * the details of packets spanning multiple pages and pages that contain * multiple packets. * * In most Xiph.org formats the comments are found in the first few packets, * this however is a reasonably complete implementation of Ogg pages that * could potentially be useful for non-metadata purposes. */ class TAGLIB_EXPORT Page { public: /*! * Read an Ogg page from the \a file at the position \a pageOffset. */ Page(File *file, offset_t pageOffset); ~Page(); Page(const Page &) = delete; Page &operator=(const Page &) = delete; /*! * Returns the page's position within the file (in bytes). */ offset_t fileOffset() const; /*! * Returns a pointer to the header for this page. This pointer will become * invalid when the page is deleted. */ const PageHeader *header() const; /*! * Returns the index of the page within the Ogg stream. This helps make it * possible to determine if pages have been lost. * * \see setPageSequenceNumber() */ int pageSequenceNumber() const; /*! * Sets the page's position in the stream to \a sequenceNumber. * * \see pageSequenceNumber() */ void setPageSequenceNumber(int sequenceNumber); /*! * Returns the index of the first packet wholly or partially contained in * this page. * * \see setFirstPacketIndex() */ int firstPacketIndex() const; /*! * Sets the index of the first packet in the page. * * \see firstPacketIndex() */ void setFirstPacketIndex(int index); /*! * When checking to see if a page contains a given packet this set of flags * represents the possible values for that packet's status in the page. * * \see containsPacket() */ enum ContainsPacketFlags { //! No part of the packet is contained in the page DoesNotContainPacket = 0x0000, //! The packet is wholly contained in the page CompletePacket = 0x0001, //! The page starts with the given packet BeginsWithPacket = 0x0002, //! The page ends with the given packet EndsWithPacket = 0x0004 }; /*! * Checks to see if the specified \a packet is contained in the current * page. * * \see ContainsPacketFlags */ ContainsPacketFlags containsPacket(int index) const; /*! * Returns the number of packets (whole or partial) in this page. */ unsigned int packetCount() const; /*! * Returns a list of the packets in this page. * * \note Either or both the first and last packets may be only partial. * \see PageHeader::firstPacketContinued() */ ByteVectorList packets() const; /*! * Returns the size of the page in bytes. */ int size() const; ByteVector render() const; /*! * Defines a strategy for pagination, or grouping pages into Ogg packets, * for use with pagination methods. * * \note Yes, I'm aware that this is not a canonical "Strategy Pattern", * the term was simply convenient. */ enum PaginationStrategy { /*! * Attempt to put the specified set of packets into a single Ogg packet. * If the sum of the packet data is greater than will fit into a single * Ogg page -- 65280 bytes -- this will fall back to repagination using * the recommended page sizes. */ SinglePagePerGroup, /*! * Split the packet or group of packets into pages that conform to the * sizes recommended in the Ogg standard. */ Repaginate }; /*! * Pack \a packets into Ogg pages using the \a strategy for pagination. * The page number indicator inside of the rendered packets will start * with \a firstPage and be incremented for each page rendered. * \a containsLastPacket should be set to \c true if \a packets contains the * last page in the stream and will set the appropriate flag in the last * rendered Ogg page's header. \a streamSerialNumber should be set to * the serial number for this stream. * * \note The "absolute granule position" is currently always zeroed using * this method as this suffices for the comment headers. * * \warning The pages returned by this method must be deleted by the user. * You can use List<T>::setAutoDelete(true) to set these pages to be * automatically deleted when this list passes out of scope. * * \see PaginationStrategy * \see List::setAutoDelete() */ static List<Page *> paginate(const ByteVectorList &packets, PaginationStrategy strategy, unsigned int streamSerialNumber, int firstPage, bool firstPacketContinued = false, bool lastPacketCompleted = true, bool containsLastPacket = false); protected: /*! * Creates an Ogg packet based on the data in \a packets. The page number * for each page will be set to \a pageNumber. */ Page(const ByteVectorList &packets, unsigned int streamSerialNumber, int pageNumber, bool firstPacketContinued = false, bool lastPacketCompleted = true, bool containsLastPacket = false); private: class PagePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PagePrivate> d; }; } // namespace Ogg } // namespace TagLib #endif ������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/oggpageheader.cpp�����������������������������������������������������������0000664�0000000�0000000�00000017743�14662262111�0020276�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "oggpageheader.h" #include <bitset> #include "tdebug.h" #include "tstring.h" #include "oggfile.h" using namespace TagLib; class Ogg::PageHeader::PageHeaderPrivate { public: bool isValid { false }; List<int> packetSizes; bool firstPacketContinued { false }; bool lastPacketCompleted { false }; bool firstPageOfStream { false }; bool lastPageOfStream { false }; long long absoluteGranularPosition { 0 }; unsigned int streamSerialNumber { 0 }; int pageSequenceNumber { -1 }; int size { 0 }; int dataSize { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Ogg::PageHeader::PageHeader(Ogg::File *file, offset_t pageOffset) : d(std::make_unique<PageHeaderPrivate>()) { if(file && pageOffset >= 0) read(file, pageOffset); } Ogg::PageHeader::~PageHeader() = default; bool Ogg::PageHeader::isValid() const { return d->isValid; } List<int> Ogg::PageHeader::packetSizes() const { return d->packetSizes; } void Ogg::PageHeader::setPacketSizes(const List<int> &sizes) { d->packetSizes = sizes; } bool Ogg::PageHeader::firstPacketContinued() const { return d->firstPacketContinued; } void Ogg::PageHeader::setFirstPacketContinued(bool continued) { d->firstPacketContinued = continued; } bool Ogg::PageHeader::lastPacketCompleted() const { return d->lastPacketCompleted; } void Ogg::PageHeader::setLastPacketCompleted(bool completed) { d->lastPacketCompleted = completed; } bool Ogg::PageHeader::firstPageOfStream() const { return d->firstPageOfStream; } void Ogg::PageHeader::setFirstPageOfStream(bool first) { d->firstPageOfStream = first; } bool Ogg::PageHeader::lastPageOfStream() const { return d->lastPageOfStream; } void Ogg::PageHeader::setLastPageOfStream(bool last) { d->lastPageOfStream = last; } long long Ogg::PageHeader::absoluteGranularPosition() const { return d->absoluteGranularPosition; } void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp) { d->absoluteGranularPosition = agp; } int Ogg::PageHeader::pageSequenceNumber() const { return d->pageSequenceNumber; } void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber) { d->pageSequenceNumber = sequenceNumber; } unsigned int Ogg::PageHeader::streamSerialNumber() const { return d->streamSerialNumber; } void Ogg::PageHeader::setStreamSerialNumber(unsigned int n) { d->streamSerialNumber = n; } int Ogg::PageHeader::size() const { return d->size; } int Ogg::PageHeader::dataSize() const { return d->dataSize; } ByteVector Ogg::PageHeader::render() const { ByteVector data; // capture pattern data.append("OggS"); // stream structure version data.append(static_cast<char>(0)); // header type flag std::bitset<8> flags; flags[0] = d->firstPacketContinued; flags[1] = d->pageSequenceNumber == 0; flags[2] = d->lastPageOfStream; data.append(static_cast<char>(flags.to_ulong())); // absolute granular position data.append(ByteVector::fromLongLong(d->absoluteGranularPosition, false)); // stream serial number data.append(ByteVector::fromUInt(d->streamSerialNumber, false)); // page sequence number data.append(ByteVector::fromUInt(d->pageSequenceNumber, false)); // checksum -- this is left empty and should be filled in by the Ogg::Page // class data.append(ByteVector(4, 0)); // page segment count and page segment table ByteVector pageSegments = lacingValues(); data.append(static_cast<unsigned char>(pageSegments.size())); data.append(pageSegments); return data; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Ogg::PageHeader::read(Ogg::File *file, offset_t pageOffset) { file->seek(pageOffset); // 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. const ByteVector data = file->readBlock(27); // Sanity check -- make sure that we were in fact able to read as much data as // we asked for and that the page begins with "OggS". if(data.size() != 27 || !data.startsWith("OggS")) { debug("Ogg::PageHeader::read() -- error reading page header"); return; } const std::bitset<8> flags(data[5]); d->firstPacketContinued = flags.test(0); d->firstPageOfStream = flags.test(1); d->lastPageOfStream = flags.test(2); d->absoluteGranularPosition = data.toLongLong(6, false); d->streamSerialNumber = data.toUInt(14, false); d->pageSequenceNumber = data.toUInt(18, false); // Byte number 27 is the number of page segments, which is the only variable // length portion of the page header. After reading the number of page // segments we'll then read in the corresponding data for this count. int pageSegmentCount = static_cast<unsigned char>(data[26]); const ByteVector pageSegments = file->readBlock(pageSegmentCount); // Another sanity check. if(pageSegmentCount < 1 || static_cast<int>(pageSegments.size()) != pageSegmentCount) return; // The base size of an Ogg page 27 bytes plus the number of lacing values. d->size = 27 + pageSegmentCount; int packetSize = 0; for(int i = 0; i < pageSegmentCount; i++) { d->dataSize += static_cast<unsigned char>(pageSegments[i]); packetSize += static_cast<unsigned char>(pageSegments[i]); if(static_cast<unsigned char>(pageSegments[i]) < 255) { d->packetSizes.append(packetSize); packetSize = 0; } } if(packetSize > 0) { d->packetSizes.append(packetSize); d->lastPacketCompleted = false; } else d->lastPacketCompleted = true; d->isValid = true; } ByteVector Ogg::PageHeader::lacingValues() const { ByteVector data; for(auto it = d->packetSizes.cbegin(); it != d->packetSizes.cend(); ++it) { // The size of a packet in an Ogg page is indicated by a series of "lacing // values" where the sum of the values is the packet size in bytes. Each of // these values is a byte. A value of less than 255 (0xff) indicates the end // of the packet. data.resize(data.size() + *it / 255, '\xff'); if(it != std::prev(d->packetSizes.cend()) || d->lastPacketCompleted) data.append(static_cast<unsigned char>(*it % 255)); } return data; } �����������������������������taglib-2.0.2/taglib/ogg/oggpageheader.h�������������������������������������������������������������0000664�0000000�0000000�00000017171�14662262111�0017736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OGGPAGEHEADER_H #define TAGLIB_OGGPAGEHEADER_H #include "tlist.h" #include "tbytevector.h" #include "taglib.h" #include "taglib_export.h" namespace TagLib { namespace Ogg { class File; //! An implementation of the page headers associated with each Ogg::Page /*! * This class implements Ogg page headers which contain the information * about Ogg pages needed to break them into packets which can be passed on * to the codecs. */ class TAGLIB_EXPORT PageHeader { public: /*! * Reads a PageHeader from \a file starting at \a pageOffset. The defaults * create a page with no (and as such, invalid) data that must be set * later. */ PageHeader(File *file = nullptr, offset_t pageOffset = -1); /*! * Deletes this instance of the PageHeader. */ ~PageHeader(); PageHeader(const PageHeader &) = delete; PageHeader &operator=(const PageHeader &) = delete; /*! * Returns \c true if the header parsed properly and is valid. */ bool isValid() const; /*! * Ogg pages contain a list of packets (which are used by the contained * codecs). The sizes of these pages are encoded in the page header. This * returns a list of the packet sizes in bytes. * * \see setPacketSizes() */ List<int> packetSizes() const; /*! * Sets the sizes of the packets in this page to \a sizes. Internally this * updates the lacing values in the header. * * \see packetSizes() */ void setPacketSizes(const List<int> &sizes); /*! * Some packets can be <i>continued</i> across multiple pages. If the * first packet in the current page is a continuation this will return * \c true. If this page starts with a new packet this will return \c false. * * \see lastPacketCompleted() * \see setFirstPacketContinued() */ bool firstPacketContinued() const; /*! * Sets the internal flag indicating if the first packet in this page is * continued to \a continued. * * \see firstPacketContinued() */ void setFirstPacketContinued(bool continued); /*! * Returns \c true if the last packet of this page is completely contained in * this page. * * \see firstPacketContinued() * \see setLastPacketCompleted() */ bool lastPacketCompleted() const; /*! * Sets the internal flag indicating if the last packet in this page is * complete to \a completed. * * \see lastPacketCompleted() */ void setLastPacketCompleted(bool completed); /*! * This returns \c true if this is the first page of the Ogg (logical) stream. * * \see setFirstPageOfStream() */ bool firstPageOfStream() const; /*! * Marks this page as the first page of the Ogg stream. * * \see firstPageOfStream() */ void setFirstPageOfStream(bool first); /*! * This returns \c true if this is the last page of the Ogg (logical) stream. * * \see setLastPageOfStream() */ bool lastPageOfStream() const; /*! * Marks this page as the last page of the Ogg stream. * * \see lastPageOfStream() */ void setLastPageOfStream(bool last); /*! * A special value of containing the position of the packet to be * interpreted by the codec. In the case of Vorbis this contains the PCM * value and is used to calculate the length of the stream. * * \see setAbsoluteGranularPosition() */ long long absoluteGranularPosition() const; /*! * A special value of containing the position of the packet to be * interpreted by the codec. It is only supported here so that it may be * copied from one page to another. * * \see absoluteGranularPosition() */ void setAbsoluteGranularPosition(long long agp); /*! * Every Ogg logical stream is given a random serial number which is common * to every page in that logical stream. This returns the serial number of * the stream associated with this packet. * * \see setStreamSerialNumber() */ unsigned int streamSerialNumber() const; /*! * Every Ogg logical stream is given a random serial number which is common * to every page in that logical stream. This sets this pages serial * number. This method should be used when adding new pages to a logical * stream. * * \see streamSerialNumber() */ void setStreamSerialNumber(unsigned int n); /*! * Returns the index of the page within the Ogg stream. This helps make it * possible to determine if pages have been lost. * * \see setPageSequenceNumber() */ int pageSequenceNumber() const; /*! * Sets the page's position in the stream to \a sequenceNumber. * * \see pageSequenceNumber() */ void setPageSequenceNumber(int sequenceNumber); /*! * Returns the complete header size. */ int size() const; /*! * Returns the size of the data portion of the page -- i.e. the size of the * page less the header size. */ int dataSize() const; /*! * Render the page header to binary data. * * \note The checksum -- bytes 22 - 25 -- will be left empty and must be * filled in when rendering the entire page. */ ByteVector render() const; private: void read(Ogg::File *file, offset_t pageOffset); ByteVector lacingValues() const; class PageHeaderPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PageHeaderPrivate> d; }; } // namespace Ogg } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/opus/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015762�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/opus/opusfile.cpp�����������������������������������������������������������0000664�0000000�0000000�00000010663�14662262111�0020322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "opusfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; using namespace TagLib::Ogg; class Opus::File::FilePrivate { public: std::unique_ptr<Ogg::XiphComment> comment; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool Ogg::Opus::File::isSupported(IOStream *stream) { // An Opus file has IDs "OggS" and "OpusHead" somewhere. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); return buffer.find("OggS") >= 0 && buffer.find("OpusHead") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Ogg::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Ogg::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Opus::File::~File() = default; Ogg::XiphComment *Opus::File::tag() const { return d->comment.get(); } PropertyMap Opus::File::properties() const { return d->comment->properties(); } PropertyMap Opus::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); } Opus::Properties *Opus::File::audioProperties() const { return d->properties.get(); } bool Opus::File::save() { if(!d->comment) d->comment = std::make_unique<Ogg::XiphComment>(); setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); return Ogg::File::save(); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Opus::File::read(bool readProperties) { ByteVector opusHeaderData = packet(0); if(!opusHeaderData.startsWith("OpusHead")) { setValid(false); debug("Opus::File::read() -- invalid Opus identification header"); return; } ByteVector commentHeaderData = packet(1); if(!commentHeaderData.startsWith("OpusTags")) { setValid(false); debug("Opus::File::read() -- invalid Opus tags header"); return; } d->comment = std::make_unique<Ogg::XiphComment>(commentHeaderData.mid(8)); if(readProperties) d->properties = std::make_unique<Properties>(this); } �����������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/opus/opusfile.h�������������������������������������������������������������0000664�0000000�0000000�00000012377�14662262111�0017773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OPUSFILE_H #define TAGLIB_OPUSFILE_H #include "oggfile.h" #include "xiphcomment.h" #include "opusproperties.h" namespace TagLib { namespace Ogg { //! A namespace containing classes for Opus metadata namespace Opus { //! An implementation of Ogg::File with Opus specific methods /*! * This is the central class in the Ogg Opus metadata processing collection * of classes. It's built upon Ogg::File which handles processing of the Ogg * logical bitstream and breaking it down into pages which are handled by * the codec implementations, in this case Opus specifically. */ class TAGLIB_EXPORT File : public Ogg::File { public: /*! * Constructs an Opus file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs an Opus file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the XiphComment for this file. XiphComment implements the tag * interface, so this serves as the reimplementation of * TagLib::File::tag(). */ Ogg::XiphComment *tag() const override; /*! * Implements the unified property interface -- export function. * This forwards directly to XiphComment::properties(). */ PropertyMap properties() const override; /*! * Implements the unified tag dictionary interface -- import function. * Like properties(), this is a forwarder to the file's XiphComment. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the Opus::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns whether or not the given \a stream can be opened as an Opus * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace Opus } // namespace Ogg } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/opus/opusproperties.cpp�����������������������������������������������������0000664�0000000�0000000�00000012466�14662262111�0021602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "opusproperties.h" #include "tstring.h" #include "tdebug.h" #include "oggpageheader.h" #include "opusfile.h" using namespace TagLib; using namespace TagLib::Ogg; class Opus::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int inputSampleRate { 0 }; int channels { 0 }; int opusVersion { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file); } Opus::Properties::~Properties() = default; int Ogg::Opus::Properties::lengthInMilliseconds() const { return d->length; } int Opus::Properties::bitrate() const { return d->bitrate; } int Opus::Properties::sampleRate() const { // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz, // so there is no single sample rate. Let's assume it's the highest // possible. return 48000; } int Opus::Properties::channels() const { return d->channels; } int Opus::Properties::inputSampleRate() const { return d->inputSampleRate; } int Opus::Properties::opusVersion() const { return d->opusVersion; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Opus::Properties::read(File *file) { // Get the identification header from the Ogg implementation. // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1 const ByteVector data = file->packet(0); // *Magic Signature* unsigned int pos = 8; // *Version* (8 bits, unsigned) d->opusVersion = static_cast<unsigned char>(data.at(pos)); pos += 1; // *Output Channel Count* 'C' (8 bits, unsigned) d->channels = static_cast<unsigned char>(data.at(pos)); pos += 1; // *Pre-skip* (16 bits, unsigned, little endian) const unsigned short preSkip = data.toUShort(pos, false); pos += 2; // *Input Sample Rate* (32 bits, unsigned, little endian) d->inputSampleRate = data.toUInt(pos, false); // pos += 4; // *Output Gain* (16 bits, signed, little endian) // pos += 2; // *Channel Mapping Family* (8 bits, unsigned) // pos += 1; const Ogg::PageHeader *first = file->firstPageHeader(); const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { const long long start = first->absoluteGranularPosition(); const long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0) { if(const long long frameCount = end - start - preSkip; frameCount > 0) { const auto length = static_cast<double>(frameCount) * 1000.0 / 48000.0; offset_t fileLengthWithoutOverhead = file->length(); // Ignore the two mandatory header packets, see "3. Packet Organization" // in https://tools.ietf.org/html/rfc7845.html for (unsigned int i = 0; i < 2; ++i) { fileLengthWithoutOverhead -= file->packet(i).size(); } d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5); } } else { debug("Opus::Properties::read() -- The PCM values for the start or " "end of this file was incorrect."); } } else debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages."); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/opus/opusproperties.h�������������������������������������������������������0000664�0000000�0000000�00000007750�14662262111�0021247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_OPUSPROPERTIES_H #define TAGLIB_OPUSPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace Ogg { namespace Opus { class File; //! An implementation of audio property reading for Ogg Opus /*! * This reads the data from an Ogg Opus stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of Opus::Properties with the data read from the * Opus::File \a file. */ Properties(File *file, ReadStyle style = Average); /*! * Destroys this Opus::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. * * \note Always returns 48000, because Opus can decode any stream at a * sample rate of 8, 12, 16, 24, or 48 kHz, */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * The Opus codec supports decoding at multiple sample rates, there is no * single sample rate of the encoded stream. This returns the sample rate * of the original audio stream. */ int inputSampleRate() const; /*! * Returns the Opus version, in the range 0...255. */ int opusVersion() const; private: void read(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace Opus } // namespace Ogg } // namespace TagLib #endif ������������������������taglib-2.0.2/taglib/ogg/speex/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016120�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/speex/speexfile.cpp���������������������������������������������������������0000664�0000000�0000000�00000010407�14662262111�0020612�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "speexfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; using namespace TagLib::Ogg; class Speex::File::FilePrivate { public: std::unique_ptr<Ogg::XiphComment> comment; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool Ogg::Speex::File::isSupported(IOStream *stream) { // A Speex file has IDs "OggS" and "Speex " somewhere. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); return buffer.find("OggS") >= 0 && buffer.find("Speex ") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Speex::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Ogg::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Speex::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Ogg::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Speex::File::~File() = default; Ogg::XiphComment *Speex::File::tag() const { return d->comment.get(); } PropertyMap Speex::File::properties() const { return d->comment->properties(); } PropertyMap Speex::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); } Speex::Properties *Speex::File::audioProperties() const { return d->properties.get(); } bool Speex::File::save() { if(!d->comment) d->comment = std::make_unique<Ogg::XiphComment>(); setPacket(1, d->comment->render()); return Ogg::File::save(); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Speex::File::read(bool readProperties) { ByteVector speexHeaderData = packet(0); if(!speexHeaderData.startsWith("Speex ")) { debug("Speex::File::read() -- invalid Speex identification header"); setValid(false); return; } ByteVector commentHeaderData = packet(1); d->comment = std::make_unique<Ogg::XiphComment>(commentHeaderData); if(readProperties) d->properties = std::make_unique<Properties>(this); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/speex/speexfile.h�����������������������������������������������������������0000664�0000000�0000000�00000012411�14662262111�0020254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_SPEEXFILE_H #define TAGLIB_SPEEXFILE_H #include "oggfile.h" #include "xiphcomment.h" #include "speexproperties.h" namespace TagLib { namespace Ogg { //! A namespace containing classes for Speex metadata namespace Speex { //! An implementation of Ogg::File with Speex specific methods /*! * This is the central class in the Ogg Speex metadata processing collection * of classes. It's built upon Ogg::File which handles processing of the Ogg * logical bitstream and breaking it down into pages which are handled by * the codec implementations, in this case Speex specifically. */ class TAGLIB_EXPORT File : public Ogg::File { public: /*! * Constructs a Speex file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs a Speex file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the XiphComment for this file. XiphComment implements the tag * interface, so this serves as the reimplementation of * TagLib::File::tag(). */ Ogg::XiphComment *tag() const override; /*! * Implements the unified property interface -- export function. * This forwards directly to XiphComment::properties(). */ PropertyMap properties() const override; /*! * Implements the unified tag dictionary interface -- import function. * Like properties(), this is a forwarder to the file's XiphComment. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the Speex::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns whether or not the given \a stream can be opened as a Speex * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace Speex } // namespace Ogg } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/speex/speexproperties.cpp���������������������������������������������������0000664�0000000�0000000�00000014013�14662262111�0022064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "speexproperties.h" #include "tstring.h" #include "tdebug.h" #include "oggpageheader.h" #include "speexfile.h" using namespace TagLib; using namespace TagLib::Ogg; class Speex::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int bitrateNominal { 0 }; int sampleRate { 0 }; int channels { 0 }; int speexVersion { 0 }; bool vbr { false }; int mode { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file); } Speex::Properties::~Properties() = default; int Speex::Properties::lengthInMilliseconds() const { return d->length; } int Speex::Properties::bitrate() const { return d->bitrate; } int Speex::Properties::bitrateNominal() const { return d->bitrateNominal; } int Speex::Properties::sampleRate() const { return d->sampleRate; } int Speex::Properties::channels() const { return d->channels; } int Speex::Properties::speexVersion() const { return d->speexVersion; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Speex::Properties::read(File *file) { // Get the identification header from the Ogg implementation. const ByteVector data = file->packet(0); if(data.size() < 64) { debug("Speex::Properties::read() -- data is too short."); return; } unsigned int pos = 28; // speex_version_id; /**< Version for Speex (for checking compatibility) */ d->speexVersion = data.toUInt(pos, false); pos += 4; // header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */ pos += 4; // rate; /**< Sampling rate used */ d->sampleRate = data.toUInt(pos, false); pos += 4; // mode; /**< Mode used (0 for narrowband, 1 for wideband) */ d->mode = data.toUInt(pos, false); pos += 4; // mode_bitstream_version; /**< Version ID of the bit-stream */ pos += 4; // nb_channels; /**< Number of channels encoded */ d->channels = data.toUInt(pos, false); pos += 4; // bitrate; /**< Bit-rate used */ d->bitrateNominal = data.toUInt(pos, false); pos += 4; // frame_size; /**< Size of frames */ // unsigned int frameSize = data.mid(pos, 4).toUInt(false); pos += 4; // vbr; /**< 1 for a VBR encoding, 0 otherwise */ d->vbr = data.toUInt(pos, false) == 1; // pos += 4; // frames_per_packet; /**< Number of frames stored per Ogg packet */ // unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false); const Ogg::PageHeader *first = file->firstPageHeader(); const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { const long long start = first->absoluteGranularPosition(); const long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0 && d->sampleRate > 0) { if(const long long frameCount = end - start; frameCount > 0) { const auto length = static_cast<double>(frameCount) * 1000.0 / d->sampleRate; offset_t fileLengthWithoutOverhead = file->length(); // Ignore the two header packets, see "Ogg file format" in // https://www.speex.org/docs/manual/speex-manual/node8.html for (unsigned int i = 0; i < 2; ++i) { fileLengthWithoutOverhead -= file->packet(i).size(); } d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5); } } else { debug("Speex::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); } } else debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages."); // Alternative to the actual average bitrate. if(d->bitrate == 0 && d->bitrateNominal > 0) d->bitrate = static_cast<int>(d->bitrateNominal / 1000.0 + 0.5); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/speex/speexproperties.h�����������������������������������������������������0000664�0000000�0000000�00000007372�14662262111�0021543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org (original Vorbis implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_SPEEXPROPERTIES_H #define TAGLIB_SPEEXPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace Ogg { namespace Speex { class File; //! An implementation of audio property reading for Ogg Speex /*! * This reads the data from an Ogg Speex stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of Speex::Properties with the data read from the * Speex::File \a file. */ Properties(File *file, ReadStyle style = Average); /*! * Destroys this Speex::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the nominal bit rate as read from the Speex header in kb/s. */ int bitrateNominal() const; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the Speex version, currently "0" (as specified by the spec). */ int speexVersion() const; private: void read(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace Speex } // namespace Ogg } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/vorbis/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016300�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/vorbis/vorbisfile.cpp�������������������������������������������������������0000664�0000000�0000000�00000010633�14662262111�0021153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "vorbisfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; class Vorbis::File::FilePrivate { public: std::unique_ptr<Ogg::XiphComment> comment; std::unique_ptr<Properties> properties; }; namespace TagLib { /*! * Vorbis headers can be found with one type ID byte and the string "vorbis" in * an Ogg stream. 0x03 indicates the comment header. */ static constexpr char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 }; } // namespace TagLib //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool Vorbis::File::isSupported(IOStream *stream) { // An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere. const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false); return buffer.find("OggS") >= 0 && buffer.find("\x01vorbis") >= 0; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Vorbis::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Ogg::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Vorbis::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Ogg::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } Vorbis::File::~File() = default; Ogg::XiphComment *Vorbis::File::tag() const { return d->comment.get(); } PropertyMap Vorbis::File::properties() const { return d->comment->properties(); } PropertyMap Vorbis::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); } Vorbis::Properties *Vorbis::File::audioProperties() const { return d->properties.get(); } bool Vorbis::File::save() { ByteVector v(vorbisCommentHeaderID); if(!d->comment) d->comment = std::make_unique<Ogg::XiphComment>(); v.append(d->comment->render()); setPacket(1, v); return Ogg::File::save(); } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Vorbis::File::read(bool readProperties) { ByteVector commentHeaderData = packet(1); if(commentHeaderData.mid(0, 7) != vorbisCommentHeaderID) { debug("Vorbis::File::read() - Could not find the Vorbis comment header."); setValid(false); return; } d->comment = std::make_unique<Ogg::XiphComment>(commentHeaderData.mid(7)); if(readProperties) d->properties = std::make_unique<Properties>(this); } �����������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/vorbis/vorbisfile.h���������������������������������������������������������0000664�0000000�0000000�00000013466�14662262111�0020627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_VORBISFILE_H #define TAGLIB_VORBISFILE_H #include "taglib_export.h" #include "oggfile.h" #include "xiphcomment.h" #include "vorbisproperties.h" namespace TagLib { /* * This is just to make this appear to be in the Ogg namespace in the * documentation. The typedef below will make this work with the current code. * In the next BIC version of TagLib this will be really moved into the Ogg * namespace. * Kept for source compatibility, the typedef in vorbisproperties.h was not * correct in TagLib 1. */ #ifdef DOXYGEN namespace Ogg { #endif //! A namespace containing classes for Vorbis metadata namespace Vorbis { //! An implementation of Ogg::File with Vorbis specific methods /*! * This is the central class in the Ogg Vorbis metadata processing collection * of classes. It's built upon Ogg::File which handles processing of the Ogg * logical bitstream and breaking it down into pages which are handled by * the codec implementations, in this case Vorbis specifically. */ class TAGLIB_EXPORT File : public Ogg::File { public: /*! * Constructs a Vorbis file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs a Vorbis file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the XiphComment for this file. XiphComment implements the tag * interface, so this serves as the reimplementation of * TagLib::File::tag(). */ Ogg::XiphComment *tag() const override; /*! * Implements the unified property interface -- export function. * This forwards directly to XiphComment::properties(). */ PropertyMap properties() const override; /*! * Implements the unified tag dictionary interface -- import function. * Like properties(), this is a forwarder to the file's XiphComment. */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the Vorbis::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Save the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Check if the given \a stream can be opened as an Ogg Vorbis file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace Vorbis /* * To keep compatibility with the current version put Vorbis in the Ogg namespace * only in the docs and provide a typedef to make it work. In the next BIC * version this will be removed and it will only exist in the Ogg namespace. * Kept for source compatibility, the typedef in vorbisproperties.h was not * correct in TagLib 1. */ #ifdef DOXYGEN } #else namespace Ogg { namespace Vorbis { using File = TagLib::Vorbis::File; } // namespace Vorbis } // namespace Ogg #endif } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/vorbis/vorbisproperties.cpp�������������������������������������������������0000664�0000000�0000000�00000013355�14662262111�0022434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "vorbisproperties.h" #include "tstring.h" #include "tdebug.h" #include "oggpageheader.h" #include "vorbisfile.h" using namespace TagLib; class Vorbis::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int vorbisVersion { 0 }; int bitrateMaximum { 0 }; int bitrateNominal { 0 }; int bitrateMinimum { 0 }; }; namespace TagLib { /*! * Vorbis headers can be found with one type ID byte and the string "vorbis" in * an Ogg stream. 0x01 indicates the setup header. */ static constexpr char vorbisSetupHeaderID[] = { 0x01, 'v', 'o', 'r', 'b', 'i', 's', 0 }; } // namespace TagLib //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file); } Vorbis::Properties::~Properties() = default; int Vorbis::Properties::lengthInMilliseconds() const { return d->length; } int Vorbis::Properties::bitrate() const { return d->bitrate; } int Vorbis::Properties::sampleRate() const { return d->sampleRate; } int Vorbis::Properties::channels() const { return d->channels; } int Vorbis::Properties::vorbisVersion() const { return d->vorbisVersion; } int Vorbis::Properties::bitrateMaximum() const { return d->bitrateMaximum; } int Vorbis::Properties::bitrateNominal() const { return d->bitrateNominal; } int Vorbis::Properties::bitrateMinimum() const { return d->bitrateMinimum; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void Vorbis::Properties::read(File *file) { // Get the identification header from the Ogg implementation. const ByteVector data = file->packet(0); if(data.size() < 28) { debug("Vorbis::Properties::read() -- data is too short."); return; } unsigned int pos = 0; if(data.mid(pos, 7) != vorbisSetupHeaderID) { debug("Vorbis::Properties::read() -- invalid Vorbis identification header"); return; } pos += 7; d->vorbisVersion = data.toUInt(pos, false); pos += 4; d->channels = static_cast<unsigned char>(data[pos]); pos += 1; d->sampleRate = data.toUInt(pos, false); pos += 4; d->bitrateMaximum = data.toUInt(pos, false); pos += 4; d->bitrateNominal = data.toUInt(pos, false); pos += 4; d->bitrateMinimum = data.toUInt(pos, false); // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/ // for my notes on the topic. const Ogg::PageHeader *first = file->firstPageHeader(); const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { const long long start = first->absoluteGranularPosition(); const long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0 && d->sampleRate > 0) { if(const long long frameCount = end - start; frameCount > 0) { const auto length = static_cast<double>(frameCount) * 1000.0 / d->sampleRate; offset_t fileLengthWithoutOverhead = file->length(); // Ignore the three initial header packets, see "1.3.1. Decode Setup" in // https://xiph.org/vorbis/doc/Vorbis_I_spec.html for (unsigned int i = 0; i < 3; ++i) { fileLengthWithoutOverhead -= file->packet(i).size(); } d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5); } } else { debug("Vorbis::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); } } else debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages."); // Alternative to the actual average bitrate. if(d->bitrate == 0 && d->bitrateNominal > 0) d->bitrate = static_cast<int>(d->bitrateNominal / 1000.0 + 0.5); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/vorbis/vorbisproperties.h���������������������������������������������������0000664�0000000�0000000�00000011251�14662262111�0022072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_VORBISPROPERTIES_H #define TAGLIB_VORBISPROPERTIES_H #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { /* * This is just to make this appear to be in the Ogg namespace in the * documentation. The typedef below will make this work with the current code. * In the next BIC version of TagLib this will be really moved into the Ogg * namespace. * Kept for source compatibility, the typedef in vorbisproperties.h was not * correct in TagLib 1. */ #ifdef DOXYGEN namespace Ogg { #endif namespace Vorbis { class File; //! An implementation of audio property reading for Ogg Vorbis /*! * This reads the data from an Ogg Vorbis stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of Vorbis::Properties with the data read from the * Vorbis::File \a file. */ Properties(File *file, ReadStyle style = Average); /*! * Destroys this VorbisProperties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the Vorbis version, currently "0" (as specified by the spec). */ int vorbisVersion() const; /*! * Returns the maximum bitrate as read from the Vorbis identification * header. */ int bitrateMaximum() const; /*! * Returns the nominal bitrate as read from the Vorbis identification * header. */ int bitrateNominal() const; /*! * Returns the minimum bitrate as read from the Vorbis identification * header. */ int bitrateMinimum() const; private: void read(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace Vorbis /* * To keep compatibility with the current version put Vorbis in the Ogg namespace * only in the docs and provide a typedef to make it work. In the next BIC * version this will be removed and it will only exist in the Ogg namespace. * Kept for source compatibility, the typedef in vorbisproperties.h was not * correct in TagLib 1. */ #ifdef DOXYGEN } #else namespace Ogg { namespace Vorbis { using Properties = TagLib::Vorbis::Properties; } // namespace Vorbis } // namespace Ogg #endif } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/xiphcomment.cpp�������������������������������������������������������������0000664�0000000�0000000�00000034067�14662262111�0020045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "xiphcomment.h" #include <utility> #include "tdebug.h" #include "tpropertymap.h" using namespace TagLib; class Ogg::XiphComment::XiphCommentPrivate { public: XiphCommentPrivate() { pictureList.setAutoDelete(true); } FieldListMap fieldListMap; String vendorID; String commentField; List<FLAC::Picture *> pictureList; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Ogg::XiphComment::XiphComment() : d(std::make_unique<XiphCommentPrivate>()) { } Ogg::XiphComment::XiphComment(const ByteVector &data) : d(std::make_unique<XiphCommentPrivate>()) { parse(data); } Ogg::XiphComment::~XiphComment() = default; String Ogg::XiphComment::title() const { StringList val = d->fieldListMap.value("TITLE"); return val.isEmpty() ? String() : joinTagValues(val); } String Ogg::XiphComment::artist() const { StringList val = d->fieldListMap.value("ARTIST"); return val.isEmpty() ? String() : joinTagValues(val); } String Ogg::XiphComment::album() const { StringList val = d->fieldListMap.value("ALBUM"); return val.isEmpty() ? String() : joinTagValues(val); } String Ogg::XiphComment::comment() const { StringList val = d->fieldListMap.value("DESCRIPTION"); if(!val.isEmpty()) { d->commentField = "DESCRIPTION"; return joinTagValues(val); } val = d->fieldListMap.value("COMMENT"); if(!val.isEmpty()) { d->commentField = "COMMENT"; return joinTagValues(val); } return String(); } String Ogg::XiphComment::genre() const { StringList val = d->fieldListMap.value("GENRE"); return val.isEmpty() ? String() : joinTagValues(val); } unsigned int Ogg::XiphComment::year() const { StringList val = d->fieldListMap.value("DATE"); if(!val.isEmpty()) return val.front().toInt(); val = d->fieldListMap.value("YEAR"); if(!val.isEmpty()) return val.front().toInt(); return 0; } unsigned int Ogg::XiphComment::track() const { StringList val = d->fieldListMap.value("TRACKNUMBER"); if(!val.isEmpty()) return val.front().toInt(); val = d->fieldListMap.value("TRACKNUM"); if(!val.isEmpty()) return val.front().toInt(); return 0; } void Ogg::XiphComment::setTitle(const String &s) { addField("TITLE", s); } void Ogg::XiphComment::setArtist(const String &s) { addField("ARTIST", s); } void Ogg::XiphComment::setAlbum(const String &s) { addField("ALBUM", s); } void Ogg::XiphComment::setComment(const String &s) { if(d->commentField.isEmpty()) { if(!d->fieldListMap.value("DESCRIPTION").isEmpty()) d->commentField = "DESCRIPTION"; else d->commentField = "COMMENT"; } addField(d->commentField, s); } void Ogg::XiphComment::setGenre(const String &s) { addField("GENRE", s); } void Ogg::XiphComment::setYear(unsigned int i) { removeFields("YEAR"); if(i == 0) removeFields("DATE"); else addField("DATE", String::number(i)); } void Ogg::XiphComment::setTrack(unsigned int i) { removeFields("TRACKNUM"); if(i == 0) removeFields("TRACKNUMBER"); else addField("TRACKNUMBER", String::number(i)); } bool Ogg::XiphComment::isEmpty() const { return std::all_of(d->fieldListMap.cbegin(), d->fieldListMap.cend(), [](const auto &field) { return field.second.isEmpty(); }); } unsigned int Ogg::XiphComment::fieldCount() const { unsigned int count = 0; for(const auto &[_, list] : std::as_const(d->fieldListMap)) count += list.size(); count += d->pictureList.size(); return count; } const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const { return d->fieldListMap; } PropertyMap Ogg::XiphComment::properties() const { return d->fieldListMap; } PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties) { // check which keys are to be deleted StringList toRemove; for(const auto &[field, _] : std::as_const(d->fieldListMap)) if(!properties.contains(field)) toRemove.append(field); for(const auto &field : std::as_const(toRemove)) removeFields(field); // now go through keys in \a properties and check that the values match those in the Xiph comment PropertyMap invalid; for(const auto &[key, sl] : properties) { if(!checkKey(key)) invalid.insert(key, sl); else if(!d->fieldListMap.contains(key) || sl != d->fieldListMap[key]) { if(sl.isEmpty()) // zero size string list -> remove the tag with all values removeFields(key); else { // replace all strings in the list for the tag addField(key, *sl.begin(), true); for(auto it = std::next(sl.begin()); it != sl.end(); ++it) addField(key, *it, false); } } } return invalid; } StringList Ogg::XiphComment::complexPropertyKeys() const { StringList keys; if(!d->pictureList.isEmpty()) { keys.append("PICTURE"); } return keys; } List<VariantMap> Ogg::XiphComment::complexProperties(const String &key) const { List<VariantMap> props; if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { for(const FLAC::Picture *picture : std::as_const(d->pictureList)) { VariantMap property; property.insert("data", picture->data()); property.insert("mimeType", picture->mimeType()); property.insert("description", picture->description()); property.insert("pictureType", FLAC::Picture::typeToString(picture->type())); property.insert("width", picture->width()); property.insert("height", picture->height()); property.insert("numColors", picture->numColors()); property.insert("colorDepth", picture->colorDepth()); props.append(property); } } return props; } bool Ogg::XiphComment::setComplexProperties(const String &key, const List<VariantMap> &value) { if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") { removeAllPictures(); for(const auto &property : value) { auto picture = new FLAC::Picture; picture->setData(property.value("data").value<ByteVector>()); picture->setMimeType(property.value("mimeType").value<String>()); picture->setDescription(property.value("description").value<String>()); picture->setType(FLAC::Picture::typeFromString( property.value("pictureType").value<String>())); picture->setWidth(property.value("width").value<int>()); picture->setHeight(property.value("height").value<int>()); picture->setNumColors(property.value("numColors").value<int>()); picture->setColorDepth(property.value("colorDepth").value<int>()); addPicture(picture); } } else { return false; } return true; } bool Ogg::XiphComment::checkKey(const String &key) { if(key.size() < 1) return false; // A key may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded. return std::none_of(key.begin(), key.end(), [](auto c) { return c < 0x20 || c > 0x7D || c == 0x3D; }); } String Ogg::XiphComment::vendorID() const { return d->vendorID; } void Ogg::XiphComment::addField(const String &key, const String &value, bool replace) { if(!checkKey(key)) { debug("Ogg::XiphComment::addField() - Invalid key. Field not added."); return; } const String upperKey = key.upper(); if(replace) removeFields(upperKey); if(!key.isEmpty() && !value.isEmpty()) d->fieldListMap[upperKey].append(value); } void Ogg::XiphComment::removeFields(const String &key) { d->fieldListMap.erase(key.upper()); } void Ogg::XiphComment::removeFields(const String &key, const String &value) { StringList &fields = d->fieldListMap[key.upper()]; for(auto it = fields.begin(); it != fields.end(); ) { if(*it == value) it = fields.erase(it); else ++it; } } void Ogg::XiphComment::removeAllFields() { d->fieldListMap.clear(); } bool Ogg::XiphComment::contains(const String &key) const { return !d->fieldListMap.value(key.upper()).isEmpty(); } void Ogg::XiphComment::removePicture(FLAC::Picture *picture, bool del) { auto it = d->pictureList.find(picture); if(it != d->pictureList.end()) d->pictureList.erase(it); if(del) delete picture; } void Ogg::XiphComment::removeAllPictures() { d->pictureList.clear(); } void Ogg::XiphComment::addPicture(FLAC::Picture * picture) { d->pictureList.append(picture); } List<FLAC::Picture *> Ogg::XiphComment::pictureList() { return d->pictureList; } ByteVector Ogg::XiphComment::render(bool addFramingBit) const { ByteVector data; // Add the vendor ID length and the vendor ID. It's important to use the // length of the data(String::UTF8) rather than the length of the string // since this is UTF8 text and there may be more characters in the data than // in the UTF16 string. ByteVector vendorData = d->vendorID.data(String::UTF8); data.append(ByteVector::fromUInt(vendorData.size(), false)); data.append(vendorData); // Add the number of fields. data.append(ByteVector::fromUInt(fieldCount(), false)); // Iterate over the field lists. Our iterator returns a // std::pair<String, StringList> where the first String is the field name and // the StringList is the values associated with that field. for(const auto &[fieldName, values] : std::as_const(d->fieldListMap)) { // And now iterate over the values of the current list. for(const auto &val : values) { ByteVector fieldData = fieldName.data(String::UTF8); fieldData.append('='); fieldData.append(val.data(String::UTF8)); data.append(ByteVector::fromUInt(fieldData.size(), false)); data.append(fieldData); } } for(const auto &p : std::as_const(d->pictureList)) { ByteVector picture = p->render().toBase64(); data.append(ByteVector::fromUInt(picture.size() + 23, false)); data.append("METADATA_BLOCK_PICTURE="); data.append(picture); } // Append the "framing bit". if(addFramingBit) data.append(static_cast<char>(1)); return data; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void Ogg::XiphComment::parse(const ByteVector &data) { // The first thing in the comment data is the vendor ID length, followed by a // UTF8 string with the vendor ID. unsigned int pos = 0; const unsigned int vendorLength = data.toUInt(0, false); pos += 4; d->vendorID = String(data.mid(pos, vendorLength), String::UTF8); pos += vendorLength; // Next the number of fields in the comment vector. const unsigned int commentFields = data.toUInt(pos, false); pos += 4; if(commentFields > (data.size() - 8) / 4) { return; } for(unsigned int i = 0; i < commentFields; i++) { // Each comment field is in the format "KEY=value" in a UTF8 string and has // 4 bytes before the text starts that gives the length. const unsigned int commentLength = data.toUInt(pos, false); pos += 4; const ByteVector entry = data.mid(pos, commentLength); pos += commentLength; // Don't go past data end if(pos > data.size()) break; // Check for field separator const int sep = entry.find('='); if(sep < 1) { debug("Ogg::XiphComment::parse() - Discarding a field. Separator not found."); continue; } // Parse the key const String key = String(entry.mid(0, sep), String::UTF8).upper(); if(!checkKey(key)) { debug("Ogg::XiphComment::parse() - Discarding a field. Invalid key."); continue; } if(key == "METADATA_BLOCK_PICTURE" || key == "COVERART") { // Handle Pictures separately const ByteVector picturedata = ByteVector::fromBase64(entry.mid(sep + 1)); if(picturedata.isEmpty()) { debug("Ogg::XiphComment::parse() - Discarding a field. Invalid base64 data"); continue; } if(key[0] == L'M') { // Decode FLAC Picture if(auto picture = new FLAC::Picture(); picture->parse(picturedata)) { d->pictureList.append(picture); } else { delete picture; debug("Ogg::XiphComment::parse() - Failed to decode FLAC Picture block"); } } else { // Assume it's some type of image file auto picture = new FLAC::Picture(); picture->setData(picturedata); picture->setMimeType("image/"); picture->setType(FLAC::Picture::Other); d->pictureList.append(picture); } } else { // Parse the text addField(key, String(entry.mid(sep + 1), String::UTF8), false); } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/ogg/xiphcomment.h���������������������������������������������������������������0000664�0000000�0000000�00000022666�14662262111�0017514�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_VORBISCOMMENT_H #define TAGLIB_VORBISCOMMENT_H #include "tlist.h" #include "tmap.h" #include "tstring.h" #include "tstringlist.h" #include "tbytevector.h" #include "taglib_export.h" #include "tag.h" #include "flacpicture.h" #ifdef _MSC_VER // Explained at end of tpropertymap.cpp extern template class TagLib::Map<TagLib::String, TagLib::StringList>; #endif namespace TagLib { namespace Ogg { /*! * A mapping between a list of field names, or keys, and a list of values * associated with that field. * * \see XiphComment::fieldListMap() */ using FieldListMap = Map<String, StringList>; //! Ogg Vorbis comment implementation /*! * This class is an implementation of the Ogg Vorbis comment specification, * to be found in section 5 of the Ogg Vorbis specification. Because this * format is also used in other (currently unsupported) Xiph.org formats, it * has been made part of a generic implementation rather than being limited * to strictly Vorbis. * * Vorbis comments are a simple vector of keys and values, called fields. * Multiple values for a given key are supported. * * \see fieldListMap() */ class TAGLIB_EXPORT XiphComment : public TagLib::Tag { public: /*! * Constructs an empty Vorbis comment. */ XiphComment(); /*! * Constructs a Vorbis comment from \a data. */ XiphComment(const ByteVector &data); /*! * Destroys this instance of the XiphComment. */ ~XiphComment() override; XiphComment(const XiphComment &) = delete; XiphComment &operator=(const XiphComment &) = delete; String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; bool isEmpty() const override; /*! * Returns the number of fields present in the comment. */ unsigned int fieldCount() const; /*! * Returns a reference to the map of field lists. Because Xiph comments * support multiple fields with the same key, a pure Map would not work. * As such this is a Map of string lists, keyed on the comment field name. * * The standard set of Xiph/Vorbis fields (which may or may not be * contained in any specific comment) is: * * <ul> * <li>TITLE</li> * <li>VERSION</li> * <li>ALBUM</li> * <li>ARTIST</li> * <li>PERFORMER</li> * <li>COPYRIGHT</li> * <li>ORGANIZATION</li> * <li>DESCRIPTION</li> * <li>GENRE</li> * <li>DATE</li> * <li>LOCATION</li> * <li>CONTACT</li> * <li>ISRC</li> * </ul> * * For a more detailed description of these fields, please see the Ogg * Vorbis specification, section 5.2.2.1. * * \note The Ogg Vorbis comment specification does allow these key values * to be either upper or lower case. However, it is conventional for them * to be upper case. As such, TagLib, when parsing a Xiph/Vorbis comment, * converts all fields to uppercase. When you are using this data * structure, you will need to specify the field name in upper case. * * \warning You should not modify this data structure directly, instead * use addField() and removeField(). */ const FieldListMap &fieldListMap() const; /*! * Implements the unified property interface -- export function. * The result is a one-to-one match of the Xiph comment, since it is * completely compatible with the property interface (in fact, a Xiph * comment is nothing more than a map from tag names to list of values, * as is the dict interface). */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * The tags from the given map will be stored one-to-one in the file, * except for invalid keys (less than one character, non-ASCII, or * containing '=' or '~') in which case the according values will * be contained in the returned PropertyMap. */ PropertyMap setProperties(const PropertyMap&) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; /*! * Check if the given String is a valid Xiph comment key. */ static bool checkKey(const String&); /*! * Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the * most common case always returns "Xiph.Org libVorbis I 20020717". */ String vendorID() const; /*! * Add the field specified by \a key with the data \a value. If \a replace * is \c true, then all of the other fields with the same key will be removed * first. * * If the field value is empty, the field will be removed. */ void addField(const String &key, const String &value, bool replace = true); /*! * Remove all the fields specified by \a key. * * \see removeAllFields() */ void removeFields(const String &key); /*! * Remove all the fields specified by \a key with the data \a value. * * \see removeAllFields() */ void removeFields(const String &key, const String &value); /*! * Remove all the fields in the comment. * * \see removeFields() */ void removeAllFields(); /*! * Returns \c true if the field is contained within the comment. * * \note This is safer than checking for membership in the FieldListMap. */ bool contains(const String &key) const; /*! * Renders the comment to a ByteVector suitable for inserting into a file. * * If \a addFramingBit is \c true the standard Vorbis comment framing bit will * be appended. However some formats (notably FLAC) do not work with this * in place. */ ByteVector render(bool addFramingBit = true) const; /*! * Returns a list of pictures attached to the xiph comment. */ List<FLAC::Picture *> pictureList(); /*! * Removes a picture. If \a del is \c true the picture's memory * will be freed; if it is \c false, it must be deleted by the user. */ void removePicture(FLAC::Picture *picture, bool del = true); /*! * Remove all pictures. */ void removeAllPictures(); /*! * Add a new picture to the comment block. The comment block takes ownership of the * picture and will handle freeing its memory. * * \note The file will be saved only after calling save(). */ void addPicture(FLAC::Picture *picture); protected: /*! * Reads the tag from the file specified in the constructor and fills the * FieldListMap. */ void parse(const ByteVector &data); private: class XiphCommentPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<XiphCommentPrivate> d; }; } // namespace Ogg } // namespace TagLib #endif ��������������������������������������������������������������������������taglib-2.0.2/taglib/riff/���������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015146�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/aiff/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016053�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/aiff/aifffile.cpp����������������������������������������������������������0000664�0000000�0000000�00000012565�14662262111�0020335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "aifffile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" using namespace TagLib; class RIFF::AIFF::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory) : ID3v2FrameFactory(frameFactory ? frameFactory : ID3v2::FrameFactory::instance()) { } ~FilePrivate() = default; const ID3v2::FrameFactory *ID3v2FrameFactory; std::unique_ptr<Properties> properties; std::unique_ptr<ID3v2::Tag> tag; bool hasID3v2 { false }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool RIFF::AIFF::File::isSupported(IOStream *stream) { // An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC". const ByteVector id = Utils::readHeader(stream, 12, false); return id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8)); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RIFF::AIFF::File::File(FileName file, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : RIFF::File(file, BigEndian), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } RIFF::AIFF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : RIFF::File(stream, BigEndian), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } RIFF::AIFF::File::~File() = default; ID3v2::Tag *RIFF::AIFF::File::tag() const { return d->tag.get(); } PropertyMap RIFF::AIFF::File::properties() const { return d->tag->properties(); } void RIFF::AIFF::File::removeUnsupportedProperties(const StringList &unsupported) { d->tag->removeUnsupportedProperties(unsupported); } PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties) { return d->tag->setProperties(properties); } RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const { return d->properties.get(); } bool RIFF::AIFF::File::save() { return save(ID3v2::v4); } bool RIFF::AIFF::File::save(ID3v2::Version version) { if(readOnly()) { debug("RIFF::AIFF::File::save() -- File is read only."); return false; } if(!isValid()) { debug("RIFF::AIFF::File::save() -- Trying to save invalid file."); return false; } if(d->hasID3v2) { removeChunk("ID3 "); removeChunk("id3 "); d->hasID3v2 = false; } if(tag() && !tag()->isEmpty()) { setChunkData("ID3 ", d->tag->render(version)); d->hasID3v2 = true; } return true; } bool RIFF::AIFF::File::hasID3v2Tag() const { return d->hasID3v2; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::AIFF::File::read(bool readProperties) { for(unsigned int i = 0; i < chunkCount(); ++i) { if(const ByteVector name = chunkName(i); name == "ID3 " || name == "id3 ") { if(!d->tag) { d->tag = std::make_unique<ID3v2::Tag>(this, chunkOffset(i), d->ID3v2FrameFactory); d->hasID3v2 = true; } else { debug("RIFF::AIFF::File::read() - Duplicate ID3v2 tag found."); } } } if(!d->tag) d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory); if(readProperties) d->properties = std::make_unique<Properties>(this, Properties::Average); } �������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/aiff/aifffile.h������������������������������������������������������������0000664�0000000�0000000�00000014046�14662262111�0017776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_AIFFFILE_H #define TAGLIB_AIFFFILE_H #include "rifffile.h" #include "id3v2tag.h" #include "aiffproperties.h" namespace TagLib { namespace RIFF { //! An implementation of AIFF metadata /*! * This is an implementation of AIFF metadata. * * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF * chunk as well as properties from the file. */ namespace AIFF { //! An implementation of TagLib::File with AIFF specific methods /*! * This implements and provides an interface for AIFF files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to AIFF files. */ class TAGLIB_EXPORT File : public TagLib::RIFF::File { public: /*! * Constructs an AIFF file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs an AIFF file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. * * \note This always returns a valid pointer regardless of whether or not * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \see hasID3v2Tag() */ ID3v2::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * This method forwards to ID3v2::Tag::properties(). */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &unsupported) override; /*! * Implements the unified property interface -- import function. * This method forwards to ID3v2::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the AIFF::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. */ bool save() override; /*! * Save using a specific ID3v2 version (e.g. v3) */ bool save(ID3v2::Version version); /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Check if the given \a stream can be opened as an AIFF file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); friend class Properties; class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace AIFF } // namespace RIFF } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/aiff/aiffproperties.cpp����������������������������������������������������0000664�0000000�0000000�00000011503�14662262111�0021601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "aiffproperties.h" #include "tdebug.h" #include "aifffile.h" using namespace TagLib; class RIFF::AIFF::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int bitsPerSample { 0 }; ByteVector compressionType; String compressionName; unsigned int sampleFrames { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RIFF::AIFF::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file); } RIFF::AIFF::Properties::~Properties() = default; int RIFF::AIFF::Properties::lengthInMilliseconds() const { return d->length; } int RIFF::AIFF::Properties::bitrate() const { return d->bitrate; } int RIFF::AIFF::Properties::sampleRate() const { return d->sampleRate; } int RIFF::AIFF::Properties::channels() const { return d->channels; } int RIFF::AIFF::Properties::bitsPerSample() const { return d->bitsPerSample; } unsigned int RIFF::AIFF::Properties::sampleFrames() const { return d->sampleFrames; } bool RIFF::AIFF::Properties::isAiffC() const { return !d->compressionType.isEmpty(); } ByteVector RIFF::AIFF::Properties::compressionType() const { return d->compressionType; } String RIFF::AIFF::Properties::compressionName() const { return d->compressionName; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::AIFF::Properties::read(File *file) { ByteVector data; unsigned int streamLength = 0; for(unsigned int i = 0; i < file->chunkCount(); i++) { if(const ByteVector name = file->chunkName(i); name == "COMM") { if(data.isEmpty()) data = file->chunkData(i); else debug("RIFF::AIFF::Properties::read() - Duplicate 'COMM' chunk found."); } else if(name == "SSND") { if(streamLength == 0) streamLength = file->chunkDataSize(i) + file->chunkPadding(i); else debug("RIFF::AIFF::Properties::read() - Duplicate 'SSND' chunk found."); } } if(data.size() < 18) { debug("RIFF::AIFF::Properties::read() - 'COMM' chunk not found or too short."); return; } if(streamLength == 0) { debug("RIFF::AIFF::Properties::read() - 'SSND' chunk not found."); return; } d->channels = data.toShort(0U); d->sampleFrames = data.toUInt(2U); d->bitsPerSample = data.toShort(6U); const long double smplRate = data.toFloat80BE(8); if(smplRate >= 1.0) d->sampleRate = static_cast<int>(smplRate + 0.5); if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / smplRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } if(data.size() >= 23) { d->compressionType = data.mid(18, 4); d->compressionName = String(data.mid(23, static_cast<unsigned char>(data[22])), String::Latin1); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/aiff/aiffproperties.h������������������������������������������������������0000664�0000000�0000000�00000010355�14662262111�0021252�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_AIFFPROPERTIES_H #define TAGLIB_AIFFPROPERTIES_H #include "tstring.h" #include "audioproperties.h" namespace TagLib { namespace RIFF { namespace AIFF { class File; //! An implementation of audio property reading for AIFF /*! * This reads the data from an AIFF stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of AIFF::Properties with the data read from the * AIFF::File \a file. */ Properties(File *file, ReadStyle style); /*! * Destroys this AIFF::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns the number of sample frames */ unsigned int sampleFrames() const; /*! * Returns \c true if the file is in AIFF-C format, \c false if AIFF format. */ bool isAiffC() const; /*! * Returns the compression type of the AIFF-C file. For example, "NONE" for * not compressed, "ACE2" for ACE 2-to-1. * * If the file is in AIFF format, always returns an empty vector. * * \see isAiffC() */ ByteVector compressionType() const; /*! * Returns the concrete compression name of the AIFF-C file. * * If the file is in AIFF format, always returns an empty string. * * \see isAiffC() */ String compressionName() const; private: void read(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace AIFF } // namespace RIFF } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/rifffile.cpp���������������������������������������������������������������0000664�0000000�0000000�00000023152�14662262111�0017443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "rifffile.h" #include <algorithm> #include <vector> #include "tdebug.h" #include "riffutils.h" using namespace TagLib; struct Chunk { ByteVector name; offset_t offset; unsigned int size; unsigned int padding; }; class RIFF::File::FilePrivate { public: FilePrivate(Endianness endianness) : endianness(endianness) { } Endianness endianness; unsigned int size { 0 }; offset_t sizeOffset { 0 }; std::vector<Chunk> chunks; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RIFF::File::~File() = default; //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file), d(std::make_unique<FilePrivate>(endianness)) { if(isOpen()) read(); } RIFF::File::File(IOStream *stream, Endianness endianness) : TagLib::File(stream), d(std::make_unique<FilePrivate>(endianness)) { if(isOpen()) read(); } unsigned int RIFF::File::riffSize() const { return d->size; } unsigned int RIFF::File::chunkCount() const { return static_cast<unsigned int>(d->chunks.size()); } unsigned int RIFF::File::chunkDataSize(unsigned int i) const { if(i >= d->chunks.size()) { debug("RIFF::File::chunkDataSize() - Index out of range. Returning 0."); return 0; } return d->chunks[i].size; } offset_t RIFF::File::chunkOffset(unsigned int i) const { if(i >= d->chunks.size()) { debug("RIFF::File::chunkOffset() - Index out of range. Returning 0."); return 0; } return d->chunks[i].offset; } unsigned int RIFF::File::chunkPadding(unsigned int i) const { if(i >= d->chunks.size()) { debug("RIFF::File::chunkPadding() - Index out of range. Returning 0."); return 0; } return d->chunks[i].padding; } ByteVector RIFF::File::chunkName(unsigned int i) const { if(i >= d->chunks.size()) { debug("RIFF::File::chunkName() - Index out of range. Returning an empty vector."); return ByteVector(); } return d->chunks[i].name; } ByteVector RIFF::File::chunkData(unsigned int i) { if(i >= d->chunks.size()) { debug("RIFF::File::chunkData() - Index out of range. Returning an empty vector."); return ByteVector(); } seek(d->chunks[i].offset); return readBlock(d->chunks[i].size); } void RIFF::File::setChunkData(unsigned int i, const ByteVector &data) { if(i >= d->chunks.size()) { debug("RIFF::File::setChunkData() - Index out of range."); return; } // Now update the specific chunk auto it = d->chunks.begin(); std::advance(it, i); const long long originalSize = static_cast<long long>(it->size) + it->padding; writeChunk(it->name, data, it->offset - 8, it->size + it->padding + 8); it->size = data.size(); it->padding = data.size() % 2; const long long diff = static_cast<long long>(it->size) + it->padding - originalSize; // Now update the internal offsets it = std::next(it); while(it != d->chunks.end()) { it->offset += static_cast<int>(diff); ++it; } // Update the global size. updateGlobalSize(); } void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) { setChunkData(name, data, false); } void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { if(d->chunks.empty()) { debug("RIFF::File::setChunkData - No valid chunks found."); return; } if(alwaysCreate && name != "LIST") { debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks."); return; } if(!alwaysCreate) { for(unsigned int i = 0; i < d->chunks.size(); i++) { if(d->chunks[i].name == name) { setChunkData(i, data); return; } } } // Couldn't find an existing chunk, so let's create a new one. // Adjust the padding of the last chunk to place the new chunk at even position. Chunk &last = d->chunks.back(); offset_t offset = last.offset + last.size + last.padding; if(offset & 1) { if(last.padding == 1) { last.padding = 0; // This should not happen unless the file is corrupted. offset--; removeBlock(offset, 1); } else { insert(ByteVector("\0", 1), offset, 0); last.padding = 1; offset++; } } // Now add the chunk to the file. writeChunk(name, data, offset, 0); // And update our internal structure Chunk chunk; chunk.name = name; chunk.size = data.size(); chunk.offset = offset + 8; chunk.padding = data.size() % 2; d->chunks.push_back(std::move(chunk)); // Update the global size. updateGlobalSize(); } void RIFF::File::removeChunk(unsigned int i) { if(i >= d->chunks.size()) { debug("RIFF::File::removeChunk() - Index out of range."); return; } auto it = d->chunks.begin(); std::advance(it, i); const unsigned int removeSize = it->size + it->padding + 8; removeBlock(it->offset - 8, removeSize); it = d->chunks.erase(it); while(it != d->chunks.end()) { it->offset -= removeSize; ++it; } // Update the global size. updateGlobalSize(); } void RIFF::File::removeChunk(const ByteVector &name) { for(int i = static_cast<int>(d->chunks.size()) - 1; i >= 0; --i) { if(d->chunks[i].name == name) removeChunk(i); } } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::File::read() { const bool bigEndian = d->endianness == BigEndian; offset_t offset = tell(); offset += 4; d->sizeOffset = offset; seek(offset); d->size = readBlock(4).toUInt(bigEndian); offset += 8; // + 8: chunk header at least, fix for additional junk bytes while(offset + 8 <= length()) { seek(offset); const ByteVector chnkName = readBlock(4); const unsigned int chunkSize = readBlock(4).toUInt(bigEndian); if(!isValidChunkName(chnkName)) { debug("RIFF::File::read() -- Chunk '" + chnkName + "' has invalid ID"); break; } if(static_cast<long long>(offset) + 8 + chunkSize > length()) { debug("RIFF::File::read() -- Chunk '" + chnkName + "' has invalid size (larger than the file size)"); break; } Chunk chunk; chunk.name = chnkName; chunk.size = chunkSize; chunk.offset = offset + 8; chunk.padding = 0; offset = chunk.offset + chunk.size; // Check padding if(offset & 1) { seek(offset); if(const ByteVector iByte = readBlock(1); iByte.size() == 1) { bool skipPadding = iByte[0] == '\0'; if(!skipPadding) { // Padding byte is not zero, check if it is good to ignore it if(const ByteVector fourCcAfterPadding = readBlock(4); isValidChunkName(fourCcAfterPadding)) { // Use the padding, it is followed by a valid chunk name. skipPadding = true; } } if(skipPadding) { chunk.padding = 1; offset++; } } } d->chunks.push_back(std::move(chunk)); } } void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, offset_t offset, unsigned long replace) { ByteVector combined; combined.append(name); combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian)); combined.append(data); if(data.size() & 1) combined.resize(combined.size() + 1, '\0'); insert(combined, offset, replace); } void RIFF::File::updateGlobalSize() { if(d->chunks.empty()) return; const Chunk first = d->chunks.front(); const Chunk last = d->chunks.back(); d->size = static_cast<unsigned int>(last.offset + last.size + last.padding - first.offset + 12); const ByteVector data = ByteVector::fromUInt(d->size, d->endianness == BigEndian); insert(data, d->sizeOffset, 4); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/rifffile.h�����������������������������������������������������������������0000664�0000000�0000000�00000013022�14662262111�0017103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_RIFFFILE_H #define TAGLIB_RIFFFILE_H #include "tfile.h" #include "taglib_export.h" namespace TagLib { //! An implementation of TagLib::File with RIFF specific methods namespace RIFF { //! A RIFF file class with some useful methods specific to RIFF /*! * This implements the generic TagLib::File API and additionally provides * access to properties that are distinct to RIFF files, notably access * to the different ID3 tags. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; protected: enum Endianness { BigEndian, LittleEndian }; File(FileName file, Endianness endianness); File(IOStream *stream, Endianness endianness); /*! * \return The size of the main RIFF chunk. */ unsigned int riffSize() const; /*! * \return The number of chunks in the file. */ unsigned int chunkCount() const; /*! * \return The offset within the file for the selected chunk number. */ offset_t chunkOffset(unsigned int i) const; /*! * \return The size of the chunk data. */ unsigned int chunkDataSize(unsigned int i) const; /*! * \return The size of the padding after the chunk (can be either 0 or 1). */ unsigned int chunkPadding(unsigned int i) const; /*! * \return The name of the specified chunk, for instance, "COMM" or "ID3 " */ ByteVector chunkName(unsigned int i) const; /*! * Reads the chunk data from the file and returns it. * * \note This \e will move the read pointer for the file. */ ByteVector chunkData(unsigned int i); /*! * Sets the data for the specified chunk to \a data. * * \warning This will update the file immediately. */ void setChunkData(unsigned int i, const ByteVector &data); /*! * Sets the data for the chunk \a name to \a data. If a chunk with the * given name already exists it will be overwritten, otherwise it will be * created after the existing chunks. * * \warning This will update the file immediately. */ void setChunkData(const ByteVector &name, const ByteVector &data); /*! * Sets the data for the chunk \a name to \a data. If a chunk with the * given name already exists it will be overwritten, otherwise it will be * created after the existing chunks. * * \note If \a alwaysCreate is \c true, a new chunk is created regardless of * whether or not the chunk \a name exists. It should only be used for * "LIST" chunks. * * \warning This will update the file immediately. */ void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate); /*! * Removes the specified chunk. * * \warning This will update the file immediately. */ void removeChunk(unsigned int i); /*! * Removes the chunk \a name. * * \warning This will update the file immediately. * \warning This removes all the chunks with the given name. */ void removeChunk(const ByteVector &name); private: void read(); void writeChunk(const ByteVector &name, const ByteVector &data, offset_t offset, unsigned long replace = 0); /*! * Update the global RIFF size based on the current internal structure. */ void updateGlobalSize(); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace RIFF } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/riffutils.h����������������������������������������������������������������0000664�0000000�0000000�00000004373�14662262111�0017335�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_RIFFUTILS_H #define TAGLIB_RIFFUTILS_H #include "tbytevector.h" // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header namespace TagLib { namespace RIFF { namespace { inline bool isValidChunkName(const ByteVector &name) { if(name.size() != 4) return false; return std::none_of(name.begin(), name.end(), [](unsigned char c) { return c < 32 || 127 < c; }); } } // namespace } // namespace RIFF } // namespace TagLib #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015743�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/infotag.cpp������������������������������������������������������������0000664�0000000�0000000�00000020030�14662262111�0020071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "infotag.h" #include <utility> #include "tbytevector.h" #include "tpropertymap.h" #include "riffutils.h" using namespace TagLib; using namespace RIFF::Info; namespace { const RIFF::Info::StringHandler defaultStringHandler; const RIFF::Info::StringHandler *stringHandler = &defaultStringHandler; } // namespace class RIFF::Info::Tag::TagPrivate { public: FieldListMap fieldListMap; }; class RIFF::Info::StringHandler::StringHandlerPrivate { }; //////////////////////////////////////////////////////////////////////////////// // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// StringHandler::StringHandler() = default; StringHandler::~StringHandler() = default; String RIFF::Info::StringHandler::parse(const ByteVector &data) const { return String(data, String::UTF8); } ByteVector RIFF::Info::StringHandler::render(const String &s) const { return s.data(String::UTF8); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RIFF::Info::Tag::Tag(const ByteVector &data) : d(std::make_unique<TagPrivate>()) { parse(data); } RIFF::Info::Tag::Tag() : d(std::make_unique<TagPrivate>()) { } RIFF::Info::Tag::~Tag() = default; String RIFF::Info::Tag::title() const { return fieldText("INAM"); } String RIFF::Info::Tag::artist() const { return fieldText("IART"); } String RIFF::Info::Tag::album() const { return fieldText("IPRD"); } String RIFF::Info::Tag::comment() const { return fieldText("ICMT"); } String RIFF::Info::Tag::genre() const { return fieldText("IGNR"); } unsigned int RIFF::Info::Tag::year() const { return fieldText("ICRD").substr(0, 4).toInt(); } unsigned int RIFF::Info::Tag::track() const { return fieldText("IPRT").toInt(); } void RIFF::Info::Tag::setTitle(const String &s) { setFieldText("INAM", s); } void RIFF::Info::Tag::setArtist(const String &s) { setFieldText("IART", s); } void RIFF::Info::Tag::setAlbum(const String &s) { setFieldText("IPRD", s); } void RIFF::Info::Tag::setComment(const String &s) { setFieldText("ICMT", s); } void RIFF::Info::Tag::setGenre(const String &s) { setFieldText("IGNR", s); } void RIFF::Info::Tag::setYear(unsigned int i) { if(i != 0) setFieldText("ICRD", String::number(i)); else d->fieldListMap.erase("ICRD"); } void RIFF::Info::Tag::setTrack(unsigned int i) { if(i != 0) setFieldText("IPRT", String::number(i)); else d->fieldListMap.erase("IPRT"); } bool RIFF::Info::Tag::isEmpty() const { return d->fieldListMap.isEmpty(); } namespace { const Map<ByteVector, String> propertyKeyForId = { {"IPRD", "ALBUM"}, {"IENG", "ARRANGER"}, {"IART", "ARTIST"}, {"IBSU", "ARTISTWEBPAGE"}, {"IBPM", "BPM"}, {"ICMT", "COMMENT"}, {"IMUS", "COMPOSER"}, {"ICOP", "COPYRIGHT"}, {"ICRD", "DATE"}, {"PRT1", "DISCSUBTITLE"}, {"ITCH", "ENCODEDBY"}, {"ISFT", "ENCODING"}, {"IDIT", "ENCODINGTIME"}, {"IGNR", "GENRE"}, {"ISRC", "ISRC"}, {"IPUB", "LABEL"}, {"ILNG", "LANGUAGE"}, {"IWRI", "LYRICIST"}, {"IMED", "MEDIA"}, {"ISTR", "PERFORMER"}, {"ICNT", "RELEASECOUNTRY"}, {"IEDT", "REMIXER"}, {"INAM", "TITLE"}, {"IPRT", "TRACKNUMBER"} }; } // namespace PropertyMap RIFF::Info::Tag::properties() const { PropertyMap props; for(const auto &[id, val] : std::as_const(d->fieldListMap)) { if(String key = propertyKeyForId.value(id); !key.isEmpty()) { props[key].append(val); } else { props.addUnsupportedData(key); } } return props; } void RIFF::Info::Tag::removeUnsupportedProperties(const StringList &props) { for(const auto &id : props) d->fieldListMap.erase(id.data(String::Latin1)); } PropertyMap RIFF::Info::Tag::setProperties(const PropertyMap &props) { static Map<String, ByteVector> idForPropertyKey; if(idForPropertyKey.isEmpty()) { for(const auto &[id, key] : propertyKeyForId) { idForPropertyKey[key] = id; } } const PropertyMap origProps = properties(); for(const auto &[key, _] : origProps) { if(!props.contains(key) || props.value(key).isEmpty()) { d->fieldListMap.erase(idForPropertyKey.value(key)); } } PropertyMap ignoredProps; for(const auto &[key, val] : props) { if(ByteVector id = idForPropertyKey.value(key); !id.isEmpty() && !val.isEmpty()) { d->fieldListMap[id] = val.front(); } else { ignoredProps.insert(key, val); } } return ignoredProps; } FieldListMap RIFF::Info::Tag::fieldListMap() const { return d->fieldListMap; } String RIFF::Info::Tag::fieldText(const ByteVector &id) const { if(d->fieldListMap.contains(id)) return String(d->fieldListMap[id]); return String(); } void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) { // id must be a four-byte long pure ascii string. if(!isValidChunkName(id)) return; if(!s.isEmpty()) d->fieldListMap[id] = s; else removeField(id); } void RIFF::Info::Tag::removeField(const ByteVector &id) { if(d->fieldListMap.contains(id)) d->fieldListMap.erase(id); } ByteVector RIFF::Info::Tag::render() const { ByteVector data("INFO"); for(const auto &[field, list] : std::as_const(d->fieldListMap)) { ByteVector text = stringHandler->render(list); if(text.isEmpty()) continue; data.append(field); data.append(ByteVector::fromUInt(text.size() + 1, false)); data.append(text); do { data.append('\0'); } while(data.size() & 1); } if(data.size() == 4) return ByteVector(); return data; } void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) { if(handler) stringHandler = handler; else stringHandler = &defaultStringHandler; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void RIFF::Info::Tag::parse(const ByteVector &data) { unsigned int p = 4; while(p < data.size()) { const unsigned int size = data.toUInt(p + 4, false); if(size > data.size() - p - 8) break; if(const ByteVector id = data.mid(p, 4); isValidChunkName(id)) { const String text = stringHandler->parse(data.mid(p + 8, size)); d->fieldListMap[id] = text; } p += ((size + 1) & ~1) + 8; } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/infotag.h��������������������������������������������������������������0000664�0000000�0000000�00000016100�14662262111�0017541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_INFOTAG_H #define TAGLIB_INFOTAG_H #include "tmap.h" #include "tstring.h" #include "tbytevector.h" #include "taglib_export.h" #include "tag.h" namespace TagLib { class File; namespace RIFF { //! A RIFF INFO tag implementation. namespace Info { using FieldListMap = Map<ByteVector, String>; //! An abstraction for the string to data encoding in Info tags. /*! * RIFF INFO tag has no clear definitions about character encodings. * In practice, local encoding of each system is largely used and UTF-8 is * popular too. * * Here is an option to read and write tags in your preferred encoding * by subclassing this class, reimplementing parse() and render() and setting * your reimplementation as the default with Info::Tag::setStringHandler(). * * \see ID3v1::Tag::setStringHandler() */ class TAGLIB_EXPORT StringHandler { public: StringHandler(); virtual ~StringHandler(); StringHandler(const StringHandler &) = delete; StringHandler &operator=(const StringHandler &) = delete; /*! * Decode a string from \a data. The default implementation assumes that * \a data is an UTF-8 character array. */ virtual String parse(const ByteVector &data) const; /*! * Encode a ByteVector with the data from \a s. The default implementation * assumes that \a s is an UTF-8 string. */ virtual ByteVector render(const String &s) const; private: class StringHandlerPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<StringHandlerPrivate> d; }; //! The main class in the INFO tag implementation /*! * This is the main class in the INFO tag implementation. RIFF INFO tag is a * metadata format found in WAV audio and AVI video files. Though it is a part * of Microsoft/IBM's RIFF specification, the author could not find the official * documents about it. So, this implementation is referring to unofficial documents * online and some applications' behaviors especially Windows Explorer. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: /*! * Constructs an empty INFO tag. */ Tag(); /*! * Constructs an INFO tag read from \a data which is the contents of the "LIST" chunk. */ Tag(const ByteVector &data); ~Tag() override; Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; // Reimplementations String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; bool isEmpty() const override; PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &props) override; PropertyMap setProperties(const PropertyMap &props) override; /*! * Returns a copy of the internal fields of the tag. The returned map directly * reflects the contents of the "INFO" chunk. * * \note Modifying this map does not affect the tag's internal data. * Use setFieldText() and removeField() instead. * * \see setFieldText() * \see removeField() */ FieldListMap fieldListMap() const; /*! * Gets the value of the field with the ID \a id. */ String fieldText(const ByteVector &id) const; /*! * Sets the value of the field with the ID \a id to \a s. * If the field does not exist, it is created. * If \a s is empty, the field is removed. * * \note fieldId must be a four-byte long pure ASCII string. This function * performs nothing if fieldId is invalid. */ void setFieldText(const ByteVector &id, const String &s); /*! * Removes the field with the ID \a id. */ void removeField(const ByteVector &id); /*! * Render the tag back to binary data, suitable to be written to disk. * * \note Returns an empty ByteVector if the tag contains no fields. */ ByteVector render() const; /*! * Sets the string handler that decides how the text data will be * converted to and from binary data. * If the parameter \a handler is null, the previous handler is * released and default UTF-8 handler is restored. * * \note The caller is responsible for deleting the previous handler * as needed after it is released. * * \see StringHandler */ static void setStringHandler(const StringHandler *handler); protected: /*! * Parses the body of the tag in \a data. */ void parse(const ByteVector &data); private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace Info } // namespace RIFF } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/wavfile.cpp������������������������������������������������������������0000664�0000000�0000000�00000016133�14662262111�0020110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "wavfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagutils.h" #include "infotag.h" #include "tagunion.h" using namespace TagLib; namespace { enum { ID3v2Index = 0, InfoIndex = 1 }; } // namespace class RIFF::WAV::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory) : ID3v2FrameFactory(frameFactory ? frameFactory : ID3v2::FrameFactory::instance()) { } ~FilePrivate() = default; const ID3v2::FrameFactory *ID3v2FrameFactory; std::unique_ptr<Properties> properties; TagUnion tag; bool hasID3v2 { false }; bool hasInfo { false }; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool RIFF::WAV::File::isSupported(IOStream *stream) { // A WAV file has to start with "RIFF????WAVE". const ByteVector id = Utils::readHeader(stream, 12, false); return id.startsWith("RIFF") && id.containsAt("WAVE", 8); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// RIFF::WAV::File::File(FileName file, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : RIFF::File(file, LittleEndian), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } RIFF::WAV::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : RIFF::File(stream, LittleEndian), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } RIFF::WAV::File::~File() = default; TagLib::Tag *RIFF::WAV::File::tag() const { return &d->tag; } ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const { return d->tag.access<ID3v2::Tag>(ID3v2Index, false); } RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const { return d->tag.access<RIFF::Info::Tag>(InfoIndex, false); } void RIFF::WAV::File::strip(TagTypes tags) { removeTagChunks(tags); if(tags & ID3v2) d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory)); if(tags & Info) d->tag.set(InfoIndex, new RIFF::Info::Tag()); } PropertyMap RIFF::WAV::File::properties() const { return d->tag.properties(); } void RIFF::WAV::File::removeUnsupportedProperties(const StringList &unsupported) { d->tag.removeUnsupportedProperties(unsupported); } PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties) { InfoTag()->setProperties(properties); return ID3v2Tag()->setProperties(properties); } RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const { return d->properties.get(); } bool RIFF::WAV::File::save() { return RIFF::WAV::File::save(AllTags); } bool RIFF::WAV::File::save(TagTypes tags, StripTags strip, ID3v2::Version version) { if(readOnly()) { debug("RIFF::WAV::File::save() -- File is read only."); return false; } if(!isValid()) { debug("RIFF::WAV::File::save() -- Trying to save invalid file."); return false; } if(strip == StripOthers) File::strip(static_cast<TagTypes>(AllTags & ~tags)); if(tags & ID3v2) { removeTagChunks(ID3v2); if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) { setChunkData("ID3 ", ID3v2Tag()->render(version)); d->hasID3v2 = true; } } if(tags & Info) { removeTagChunks(Info); if(InfoTag() && !InfoTag()->isEmpty()) { setChunkData("LIST", InfoTag()->render(), true); d->hasInfo = true; } } return true; } bool RIFF::WAV::File::hasID3v2Tag() const { return d->hasID3v2; } bool RIFF::WAV::File::hasInfoTag() const { return d->hasInfo; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::WAV::File::read(bool readProperties) { for(unsigned int i = 0; i < chunkCount(); ++i) { if(const ByteVector name = chunkName(i); name == "ID3 " || name == "id3 ") { if(!d->tag[ID3v2Index]) { d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i), d->ID3v2FrameFactory)); d->hasID3v2 = true; } else { debug("RIFF::WAV::File::read() - Duplicate ID3v2 tag found."); } } else if(name == "LIST") { if(const ByteVector data = chunkData(i); data.startsWith("INFO")) { if(!d->tag[InfoIndex]) { d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); d->hasInfo = true; } else { debug("RIFF::WAV::File::read() - Duplicate INFO tag found."); } } } } if(!d->tag[ID3v2Index]) d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory)); if(!d->tag[InfoIndex]) d->tag.set(InfoIndex, new RIFF::Info::Tag()); if(readProperties) d->properties = std::make_unique<Properties>(this, Properties::Average); } void RIFF::WAV::File::removeTagChunks(TagTypes tags) { if((tags & ID3v2) && d->hasID3v2) { removeChunk("ID3 "); removeChunk("id3 "); d->hasID3v2 = false; } if((tags & Info) && d->hasInfo) { for(int i = static_cast<int>(chunkCount()) - 1; i >= 0; --i) { if(chunkName(i) == "LIST" && chunkData(i).startsWith("INFO")) removeChunk(i); } d->hasInfo = false; } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/wavfile.h��������������������������������������������������������������0000664�0000000�0000000�00000017412�14662262111�0017556�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_WAVFILE_H #define TAGLIB_WAVFILE_H #include "rifffile.h" #include "id3v2tag.h" #include "infotag.h" #include "wavproperties.h" namespace TagLib { namespace RIFF { //! An implementation of WAV metadata /*! * This is an implementation of WAV metadata. * * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF * chunk as well as properties from the file. */ namespace WAV { //! An implementation of TagLib::File with WAV specific methods /*! * This implements and provides an interface for WAV files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to WAV files. */ class TAGLIB_EXPORT File : public TagLib::RIFF::File { public: enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v2 tags. ID3v2 = 0x0001, //! Matches INFO tags. Info = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs a WAV file from \a file. If \a readProperties is \c true the * file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a WAV file from \a stream. If \a readProperties is \c true the * file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note In the current implementation, \a propertiesStyle is ignored. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the tag for this file. This will be a RIFF INFO tag, an * ID3v2 tag or a combination of the two. */ TagLib::Tag *tag() const override; /*! * Returns the ID3v2 Tag for this file. * * \note This always returns a valid pointer regardless of whether or not * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the * file on disk actually has an ID3v2 tag. * * \see hasID3v2Tag() */ ID3v2::Tag *ID3v2Tag() const; /*! * Returns the RIFF INFO Tag for this file. * * \note This always returns a valid pointer regardless of whether or not * the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the * file on disk actually has a RIFF INFO tag. * * \see hasInfoTag() */ Info::Tag *InfoTag() const; /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns \c true if the tags are * successfully stripped. * * \note This will update the file immediately. */ void strip(TagTypes tags = AllTags); /*! * Implements the unified property interface -- export function. * This method forwards to ID3v2::Tag::properties(). */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &unsupported) override; /*! * Implements the unified property interface -- import function. * This method forwards to ID3v2::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the WAV::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. */ bool save() override; /*! * Save the file. If \a strip is specified, it is possible to choose if * tags not specified in \a tags should be stripped from the file or * retained. With \a version, it is possible to specify whether ID3v2.4 * or ID3v2.3 should be used. */ bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4); /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Returns whether or not the file on disk actually has a RIFF INFO tag. * * \see InfoTag() */ bool hasInfoTag() const; /*! * Returns whether or not the given \a stream can be opened as a WAV * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); void removeTagChunks(TagTypes tags); friend class Properties; class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace WAV } // namespace RIFF } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/wavproperties.cpp������������������������������������������������������0000664�0000000�0000000�00000013264�14662262111�0021367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "wavproperties.h" #include "tdebug.h" #include "wavfile.h" using namespace TagLib; namespace { // Quoted from RFC 2361. enum WaveFormat { FORMAT_UNKNOWN = 0x0000, FORMAT_PCM = 0x0001, FORMAT_IEEE_FLOAT = 0x0003 }; } // namespace class RIFF::WAV::Properties::PropertiesPrivate { public: int format { 0 }; int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int bitsPerSample { 0 }; unsigned int sampleFrames { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// TagLib::RIFF::WAV::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file); } RIFF::WAV::Properties::~Properties() = default; int RIFF::WAV::Properties::lengthInMilliseconds() const { return d->length; } int RIFF::WAV::Properties::bitrate() const { return d->bitrate; } int RIFF::WAV::Properties::sampleRate() const { return d->sampleRate; } int RIFF::WAV::Properties::channels() const { return d->channels; } int RIFF::WAV::Properties::bitsPerSample() const { return d->bitsPerSample; } unsigned int RIFF::WAV::Properties::sampleFrames() const { return d->sampleFrames; } int RIFF::WAV::Properties::format() const { return d->format; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::WAV::Properties::read(File *file) { ByteVector data; unsigned int streamLength = 0; unsigned int totalSamples = 0; for(unsigned int i = 0; i < file->chunkCount(); ++i) { if(const ByteVector name = file->chunkName(i); name == "fmt ") { if(data.isEmpty()) data = file->chunkData(i); else debug("RIFF::WAV::Properties::read() - Duplicate 'fmt ' chunk found."); } else if(name == "data") { if(streamLength == 0) streamLength = file->chunkDataSize(i) + file->chunkPadding(i); else debug("RIFF::WAV::Properties::read() - Duplicate 'data' chunk found."); } else if(name == "fact") { if(totalSamples == 0) totalSamples = file->chunkData(i).toUInt(0, false); else debug("RIFF::WAV::Properties::read() - Duplicate 'fact' chunk found."); } } if(data.size() < 16) { debug("RIFF::WAV::Properties::read() - 'fmt ' chunk not found or too short."); return; } if(streamLength == 0) { debug("RIFF::WAV::Properties::read() - 'data' chunk not found."); return; } d->format = data.toShort(0, false); if((d->format & 0xffff) == 0xfffe) { // if extensible then read the format from the subformat if(data.size() != 40) { debug("RIFF::WAV::Properties::read() - extensible size incorrect"); return; } d->format = data.toShort(24, false); } if(d->format != FORMAT_PCM && d->format != FORMAT_IEEE_FLOAT && totalSamples == 0) { debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found."); return; } d->channels = data.toShort(2, false); d->sampleRate = data.toUInt(4, false); d->bitsPerSample = data.toShort(14, false); if(d->format != FORMAT_PCM && (d->format != FORMAT_IEEE_FLOAT || totalSamples != 0)) d->sampleFrames = totalSamples; else if(d->channels > 0 && d->bitsPerSample > 0) d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8)); if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } else { if(const unsigned int byteRate = data.toUInt(8, false); byteRate > 0) { d->length = static_cast<int>(streamLength * 1000.0 / byteRate + 0.5); d->bitrate = static_cast<int>(byteRate * 8.0 / 1000.0 + 0.5); } } } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/riff/wav/wavproperties.h��������������������������������������������������������0000664�0000000�0000000�00000007522�14662262111�0021034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_WAVPROPERTIES_H #define TAGLIB_WAVPROPERTIES_H #include "audioproperties.h" namespace TagLib { class ByteVector; namespace RIFF { namespace WAV { class File; //! An implementation of audio property reading for WAV /*! * This reads the data from a WAV stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of WAV::Properties with the data read from the * WAV::File \a file. */ Properties(File *file, ReadStyle style); /*! * Destroys this WAV::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns the number of sample frames. */ unsigned int sampleFrames() const; /*! * Returns the format ID of the file. * 0 for unknown, 1 for PCM, 2 for ADPCM, 3 for 32/64-bit IEEE754, and * so forth. * * \note For further information, refer to the WAVE Form Registration * Numbers in RFC 2361. */ int format() const; private: void read(File *file); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace WAV } // namespace RIFF } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/s3m/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014722�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/s3m/s3mfile.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000015473�14662262111�0017002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "s3mfile.h" #include "tstringlist.h" #include "tdebug.h" #include "tpropertymap.h" #include "modfileprivate.h" using namespace TagLib; using namespace S3M; class S3M::File::FilePrivate { public: FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) { } Mod::Tag tag; S3M::Properties properties; }; S3M::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } S3M::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } S3M::File::~File() = default; Mod::Tag *S3M::File::tag() const { return &d->tag; } PropertyMap S3M::File::properties() const { return d->tag.properties(); } PropertyMap S3M::File::setProperties(const PropertyMap &properties) { return d->tag.setProperties(properties); } S3M::Properties *S3M::File::audioProperties() const { return &d->properties; } bool S3M::File::save() { if(readOnly()) { debug("S3M::File::save() - Cannot save to a read only file."); return false; } // note: if title starts with "Extended Module: " // the file would look like an .xm file seek(0); writeString(d->tag.title(), 27); // string terminating NUL is not optional: writeByte(0); seek(32); unsigned short length = 0; unsigned short sampleCount = 0; if(!readU16L(length) || !readU16L(sampleCount)) return false; seek(28, Current); int channels = 0; for(int i = 0; i < 32; ++ i) { unsigned char setting = 0; if(!readByte(setting)) return false; // or if(setting >= 128)? // or channels = i + 1;? // need a better spec! if(setting != 0xff) ++ channels; } seek(channels, Current); StringList lines = d->tag.comment().split("\n"); // write comment as sample names: for(unsigned short i = 0; i < sampleCount; ++ i) { seek(96L + length + (static_cast<long>(i) << 1)); unsigned short instrumentOffset = 0; if(!readU16L(instrumentOffset)) return false; seek((static_cast<long>(instrumentOffset) << 4) + 48); if(i < lines.size()) writeString(lines[i], 27); else writeString(String(), 27); // string terminating NUL is not optional: writeByte(0); } return true; } void S3M::File::read(bool) { if(!isOpen()) return; READ_STRING(d->tag.setTitle, 28); READ_BYTE_AS(mark); READ_BYTE_AS(type); READ_ASSERT(mark == 0x1A && type == 0x10); seek(32); READ_U16L_AS(length); READ_U16L_AS(sampleCount); d->properties.setSampleCount(sampleCount); READ_U16L(d->properties.setPatternCount); READ_U16L(d->properties.setFlags); READ_U16L(d->properties.setTrackerVersion); READ_U16L(d->properties.setFileFormatVersion); READ_ASSERT(readBlock(4) == "SCRM"); READ_BYTE(d->properties.setGlobalVolume); READ_BYTE(d->properties.setBpmSpeed); READ_BYTE(d->properties.setTempo); READ_BYTE_AS(masterVolume); d->properties.setMasterVolume(masterVolume & 0x7f); d->properties.setStereo((masterVolume & 0x80) != 0); // I've seen players who call the next two bytes // "ultra click" and "use panning values" (if == 0xFC). // I don't see them in any spec, though. // Hm, but there is "UltraClick-removal" and some other // variables in ScreamTracker III's GUI. seek(12, Current); int channels = 0; for(int i = 0; i < 32; ++ i) { READ_BYTE_AS(setting); // or if(setting >= 128)? // or channels = i + 1;? // need a better spec! if(setting != 0xff) ++ channels; } d->properties.setChannels(channels); seek(96); unsigned short realLength = 0; for(unsigned short i = 0; i < length; ++ i) { READ_BYTE_AS(order); if(order == 255) break; if(order != 254) ++ realLength; } d->properties.setLengthInPatterns(realLength); seek(channels, Current); // Note: The S3M spec mentions samples and instruments, but in // the header there are only pointers to instruments. // However, there I never found instruments (SCRI) but // instead samples (SCRS). StringList comment; for(unsigned short i = 0; i < sampleCount; ++ i) { seek(96L + length + (static_cast<long>(i) << 1)); READ_U16L_AS(sampleHeaderOffset); seek(static_cast<long>(sampleHeaderOffset) << 4); READ_BYTE_AS(sampleType); READ_STRING_AS(dosFileName, 13); READ_U16L_AS(sampleDataOffset); READ_U32L_AS(sampleLength); READ_U32L_AS(repeatStart); READ_U32L_AS(repeatStop); READ_BYTE_AS(sampleVolume); seek(1, Current); READ_BYTE_AS(packing); READ_BYTE_AS(sampleFlags); READ_U32L_AS(baseFrequency); seek(12, Current); READ_STRING_AS(sampleName, 28); // The next 4 bytes should be "SCRS", but I've found // files that are otherwise ok with 4 nils instead. // READ_ASSERT(readBlock(4) == "SCRS"); comment.append(sampleName); } d->tag.setComment(comment.toString("\n")); d->tag.setTrackerName("ScreamTracker III"); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/s3m/s3mfile.h�������������������������������������������������������������������0000664�0000000�0000000�00000011331�14662262111�0016434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_S3MFILE_H #define TAGLIB_S3MFILE_H #include "tfile.h" #include "taglib_export.h" #include "audioproperties.h" #include "modfilebase.h" #include "modtag.h" #include "s3mproperties.h" namespace TagLib { //! An implementation of ScreamTracker III metadata /*! * This is an implementation of ScreamTracker III metadata. */ namespace S3M { //! An implementation of TagLib::File with S3M specific methods /*! * This implements and provides an interface for S3M files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to S3M files. */ class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! * Constructs a ScreamTracker III from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Constructs a ScreamTracker III file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; Mod::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * Forwards to Mod::Tag::properties(). */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Forwards to Mod::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the S3M::Properties for this file. If no audio properties * were read then this will return a null pointer. */ S3M::Properties *audioProperties() const override; /*! * Save the file. * This is the same as calling save(AllTags); * * \note Saving ScreamTracker III tags is not supported. */ bool save() override; private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace S3M } // namespace TagLib #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/s3m/s3mproperties.cpp�����������������������������������������������������������0000664�0000000�0000000�00000010757�14662262111�0020257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "s3mproperties.h" using namespace TagLib; using namespace S3M; class S3M::Properties::PropertiesPrivate { public: unsigned short lengthInPatterns { 0 }; int channels { 0 }; bool stereo { false }; unsigned short sampleCount { 0 }; unsigned short patternCount { 0 }; unsigned short flags { 0 }; unsigned short trackerVersion { 0 }; unsigned short fileFormatVersion { 0 }; unsigned char globalVolume { 0 }; unsigned char masterVolume { 0 }; unsigned char tempo { 0 }; unsigned char bpmSpeed { 0 }; }; S3M::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(std::make_unique<PropertiesPrivate>()) { } S3M::Properties::~Properties() = default; int S3M::Properties::channels() const { return d->channels; } unsigned short S3M::Properties::lengthInPatterns() const { return d->lengthInPatterns; } bool S3M::Properties::stereo() const { return d->stereo; } unsigned short S3M::Properties::sampleCount() const { return d->sampleCount; } unsigned short S3M::Properties::patternCount() const { return d->patternCount; } unsigned short S3M::Properties::flags() const { return d->flags; } unsigned short S3M::Properties::trackerVersion() const { return d->trackerVersion; } unsigned short S3M::Properties::fileFormatVersion() const { return d->fileFormatVersion; } unsigned char S3M::Properties::globalVolume() const { return d->globalVolume; } unsigned char S3M::Properties::masterVolume() const { return d->masterVolume; } unsigned char S3M::Properties::tempo() const { return d->tempo; } unsigned char S3M::Properties::bpmSpeed() const { return d->bpmSpeed; } void S3M::Properties::setLengthInPatterns(unsigned short lengthInPatterns) { d->lengthInPatterns = lengthInPatterns; } void S3M::Properties::setChannels(int channels) { d->channels = channels; } void S3M::Properties::setStereo(bool stereo) { d->stereo = stereo; } void S3M::Properties::setSampleCount(unsigned short sampleCount) { d->sampleCount = sampleCount; } void S3M::Properties::setPatternCount(unsigned short patternCount) { d->patternCount = patternCount; } void S3M::Properties::setFlags(unsigned short flags) { d->flags = flags; } void S3M::Properties::setTrackerVersion(unsigned short trackerVersion) { d->trackerVersion = trackerVersion; } void S3M::Properties::setFileFormatVersion(unsigned short fileFormatVersion) { d->fileFormatVersion = fileFormatVersion; } void S3M::Properties::setGlobalVolume(unsigned char globalVolume) { d->globalVolume = globalVolume; } void S3M::Properties::setMasterVolume(unsigned char masterVolume) { d->masterVolume = masterVolume; } void S3M::Properties::setTempo(unsigned char tempo) { d->tempo = tempo; } void S3M::Properties::setBpmSpeed(unsigned char bpmSpeed) { d->bpmSpeed = bpmSpeed; } �����������������taglib-2.0.2/taglib/s3m/s3mproperties.h�������������������������������������������������������������0000664�0000000�0000000�00000007350�14662262111�0017717�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_S3MPROPERTIES_H #define TAGLIB_S3MPROPERTIES_H #include "audioproperties.h" namespace TagLib { namespace S3M { //! An implementation of audio property reading for S3M class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! Flag bits. */ enum { ST2Vibrato = 1, ST2Tempo = 2, AmigaSlides = 4, Vol0MixOptimizations = 8, AmigaLimits = 16, EnableFilter = 32, CustomData = 128 }; Properties(AudioProperties::ReadStyle propertiesStyle); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; int channels() const override; unsigned short lengthInPatterns() const; bool stereo() const; unsigned short sampleCount() const; unsigned short patternCount() const; unsigned short flags() const; unsigned short trackerVersion() const; unsigned short fileFormatVersion() const; unsigned char globalVolume() const; unsigned char masterVolume() const; unsigned char tempo() const; unsigned char bpmSpeed() const; void setChannels(int channels); void setLengthInPatterns(unsigned short lengthInPatterns); void setStereo(bool stereo); void setSampleCount(unsigned short sampleCount); void setPatternCount(unsigned short patternCount); void setFlags(unsigned short flags); void setTrackerVersion(unsigned short trackerVersion); void setFileFormatVersion(unsigned short fileFormatVersion); void setGlobalVolume(unsigned char globalVolume); void setMasterVolume(unsigned char masterVolume); void setTempo(unsigned char tempo); void setBpmSpeed(unsigned char bpmSpeed); private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace S3M } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/tag.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000013151�14662262111�0015500�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tag.h" #include <utility> #include "tstringlist.h" #include "tpropertymap.h" using namespace TagLib; class Tag::TagPrivate { }; Tag::Tag() = default; Tag::~Tag() = default; bool Tag::isEmpty() const { return title().isEmpty() && artist().isEmpty() && album().isEmpty() && comment().isEmpty() && genre().isEmpty() && year() == 0 && track() == 0; } PropertyMap Tag::properties() const { PropertyMap map; if(!title().isEmpty()) map["TITLE"].append(title()); if(!artist().isEmpty()) map["ARTIST"].append(artist()); if(!album().isEmpty()) map["ALBUM"].append(album()); if(!comment().isEmpty()) map["COMMENT"].append(comment()); if(!genre().isEmpty()) map["GENRE"].append(genre()); if(year() != 0) map["DATE"].append(String::number(year())); if(track() != 0) map["TRACKNUMBER"].append(String::number(track())); return map; } void Tag::removeUnsupportedProperties(const StringList&) { } PropertyMap Tag::setProperties(const PropertyMap &origProps) { PropertyMap props(origProps); props.removeEmpty(); StringList oneValueSet; // can this be simplified by using some preprocessor defines / function pointers? if(props.contains("TITLE")) { setTitle(props["TITLE"].front()); oneValueSet.append("TITLE"); } else setTitle(String()); if(props.contains("ARTIST")) { setArtist(props["ARTIST"].front()); oneValueSet.append("ARTIST"); } else setArtist(String()); if(props.contains("ALBUM")) { setAlbum(props["ALBUM"].front()); oneValueSet.append("ALBUM"); } else setAlbum(String()); if(props.contains("COMMENT")) { setComment(props["COMMENT"].front()); oneValueSet.append("COMMENT"); } else setComment(String()); if(props.contains("GENRE")) { setGenre(props["GENRE"].front()); oneValueSet.append("GENRE"); } else setGenre(String()); if(props.contains("DATE")) { bool ok; int date = props["DATE"].front().toInt(&ok); if(ok) { setYear(date); oneValueSet.append("DATE"); } else setYear(0); } else setYear(0); if(props.contains("TRACKNUMBER")) { bool ok; int trackNumber = props["TRACKNUMBER"].front().toInt(&ok); if(ok) { setTrack(trackNumber); oneValueSet.append("TRACKNUMBER"); } else setTrack(0); } else setTrack(0); // for each tag that has been set above, remove the first entry in the corresponding // value list. The others will be returned as unsupported by this format. for(const auto &entry : std::as_const(oneValueSet)) { if(props[entry].size() == 1) props.erase(entry); else props[entry].erase(props[entry].begin()); } return props; } StringList Tag::complexPropertyKeys() const { return StringList(); } List<VariantMap> Tag::complexProperties(const String &) const { return {}; } bool Tag::setComplexProperties(const String &, const List<VariantMap> &) { return false; } void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static { if(overwrite) { target->setTitle(source->title()); target->setArtist(source->artist()); target->setAlbum(source->album()); target->setComment(source->comment()); target->setGenre(source->genre()); target->setYear(source->year()); target->setTrack(source->track()); } else { if(target->title().isEmpty()) target->setTitle(source->title()); if(target->artist().isEmpty()) target->setArtist(source->artist()); if(target->album().isEmpty()) target->setAlbum(source->album()); if(target->comment().isEmpty()) target->setComment(source->comment()); if(target->genre().isEmpty()) target->setGenre(source->genre()); if(target->year() == 0) target->setYear(source->year()); if(target->track() == 0) target->setTrack(source->track()); } } String Tag::joinTagValues(const StringList &values) { return values.toString(" / "); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/tag.h���������������������������������������������������������������������������0000664�0000000�0000000�00000022371�14662262111�0015151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TAG_H #define TAGLIB_TAG_H #include "taglib_export.h" #include "tstring.h" #include "tlist.h" #include "tvariant.h" namespace TagLib { class PropertyMap; //! A simple, generic interface to common audio metadata fields. /*! * This is an attempt to abstract away the difference in the metadata formats * of various audio codecs and tagging schemes. As such it is generally a * subset of what is available in the specific formats but should be suitable * for most applications. This is meant to compliment the generic APIs found * in TagLib::AudioProperties, TagLib::File and TagLib::FileRef. */ class TAGLIB_EXPORT Tag { public: /*! * Destroys this Tag instance. */ virtual ~Tag(); Tag(const Tag &) = delete; Tag &operator=(const Tag &) = delete; /*! * Exports the tags of the file as dictionary mapping (human readable) tag * names (Strings) to StringLists of tag values. * The default implementation in this class considers only the usual built-in * tags (artist, album, ...) and only one value per key. */ virtual PropertyMap properties() const; /*! * Removes unsupported properties, or a subset of them, from the tag. * The parameter \a properties must contain only entries from * properties().unsupportedData(). */ virtual void removeUnsupportedProperties(const StringList& properties); /*! * Sets the tags of this File to those specified in \a origProps. This default * implementation sets only the tags for which setter methods exist in this class * (artist, album, ...), and only one value per key; the rest will be contained * in the returned PropertyMap. */ virtual PropertyMap setProperties(const PropertyMap &origProps); /*! * Get the keys of complex properties, i.e. properties which cannot be * represented simply by a string. * Because such properties might be expensive to fetch, there are separate * operations to get the available keys - which is expected to be cheap - * and getting and setting the property values. * The default implementation returns only an empty list. Reimplementations * should provide "PICTURE" if embedded cover art is present, and optionally * support other properties. */ virtual StringList complexPropertyKeys() const; /*! * Get the complex properties for a given \a key. * In order to be flexible for different metadata formats, the properties * are represented as variant maps. Despite this dynamic nature, some * degree of standardization should be achieved between formats: * * - PICTURE * - data: ByteVector with picture data * - description: String with description * - pictureType: String with type as specified for ID3v2, * e.g. "Front Cover", "Back Cover", "Band" * - mimeType: String with image format, e.g. "image/jpeg" * - optionally more information found in the tag, such as * "width", "height", "numColors", "colorDepth" int values * in FLAC pictures * - GENERALOBJECT * - data: ByteVector with object data * - description: String with description * - fileName: String with file name * - mimeType: String with MIME type * - this is currently only implemented for ID3v2 GEOB frames */ virtual List<VariantMap> complexProperties(const String &key) const; /*! * Set all complex properties for a given \a key using variant maps as * \a value with the same format as returned by complexProperties(). * An empty list as \a value removes all complex properties for \a key. */ virtual bool setComplexProperties(const String &key, const List<VariantMap> &value); /*! * Returns the track name; if no track name is present in the tag * an empty string will be returned. */ virtual String title() const = 0; /*! * Returns the artist name; if no artist name is present in the tag * an empty string will be returned. */ virtual String artist() const = 0; /*! * Returns the album name; if no album name is present in the tag * an empty string will be returned. */ virtual String album() const = 0; /*! * Returns the track comment; if no comment is present in the tag * an empty string will be returned. */ virtual String comment() const = 0; /*! * Returns the genre name; if no genre is present in the tag an empty string * will be returned. */ virtual String genre() const = 0; /*! * Returns the year; if there is no year set, this will return 0. */ virtual unsigned int year() const = 0; /*! * Returns the track number; if there is no track number set, this will * return 0. */ virtual unsigned int track() const = 0; /*! * Sets the title to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setTitle(const String &s) = 0; /*! * Sets the artist to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setArtist(const String &s) = 0; /*! * Sets the album to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setAlbum(const String &s) = 0; /*! * Sets the comment to \a s. If \a s is an empty string then this value will be * cleared. */ virtual void setComment(const String &s) = 0; /*! * Sets the genre to \a s. If \a s is an empty string then this value will be * cleared. For tag formats that use a fixed set of genres, the appropriate * value will be selected based on a string comparison. A list of available * genres for those formats should be available in that type's * implementation. */ virtual void setGenre(const String &s) = 0; /*! * Sets the year to \a i. If \a s is 0 then this value will be cleared. */ virtual void setYear(unsigned int i) = 0; /*! * Sets the track to \a i. If \a s is 0 then this value will be cleared. */ virtual void setTrack(unsigned int i) = 0; /*! * Returns \c true if the tag does not contain any data. This should be * reimplemented in subclasses that provide more than the basic tagging * abilities in this class. */ virtual bool isEmpty() const; /*! * Copies the generic data from one tag to another. * * \note This will not affect any of the lower level details of the tag. For * instance if any of the tag type specific data (maybe a URL for a band) is * set, this will not modify or copy that. This just copies using the API * in this class. * * If \a overwrite is \c true then the values will be unconditionally copied. * If \c false only empty values will be overwritten. */ static void duplicate(const Tag *source, Tag *target, bool overwrite = true); /*! * Join the \a values of a tag to a single string separated by " / ". * If the tag implementation can have multiple values for a basic tag * (e.g. artist), they can be combined to a single string for the basic * tag getters (e.g. artist()). */ static String joinTagValues(const StringList &values); protected: /*! * Construct a Tag. This is protected since tags should only be instantiated * through subclasses. */ Tag(); private: class TagPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<TagPrivate> d; }; } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/taglib_export.h�����������������������������������������������������������������0000664�0000000�0000000�00000005437�14662262111�0017245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_EXPORT_H #define TAGLIB_EXPORT_H #if defined(TAGLIB_STATIC) #define TAGLIB_EXPORT #elif (defined(_WIN32) || defined(_WIN64)) #ifdef MAKE_TAGLIB_LIB #define TAGLIB_EXPORT __declspec(dllexport) #else #define TAGLIB_EXPORT __declspec(dllimport) #endif #elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1) #define TAGLIB_EXPORT __attribute__ ((visibility("default"))) #else #define TAGLIB_EXPORT #endif #if defined _MSC_VER && !defined TAGLIB_STATIC /*! * Suppress MSVC C4251 warning for next statement. * Unfortunately, MSVC exports everything (not only public members) when * __declspec(dllexport) is set at the class level via TAGLIB_EXPORT, which * leads to many "needs to have dll-interface to be used by clients" C4251 * warnings issued by MSVC, because the std::unique_ptr pimpls are * exported too. This macro can be used before private STL fields to suppress * such warnings. */ #define TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE _Pragma("warning(suppress: 4251)") #else #define TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE #endif #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/tagunion.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000015305�14662262111�0016554�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tagunion.h" #include <array> #include "tstringlist.h" #include "tpropertymap.h" using namespace TagLib; #define stringUnion(method) \ do { \ if(tag(0) && !tag(0)->method().isEmpty()) \ return tag(0)->method(); \ if(tag(1) && !tag(1)->method().isEmpty()) \ return tag(1)->method(); \ if(tag(2) && !tag(2)->method().isEmpty()) \ return tag(2)->method(); \ return String(); \ } while(0) #define numberUnion(method) \ do { \ if(tag(0) && tag(0)->method() > 0) \ return tag(0)->method(); \ if(tag(1) && tag(1)->method() > 0) \ return tag(1)->method(); \ if(tag(2) && tag(2)->method() > 0) \ return tag(2)->method(); \ return 0; \ } while(0) #define setUnion(method, value) \ do { \ if(tag(0)) \ tag(0)->set##method(value); \ if(tag(1)) \ tag(1)->set##method(value); \ if(tag(2)) \ tag(2)->set##method(value); \ } while(0) class TagUnion::TagUnionPrivate { public: TagUnionPrivate() = default; ~TagUnionPrivate() { for(Tag *tag : tags) delete tag; } TagUnionPrivate(const TagUnionPrivate &) = delete; TagUnionPrivate &operator=(const TagUnionPrivate &) = delete; std::array<Tag *, 3> tags { nullptr, nullptr, nullptr }; }; TagUnion::TagUnion(Tag *first, Tag *second, Tag *third) : d(std::make_unique<TagUnionPrivate>()) { d->tags = { first, second, third }; } TagUnion::~TagUnion() = default; Tag *TagUnion::operator[](int index) const { return tag(index); } Tag *TagUnion::tag(int index) const { return d->tags[index]; } void TagUnion::set(int index, Tag *tag) { delete d->tags[index]; d->tags[index] = tag; } PropertyMap TagUnion::properties() const { auto it = std::find_if(d->tags.cbegin(), d->tags.cend(), [](const Tag *t) { return t && !t->isEmpty(); }); return it != d->tags.cend() ? (*it)->properties() : PropertyMap(); } void TagUnion::removeUnsupportedProperties(const StringList &unsupported) { for(const auto &t : d->tags) { if(t) { t->removeUnsupportedProperties(unsupported); } } } StringList TagUnion::complexPropertyKeys() const { for(const auto &t : d->tags) { if(t) { if(const StringList keys = t->complexPropertyKeys(); !keys.isEmpty()) { return keys; } } } return StringList(); } List<VariantMap> TagUnion::complexProperties(const String &key) const { for(const auto &t : d->tags) { if(t) { if(const List<VariantMap> props = t->complexProperties(key); !props.isEmpty()) { return props; } } } return List<VariantMap>(); } bool TagUnion::setComplexProperties(const String &key, const List<VariantMap> &value) { bool combinedResult = false; for(const auto &t : d->tags) { if(t) { if(t->setComplexProperties(key, value)) { combinedResult = true; } } } return combinedResult; } String TagUnion::title() const { stringUnion(title); } String TagUnion::artist() const { stringUnion(artist); } String TagUnion::album() const { stringUnion(album); } String TagUnion::comment() const { stringUnion(comment); } String TagUnion::genre() const { stringUnion(genre); } unsigned int TagUnion::year() const { numberUnion(year); } unsigned int TagUnion::track() const { numberUnion(track); } void TagUnion::setTitle(const String &s) { setUnion(Title, s); } void TagUnion::setArtist(const String &s) { setUnion(Artist, s); } void TagUnion::setAlbum(const String &s) { setUnion(Album, s); } void TagUnion::setComment(const String &s) { setUnion(Comment, s); } void TagUnion::setGenre(const String &s) { setUnion(Genre, s); } void TagUnion::setYear(unsigned int i) { setUnion(Year, i); } void TagUnion::setTrack(unsigned int i) { setUnion(Track, i); } bool TagUnion::isEmpty() const { return std::none_of(d->tags.begin(), d->tags.end(), [](auto t) { return t && !t->isEmpty(); }); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/tagunion.h����������������������������������������������������������������������0000664�0000000�0000000�00000007747�14662262111�0016234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TAGUNION_H #define TAGLIB_TAGUNION_H #include "tag.h" #ifndef DO_NOT_DOCUMENT namespace TagLib { /*! * \internal */ class TagUnion : public Tag { public: enum AccessType { Read, Write }; /*! * Creates a TagLib::Tag that is the union of \a first, \a second, and * \a third. The TagUnion takes ownership of these tags and will handle * their deletion. */ TagUnion(Tag *first = nullptr, Tag *second = nullptr, Tag *third = nullptr); ~TagUnion() override; TagUnion(const TagUnion &) = delete; TagUnion &operator=(const TagUnion &) = delete; Tag *operator[](int index) const; Tag *tag(int index) const; void set(int index, Tag *tag); PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &unsupported) override; StringList complexPropertyKeys() const override; List<VariantMap> complexProperties(const String &key) const override; bool setComplexProperties(const String &key, const List<VariantMap> &value) override; String title() const override; String artist() const override; String album() const override; String comment() const override; String genre() const override; unsigned int year() const override; unsigned int track() const override; void setTitle(const String &s) override; void setArtist(const String &s) override; void setAlbum(const String &s) override; void setComment(const String &s) override; void setGenre(const String &s) override; void setYear(unsigned int i) override; void setTrack(unsigned int i) override; bool isEmpty() const override; template <class T> T *access(int index, bool create) { if(!create || tag(index)) return static_cast<T *>(tag(index)); set(index, new T); return static_cast<T *>(tag(index)); } template <class T, class F> T *access(int index, bool create, const F *factory) { if(!create || tag(index)) return static_cast<T *>(tag(index)); set(index, new T(nullptr, 0, factory)); return static_cast<T *>(tag(index)); } private: TagUnion(const Tag &); TagUnion &operator=(const Tag &); class TagUnionPrivate; std::unique_ptr<TagUnionPrivate> d; }; } // namespace TagLib #endif #endif �������������������������taglib-2.0.2/taglib/tagutils.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000007215�14662262111�0016565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tagutils.h" #include "tfile.h" #include "id3v1tag.h" #include "id3v2header.h" #include "apetag.h" using namespace TagLib; offset_t Utils::findID3v1(File *file) { if(!file->isValid()) return -1; // Differentiate between a match of APEv2 magic and a match of ID3v1 magic. if (file->length() >= 131) { file->seek(-131, File::End); const offset_t p = file->tell() + 3; if(const TagLib::ByteVector data = file->readBlock(8); data.containsAt(ID3v1::Tag::fileIdentifier(), 3) && data != APE::Tag::fileIdentifier()) return p; } else { file->seek(-128, File::End); const offset_t p = file->tell(); if(file->readBlock(3) == ID3v1::Tag::fileIdentifier()) return p; } return -1; } offset_t Utils::findID3v2(File *file) { if(!file->isValid()) return -1; file->seek(0); if(file->readBlock(3) == ID3v2::Header::fileIdentifier()) return 0; return -1; } offset_t Utils::findAPE(File *file, offset_t id3v1Location) { if(!file->isValid()) return -1; if(id3v1Location >= 0) file->seek(id3v1Location - 32, File::Beginning); else file->seek(-32, File::End); const offset_t p = file->tell(); if(file->readBlock(8) == APE::Tag::fileIdentifier()) return p; return -1; } ByteVector TagLib::Utils::readHeader(IOStream *stream, unsigned int length, bool skipID3v2, offset_t *headerOffset) { if(!stream || !stream->isOpen()) return ByteVector(); const offset_t originalPosition = stream->tell(); offset_t bufferOffset = 0; if(skipID3v2) { stream->seek(0); if(const ByteVector data = stream->readBlock(ID3v2::Header::size()); data.startsWith(ID3v2::Header::fileIdentifier())) bufferOffset = ID3v2::Header(data).completeTagSize(); } stream->seek(bufferOffset); const ByteVector header = stream->readBlock(length); stream->seek(originalPosition); if(headerOffset) *headerOffset = bufferOffset; return header; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/tagutils.h����������������������������������������������������������������������0000664�0000000�0000000�00000004456�14662262111�0016236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TAGUTILS_H #define TAGLIB_TAGUTILS_H // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header #include "tbytevector.h" #include "taglib.h" namespace TagLib { class File; class IOStream; namespace Utils { offset_t findID3v1(File *file); offset_t findID3v2(File *file); offset_t findAPE(File *file, offset_t id3v1Location); ByteVector readHeader(IOStream *stream, unsigned int length, bool skipID3v2, offset_t *headerOffset = nullptr); } // namespace Utils } // namespace TagLib #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015705�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/propertymapping.dox�����������������������������������������������������0000664�0000000�0000000�00000042300�14662262111�0021660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! \page p_propertymapping Mapping of Properties to Various Formats - If no mapping is given for an %ID3v2 tag, a TXXX frame will be used with the key as a description. - Vorbis Comments are not included in the table because they use the names from the *Key* column. - %APE tags will also use these keys unless another name can be found in its column. | Key | %ID3v2 | %RIFF | %MP4 | %APE | %ASF | | -------------------------- | ------ | ----- | -------------------------------------------------------- | ----------------------- | --------------------------------- | | ACOUSTID_FINGERPRINT | | | | | Acoustid/Fingerprint | | ACOUSTID_ID | | | | | Acoustid/Id | | ALBUM | TALB | IPRD | ©alb | | WM/AlbumTitle | | ALBUMARTIST | TPE2 | | aART | ALBUM ARTIST | WM/AlbumArtist | | ALBUMARTISTSORT | TSO2 | | soaa | | WM/AlbumArtistSortOrder | | ALBUMSORT | TSOA | | soal | | WM/AlbumSortOrder | | ARRANGER | | IENG | | | | | ARTIST | TPE1 | IART | ©ART | | | | ARTISTS | | | \----:com.apple.iTunes:ARTISTS | | WM/ARTISTS | | ARTISTSORT | TSOP | | soar | | WM/ArtistSortOrder | | ARTISTWEBPAGE | WOAR | IBSU | | | WM/AuthorURL | | ASIN | | | \----:com.apple.iTunes:ASIN | | | | AUDIOSOURCEWEBPAGE | WOAS | | | | ASIN | | BARCODE | | | \----:com.apple.iTunes:BARCODE | | WM/Barcode | | BPM | TBPM | IBPM | tmpo | | WM/BeatsPerMinute | | CATALOGNUMBER | | | \----:com.apple.iTunes:CATALOGNUMBER | | WM/CatalogNo | | COMMENT | COMM | ICMT | ©cmt | | | | COMPILATION | TCMP | | cpil | | | | COMPOSER | TCOM | IMUS | ©wrt | | WM/Composer | | COMPOSERSORT | TSOC | | soco | | | | CONDUCTOR | TPE3 | | \----:com.apple.iTunes:CONDUCTOR | | WM/Conductor | | COPYRIGHT | TCOP | ICOP | cprt | | | | COPYRIGHTURL | WCOP | | | | | | DATE | TDRC | ICRD | ©day | YEAR | WM/Year | | DISCNUMBER | TPOS | | disk | DISC | WM/PartOfSet | | DISCSUBTITLE | TSST | PRT1 | \----:com.apple.iTunes:DISCSUBTITLE | | WM/SetSubTitle | | DJMIXER | | | \----:com.apple.iTunes:DJMIXER | | | | ENCODEDBY | TENC | ITCH | ©enc | | WM/EncodedBy | | ENCODING | TSSE | ISFT | ©too | | WM/EncodingSettings | | ENCODINGTIME | TDEN | IDIT | | | WM/EncodingTime | | ENGINEER | | | \----:com.apple.iTunes:ENGINEER | | | | FILETYPE | TFLT | | | | | | FILEWEBPAGE | WOAF | | | | WM/AudioFileURL | | GAPLESSPLAYBACK | | | pgap | | | | GENRE | TCON | IGNR | ©gen | | WM/Genre | | GROUPING | GRP1 | | ©grp | | | | INITIALKEY | TKEY | | | | WM/InitialKey | | INVOLVEDPEOPLE | TIPL | | | | | | ISRC | TSRC | ISRC | \----:com.apple.iTunes:ISRC | | WM/ISRC | | LABEL | TPUB | IPUB | \----:com.apple.iTunes:LABEL | | WM/Publisher | | LANGUAGE | TLAN | ILNG | \----:com.apple.iTunes:LANGUAGE | | WM/Language | | LENGTH | TLEN | | | | | | LICENSE | | | \----:com.apple.iTunes:LICENSE | | | | LYRICIST | TEXT | IWRI | \----:com.apple.iTunes:LYRICIST | | WM/Writer | | LYRICS | USLT | | ©lyr | | WM/Lyrics | | MEDIA | TMED | IMED | \----:com.apple.iTunes:MEDIA | | WM/Media | | MIXER | | | \----:com.apple.iTunes:MIXER | | | | MOOD | TMOO | | \----:com.apple.iTunes:MOOD | | WM/Mood | | MOVEMENTCOUNT | | | ©mvc | | | | MOVEMENTNAME | MVNM | | ©mvn | | | | MOVEMENTNUMBER | MVIN | | ©mvi | | | | MUSICBRAINZ_ALBUMID | | | \----:com.apple.iTunes:MusicBrainz Album Id | | MusicBrainz/Album Id | | MUSICBRAINZ_ALBUMARTISTID | | | \----:com.apple.iTunes:MusicBrainz Album Artist Id | | MusicBrainz/Album Artist Id | | MUSICBRAINZ_ARTISTID | | | \----:com.apple.iTunes:MusicBrainz Artist Id | | MusicBrainz/Artist Id | | MUSICBRAINZ_RELEASEGROUPID | | | \----:com.apple.iTunes:MusicBrainz Release Group Id | | MusicBrainz/Release Group Id | | MUSICBRAINZ_RELEASETRACKID | | | \----:com.apple.iTunes:MusicBrainz Release Track Id | | MusicBrainz/Release Track Id | | MUSICBRAINZ_TRACKID | | | \----:com.apple.iTunes:MusicBrainz Track Id | | MusicBrainz/Track Id | | MUSICBRAINZ_WORKID | | | \----:com.apple.iTunes:MusicBrainz Work Id | | MusicBrainz/Work Id | | MUSICIANCREDITS | TMCL | | | | | | MUSICIP_PUID | | | | | MusicIP/PUID | | ORIGINALALBUM | TOAL | | | | WM/OriginalAlbumTitle | | ORIGINALARTIST | TOPE | | | | WM/OriginalArtist | | ORIGINALDATE | TDOR | | \----:com.apple.iTunes:ORIGINALDATE | | WM/OriginalReleaseYear | | ORIGINALFILENAME | TOFN | | | | WM/OriginalFilename | | ORIGINALLYRICIST | TOLY | | | | WM/OriginalLyricist | | OWNER | TOWN | | ownr | | | | PAYMENTWEBPAGE | WPAY | | | | | | PERFORMER | | ISTR | | | | | PLAYLISTDELAY | TDLY | | | | | | PODCAST | PCST | | pcst | | | | PODCASTCATEGORY | TCAT | | catg | | | | PODCASTDESC | TDES | | desc | | | | PODCASTID | TGID | | egid | | | | PODCASTURL | WFED | | purl | | | | PRODUCEDNOTICE | TPRO | | | | | | PRODUCER | | | \----:com.apple.iTunes:PRODUCER | | WM/Producer | | PUBLISHERWEBPAGE | WPUB | | | | | | RADIOSTATION | TRSN | | | | | | RADIOSTATIONOWNER | TRSO | | | | | | RADIOSTATIONWEBPAGE | WORS | | | | | | RELEASECOUNTRY | | ICNT | \----:com.apple.iTunes:MusicBrainz Album Release Country | | MusicBrainz/Album Release Country | | RELEASEDATE | TDRL | | \----:com.apple.iTunes:RELEASEDATE | | | | RELEASESTATUS | | | \----:com.apple.iTunes:MusicBrainz Album Status | MUSICBRAINZ_ALBUMSTATUS | MusicBrainz/Album Status | | RELEASETYPE | | | \----:com.apple.iTunes:MusicBrainz Album Type | MUSICBRAINZ_ALBUMTYPE | MusicBrainz/Album Type | | REMIXER | TPE4 | IEDT | \----:com.apple.iTunes:REMIXER | MIXARTIST | WM/ModifiedBy | | SCRIPT | | | \----:com.apple.iTunes:SCRIPT | | WM/Script | | SHOWSORT | | | sosn | | | | SHOWWORKMOVEMENT | | | shwm | | | | SUBTITLE | TIT3 | | \----:com.apple.iTunes:SUBTITLE | | WM/SubTitle | | TAGGINGDATE | TDTG | | | | | | TITLE | TIT2 | INAM | ©nam | | | | TITLESORT | TSOT | | sonm | | WM/TitleSortOrder | | TRACKNUMBER | TRCK | IPRT | trkn | TRACK | WM/TrackNumber | | TVEPISODE | | | tves | | | | TVEPISODEID | | | tven | | | | TVNETWORK | | | tvnn | | | | TVSEASON | | | tvsn | | | | TVSHOW | | | tvsh | | | | URL | WXXX | | | | | | WORK | TIT1 | | ©wrk | | WM/ContentGroupDescription | */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/taglib.h����������������������������������������������������������������0000664�0000000�0000000�00000021753�14662262111�0017330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_H #define TAGLIB_H #define TAGLIB_MAJOR_VERSION 2 #define TAGLIB_MINOR_VERSION 0 #define TAGLIB_PATCH_VERSION 2 #if (defined(_MSC_VER) && _MSC_VER >= 1600) #define TAGLIB_CONSTRUCT_BITSET(x) static_cast<unsigned long long>(x) #else #define TAGLIB_CONSTRUCT_BITSET(x) static_cast<unsigned long>(x) #endif #define TAGLIB_DEPRECATED [[deprecated]] #ifndef _WIN32 #include <sys/types.h> #endif //! A namespace for all TagLib related classes and functions /*! * This namespace contains everything in TagLib. For projects working with * TagLib extensively it may be convenient to add a * \code * using namespace TagLib; * \endcode */ namespace TagLib { class String; // Offset or length type for I/O streams. // In Win32, always 64bit. Otherwise, equivalent to off_t. #ifdef _WIN32 using offset_t = long long; #elif !defined(__illumos__) using offset_t = off_t; #endif } // namespace TagLib /*! * \mainpage TagLib * * \section intro Introduction * * TagLib is a library for reading and editing audio metadata, commonly known as \e tags. * * Features: * - A clean, high level, C++ API for handling audio metadata. * - Format specific APIs for advanced API users. * - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats. * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, DSF, DFF, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats. * - Basic audio file properties such as length, sample rate, etc. * - Long term binary and source compatibility. * - Extensible design, notably the ability to add other formats or extend current formats as a library user. * - Full support for unicode and internationalized tags. * - Dual <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL</a> and * <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">LGPL</a> licenses. * - No external toolkit dependencies. * * \section why Why TagLib? * * TagLib originally was written to provide an updated and improved ID3v2 implementation in C++ for use * in a variety of Open Source projects. Since development began in 2002 and the 1.0 release in 2004 * it has expanded to cover a wide variety of tag and file formats and is used in a wide variety of * Open Source and proprietary applications. It now supports a variety of UNIXes, including Apple's * macOS, as well as Microsoft Windows. * * \section commercial Usage in Commercial Applications * * TagLib's licenses \e do allow usage within proprietary (\e closed) applications, however TagLib is \e not * public domain. Please note the requirements of the LGPL or MPL, and adhere to at least one of them. * In simple terms, you must at a minimum note your usage of TagLib, note the licensing terms of TagLib and * if you make changes to TagLib publish them. Please review the licenses above before using TagLib in your * software. Note that you may choose either the MPL or the LGPL, you do not have to fulfill the * requirements of both. * * \section installing Installing TagLib * * Please see the <a href="http://taglib.org/">TagLib website</a> for the latest * downloads. * * TagLib can be built using the CMake build system. TagLib installs a CMake * configuration and a taglib-config and pkg-config file to * make it easier to integrate into various build systems. Note that TagLib's include install directory \e must * be included in the header include path. Simply adding <taglib/tag.h> will \e not work. * * Detailed instructions about building TagLib itself and building with TagLib * can be found in <a href="https://github.com/taglib/taglib/blob/master/INSTALL.md">INSTALL.md</a> * * \section start Getting Started * * TagLib provides both simple, abstract APIs which make it possible to ignore the differences between tagging * formats and format specific APIs which allow programmers to work with the features of specific tagging * schemes. There is a similar abstraction mechanism for \link TagLib::AudioProperties AudioProperties \endlink. * * The best place to start is with the <b>Class Hierarchy</b> linked at the top of the page. * The \link TagLib::File File \endlink and \link TagLib::AudioProperties AudioProperties \endlink * classes and their subclasses are the core of TagLib. The \link TagLib::FileRef FileRef \endlink * class is also a convenient way for using a value-based handle. * * \note When working with \link TagLib::FileRef FileRef \endlink please consider that it has only * the most basic (extension-based) file type resolution. Please see its documentation on how to * plug in more advanced file type resolution. * (Such resolution may be part of later TagLib releases by default.) * * Here's a very simple example with TagLib: * * \code {.cpp} * TagLib::FileRef f("Latex Solar Beef.mp3"); * TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa" * * f.tag()->setAlbum("Fillmore East"); * f.save(); * * TagLib::FileRef g("Free City Rhymes.ogg"); * TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers" * * g.tag()->setTrack(1); * g.save(); * \endcode * * If the basic tag interface, which provides methods like * \link TagLib::Tag::title() title() \endlink, * \link TagLib::Tag::artist() artist() \endlink, * \link TagLib::Tag::album() album() \endlink, * \link TagLib::Tag::comment() comment() \endlink, * \link TagLib::Tag::genre() genre() \endlink, * \link TagLib::Tag::year() year() \endlink, * \link TagLib::Tag::track() track() \endlink * and the corresponding setters, is not enough, the * \link TagLib::PropertyMap PropertyMap \endlink interface * offers a flexible abstraction for textual metadata. * See \ref p_propertymapping for details about the mapping of tags to properties. * * \code {.cpp} * TagLib::PropertyMap props = f.properties(); * TagLib::StringList artists = props["ARTIST"]; * artists.append("Jim Pons"); * props["ARTIST"] = artists; * f.setProperties(props); * f.save(); * \endcode * * An additional \link TagLib::FileRef::complexProperties() abstraction \endlink is * provided to handle complex (i.e. non textual) properties. * * \code {.cpp} * TagLib::ByteVector data = ...; * f.setComplexProperties("PICTURE", { * { * {"data", data}, * {"pictureType", "Front Cover"}, * {"mimeType", "image/jpeg"} * } * }); * \endcode * * Finally, for full control, there are specific types for all supported metadata formats. * * \code {.cpp} * if(auto file = dynamic_cast<TagLib::MPEG::File *>(f.file())) { * if(auto id3v2Tag = file->ID3v2Tag()) { * auto frames = id3v2Tag->frameList("SYLT"); * if(!frames.isEmpty()) { * if(auto syltFrame = dynamic_cast<TagLib::ID3v2::SynchronizedLyricsFrame *>( * frames.front())) { * auto text = syltFrame->synchedText(); * // ... * } * } * } * } * \endcode * * More examples can be found in the <a href="https://github.com/taglib/taglib/tree/master/examples"> * examples</a> directory of the source distribution. * * \section Contact * * Questions about TagLib should be directed to the TagLib mailing list, not directly to the author. * * - <a href="http://taglib.org/">TagLib Homepage</a> * - <a href="https://mail.kde.org/mailman/listinfo/taglib-devel">TagLib Mailing List (taglib-devel@kde.org)</a> * * \author <a href="https://github.com/taglib/taglib/blob/master/AUTHORS">TagLib authors</a>. */ #endif ���������������������taglib-2.0.2/taglib/toolkit/tbytevector.cpp���������������������������������������������������������0000664�0000000�0000000�00000064337�14662262111�0021000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevector.h" #include <algorithm> #include <array> #include <cmath> #include <cstring> #include <iostream> #include "tdebug.h" #include "tutils.h" // This is a bit ugly to keep writing over and over again. // A rather obscure feature of the C++ spec that I hadn't thought of that makes // working with C libs much more efficient. There's more here: // // http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp namespace TagLib { template <class TIterator> int findChar( const TIterator dataBegin, const TIterator dataEnd, char c, unsigned int offset, int byteAlign) { if(const size_t dataSize = dataEnd - dataBegin; offset + 1 > dataSize) return -1; // n % 0 is invalid if(byteAlign == 0) return -1; for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) { if(*it == c) return static_cast<int>(it - dataBegin); } return -1; } template <class TIterator> int findVector( const TIterator dataBegin, const TIterator dataEnd, const TIterator patternBegin, const TIterator patternEnd, unsigned int offset, int byteAlign) { const size_t dataSize = dataEnd - dataBegin; const size_t patternSize = patternEnd - patternBegin; if(patternSize == 0 || offset + patternSize > dataSize) return -1; // Special case that pattern contains just single char. if(patternSize == 1) return findChar(dataBegin, dataEnd, *patternBegin, offset, byteAlign); // n % 0 is invalid if(byteAlign == 0) return -1; // We don't use sophisticated algorithms like Knuth-Morris-Pratt here. // In the current implementation of TagLib, data and patterns are too small // for such algorithms to work effectively. for(TIterator it = dataBegin + offset; it < dataEnd - patternSize + 1; it += byteAlign) { TIterator itData = it; TIterator itPattern = patternBegin; while(*itData == *itPattern) { ++itData; ++itPattern; if(itPattern == patternEnd) return static_cast<int>(it - dataBegin); } } return -1; } template <class T> T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignificantByteFirst) { if(offset >= v.size()) { debug("toNumber<T>() -- No data to convert. Returning 0."); return 0; } length = std::min(length, v.size() - offset); T sum = 0; for(size_t i = 0; i < length; i++) { const size_t shift = (mostSignificantByteFirst ? length - 1 - i : i) * 8; sum |= static_cast<T>(static_cast<unsigned char>(v[static_cast<int>(offset + i)])) << shift; } return sum; } template <class T> T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst) { const bool isBigEndian = Utils::systemByteOrder() == Utils::BigEndian; const bool swap = mostSignificantByteFirst != isBigEndian; if(offset + sizeof(T) > v.size()) return toNumber<T>(v, offset, v.size() - offset, mostSignificantByteFirst); // Uses memcpy instead of reinterpret_cast to avoid an alignment exception. T tmp; ::memcpy(&tmp, v.data() + offset, sizeof(T)); if(swap) return Utils::byteSwap(tmp); return tmp; } template <class T> ByteVector fromNumber(T value, bool mostSignificantByteFirst) { const bool isBigEndian = Utils::systemByteOrder() == Utils::BigEndian; if(mostSignificantByteFirst != isBigEndian) value = Utils::byteSwap(value); return ByteVector(reinterpret_cast<const char *>(&value), sizeof(T)); } template <typename TFloat, typename TInt, Utils::ByteOrder ENDIAN> TFloat toFloat(const ByteVector &v, size_t offset) { if(offset > v.size() - sizeof(TInt)) { debug("toFloat() - offset is out of range. Returning 0."); return 0.0; } union { TInt i; TFloat f; } tmp; ::memcpy(&tmp, v.data() + offset, sizeof(TInt)); if(ENDIAN != Utils::systemByteOrder()) tmp.i = Utils::byteSwap(tmp.i); return tmp.f; } template <typename TFloat, typename TInt, Utils::ByteOrder ENDIAN> ByteVector fromFloat(TFloat value) { union { TInt i; TFloat f; } tmp; tmp.f = value; if(ENDIAN != Utils::systemByteOrder()) tmp.i = Utils::byteSwap(tmp.i); return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt)); } template <Utils::ByteOrder ENDIAN> long double toFloat80(const ByteVector &v, size_t offset) { using std::swap; if(offset > v.size() - 10) { debug("toFloat80() - offset is out of range. Returning 0."); return 0.0; } unsigned char bytes[10]; ::memcpy(bytes, v.data() + offset, 10); if constexpr(ENDIAN == Utils::LittleEndian) { swap(bytes[0], bytes[9]); swap(bytes[1], bytes[8]); swap(bytes[2], bytes[7]); swap(bytes[3], bytes[6]); swap(bytes[4], bytes[5]); } // 1-bit sign const bool negative = ((bytes[0] & 0x80) != 0); // 15-bit exponent const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1]; // 64-bit fraction. Leading 1 is explicit. const unsigned long long fraction = (static_cast<unsigned long long>(bytes[2]) << 56) | (static_cast<unsigned long long>(bytes[3]) << 48) | (static_cast<unsigned long long>(bytes[4]) << 40) | (static_cast<unsigned long long>(bytes[5]) << 32) | (static_cast<unsigned long long>(bytes[6]) << 24) | (static_cast<unsigned long long>(bytes[7]) << 16) | (static_cast<unsigned long long>(bytes[8]) << 8) | (static_cast<unsigned long long>(bytes[9])); long double val; if(exponent == 0 && fraction == 0) val = 0; else { if(exponent == 0x7FFF) { debug("toFloat80() - can't handle the infinity or NaN. Returning 0."); return 0.0; } val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63); } if(negative) return -val; return val; } class ByteVector::ByteVectorPrivate { public: ByteVectorPrivate(unsigned int l, char c) : data(std::make_shared<std::vector<char>>(l, c)), offset(0), length(l) { } ByteVectorPrivate(const char *s, unsigned int l) : data(std::make_shared<std::vector<char>>(s, s + l)), offset(0), length(l) { } ByteVectorPrivate(const ByteVectorPrivate &d, unsigned int o, unsigned int l) : data(d.data), offset(d.offset + o), length(l) { } std::shared_ptr<std::vector<char>> data; unsigned int offset; unsigned int length; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// ByteVector ByteVector::fromCString(const char *s, unsigned int length) { if(length == 0xffffffff) return ByteVector(s, static_cast<unsigned int>(::strlen(s))); return ByteVector(s, length); } ByteVector ByteVector::fromUInt(unsigned int value, bool mostSignificantByteFirst) { return fromNumber<unsigned int>(value, mostSignificantByteFirst); } ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst) { return fromNumber<unsigned short>(value, mostSignificantByteFirst); } ByteVector ByteVector::fromUShort(unsigned short value, bool mostSignificantByteFirst) { return fromNumber<unsigned short>(value, mostSignificantByteFirst); } ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst) { return fromNumber<unsigned long long>(value, mostSignificantByteFirst); } ByteVector ByteVector::fromULongLong(unsigned long long value, bool mostSignificantByteFirst) { return fromNumber<unsigned long long>(value, mostSignificantByteFirst); } ByteVector ByteVector::fromFloat32LE(float value) { return fromFloat<float, unsigned int, Utils::LittleEndian>(value); } ByteVector ByteVector::fromFloat32BE(float value) { return fromFloat<float, unsigned int, Utils::BigEndian>(value); } ByteVector ByteVector::fromFloat64LE(double value) { return fromFloat<double, unsigned long long, Utils::LittleEndian>(value); } ByteVector ByteVector::fromFloat64BE(double value) { return fromFloat<double, unsigned long long, Utils::BigEndian>(value); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ByteVector::ByteVector() : d(std::make_unique<ByteVectorPrivate>(0, '\0')) { } ByteVector::ByteVector(unsigned int size, char value) : d(std::make_unique<ByteVectorPrivate>(size, value)) { } ByteVector::ByteVector(const ByteVector &v) : d(std::make_unique<ByteVectorPrivate>(*v.d, 0, v.d->length)) { } ByteVector::ByteVector(const ByteVector &v, unsigned int offset, unsigned int length) : d(std::make_unique<ByteVectorPrivate>(*v.d, offset, length)) { } ByteVector::ByteVector(char c) : d(std::make_unique<ByteVectorPrivate>(1, c)) { } ByteVector::ByteVector(const char *data, unsigned int length) : d(std::make_unique<ByteVectorPrivate>(data, length)) { } ByteVector::ByteVector(const char *data) : d(std::make_unique<ByteVectorPrivate>(data, static_cast<unsigned int>(::strlen(data)))) { } ByteVector::~ByteVector() = default; ByteVector &ByteVector::setData(const char *s, unsigned int length) { ByteVector(s, length).swap(*this); return *this; } ByteVector &ByteVector::setData(const char *data) { ByteVector(data).swap(*this); return *this; } char *ByteVector::data() { detach(); return !isEmpty() ? &(*d->data)[d->offset] : nullptr; } const char *ByteVector::data() const { return !isEmpty() ? &(*d->data)[d->offset] : nullptr; } ByteVector ByteVector::mid(unsigned int index, unsigned int length) const { index = std::min(index, size()); length = std::min(length, size() - index); return ByteVector(*this, index, length); } char ByteVector::at(unsigned int index) const { return index < size() ? (*d->data)[d->offset + index] : 0; } int ByteVector::find(const ByteVector &pattern, unsigned int offset, int byteAlign) const { return findVector<ConstIterator>( begin(), end(), pattern.begin(), pattern.end(), offset, byteAlign); } int ByteVector::find(char c, unsigned int offset, int byteAlign) const { return findChar<ConstIterator>(begin(), end(), c, offset, byteAlign); } int ByteVector::rfind(const ByteVector &pattern, unsigned int offset, int byteAlign) const { if(offset > 0) { offset = size() - offset - pattern.size(); if(offset >= size()) offset = 0; } const int pos = findVector<ConstReverseIterator>( rbegin(), rend(), pattern.rbegin(), pattern.rend(), offset, byteAlign); if(pos == -1) return -1; return size() - pos - pattern.size(); } bool ByteVector::containsAt(const ByteVector &pattern, unsigned int offset, unsigned int patternOffset, unsigned int patternLength) const { if(pattern.size() < patternLength) patternLength = pattern.size(); // do some sanity checking -- all of these things are needed for the search to be valid const unsigned int compareLength = patternLength - patternOffset; if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0) return false; return ::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0; } bool ByteVector::startsWith(const ByteVector &pattern) const { return containsAt(pattern, 0); } bool ByteVector::endsWith(const ByteVector &pattern) const { return containsAt(pattern, size() - pattern.size()); } ByteVector &ByteVector::replace(char oldByte, char newByte) { detach(); std::replace(this->begin(), this->end(), oldByte, newByte); return *this; } ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with) { if(pattern.size() == 1 && with.size() == 1) return replace(pattern[0], with[0]); // Check if there is at least one occurrence of the pattern. int offset = find(pattern, 0); if(offset == -1) return *this; if(pattern.size() == with.size()) { // We think this case might be common enough to optimize it. detach(); do { ::memcpy(data() + offset, with.data(), with.size()); offset = find(pattern, offset + pattern.size()); } while(offset != -1); } else { // Loop once to calculate the result size. unsigned int dstSize = size(); do { dstSize += with.size() - pattern.size(); offset = find(pattern, offset + pattern.size()); } while(offset != -1); // Loop again to copy modified data to the new vector. ByteVector dst(dstSize); int dstOffset = 0; offset = 0; while(true) { const int next = find(pattern, offset); if(next == -1) { ::memcpy(dst.data() + dstOffset, data() + offset, size() - offset); break; } ::memcpy(dst.data() + dstOffset, data() + offset, next - offset); dstOffset += next - offset; ::memcpy(dst.data() + dstOffset, with.data(), with.size()); dstOffset += with.size(); offset = next + pattern.size(); } swap(dst); } return *this; } int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const { if(pattern.size() > size()) return -1; const int startIndex = size() - pattern.size(); // try to match the last n-1 bytes from the vector (where n is the pattern // size) -- continue trying to match n-2, n-3...1 bytes for(unsigned int i = 1; i < pattern.size(); i++) { if(containsAt(pattern, startIndex + i, 0, pattern.size() - i)) return startIndex + i; } return -1; } ByteVector &ByteVector::append(const ByteVector &v) { if(v.isEmpty()) return *this; detach(); const unsigned int originalSize = size(); const unsigned int appendSize = v.size(); resize(originalSize + appendSize); ::memcpy(data() + originalSize, v.data(), appendSize); return *this; } ByteVector &ByteVector::append(char c) { resize(size() + 1, c); return *this; } ByteVector &ByteVector::clear() { ByteVector().swap(*this); return *this; } unsigned int ByteVector::size() const { return d->length; } ByteVector &ByteVector::resize(unsigned int size, char padding) { if(size != d->length) { detach(); // Remove the excessive length of the internal buffer first to pad correctly. // This doesn't reallocate the buffer, since std::vector::resize() doesn't // reallocate the buffer when shrinking. d->data->resize(d->offset + d->length); d->data->resize(d->offset + size, padding); d->length = size; } return *this; } ByteVector::Iterator ByteVector::begin() { detach(); return d->data->begin() + d->offset; } ByteVector::ConstIterator ByteVector::begin() const { return d->data->begin() + d->offset; } ByteVector::ConstIterator ByteVector::cbegin() const { return d->data->cbegin() + d->offset; } ByteVector::Iterator ByteVector::end() { detach(); return d->data->begin() + d->offset + d->length; } ByteVector::ConstIterator ByteVector::end() const { return d->data->begin() + d->offset + d->length; } ByteVector::ConstIterator ByteVector::cend() const { return d->data->cbegin() + d->offset + d->length; } ByteVector::ReverseIterator ByteVector::rbegin() { detach(); return d->data->rbegin() + (d->data->size() - (d->offset + d->length)); } ByteVector::ConstReverseIterator ByteVector::rbegin() const { // Workaround for the Solaris Studio 12.4 compiler. // We need a const reference to the data vector so we can ensure the const version of rbegin() is called. const std::vector<char> &v = *d->data; return v.rbegin() + (v.size() - (d->offset + d->length)); } ByteVector::ReverseIterator ByteVector::rend() { detach(); return d->data->rbegin() + (d->data->size() - d->offset); } ByteVector::ConstReverseIterator ByteVector::rend() const { // Workaround for the Solaris Studio 12.4 compiler. // We need a const reference to the data vector so we can ensure the const version of rbegin() is called. const std::vector<char> &v = *d->data; return v.rbegin() + (v.size() - d->offset); } bool ByteVector::isEmpty() const { return d->length == 0; } unsigned int ByteVector::toUInt(bool mostSignificantByteFirst) const { return toNumber<unsigned int>(*this, 0, mostSignificantByteFirst); } unsigned int ByteVector::toUInt(unsigned int offset, bool mostSignificantByteFirst) const { return toNumber<unsigned int>(*this, offset, mostSignificantByteFirst); } unsigned int ByteVector::toUInt(unsigned int offset, unsigned int length, bool mostSignificantByteFirst) const { return toNumber<unsigned int>(*this, offset, length, mostSignificantByteFirst); } short ByteVector::toShort(bool mostSignificantByteFirst) const { return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst); } short ByteVector::toShort(unsigned int offset, bool mostSignificantByteFirst) const { return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst); } unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const { return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst); } unsigned short ByteVector::toUShort(unsigned int offset, bool mostSignificantByteFirst) const { return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst); } long long ByteVector::toLongLong(bool mostSignificantByteFirst) const { return toNumber<unsigned long long>(*this, 0, mostSignificantByteFirst); } long long ByteVector::toLongLong(unsigned int offset, bool mostSignificantByteFirst) const { return toNumber<unsigned long long>(*this, offset, mostSignificantByteFirst); } unsigned long long ByteVector::toULongLong(bool mostSignificantByteFirst) const { return toNumber<unsigned long long>(*this, 0, mostSignificantByteFirst); } unsigned long long ByteVector::toULongLong(unsigned int offset, bool mostSignificantByteFirst) const { return toNumber<unsigned long long>(*this, offset, mostSignificantByteFirst); } float ByteVector::toFloat32LE(size_t offset) const { return toFloat<float, unsigned int, Utils::LittleEndian>(*this, offset); } float ByteVector::toFloat32BE(size_t offset) const { return toFloat<float, unsigned int, Utils::BigEndian>(*this, offset); } double ByteVector::toFloat64LE(size_t offset) const { return toFloat<double, unsigned long long, Utils::LittleEndian>(*this, offset); } double ByteVector::toFloat64BE(size_t offset) const { return toFloat<double, unsigned long long, Utils::BigEndian>(*this, offset); } long double ByteVector::toFloat80LE(size_t offset) const { return toFloat80<Utils::LittleEndian>(*this, offset); } long double ByteVector::toFloat80BE(size_t offset) const { return toFloat80<Utils::BigEndian>(*this, offset); } const char &ByteVector::operator[](int index) const { return (*d->data)[d->offset + index]; } char &ByteVector::operator[](int index) { detach(); return (*d->data)[d->offset + index]; } bool ByteVector::operator==(const ByteVector &v) const { if(size() != v.size()) return false; return ::memcmp(data(), v.data(), size()) == 0; } bool ByteVector::operator!=(const ByteVector &v) const { return !(*this == v); } bool ByteVector::operator==(const char *s) const { if(size() != ::strlen(s)) return false; return ::memcmp(data(), s, size()) == 0; } bool ByteVector::operator!=(const char *s) const { return !(*this == s); } bool ByteVector::operator<(const ByteVector &v) const { if(const int result = ::memcmp(data(), v.data(), std::min(size(), v.size())); result != 0) return result < 0; return size() < v.size(); } bool ByteVector::operator>(const ByteVector &v) const { return v < *this; } ByteVector ByteVector::operator+(const ByteVector &v) const { ByteVector sum(*this); sum.append(v); return sum; } ByteVector &ByteVector::operator=(const ByteVector &v) { ByteVector(v).swap(*this); return *this; } ByteVector &ByteVector::operator=(char c) { ByteVector(c).swap(*this); return *this; } ByteVector &ByteVector::operator=(const char *data) { ByteVector(data).swap(*this); return *this; } void ByteVector::swap(ByteVector &v) noexcept { using std::swap; swap(d, v.d); } ByteVector ByteVector::toHex() const { static constexpr char hexTable[17] = "0123456789abcdef"; ByteVector encoded(size() * 2); char *p = encoded.data(); for(unsigned int i = 0; i < size(); i++) { unsigned char c = data()[i]; *p++ = hexTable[(c >> 4) & 0x0F]; *p++ = hexTable[ c & 0x0F]; } return encoded; } ByteVector ByteVector::fromBase64(const ByteVector & input) { static constexpr std::array<unsigned char, 256> base64 { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; unsigned int len = input.size(); ByteVector output(len); auto src = reinterpret_cast<const unsigned char*>(input.data()); auto dst = reinterpret_cast<unsigned char*>(output.data()); while(4 <= len) { // Check invalid character if(base64[src[0]] == 0x80) break; // Check invalid character if(base64[src[1]] == 0x80) break; // Decode first byte *dst++ = ((base64[src[0]] << 2) & 0xfc) | ((base64[src[1]] >> 4) & 0x03); if(src[2] != '=') { // Check invalid character if(base64[src[2]] == 0x80) break; // Decode second byte *dst++ = ((base64[src[1]] & 0x0f) << 4) | ((base64[src[2]] >> 2) & 0x0f); if(src[3] != '=') { // Check invalid character if(base64[src[3]] == 0x80) break; // Decode third byte *dst++ = ((base64[src[2]] & 0x03) << 6) | (base64[src[3]] & 0x3f); } else { // assume end of data len -= 4; break; } } else { // assume end of data len -= 4; break; } src += 4; len -= 4; } // Only return output if we processed all bytes if(len == 0) { output.resize(static_cast<unsigned int>(dst - reinterpret_cast<unsigned char*>(output.data()))); return output; } return ByteVector(); } ByteVector ByteVector::toBase64() const { static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if(!isEmpty()) { unsigned int len = size(); ByteVector output(4 * ((len - 1) / 3 + 1)); // note roundup const char * src = data(); char * dst = output.data(); while(3 <= len) { *dst++ = alphabet[(src[0] >> 2) & 0x3f]; *dst++ = alphabet[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0f)]; *dst++ = alphabet[((src[1] & 0x0f) << 2) | ((src[2] >> 6) & 0x03)]; *dst++ = alphabet[src[2] & 0x3f]; src += 3; len -= 3; } if(len) { *dst++ = alphabet[(src[0] >> 2) & 0x3f]; if(len>1) { *dst++ = alphabet[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0f)]; *dst++ = alphabet[((src[1] & 0x0f) << 2)]; } else { *dst++ = alphabet[(src[0] & 0x03) << 4]; *dst++ = '='; } *dst++ = '='; } return output; } return ByteVector(); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void ByteVector::detach() { if(d->data.use_count() > 1) { if(!isEmpty()) ByteVector(&d->data->front() + d->offset, d->length).swap(*this); else ByteVector().swap(*this); } } } // namespace TagLib //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v) { for(const auto &byte : v) { s << byte; } return s; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tbytevector.h�����������������������������������������������������������0000664�0000000�0000000�00000055220�14662262111�0020434�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_BYTEVECTOR_H #define TAGLIB_BYTEVECTOR_H #include <memory> #include <vector> #include <iosfwd> #include "taglib_export.h" namespace TagLib { //! A byte vector /*! * This class provides an implicitly shared byte vector with some methods that * are useful for tagging purposes. Many of the search functions are tailored * to what is useful for finding tag related patterns in a data array. */ class TAGLIB_EXPORT ByteVector { public: #ifndef DO_NOT_DOCUMENT using Iterator = std::vector<char>::iterator; using ConstIterator = std::vector<char>::const_iterator; using ReverseIterator = std::vector<char>::reverse_iterator; using ConstReverseIterator = std::vector<char>::const_reverse_iterator; #endif /*! * Constructs an empty byte vector. */ ByteVector(); /*! * Construct a vector of size \a size with all values set to \a value by * default. */ ByteVector(unsigned int size, char value = 0); /*! * Constructs a byte vector that is a copy of \a v. */ ByteVector(const ByteVector &v); /*! * Constructs a byte vector that is a copy of \a v. */ ByteVector(const ByteVector &v, unsigned int offset, unsigned int length); /*! * Constructs a byte vector that contains \a c. */ ByteVector(char c); /*! * Constructs a byte vector that copies \a data for up to \a length bytes. */ ByteVector(const char *data, unsigned int length); /*! * Constructs a byte vector that copies \a data up to the first null * byte. This is particularly useful for constructing byte arrays from * string constants. * * \warning The behavior is undefined if \a data is not null terminated. */ ByteVector(const char *data); /*! * Destroys this ByteVector instance. */ ~ByteVector(); /*! * Sets the data for the byte array using the first \a length bytes of \a s */ ByteVector &setData(const char *s, unsigned int length); /*! * Sets the data for the byte array copies \a data up to the first null * byte. The behavior is undefined if \a data is not null terminated. */ ByteVector &setData(const char *data); /*! * Returns a pointer to the internal data structure. * * \warning Care should be taken when modifying this data structure as it is * easy to corrupt the ByteVector when doing so. Specifically, while the * data may be changed, its length may not be. */ char *data(); /*! * Returns a pointer to the internal data structure which may not be modified. */ const char *data() const; /*! * Returns a byte vector made up of the bytes starting at \a index and * for \a length bytes. If \a length is not specified it will return the bytes * from \a index to the end of the vector. */ ByteVector mid(unsigned int index, unsigned int length = 0xffffffff) const; /*! * This essentially performs the same as operator[](), but instead of causing * a runtime error if the index is out of bounds, it will return a null byte. */ char at(unsigned int index) const; /*! * Searches the ByteVector for \a pattern starting at \a offset and returns * the offset. Returns -1 if the pattern was not found. If \a byteAlign is * specified the pattern will only be matched if it starts on a byte divisible * by \a byteAlign (starting from \a offset). */ int find(const ByteVector &pattern, unsigned int offset = 0, int byteAlign = 1) const; /*! * Searches the char for \a c starting at \a offset and returns * the offset. Returns \a -1 if the pattern was not found. If \a byteAlign is * specified the pattern will only be matched if it starts on a byte divisible * by \a byteAlign (starting from \a offset). */ int find(char c, unsigned int offset = 0, int byteAlign = 1) const; /*! * Searches the ByteVector for \a pattern starting from either the end of the * vector or \a offset and returns the offset. Returns -1 if the pattern was * not found. If \a byteAlign is specified the pattern will only be matched * if it starts on a byte divisible by \a byteAlign (starting from \a offset). */ int rfind(const ByteVector &pattern, unsigned int offset = 0, int byteAlign = 1) const; /*! * Checks to see if the vector contains the \a pattern starting at position * \a offset. Optionally, if you only want to search for part of the pattern * you can specify an offset within the pattern to start from. Also, you can * specify to only check for the first \a patternLength bytes of \a pattern with * the \a patternLength argument. */ bool containsAt(const ByteVector &pattern, unsigned int offset, unsigned int patternOffset = 0, unsigned int patternLength = 0xffffffff) const; /*! * Returns \c true if the vector starts with \a pattern. */ bool startsWith(const ByteVector &pattern) const; /*! * Returns \c true if the vector ends with \a pattern. */ bool endsWith(const ByteVector &pattern) const; /*! * Replaces \a oldByte with \a newByte and returns a reference to the * ByteVector after the operation. This \e does modify the vector. */ ByteVector &replace(char oldByte, char newByte); /*! * Replaces \a pattern with \a with and returns a reference to the ByteVector * after the operation. This \e does modify the vector. */ ByteVector &replace(const ByteVector &pattern, const ByteVector &with); /*! * Checks for a partial match of \a pattern at the end of the vector. It * returns the offset of the partial match within the vector, or -1 if the * pattern is not found. This method is particularly useful when searching for * patterns that start in one vector and end in another. When combined with * startsWith() it can be used to find a pattern that overlaps two buffers. * * \note This will not match the complete pattern at the end of the string; use * endsWith() for that. */ int endsWithPartialMatch(const ByteVector &pattern) const; /*! * Appends \a v to the end of the ByteVector. */ ByteVector &append(const ByteVector &v); /*! * Appends \a c to the end of the ByteVector. */ ByteVector &append(char c); /*! * Clears the data. */ ByteVector &clear(); /*! * Returns the size of the array. */ unsigned int size() const; /*! * Resize the vector to \a size. If the vector is currently less than * \a size, pad the remaining spaces with \a padding. Returns a reference * to the resized vector. */ ByteVector &resize(unsigned int size, char padding = 0); /*! * Returns an Iterator that points to the front of the vector. */ Iterator begin(); /*! * Returns a ConstIterator that points to the front of the vector. */ ConstIterator begin() const; /*! * Returns a ConstIterator that points to the front of the vector. */ ConstIterator cbegin() const; /*! * Returns an Iterator that points to the back of the vector. */ Iterator end(); /*! * Returns a ConstIterator that points to the back of the vector. */ ConstIterator end() const; /*! * Returns a ConstIterator that points to the back of the vector. */ ConstIterator cend() const; /*! * Returns a ReverseIterator that points to the front of the vector. */ ReverseIterator rbegin(); /*! * Returns a ConstReverseIterator that points to the front of the vector. */ ConstReverseIterator rbegin() const; /*! * Returns a ReverseIterator that points to the back of the vector. */ ReverseIterator rend(); /*! * Returns a ConstReverseIterator that points to the back of the vector. */ ConstReverseIterator rend() const; /*! * Returns \c true if the ByteVector is empty. * * \see size() */ bool isEmpty() const; /*! * Converts the first 4 bytes of the vector to an unsigned integer. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $00 $00 $01 == 0x00000001 == 1, if \c false, $01 00 00 00 == * 0x01000000 == 1. * * \see fromUInt() */ unsigned int toUInt(bool mostSignificantByteFirst = true) const; /*! * Converts the 4 bytes at \a offset of the vector to an unsigned integer. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $00 $00 $01 == 0x00000001 == 1, if \c false, $01 00 00 00 == * 0x01000000 == 1. * * \see fromUInt() */ unsigned int toUInt(unsigned int offset, bool mostSignificantByteFirst = true) const; /*! * Converts the \a length bytes at \a offset of the vector to an unsigned * integer. If \a length is larger than 4, the excess is ignored. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $00 $00 $01 == 0x00000001 == 1, if \c false, $01 00 00 00 == * 0x01000000 == 1. * * \see fromUInt() */ unsigned int toUInt(unsigned int offset, unsigned int length, bool mostSignificantByteFirst = true) const; /*! * Converts the first 2 bytes of the vector to a (signed) short. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $01 == 0x0001 == 1, if \c false, $01 00 == 0x01000000 == 1. * * \see fromShort() */ short toShort(bool mostSignificantByteFirst = true) const; /*! * Converts the 2 bytes at \a offset of the vector to a (signed) short. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $01 == 0x0001 == 1, if \c false, $01 00 == 0x01000000 == 1. * * \see fromShort() */ short toShort(unsigned int offset, bool mostSignificantByteFirst = true) const; /*! * Converts the first 2 bytes of the vector to an unsigned short. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $01 == 0x0001 == 1, if \c false, $01 00 == 0x01000000 == 1. * * \see fromUShort() */ unsigned short toUShort(bool mostSignificantByteFirst = true) const; /*! * Converts the 2 bytes at \a offset of the vector to an unsigned short. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 $01 == 0x0001 == 1, if \c false, $01 00 == 0x01000000 == 1. * * \see fromUShort() */ unsigned short toUShort(unsigned int offset, bool mostSignificantByteFirst = true) const; /*! * Converts the first 8 bytes of the vector to a (signed) long long. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1, * if \c false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see fromLongLong() */ long long toLongLong(bool mostSignificantByteFirst = true) const; /*! * Converts the 8 bytes at \a offset of the vector to a (signed) long long. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1, * if \c false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see fromLongLong() */ long long toLongLong(unsigned int offset, bool mostSignificantByteFirst = true) const; /*! * Converts the first 8 bytes of the vector to an unsigned long long. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1, * if \c false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see fromULongLong() */ unsigned long long toULongLong(bool mostSignificantByteFirst = true) const; /*! * Converts the 8 bytes at \a offset of the vector to an unsigned long long. * * If \a mostSignificantByteFirst is \c true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1, * if \c false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see fromULongLong() */ unsigned long long toULongLong(unsigned int offset, bool mostSignificantByteFirst = true) const; /*! * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 * 32-bit little-endian floating point number. * * \see fromFloat32LE() */ float toFloat32LE(size_t offset) const; /*! * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 * 32-bit big-endian floating point number. * * \see fromFloat32BE() */ float toFloat32BE(size_t offset) const; /*! * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 * 64-bit little-endian floating point number. * * \see fromFloat64LE() */ double toFloat64LE(size_t offset) const; /*! * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 * 64-bit big-endian floating point number. * * \see fromFloat64BE() */ double toFloat64BE(size_t offset) const; /*! * Converts the 10 bytes at \a offset of the vector to a long double as an * IEEE754 80-bit little-endian floating point number. * * \note This may compromise the precision depending on the size of long double. */ long double toFloat80LE(size_t offset) const; /*! * Converts the 10 bytes at \a offset of the vector to a long double as an * IEEE754 80-bit big-endian floating point number. * * \note This may compromise the precision depending on the size of long double. */ long double toFloat80BE(size_t offset) const; /*! * Creates a 4 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is \c true, then this will operate left to right * in building the ByteVector. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 01 == 0x00000001 == 1, if \c false, $01 00 00 00 == * 0x01000000 == 1. * * \see toUInt() */ static ByteVector fromUInt(unsigned int value, bool mostSignificantByteFirst = true); /*! * Creates a 2 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is \c true, then this will operate left to right * in building the ByteVector. For example if \a mostSignificantByteFirst is * \c true then $00 01 == 0x0001 == 1, if \c false, $01 00 == 0x0100 == 1. * * \see toShort() */ static ByteVector fromShort(short value, bool mostSignificantByteFirst = true); /*! * Creates a 2 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is \c true, then this will operate left to right * in building the ByteVector. For example if \a mostSignificantByteFirst is * \c true then $00 01 == 0x0001 == 1, if \c false, $01 00 == 0x0100 == 1. * * \see toUShort() */ static ByteVector fromUShort(unsigned short value, bool mostSignificantByteFirst = true); /*! * Creates an 8 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is \c true, then this will operate left to right * in building the ByteVector. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 01 == 0x0000000000000001 == 1, if \c false, * $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see toLongLong() */ static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true); /*! * Creates an 8 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is \c true, then this will operate left to right * in building the ByteVector. For example if \a mostSignificantByteFirst is * \c true then $00 00 00 01 == 0x0000000000000001 == 1, if \c false, * $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. * * \see toULongLong() */ static ByteVector fromULongLong(unsigned long long value, bool mostSignificantByteFirst = true); /*! * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit * little-endian floating point number. * * \see toFloat32LE() */ static ByteVector fromFloat32LE(float value); /*! * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit * big-endian floating point number. * * \see toFloat32BE() */ static ByteVector fromFloat32BE(float value); /*! * Creates an 8 byte ByteVector based on \a value as an IEEE754 64-bit * little-endian floating point number. * * \see toFloat64LE() */ static ByteVector fromFloat64LE(double value); /*! * Creates an 8 byte ByteVector based on \a value as an IEEE754 64-bit * big-endian floating point number. * * \see toFloat64BE() */ static ByteVector fromFloat64BE(double value); /*! * Returns a ByteVector based on the CString \a s. */ static ByteVector fromCString(const char *s, unsigned int length = 0xffffffff); /*! * Returns a const reference to the byte at \a index. */ const char &operator[](int index) const; /*! * Returns a reference to the byte at \a index. */ char &operator[](int index); /*! * Returns \c true if this ByteVector and \a v are equal. */ bool operator==(const ByteVector &v) const; /*! * Returns \c true if this ByteVector and \a v are not equal. */ bool operator!=(const ByteVector &v) const; /*! * Returns \c true if this ByteVector and the null terminated C string \a s * contain the same data. */ bool operator==(const char *s) const; /*! * Returns \c true if this ByteVector and the null terminated C string \a s * do not contain the same data. */ bool operator!=(const char *s) const; /*! * Returns \c true if this ByteVector is less than \a v. The value of the * vectors is determined by evaluating the character from left to right, and * in the event one vector is a superset of the other, the size is used. */ bool operator<(const ByteVector &v) const; /*! * Returns \c true if this ByteVector is greater than \a v. */ bool operator>(const ByteVector &v) const; /*! * Returns a vector that is \a v appended to this vector. */ ByteVector operator+(const ByteVector &v) const; /*! * Copies ByteVector \a v. */ ByteVector &operator=(const ByteVector &v); /*! * Copies a byte \a c. */ ByteVector &operator=(char c); /*! * Copies \a data up to the first null byte. * * \warning The behavior is undefined if \a data is not null terminated. */ ByteVector &operator=(const char *data); /*! * Exchanges the content of the ByteVector with the content of \a v. */ void swap(ByteVector &v) noexcept; /*! * Returns a hex-encoded copy of the byte vector. */ ByteVector toHex() const; /*! * Returns a base64 encoded copy of the byte vector * * \see fromBase64() */ ByteVector toBase64() const; /*! * Decodes the base64 encoded byte vector. * * \see toBase64() */ static ByteVector fromBase64(const ByteVector &); protected: /*! * If this ByteVector is being shared via implicit sharing, do a deep copy * of the data and separate from the shared members. This should be called * by all non-const subclass members. */ void detach(); private: class ByteVectorPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ByteVectorPrivate> d; }; } // namespace TagLib /*! * \relates TagLib::ByteVector * Streams the ByteVector \a v to the output stream \a s. */ TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v); #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tbytevectorlist.cpp�����������������������������������������������������0000664�0000000�0000000�00000010260�14662262111�0021656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevectorlist.h" using namespace TagLib; class ByteVectorList::ByteVectorListPrivate { }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern, int byteAlign, int max) { ByteVectorList l; unsigned int previousOffset = 0; for(int offset = v.find(pattern, 0, byteAlign); offset != -1 && (max == 0 || max > static_cast<int>(l.size()) + 1); offset = v.find(pattern, offset + pattern.size(), byteAlign)) { if(offset - previousOffset >= 1) l.append(v.mid(previousOffset, offset - previousOffset)); else l.append(ByteVector()); previousOffset = offset + pattern.size(); } if(previousOffset < v.size()) l.append(v.mid(previousOffset, v.size() - previousOffset)); return l; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ByteVectorList::ByteVectorList() = default; ByteVectorList::~ByteVectorList() = default; ByteVectorList::ByteVectorList(const ByteVectorList &l) : List<ByteVector>(l) { // Uncomment if d is used, d.get() is nullptr and *d behavior undefined // *d = *l.d; } ByteVectorList::ByteVectorList(std::initializer_list<ByteVector> init) : List<ByteVector>(init) { } ByteVectorList &ByteVectorList::operator=(const ByteVectorList &l) { if(this == &l) return *this; List<ByteVector>::operator=(l); // Uncomment if d is used, d.get() is nullptr and *d behavior undefined // *d = *l.d; return *this; } ByteVectorList &ByteVectorList::operator=(std::initializer_list<ByteVector> init) { List<ByteVector>::operator=(init); return *this; } ByteVector ByteVectorList::toByteVector(const ByteVector &separator) const { ByteVector v; for(auto it = begin(); it != end(); ++it) { v.append(*it); if(std::next(it) != end()) v.append(separator); } return v; } //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// std::ostream &operator<<(std::ostream &s, const ByteVectorList &l) { for(auto it = l.begin(); it != l.end(); ++it) { if(it != l.begin()) { s << ' '; } s << *it; } return s; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tbytevectorlist.h�������������������������������������������������������0000664�0000000�0000000�00000007501�14662262111�0021327�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_BYTEVECTORLIST_H #define TAGLIB_BYTEVECTORLIST_H #include "tbytevector.h" #include "tlist.h" #include "taglib_export.h" namespace TagLib { //! A list of ByteVectors /*! * A List specialization with some handy features useful for ByteVectors. */ class ByteVectorList : public List<ByteVector> { public: /*! * Construct an empty ByteVectorList. */ TAGLIB_EXPORT ByteVectorList(); /*! * Destroys this ByteVectorList instance. */ TAGLIB_EXPORT ~ByteVectorList(); /*! * Make a shallow, implicitly shared, copy of \a l. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ TAGLIB_EXPORT ByteVectorList(const ByteVectorList &l); /*! * Construct a ByteVectorList with the contents of the braced initializer list. */ TAGLIB_EXPORT ByteVectorList(std::initializer_list<ByteVector> init); TAGLIB_EXPORT ByteVectorList &operator=(const ByteVectorList &); TAGLIB_EXPORT ByteVectorList &operator=(std::initializer_list<ByteVector> init); /*! * Convert the ByteVectorList to a ByteVector separated by \a separator. By * default a space is used. */ TAGLIB_EXPORT ByteVector toByteVector(const ByteVector &separator = " ") const; /*! * Splits the ByteVector \a v into several strings at \a pattern. This will * not include the pattern in the returned ByteVectors. \a max is the * maximum number of entries that will be separated. If \a max for instance * is 2 then a maximum of 1 match will be found and the vector will be split * on that match. */ TAGLIB_EXPORT static ByteVectorList split(const ByteVector &v, const ByteVector &pattern, int byteAlign = 1, int max = 0); private: class ByteVectorListPrivate; std::unique_ptr<ByteVectorListPrivate> d; }; } // namespace TagLib /*! * \related TagLib::ByteVectorList * Send the ByteVectorList to an output stream. */ std::ostream TAGLIB_EXPORT &operator<<(std::ostream &s, const TagLib::ByteVectorList &l); #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tbytevectorstream.cpp���������������������������������������������������0000664�0000000�0000000�00000011057�14662262111�0022203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevectorstream.h" #include <cstring> #include "tstring.h" using namespace TagLib; class ByteVectorStream::ByteVectorStreamPrivate { public: ByteVectorStreamPrivate(const ByteVector &data) : data(data) { } ByteVector data; offset_t position { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// ByteVectorStream::ByteVectorStream(const ByteVector &data) : d(std::make_unique<ByteVectorStreamPrivate>(data)) { } ByteVectorStream::~ByteVectorStream() = default; FileName ByteVectorStream::name() const { return ""; } ByteVector ByteVectorStream::readBlock(size_t length) { if(length == 0) return ByteVector(); ByteVector v = d->data.mid(static_cast<unsigned int>(d->position), static_cast<unsigned int>(length)); d->position += v.size(); return v; } void ByteVectorStream::writeBlock(const ByteVector &data) { unsigned int size = data.size(); if(d->position + size > length()) { truncate(d->position + size); } memcpy(d->data.data() + d->position, data.data(), size); d->position += size; } void ByteVectorStream::insert(const ByteVector &data, offset_t start, size_t replace) { offset_t sizeDiff = data.size() - replace; if(sizeDiff < 0) { removeBlock(start + data.size(), -sizeDiff); } else if(sizeDiff > 0) { truncate(length() + sizeDiff); offset_t readPosition = start + replace; offset_t writePosition = start + data.size(); memmove(d->data.data() + writePosition, d->data.data() + readPosition, length() - sizeDiff - readPosition); } seek(start); writeBlock(data); } void ByteVectorStream::removeBlock(offset_t start, size_t length) { offset_t readPosition = start + length; offset_t writePosition = start; if(readPosition < ByteVectorStream::length()) { offset_t bytesToMove = ByteVectorStream::length() - readPosition; memmove(d->data.data() + writePosition, d->data.data() + readPosition, bytesToMove); writePosition += bytesToMove; } d->position = writePosition; truncate(writePosition); } bool ByteVectorStream::readOnly() const { return false; } bool ByteVectorStream::isOpen() const { return true; } void ByteVectorStream::seek(offset_t offset, Position p) { switch(p) { case Beginning: d->position = offset; break; case Current: d->position += offset; break; case End: d->position = length() + offset; // offset is expected to be negative break; } } void ByteVectorStream::clear() { } offset_t ByteVectorStream::tell() const { return d->position; } offset_t ByteVectorStream::length() { return d->data.size(); } void ByteVectorStream::truncate(offset_t length) { d->data.resize(static_cast<unsigned int>(length)); } ByteVector *ByteVectorStream::data() { return &d->data; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tbytevectorstream.h�����������������������������������������������������0000664�0000000�0000000�00000011014�14662262111�0021641�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_BYTEVECTORSTREAM_H #define TAGLIB_BYTEVECTORSTREAM_H #include "tbytevector.h" #include "tiostream.h" #include "taglib_export.h" #include "taglib.h" namespace TagLib { class String; class Tag; class AudioProperties; //! In-memory Stream class using ByteVector for its storage. class TAGLIB_EXPORT ByteVectorStream : public IOStream { public: /*! * Construct a ByteVectorStream from the bytes in \a data. */ ByteVectorStream(const ByteVector &data); /*! * Destroys this ByteVectorStream instance. */ ~ByteVectorStream() override; ByteVectorStream(const ByteVectorStream &) = delete; ByteVectorStream &operator=(const ByteVectorStream &) = delete; /*! * Returns an empty string. */ FileName name() const override; /*! * Reads a block of size \a length at the current get pointer. */ ByteVector readBlock(size_t length) override; /*! * Writes the block \a data at the current get pointer. * * \note This should be used instead of using the streaming output operator * for a ByteVector. And even this function is significantly slower than * doing output with a char[]. */ void writeBlock(const ByteVector &data) override; /*! * Insert \a data at position \a start in the file overwriting \a replace * bytes of the original content. * * \note This method is slow since it requires rewriting all of the file * after the insertion point. */ void insert(const ByteVector &data, offset_t start = 0, size_t replace = 0) override; /*! * Removes a block of the file starting a \a start and continuing for * \a length bytes. * * \note This method is slow since it involves rewriting all of the file * after the removed portion. */ void removeBlock(offset_t start = 0, size_t length = 0) override; /*! * Returns \c false. */ bool readOnly() const override; /*! * Returns \c true. */ bool isOpen() const override; /*! * Move the I/O pointer to \a offset in the file from position \a p. This * defaults to seeking from the beginning of the file. * * \see Position */ void seek(offset_t offset, Position p = Beginning) override; /*! * Does nothing. */ void clear() override; /*! * Returns the current offset within the file. */ offset_t tell() const override; /*! * Returns the length of the file. */ offset_t length() override; /*! * Truncates the file to a \a length. */ void truncate(offset_t length) override; ByteVector *data(); private: class ByteVectorStreamPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<ByteVectorStreamPrivate> d; }; } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tdebug.cpp��������������������������������������������������������������0000664�0000000�0000000�00000004734�14662262111�0017673�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) #include <bitset> #include "tdebug.h" #include "tstring.h" #include "tdebuglistener.h" #include "tutils.h" namespace TagLib { // The instance is defined in tdebuglistener.cpp. extern DebugListener *debugListener; void debug(const String &s) { debugListener->printMessage("TagLib: " + s + "\n"); } void debugData(const ByteVector &v) { for(unsigned int i = 0; i < v.size(); ++i) { const std::string bits = std::bitset<8>(v[i]).to_string(); const String msg = Utils::formatString( "*** [%u] - char '%c' - int %d, 0x%02x, 0b%s\n", i, v[i], v[i], v[i], bits.c_str()); debugListener->printMessage(msg); } } } // namespace TagLib #endif ������������������������������������taglib-2.0.2/taglib/toolkit/tdebug.h����������������������������������������������������������������0000664�0000000�0000000�00000005322�14662262111�0017332�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DEBUG_H #define TAGLIB_DEBUG_H #ifdef HAVE_CONFIG_H #include "config.h" #endif namespace TagLib { class String; class ByteVector; #ifndef DO_NOT_DOCUMENT #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) /*! * A simple function that outputs the debug messages to the listener. * The default listener redirects the messages to \a stderr when NDEBUG is * not defined. * * \warning Do not use this outside of TagLib, it could lead to undefined * symbols in your build if TagLib is built with NDEBUG defined and your * application is not. * * \internal */ void debug(const String &s); /*! * For debugging binary data. * * \warning Do not use this outside of TagLib, it could lead to undefined * symbols in your build if TagLib is built with NDEBUG defined and your * application is not. * * \internal */ void debugData(const ByteVector &v); #else #define debug(x) ((void)0) #define debugData(x) ((void)0) #endif } // namespace TagLib #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tdebuglistener.cpp������������������������������������������������������0000664�0000000�0000000�00000005444�14662262111�0021440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tdebuglistener.h" #include <iostream> #ifdef _WIN32 # include <windows.h> #endif using namespace TagLib; namespace { class DefaultListener : public DebugListener { public: void printMessage(const String &msg) override { #ifdef _WIN32 const std::wstring wstr = msg.toWString(); const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); if(len != 0) { std::vector<char> buf(len); WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, buf.data(), len, nullptr, nullptr); std::cerr << std::string(buf.begin(), buf.end()); } #else std::cerr << msg; #endif } }; DefaultListener defaultListener; } // namespace namespace TagLib { class DebugListener::DebugListenerPrivate { }; DebugListener *debugListener = &defaultListener; DebugListener::DebugListener() = default; DebugListener::~DebugListener() = default; void setDebugListener(DebugListener *listener) { if(listener) debugListener = listener; else debugListener = &defaultListener; } } // namespace TagLib ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tdebuglistener.h��������������������������������������������������������0000664�0000000�0000000�00000006216�14662262111�0021103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_DEBUGLISTENER_H #define TAGLIB_DEBUGLISTENER_H #include "tstring.h" #include "taglib_export.h" namespace TagLib { //! An abstraction for the listener to the debug messages. /*! * This class enables you to handle the debug messages in your preferred * way by subclassing this class, reimplementing printMessage() and setting * your reimplementation as the default with setDebugListener(). * * \see setDebugListener() */ class TAGLIB_EXPORT DebugListener { public: DebugListener(); virtual ~DebugListener(); DebugListener(const DebugListener &) = delete; DebugListener &operator=(const DebugListener &) = delete; /*! * When overridden in a derived class, redirects \a msg to your preferred * channel such as stderr, Windows debugger or so forth. */ virtual void printMessage(const String &msg) = 0; private: class DebugListenerPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<DebugListenerPrivate> d; }; /*! * Sets the listener that decides how the debug messages are redirected. * If the parameter \a listener is null, the previous listener is released * and the default stderr listener is restored. * * \note The caller is responsible for deleting the previous listener * as needed after it is released. * * \see DebugListener */ TAGLIB_EXPORT void setDebugListener(DebugListener *listener); } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tfile.cpp���������������������������������������������������������������0000664�0000000�0000000�00000022073�14662262111�0017520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tfile.h" #include "tfilestream.h" #include "tpropertymap.h" #include "tstring.h" #ifdef _WIN32 # include <windows.h> # include <io.h> #else # include <unistd.h> #endif #ifndef R_OK # define R_OK 4 #endif #ifndef W_OK # define W_OK 2 #endif using namespace TagLib; class File::FilePrivate { public: FilePrivate(IOStream *stream, bool owner) : stream(stream), streamOwner(owner) { } ~FilePrivate() { if(streamOwner) delete stream; } FilePrivate(const FilePrivate &) = delete; FilePrivate &operator=(const FilePrivate &) = delete; IOStream *stream; bool streamOwner; bool valid { true }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// File::File(FileName fileName) : d(std::make_unique<FilePrivate>(new FileStream(fileName), true)) { } File::File(IOStream *stream) : d(std::make_unique<FilePrivate>(stream, false)) { } File::~File() = default; FileName File::name() const { return d->stream->name(); } PropertyMap File::properties() const { return tag()->properties(); } void File::removeUnsupportedProperties(const StringList &properties) { tag()->removeUnsupportedProperties(properties); } PropertyMap File::setProperties(const PropertyMap &properties) { return tag()->setProperties(properties); } StringList File::complexPropertyKeys() const { return tag()->complexPropertyKeys(); } List<VariantMap> File::complexProperties(const String &key) const { return tag()->complexProperties(key); } bool File::setComplexProperties(const String &key, const List<VariantMap> &value) { return tag()->setComplexProperties(key, value); } ByteVector File::readBlock(size_t length) { return d->stream->readBlock(length); } void File::writeBlock(const ByteVector &data) { d->stream->writeBlock(data); } offset_t File::find(const ByteVector &pattern, offset_t fromOffset, const ByteVector &before) { if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. offset_t bufferOffset = fromOffset; // These variables are used to keep track of a partial match that happens at // the end of a buffer. int previousPartialMatch = -1; int beforePreviousPartialMatch = -1; // Save the location of the current read pointer. We will restore the // position using seek() before all returns. offset_t originalPosition = tell(); // Start the search at the offset. seek(fromOffset); // This loop is the crux of the find method. There are three cases that we // want to account for: // // (1) The previously searched buffer contained a partial match of the search // pattern and we want to see if the next one starts with the remainder of // that pattern. // // (2) The search pattern is wholly contained within the current buffer. // // (3) The current buffer ends with a partial match of the pattern. We will // note this for use in the next 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(auto buffer = readBlock(bufferSize()); !buffer.isEmpty(); buffer = readBlock(bufferSize())) { // (1) previous partial match if(previousPartialMatch >= 0 && static_cast<int>(bufferSize()) > previousPartialMatch) { if(const int patternOffset = bufferSize() - previousPartialMatch; buffer.containsAt(pattern, 0, patternOffset)) { seek(originalPosition); return bufferOffset - bufferSize() + previousPartialMatch; } } if(!before.isEmpty() && beforePreviousPartialMatch >= 0 && static_cast<int>(bufferSize()) > beforePreviousPartialMatch) { if(const int beforeOffset = bufferSize() - beforePreviousPartialMatch; buffer.containsAt(before, 0, beforeOffset)) { seek(originalPosition); return -1; } } // (2) pattern contained in current buffer long location = buffer.find(pattern); if(location >= 0) { seek(originalPosition); return bufferOffset + location; } if(!before.isEmpty() && buffer.find(before) >= 0) { seek(originalPosition); return -1; } // (3) partial match previousPartialMatch = buffer.endsWithPartialMatch(pattern); if(!before.isEmpty()) beforePreviousPartialMatch = buffer.endsWithPartialMatch(before); bufferOffset += bufferSize(); } // Since we hit the end of the file, reset the status before continuing. clear(); seek(originalPosition); return -1; } offset_t File::rfind(const ByteVector &pattern, offset_t fromOffset, const ByteVector &before) { if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. ByteVector buffer; // These variables are used to keep track of a partial match that happens at // the end of a buffer. /* int previousPartialMatch = -1; int beforePreviousPartialMatch = -1; */ // Save the location of the current read pointer. We will restore the // position using seek() before all returns. offset_t originalPosition = tell(); // Start the search at the offset. if(fromOffset == 0) fromOffset = length(); offset_t bufferLength = bufferSize(); offset_t bufferOffset = fromOffset + pattern.size(); // See the notes in find() for an explanation of this algorithm. while(true) { if(bufferOffset > bufferLength) { bufferOffset -= bufferLength; } else { bufferLength = bufferOffset; bufferOffset = 0; } seek(bufferOffset); buffer = readBlock(bufferLength); if(buffer.isEmpty()) break; // TODO: (1) previous partial match // (2) pattern contained in current buffer if(const long location = buffer.rfind(pattern); location >= 0) { seek(originalPosition); return bufferOffset + location; } if(!before.isEmpty() && buffer.find(before) >= 0) { seek(originalPosition); return -1; } // TODO: (3) partial match } // Since we hit the end of the file, reset the status before continuing. clear(); seek(originalPosition); return -1; } void File::insert(const ByteVector &data, offset_t start, size_t replace) { d->stream->insert(data, start, replace); } void File::removeBlock(offset_t start, size_t length) { d->stream->removeBlock(start, length); } bool File::readOnly() const { return d->stream->readOnly(); } bool File::isOpen() const { return d->stream->isOpen(); } bool File::isValid() const { return isOpen() && d->valid; } void File::seek(offset_t offset, Position p) { d->stream->seek(offset, static_cast<IOStream::Position>(p)); } void File::truncate(offset_t length) { d->stream->truncate(length); } void File::clear() { d->stream->clear(); } offset_t File::tell() const { return d->stream->tell(); } offset_t File::length() { return d->stream->length(); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// unsigned int File::bufferSize() { return 1024; } void File::setValid(bool valid) { d->valid = valid; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tfile.h�����������������������������������������������������������������0000664�0000000�0000000�00000027341�14662262111�0017170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FILE_H #define TAGLIB_FILE_H #include "tbytevector.h" #include "tiostream.h" #include "taglib_export.h" #include "taglib.h" #include "tag.h" namespace TagLib { class String; class Tag; class AudioProperties; class PropertyMap; //! A file class with some useful methods for tag manipulation /*! * This class is a basic file class with some methods that are particularly * useful for tag editors. It has methods to take advantage of * ByteVector and a binary search method for finding patterns in a file. */ class TAGLIB_EXPORT File { public: /*! * Position in the file used for seeking. */ enum Position { //! Seek from the beginning of the file. Beginning, //! Seek from the current position in the file. Current, //! Seek from the end of the file. End }; /*! * Specify which tags to strip either explicitly, or on save. */ enum StripTags { StripNone, //!< Don't strip any tags StripOthers //!< Strip all tags not explicitly referenced in method call }; /*! * Used to specify if when saving files, if values between different tag * types should be synchronized. */ enum DuplicateTags { Duplicate, //!< Synchronize values between different tag types DoNotDuplicate //!< Do not synchronize values between different tag types }; /*! * Destroys this File instance. */ virtual ~File(); File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the file name in the local file system encoding. */ FileName name() const; /*! * Returns a pointer to this file's tag. This should be reimplemented in * the concrete subclasses. */ virtual Tag *tag() const = 0; /*! * Exports the tags of the file as dictionary mapping (human readable) tag * names (uppercase Strings) to StringLists of tag values. Calls the according * specialization in the File subclasses. * For each metadata object of the file that could not be parsed into the PropertyMap * format, the returned map's unsupportedData() list will contain one entry identifying * that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties() * to remove (a subset of) them. * For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2 * tag) only the most "modern" one will be exported (ID3v2 in this case). */ virtual PropertyMap properties() const; /*! * Removes unsupported properties, or a subset of them, from the file's metadata. * The parameter \a properties must contain only entries from * properties().unsupportedData(). */ virtual void removeUnsupportedProperties(const StringList& properties); /*! * Sets the tags of this File to those specified in \a properties. Calls the * according specialization method in the subclasses of File to do the translation * into the format-specific details. * If some value(s) could not be written to the specific metadata format, * the returned PropertyMap will contain those value(s). Otherwise it will be empty, * indicating that no problems occurred. * With file types that support several tag formats (for instance, MP3 files can have * ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one * (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't * be taken into account for the return value of this function. * See the documentation of the subclass implementations for detailed descriptions. */ virtual PropertyMap setProperties(const PropertyMap &properties); /*! * Get the keys of complex properties, i.e. properties which cannot be * represented simply by a string. * The default implementation calls Tag::complexPropertyKeys(). * \see Tag::complexPropertyKeys() */ virtual StringList complexPropertyKeys() const; /*! * Get the complex properties for a given \a key. * The default implementation calls Tag::complexProperties(). * \see Tag::complexProperties() */ virtual List<VariantMap> complexProperties(const String &key) const; /*! * Set all complex properties for \a key using the variant maps \a value. * The default implementation calls Tag::setComplexProperties(). * \see Tag::setComplexProperties() */ virtual bool setComplexProperties(const String &key, const List<VariantMap> &value); /*! * Returns a pointer to this file's audio properties. This should be * reimplemented in the concrete subclasses. If no audio properties were * read then this will return a null pointer. */ virtual AudioProperties *audioProperties() const = 0; /*! * Save the file and its associated tags. This should be reimplemented in * the concrete subclasses. Returns \c true if the save succeeds. * * \warning On UNIX multiple processes are able to write to the same file at * the same time. This can result in serious file corruption. If you are * developing a program that makes use of TagLib from multiple processes you * must insure that you are only doing writes to a particular file from one * of them. */ virtual bool save() = 0; /*! * Reads a block of size \a length at the current get pointer. */ ByteVector readBlock(size_t length); /*! * Attempts to write the block \a data at the current get pointer. If the * file is currently only opened read only -- i.e. readOnly() returns \c true -- * this attempts to reopen the file in read/write mode. * * \note This should be used instead of using the streaming output operator * for a ByteVector. And even this function is significantly slower than * doing output with a char[]. */ void writeBlock(const ByteVector &data); /*! * Returns the offset in the file that \a pattern occurs at or -1 if it can * not be found. If \a before is set, the search will only continue until the * pattern \a before is found. This is useful for tagging purposes to search * for a tag before the sync frame. * * Searching starts at \a fromOffset, which defaults to the beginning of the * file. * * \note This has the practical limitation that \a pattern can not be longer * than the buffer size used by readBlock(). Currently this is 1024 bytes. */ offset_t find(const ByteVector &pattern, offset_t fromOffset = 0, const ByteVector &before = ByteVector()); /*! * Returns the offset in the file that \a pattern occurs at or -1 if it can * not be found. If \a before is set, the search will only continue until the * pattern \a before is found. This is useful for tagging purposes to search * for a tag before the sync frame. * * Searching starts at \a fromOffset and proceeds from the that point to the * beginning of the file and defaults to the end of the file. * * \note This has the practical limitation that \a pattern can not be longer * than the buffer size used by readBlock(). Currently this is 1024 bytes. */ offset_t rfind(const ByteVector &pattern, offset_t fromOffset = 0, const ByteVector &before = ByteVector()); /*! * Insert \a data at position \a start in the file overwriting \a replace * bytes of the original content. * * \note This method is slow since it requires rewriting all of the file * after the insertion point. */ void insert(const ByteVector &data, offset_t start = 0, size_t replace = 0); /*! * Removes a block of the file starting a \a start and continuing for * \a length bytes. * * \note This method is slow since it involves rewriting all of the file * after the removed portion. */ void removeBlock(offset_t start = 0, size_t length = 0); /*! * Returns \c true if the file is read only (or if the file can not be opened). */ bool readOnly() const; /*! * Since the file can currently only be opened as an argument to the * constructor (sort-of by design), this returns if that open succeeded. */ bool isOpen() const; /*! * Returns \c true if the file is open and readable. */ bool isValid() const; /*! * Move the I/O pointer to \a offset in the file from position \a p. This * defaults to seeking from the beginning of the file. * * \see Position */ void seek(offset_t offset, Position p = Beginning); /*! * Reset the end-of-file and error flags on the file. */ void clear(); /*! * Returns the current offset within the file. */ offset_t tell() const; /*! * Returns the length of the file. */ offset_t length(); protected: /*! * Construct a File object and open the \a fileName. \a fileName should be a * C-string in the local file system encoding. * * \note Constructor is protected since this class should only be * instantiated through subclasses. */ File(FileName fileName); /*! * Construct a File object and use the \a stream instance. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * \note Constructor is protected since this class should only be * instantiated through subclasses. */ File(IOStream *stream); /*! * Marks the file as valid or invalid. * * \see isValid() */ void setValid(bool valid); /*! * Truncates the file to a \a length. */ void truncate(offset_t length); /*! * Returns the buffer size that is used for internal buffering. */ static unsigned int bufferSize(); private: class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace TagLib #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tfilestream.cpp���������������������������������������������������������0000664�0000000�0000000�00000030567�14662262111�0020743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tfilestream.h" #ifdef _WIN32 # include <windows.h> #else # include <climits> # include <cstdio> # include <unistd.h> #endif #include "tstring.h" #include "tdebug.h" using namespace TagLib; namespace { #ifdef _WIN32 // Uses Win32 native API instead of POSIX API to reduce the resource consumption. using FileNameHandle = FileName; using FileHandle = HANDLE; const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE; FileHandle openFile(const FileName &path, bool readOnly) { const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); #if defined (PLATFORM_WINRT) return CreateFile2(path.wstr().c_str(), access, FILE_SHARE_READ, OPEN_EXISTING, nullptr); #else constexpr wchar_t LongLocalPathPrefix[] = L"\\\\?\\"; constexpr wchar_t UNCPathPrefix[] = L"\\\\"; constexpr wchar_t LongUNCPathPrefix[] = L"\\\\?\\UNC\\"; std::wstring pathWStr = path.wstr(); if(pathWStr.length() > MAX_PATH && pathWStr.compare(0, std::size(LongLocalPathPrefix) - 1, LongLocalPathPrefix) != 0 && pathWStr.compare(0, std::size(LongUNCPathPrefix) - 1, LongUNCPathPrefix) != 0) { if(pathWStr.compare(0, std::size(UNCPathPrefix) - 1, UNCPathPrefix) == 0) { pathWStr = LongUNCPathPrefix + pathWStr.substr(2); } else { pathWStr = LongLocalPathPrefix + pathWStr; } } return CreateFileW(pathWStr.c_str(), access, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); #endif } FileHandle openFile([[maybe_unused]] const int fileDescriptor, [[maybe_unused]] bool readOnly) { return InvalidFileHandle; } void closeFile(FileHandle file) { CloseHandle(file); } size_t readFile(FileHandle file, ByteVector &buffer) { DWORD length; if(ReadFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, nullptr)) return static_cast<size_t>(length); return 0; } size_t writeFile(FileHandle file, const ByteVector &buffer) { DWORD length; if(WriteFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, nullptr)) return static_cast<size_t>(length); return 0; } #else // _WIN32 struct FileNameHandle : public std::string { FileNameHandle(FileName name) : std::string(name) {} operator FileName () const { return c_str(); } }; using FileHandle = FILE *; const FileHandle InvalidFileHandle = nullptr; FileHandle openFile(const FileName &path, bool readOnly) { return fopen(path, readOnly ? "rb" : "rb+"); } FileHandle openFile(const int fileDescriptor, bool readOnly) { return fdopen(fileDescriptor, readOnly ? "rb" : "rb+"); } void closeFile(FileHandle file) { fclose(file); } size_t readFile(FileHandle file, ByteVector &buffer) { return fread(buffer.data(), sizeof(char), buffer.size(), file); } size_t writeFile(FileHandle file, const ByteVector &buffer) { return fwrite(buffer.data(), sizeof(char), buffer.size(), file); } #endif // _WIN32 } // namespace class FileStream::FileStreamPrivate { public: FileStreamPrivate(const FileName &fileName) : name(fileName) { } FileHandle file { InvalidFileHandle }; FileNameHandle name; bool readOnly { true }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FileStream::FileStream(FileName fileName, bool openReadOnly) : d(std::make_unique<FileStreamPrivate>(fileName)) { // First try with read / write mode, if that fails, fall back to read only. if(!openReadOnly) d->file = openFile(fileName, false); if(d->file != InvalidFileHandle) d->readOnly = false; else d->file = openFile(fileName, true); if(d->file == InvalidFileHandle) # ifdef _WIN32 debug("Could not open file " + fileName.toString()); # else debug("Could not open file " + String(static_cast<const char *>(d->name))); # endif } FileStream::FileStream(int fileDescriptor, bool openReadOnly) : d(std::make_unique<FileStreamPrivate>("")) { // First try with read / write mode, if that fails, fall back to read only. if(!openReadOnly) d->file = openFile(fileDescriptor, false); if(d->file != InvalidFileHandle) d->readOnly = false; else d->file = openFile(fileDescriptor, true); if(d->file == InvalidFileHandle) debug("Could not open file using file descriptor"); } FileStream::~FileStream() { if(isOpen()) closeFile(d->file); } FileName FileStream::name() const { return d->name; } ByteVector FileStream::readBlock(size_t length) { if(!isOpen()) { debug("FileStream::readBlock() -- invalid file."); return ByteVector(); } if(length == 0) return ByteVector(); if(length > bufferSize()) { if(const auto streamLength = static_cast<size_t>(FileStream::length()); length > streamLength) { length = streamLength; } } ByteVector buffer(static_cast<unsigned int>(length)); const size_t count = readFile(d->file, buffer); buffer.resize(static_cast<unsigned int>(count)); return buffer; } void FileStream::writeBlock(const ByteVector &data) { if(!isOpen()) { debug("FileStream::writeBlock() -- invalid file."); return; } if(readOnly()) { debug("FileStream::writeBlock() -- read only file."); return; } writeFile(d->file, data); } void FileStream::insert(const ByteVector &data, offset_t start, size_t replace) { if(!isOpen()) { debug("FileStream::insert() -- invalid file."); return; } if(readOnly()) { debug("FileStream::insert() -- read only file."); return; } if(data.size() == replace) { seek(start); writeBlock(data); return; } if(data.size() < replace) { seek(start); writeBlock(data); removeBlock(start + data.size(), replace - data.size()); return; } // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore // and avoid TagLib's high level API for rendering just copying parts of // the file that don't contain tag data. // // Now I'll explain the steps in this ugliness: // First, make sure that we're working with a buffer that is longer than // the *difference* in the tag sizes. We want to avoid overwriting parts // that aren't yet in memory, so this is necessary. size_t bufferLength = bufferSize(); while(data.size() - replace > bufferLength) bufferLength += bufferSize(); // Set where to start the reading and writing. offset_t readPosition = start + replace; offset_t writePosition = start; ByteVector buffer = data; ByteVector aboutToOverwrite(static_cast<unsigned int>(bufferLength)); while(true) { // Seek to the current read position and read the data that we're about // to overwrite. Appropriately increment the readPosition. seek(readPosition); const auto bytesRead = static_cast<unsigned int>(readFile(d->file, aboutToOverwrite)); aboutToOverwrite.resize(bytesRead); readPosition += bufferLength; // Check to see if we just read the last block. We need to call clear() // if we did so that the last write succeeds. if(bytesRead < bufferLength) clear(); // Seek to the write position and write our buffer. Increment the // writePosition. seek(writePosition); writeBlock(buffer); // We hit the end of the file. if(bytesRead == 0) break; writePosition += buffer.size(); // Make the current buffer the data that we read in the beginning. buffer = aboutToOverwrite; } } void FileStream::removeBlock(offset_t start, size_t length) { if(!isOpen()) { debug("FileStream::removeBlock() -- invalid file."); return; } unsigned int bufferLength = bufferSize(); offset_t readPosition = start + length; offset_t writePosition = start; ByteVector buffer(bufferLength); unsigned int bytesRead = UINT_MAX; while(bytesRead != 0) { seek(readPosition); bytesRead = static_cast<unsigned int>(readFile(d->file, buffer)); readPosition += bytesRead; // Check to see if we just read the last block. We need to call clear() // if we did so that the last write succeeds. if(bytesRead < buffer.size()) { clear(); buffer.resize(bytesRead); } seek(writePosition); writeFile(d->file, buffer); writePosition += bytesRead; } truncate(writePosition); } bool FileStream::readOnly() const { return d->readOnly; } bool FileStream::isOpen() const { return d->file != InvalidFileHandle; } void FileStream::seek(offset_t offset, Position p) { if(!isOpen()) { debug("FileStream::seek() -- invalid file."); return; } #ifdef _WIN32 if(p != Beginning && p != Current && p != End) { debug("FileStream::seek() -- Invalid Position value."); return; } LARGE_INTEGER liOffset; liOffset.QuadPart = offset; if(!SetFilePointerEx(d->file, liOffset, nullptr, static_cast<DWORD>(p))) { debug("FileStream::seek() -- Failed to set the file pointer."); } #else int whence; switch(p) { case Beginning: whence = SEEK_SET; break; case Current: whence = SEEK_CUR; break; case End: whence = SEEK_END; break; default: debug("FileStream::seek() -- Invalid Position value."); return; } fseek(d->file, offset, whence); #endif } void FileStream::clear() { #ifdef _WIN32 // NOP #else clearerr(d->file); #endif } offset_t FileStream::tell() const { #ifdef _WIN32 const LARGE_INTEGER zero = {}; LARGE_INTEGER position; if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT)) { return position.QuadPart; } debug("FileStream::tell() -- Failed to get the file pointer."); return 0; #else return ftell(d->file); #endif } offset_t FileStream::length() { if(!isOpen()) { debug("FileStream::length() -- invalid file."); return 0; } #ifdef _WIN32 LARGE_INTEGER fileSize; if(GetFileSizeEx(d->file, &fileSize)) { return fileSize.QuadPart; } debug("FileStream::length() -- Failed to get the file size."); return 0; #else const offset_t curpos = tell(); seek(0, End); const offset_t endpos = tell(); seek(curpos, Beginning); return endpos; #endif } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void FileStream::truncate(offset_t length) { #ifdef _WIN32 const offset_t currentPos = tell(); seek(length); if(!SetEndOfFile(d->file)) { debug("FileStream::truncate() -- Failed to truncate the file."); } seek(currentPos); #else fflush(d->file); if(const int error = ftruncate(fileno(d->file), length); error != 0) debug("FileStream::truncate() -- Couldn't truncate the file."); #endif } unsigned int FileStream::bufferSize() { return 1024; } �����������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tfilestream.h�����������������������������������������������������������0000664�0000000�0000000�00000012276�14662262111�0020405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_FILESTREAM_H #define TAGLIB_FILESTREAM_H #include "tbytevector.h" #include "tiostream.h" #include "taglib_export.h" #include "taglib.h" namespace TagLib { class String; class Tag; class AudioProperties; //! I/O stream with data from a file. class TAGLIB_EXPORT FileStream : public IOStream { public: /*! * Construct a FileStream object and open the \a fileName. \a fileName should be a * C-string in the local file system encoding. */ FileStream(FileName fileName, bool openReadOnly = false); /*! * Construct a FileStream object using an existing \a fileDescriptor. */ FileStream(int fileDescriptor, bool openReadOnly = false); /*! * Destroys this FileStream instance. */ ~FileStream() override; FileStream(const FileStream &) = delete; FileStream &operator=(const FileStream &) = delete; /*! * Returns the file name in the local file system encoding. */ FileName name() const override; /*! * Reads a block of size \a length at the current get pointer. */ ByteVector readBlock(size_t length) override; /*! * Attempts to write the block \a data at the current get pointer. If the * file is currently only opened read only -- i.e. readOnly() returns \c true -- * this attempts to reopen the file in read/write mode. * * \note This should be used instead of using the streaming output operator * for a ByteVector. And even this function is significantly slower than * doing output with a char[]. */ void writeBlock(const ByteVector &data) override; /*! * Insert \a data at position \a start in the file overwriting \a replace * bytes of the original content. * * \note This method is slow since it requires rewriting all of the file * after the insertion point. */ void insert(const ByteVector &data, offset_t start = 0, size_t replace = 0) override; /*! * Removes a block of the file starting a \a start and continuing for * \a length bytes. * * \note This method is slow since it involves rewriting all of the file * after the removed portion. */ void removeBlock(offset_t start = 0, size_t length = 0) override; /*! * Returns \c true if the file is read only (or if the file can not be opened). */ bool readOnly() const override; /*! * Since the file can currently only be opened as an argument to the * constructor (sort-of by design), this returns if that open succeeded. */ bool isOpen() const override; /*! * Move the I/O pointer to \a offset in the file from position \a p. This * defaults to seeking from the beginning of the file. * * \see Position */ void seek(offset_t offset, Position p = Beginning) override; /*! * Reset the end-of-file and error flags on the file. */ void clear() override; /*! * Returns the current offset within the file. */ offset_t tell() const override; /*! * Returns the length of the file. */ offset_t length() override; /*! * Truncates the file to a \a length. */ void truncate(offset_t length) override; protected: /*! * Returns the buffer size that is used for internal buffering. */ static unsigned int bufferSize(); private: class FileStreamPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FileStreamPrivate> d; }; } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tiostream.cpp�����������������������������������������������������������0000664�0000000�0000000�00000005531�14662262111�0020424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tiostream.h" #ifdef _WIN32 # include <windows.h> # include "tstring.h" #endif using namespace TagLib; #ifdef _WIN32 namespace { std::wstring ansiToUnicode(const char *str) { const int len = MultiByteToWideChar(CP_ACP, 0, str, -1, nullptr, 0); if(len == 0) return std::wstring(); std::wstring wstr(len - 1, L'\0'); MultiByteToWideChar(CP_ACP, 0, str, -1, wstr.data(), len); return wstr; } } // namespace FileName::FileName(const wchar_t *name) : m_wname(name) { } FileName::FileName(const char *name) : m_wname(ansiToUnicode(name)) { } FileName::FileName(const FileName &) = default; FileName::operator const wchar_t *() const { return m_wname.c_str(); } const std::wstring &FileName::wstr() const { return m_wname; } String FileName::toString() const { return String(m_wname.c_str()); } #endif // _WIN32 class IOStream::IOStreamPrivate { }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// IOStream::IOStream() = default; IOStream::~IOStream() = default; void IOStream::clear() { } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tiostream.h�������������������������������������������������������������0000664�0000000�0000000�00000012646�14662262111�0020076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Lukas Lalinsky email : lalinsky@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_IOSTREAM_H #define TAGLIB_IOSTREAM_H #include "tbytevector.h" #include "taglib_export.h" #include "taglib.h" #ifdef _WIN32 #include <string> #endif namespace TagLib { #ifdef _WIN32 class TAGLIB_EXPORT FileName { public: FileName(const wchar_t *name); FileName(const char *name); FileName(const FileName &name); operator const wchar_t *() const; const std::wstring &wstr() const; String toString() const; private: TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE const std::wstring m_wname; }; #else using FileName = const char *; #endif //! An abstract class that provides operations on a sequence of bytes class TAGLIB_EXPORT IOStream { public: /*! * Position in the file used for seeking. */ enum Position { //! Seek from the beginning of the file. Beginning, //! Seek from the current position in the file. Current, //! Seek from the end of the file. End }; IOStream(); /*! * Destroys this IOStream instance. */ virtual ~IOStream(); IOStream(const IOStream &) = delete; IOStream &operator=(const IOStream &) = delete; /*! * Returns the stream name in the local file system encoding. */ virtual FileName name() const = 0; /*! * Reads a block of size \a length at the current get pointer. */ virtual ByteVector readBlock(size_t length) = 0; /*! * Attempts to write the block \a data at the current get pointer. If the * file is currently only opened read only -- i.e. readOnly() returns \c true -- * this attempts to reopen the file in read/write mode. * * \note This should be used instead of using the streaming output operator * for a ByteVector. And even this function is significantly slower than * doing output with a char[]. */ virtual void writeBlock(const ByteVector &data) = 0; /*! * Insert \a data at position \a start in the file overwriting \a replace * bytes of the original content. * * \note This method is slow since it requires rewriting all of the file * after the insertion point. */ virtual void insert(const ByteVector &data, offset_t start = 0, size_t replace = 0) = 0; /*! * Removes a block of the file starting a \a start and continuing for * \a length bytes. * * \note This method is slow since it involves rewriting all of the file * after the removed portion. */ virtual void removeBlock(offset_t start = 0, size_t length = 0) = 0; /*! * Returns \c true if the file is read only (or if the file can not be opened). */ virtual bool readOnly() const = 0; /*! * Since the file can currently only be opened as an argument to the * constructor (sort-of by design), this returns if that open succeeded. */ virtual bool isOpen() const = 0; /*! * Move the I/O pointer to \a offset in the stream from position \a p. This * defaults to seeking from the beginning of the stream. * * \see Position */ virtual void seek(offset_t offset, Position p = Beginning) = 0; /*! * Reset the end-of-stream and error flags on the stream. */ virtual void clear(); /*! * Returns the current offset within the stream. */ virtual offset_t tell() const = 0; /*! * Returns the length of the stream. */ virtual offset_t length() = 0; /*! * Truncates the stream to a \a length. */ virtual void truncate(offset_t length) = 0; private: class IOStreamPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<IOStreamPrivate> d; }; } // namespace TagLib #endif ������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tlist.h�����������������������������������������������������������������0000664�0000000�0000000�00000022737�14662262111�0017230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_LIST_H #define TAGLIB_LIST_H #include <list> #include <initializer_list> #include <memory> namespace TagLib { //! A generic, implicitly shared list. /*! * This is a basic generic list that's somewhere between a std::list and a * QList. This class is implicitly shared. For example: * * \code * * TagLib::List<int> l = someOtherIntList; * * \endcode * * The above example is very cheap. This also makes lists suitable as * return types of functions. The above example will just copy a pointer rather * than copying the data in the list. When your \e shared list's data changes, * only \e then will the data be copied. */ template <class T> class List { public: #ifndef DO_NOT_DOCUMENT using Iterator = typename std::list<T>::iterator; using ConstIterator = typename std::list<T>::const_iterator; #endif /*! * Constructs an empty list. */ List(); /*! * Make a shallow, implicitly shared, copy of \a l. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ List(const List<T> &l); /*! * Construct a List with the contents of the braced initializer list. */ List(std::initializer_list<T> init); /*! * Destroys this List instance. If auto deletion is enabled and this list * contains a pointer type, all of the members are also deleted. */ ~List(); /*! * Returns an STL style iterator to the beginning of the list. See * \c std::list::const_iterator for the semantics. */ Iterator begin(); /*! * Returns an STL style constant iterator to the beginning of the list. See * \c std::list::iterator for the semantics. */ ConstIterator begin() const; /*! * Returns an STL style constant iterator to the beginning of the list. See * \c std::list::iterator for the semantics. */ ConstIterator cbegin() const; /*! * Returns an STL style iterator to the end of the list. See * \c std::list::iterator for the semantics. */ Iterator end(); /*! * Returns an STL style constant iterator to the end of the list. See * \c std::list::const_iterator for the semantics. */ ConstIterator end() const; /*! * Returns an STL style constant iterator to the end of the list. See * \c std::list::const_iterator for the semantics. */ ConstIterator cend() const; /*! * Inserts a copy of \a item before \a it. * * \note This method cannot detach because \a it is tied to the internal * list. Do not make an implicitly shared copy of this list between * getting the iterator and calling this method! */ Iterator insert(Iterator it, const T &item); /*! * Inserts the \a value into the list. This assumes that the list is * currently sorted. If \a unique is \c true then the value will not * be inserted if it is already in the list. */ List<T> &sortedInsert(const T &value, bool unique = false); /*! * Appends \a item to the end of the list and returns a reference to the * list. */ List<T> &append(const T &item); /*! * Appends all of the values in \a l to the end of the list and returns a * reference to the list. */ List<T> &append(const List<T> &l); /*! * Prepends \a item to the beginning list and returns a reference to the * list. */ List<T> &prepend(const T &item); /*! * Prepends all of the items in \a l to the beginning list and returns a * reference to the list. */ List<T> &prepend(const List<T> &l); /*! * Clears the list. If auto deletion is enabled and this list contains a * pointer type the members are also deleted. * * \see setAutoDelete() */ List<T> &clear(); /*! * Returns the number of elements in the list. * * \see isEmpty() */ unsigned int size() const; /*! * Returns whether or not the list is empty. * * \see size() */ bool isEmpty() const; /*! * Find the first occurrence of \a value. */ Iterator find(const T &value); /*! * Find the first occurrence of \a value. */ ConstIterator find(const T &value) const; /*! * Find the first occurrence of \a value. */ ConstIterator cfind(const T &value) const; /*! * Returns \c true if the list contains \a value. */ bool contains(const T &value) const; /*! * Erase the item at \a it from the list. * * \note This method cannot detach because \a it is tied to the internal * list. Do not make an implicitly shared copy of this list between * getting the iterator and calling this method! */ Iterator erase(Iterator it); /*! * Returns a reference to the first item in the list. */ const T &front() const; /*! * Returns a reference to the first item in the list. */ T &front(); /*! * Returns a reference to the last item in the list. */ const T &back() const; /*! * Returns a reference to the last item in the list. */ T &back(); /*! * Auto delete the members of the list when the last reference to the list * passes out of scope. This will have no effect on lists which do not * contain a pointer type. * * \note This relies on partial template instantiation -- most modern C++ * compilers should now support this. */ void setAutoDelete(bool autoDelete); /*! * Returns \c true if auto-deletion is enabled. */ bool autoDelete() const; /*! * Returns a reference to item \a i in the list. * * \warning This method is slow. Use iterators to loop through the list. */ T &operator[](unsigned int i); /*! * Returns a const reference to item \a i in the list. * * \warning This method is slow. Use iterators to loop through the list. */ const T &operator[](unsigned int i) const; /*! * Make a shallow, implicitly shared, copy of \a l. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ List<T> &operator=(const List<T> &l); /*! * Replace the contents of the list with those of the braced initializer list. * * If auto deletion is enabled and the list contains a pointer type, the members are also deleted */ List<T> &operator=(std::initializer_list<T> init); /*! * Exchanges the content of this list with the content of \a l. */ void swap(List<T> &l) noexcept; /*! * Compares this list with \a l and returns \c true if all of the elements are * the same. */ bool operator==(const List<T> &l) const; /*! * Compares this list with \a l and returns \c true if the lists differ. */ bool operator!=(const List<T> &l) const; /*! * Sorts this list in ascending order using operator< of T. */ void sort(); /*! * Sorts this list in ascending order using the comparison * function object \a comp which returns \c true if the first argument is * less than the second. */ template<class Compare> void sort(Compare&& comp); protected: /*! * If this List is being shared via implicit sharing, do a deep copy of the * data and separate from the shared members. This should be called by all * non-const subclass members without Iterator parameters. */ void detach(); private: #ifndef DO_NOT_DOCUMENT template <class TP> class ListPrivate; std::shared_ptr<ListPrivate<T>> d; #endif }; } // namespace TagLib // Since GCC doesn't support the "export" keyword, we have to include the // implementation. #include "tlist.tcc" #endif ���������������������������������taglib-2.0.2/taglib/toolkit/tlist.tcc���������������������������������������������������������������0000664�0000000�0000000�00000020203�14662262111�0017534�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <algorithm> #include <memory> namespace TagLib { //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// // The functionality of List<T>::setAutoDelete() is implemented here partial // template specialization. This is implemented in such a way that calling // setAutoDelete() on non-pointer types will simply have no effect. // A base for the generic and specialized private class types. New // non-templatized members should be added here. class ListPrivateBase { public: bool autoDelete{}; }; // A generic implementation template <class T> template <class TP> class List<T>::ListPrivate : public ListPrivateBase { public: using ListPrivateBase::ListPrivateBase; ListPrivate(const std::list<TP> &l) : list(l) {} ListPrivate(std::initializer_list<TP> init) : list(init) {} void clear() { list.clear(); } std::list<TP> list; }; // A partial specialization for all pointer types that implements the // setAutoDelete() functionality. template <class T> template <class TP> class List<T>::ListPrivate<TP *> : public ListPrivateBase { public: using ListPrivateBase::ListPrivateBase; ListPrivate(const std::list<TP *> &l) : list(l) {} ListPrivate(std::initializer_list<TP *> init) : list(init) {} ~ListPrivate() { clear(); } ListPrivate(const ListPrivate &) = delete; ListPrivate &operator=(const ListPrivate &) = delete; void clear() { if(autoDelete) { for(auto &m : list) delete m; } list.clear(); } std::list<TP *> list; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// template <class T> List<T>::List() : d(std::make_shared<ListPrivate<T>>()) { } template <class T> List<T>::List(const List<T> &) = default; template <class T> List<T>::List(std::initializer_list<T> init) : d(std::make_shared<ListPrivate<T>>(init)) { } template <class T> List<T>::~List() = default; template <class T> typename List<T>::Iterator List<T>::begin() { detach(); return d->list.begin(); } template <class T> typename List<T>::ConstIterator List<T>::begin() const { return d->list.begin(); } template <class T> typename List<T>::ConstIterator List<T>::cbegin() const { return d->list.cbegin(); } template <class T> typename List<T>::Iterator List<T>::end() { detach(); return d->list.end(); } template <class T> typename List<T>::ConstIterator List<T>::end() const { return d->list.end(); } template <class T> typename List<T>::ConstIterator List<T>::cend() const { return d->list.cend(); } template <class T> typename List<T>::Iterator List<T>::insert(Iterator it, const T &item) { return d->list.insert(it, item); } template <class T> List<T> &List<T>::sortedInsert(const T &value, bool unique) { detach(); Iterator it = begin(); while(it != end() && *it < value) ++it; if(unique && it != end() && *it == value) return *this; insert(it, value); return *this; } template <class T> List<T> &List<T>::append(const T &item) { detach(); d->list.push_back(item); return *this; } template <class T> List<T> &List<T>::append(const List<T> &l) { detach(); d->list.insert(d->list.end(), l.begin(), l.end()); return *this; } template <class T> List<T> &List<T>::prepend(const T &item) { detach(); d->list.push_front(item); return *this; } template <class T> List<T> &List<T>::prepend(const List<T> &l) { detach(); d->list.insert(d->list.begin(), l.begin(), l.end()); return *this; } template <class T> List<T> &List<T>::clear() { detach(); d->clear(); return *this; } template <class T> unsigned int List<T>::size() const { return static_cast<unsigned int>(d->list.size()); } template <class T> bool List<T>::isEmpty() const { return d->list.empty(); } template <class T> typename List<T>::Iterator List<T>::find(const T &value) { detach(); return std::find(d->list.begin(), d->list.end(), value); } template <class T> typename List<T>::ConstIterator List<T>::find(const T &value) const { return std::find(d->list.begin(), d->list.end(), value); } template <class T> typename List<T>::ConstIterator List<T>::cfind(const T &value) const { return std::find(d->list.cbegin(), d->list.cend(), value); } template <class T> bool List<T>::contains(const T &value) const { return std::find(d->list.begin(), d->list.end(), value) != d->list.end(); } template <class T> typename List<T>::Iterator List<T>::erase(Iterator it) { return d->list.erase(it); } template <class T> const T &List<T>::front() const { return d->list.front(); } template <class T> T &List<T>::front() { detach(); return d->list.front(); } template <class T> const T &List<T>::back() const { return d->list.back(); } template <class T> void List<T>::setAutoDelete(bool autoDelete) { detach(); d->autoDelete = autoDelete; } template <class T> bool List<T>::autoDelete() const { return d->autoDelete; } template <class T> T &List<T>::back() { detach(); return d->list.back(); } template <class T> T &List<T>::operator[](unsigned int i) { auto it = d->list.begin(); std::advance(it, i); return *it; } template <class T> const T &List<T>::operator[](unsigned int i) const { auto it = d->list.begin(); std::advance(it, i); return *it; } template <class T> List<T> &List<T>::operator=(const List<T> &) = default; template <class T> List<T> &List<T>::operator=(std::initializer_list<T> init) { bool autoDeleteEnabled = d->autoDelete; List(init).swap(*this); setAutoDelete(autoDeleteEnabled); return *this; } template <class T> void List<T>::swap(List<T> &l) noexcept { using std::swap; swap(d, l.d); } template <class T> bool List<T>::operator==(const List<T> &l) const { return d->list == l.d->list; } template <class T> bool List<T>::operator!=(const List<T> &l) const { return d->list != l.d->list; } template <class T> void List<T>::sort() { detach(); d->list.sort(); } template <class T> template <class Compare> void List<T>::sort(Compare&& comp) { detach(); d->list.sort(std::forward<Compare>(comp)); } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// template <class T> void List<T>::detach() { if(d.use_count() > 1) { d = std::make_shared<ListPrivate<T>>(d->list); } } } // namespace TagLib ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tmap.h������������������������������������������������������������������0000664�0000000�0000000�00000017452�14662262111�0017030�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_MAP_H #define TAGLIB_MAP_H #include <map> #include <memory> #include <initializer_list> #include <utility> namespace TagLib { //! A generic, implicitly shared map. /*! * This implements a standard map container that associates a key with a value * and has fast key-based lookups. This map is also implicitly shared making * it suitable for pass-by-value usage. */ template <class Key, class T> class Map { public: #ifndef DO_NOT_DOCUMENT #ifdef WANT_CLASS_INSTANTIATION_OF_MAP // Some STL implementations get snippy over the use of the // class keyword to distinguish different templates; Sun Studio // in particular finds multiple specializations in certain rare // cases and complains about that. GCC doesn't seem to mind, // and uses the typedefs further below without the class keyword. // Not all the specializations of Map can use the class keyword // (when T is not actually a class type), so don't apply this // generally. using Iterator = typename std::map<class Key, class T>::iterator; using ConstIterator = typename std::map<class Key, class T>::const_iterator; #else using Iterator = typename std::map<Key, T>::iterator; using ConstIterator = typename std::map<Key, T>::const_iterator; #endif #endif /*! * Constructs an empty Map. */ Map(); /*! * Make a shallow, implicitly shared, copy of \a m. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ Map(const Map<Key, T> &m); /*! * Constructs a Map with the contents of the braced initializer list. */ Map(std::initializer_list<std::pair<const Key, T>> init); /*! * Destroys this instance of the Map. */ ~Map(); /*! * Returns an STL style iterator to the beginning of the map. See * \c std::map::iterator for the semantics. */ Iterator begin(); /*! * Returns an STL style iterator to the beginning of the map. See * \c std::map::const_iterator for the semantics. */ ConstIterator begin() const; /*! * Returns an STL style iterator to the beginning of the map. See * \c std::map::const_iterator for the semantics. */ ConstIterator cbegin() const; /*! * Returns an STL style iterator to the end of the map. See * \c std::map::iterator for the semantics. */ Iterator end(); /*! * Returns an STL style iterator to the end of the map. See * \c std::map::const_iterator for the semantics. */ ConstIterator end() const; /*! * Returns an STL style iterator to the end of the map. See * \c std::map::const_iterator for the semantics. */ ConstIterator cend() const; /*! * Inserts \a value under \a key in the map. If a value for \a key already * exists it will be overwritten. */ Map<Key, T> &insert(const Key &key, const T &value); /*! * Removes all of the elements from the map. This however * will not delete pointers if the mapped type is a pointer type. */ Map<Key, T> &clear(); /*! * The number of elements in the map. * * \see isEmpty() */ unsigned int size() const; /*! * Returns \c true if the map is empty. * * \see size() */ bool isEmpty() const; /*! * Find the first occurrence of \a key. */ Iterator find(const Key &key); /*! * Find the first occurrence of \a key. */ ConstIterator find(const Key &key) const; /*! * Returns \c true if the map contains an item for \a key. */ bool contains(const Key &key) const; /*! * Erase the item at \a it from the map. * * \note This method cannot detach because \a it is tied to the internal * map. Do not make an implicitly shared copy of this map between * getting the iterator and calling this method! */ Map<Key, T> &erase(Iterator it); /*! * Erase the item with \a key from the map. */ Map<Key, T> &erase(const Key &key); /*! * Returns the value associated with \a key. * * If the map does not contain \a key, it returns \a defaultValue. * If no \a defaultValue is specified, it returns a default-constructed value. */ T value(const Key &key, const T &defaultValue = T()) const; /*! * Returns a reference to the value associated with \a key. * * \note This has undefined behavior if the key is not present in the map. */ const T &operator[](const Key &key) const; /*! * Returns a reference to the value associated with \a key. * * \note This has undefined behavior if the key is not present in the map. */ T &operator[](const Key &key); /*! * Make a shallow, implicitly shared, copy of \a m. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ Map<Key, T> &operator=(const Map<Key, T> &m); /*! * Replace the contents of the map with those of the braced initializer list */ Map<Key, T> &operator=(std::initializer_list<std::pair<const Key, T>> init); /*! * Exchanges the content of this map with the content of \a m. */ void swap(Map<Key, T> &m) noexcept; /*! * Compares this map with \a m and returns \c true if all of the elements are * the same. */ bool operator==(const Map<Key, T> &m) const; /*! * Compares this map with \a m and returns \c true if the maps differ. */ bool operator!=(const Map<Key, T> &m) const; protected: /*! * If this Map is being shared via implicit sharing, do a deep copy of the * data and separate from the shared members. This should be called by all * non-const subclass members without Iterator parameters. */ void detach(); private: #ifndef DO_NOT_DOCUMENT template <class KeyP, class TP> class MapPrivate; std::shared_ptr<MapPrivate<Key, T>> d; #endif }; } // namespace TagLib // Since GCC doesn't support the "export" keyword, we have to include the // implementation. #include "tmap.tcc" #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tmap.tcc����������������������������������������������������������������0000664�0000000�0000000�00000013755�14662262111�0017354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ namespace TagLib { //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// template <class Key, class T> template <class KeyP, class TP> class Map<Key, T>::MapPrivate { public: MapPrivate() = default; #ifdef WANT_CLASS_INSTANTIATION_OF_MAP MapPrivate(const std::map<class KeyP, class TP>& m) : map(m) {} MapPrivate(std::initializer_list<std::pair<const class KeyP, class TP>> init) : map(init) {} std::map<class KeyP, class TP> map; #else MapPrivate(const std::map<KeyP, TP>& m) : map(m) {} MapPrivate(std::initializer_list<std::pair<const KeyP, TP>> init) : map(init) {} std::map<KeyP, TP> map; #endif }; template <class Key, class T> Map<Key, T>::Map() : d(std::make_shared<MapPrivate<Key, T>>()) { } template <class Key, class T> Map<Key, T>::Map(const Map<Key, T> &) = default; template <class Key, class T> Map<Key, T>::Map(std::initializer_list<std::pair<const Key, T>> init) : d(std::make_shared<MapPrivate<Key, T>>(init)) { } template <class Key, class T> Map<Key, T>::~Map() = default; template <class Key, class T> typename Map<Key, T>::Iterator Map<Key, T>::begin() { detach(); return d->map.begin(); } template <class Key, class T> typename Map<Key, T>::ConstIterator Map<Key, T>::begin() const { return d->map.begin(); } template <class Key, class T> typename Map<Key, T>::ConstIterator Map<Key, T>::cbegin() const { return d->map.cbegin(); } template <class Key, class T> typename Map<Key, T>::Iterator Map<Key, T>::end() { detach(); return d->map.end(); } template <class Key, class T> typename Map<Key, T>::ConstIterator Map<Key, T>::end() const { return d->map.end(); } template <class Key, class T> typename Map<Key, T>::ConstIterator Map<Key, T>::cend() const { return d->map.cend(); } template <class Key, class T> Map<Key, T> &Map<Key, T>::insert(const Key &key, const T &value) { detach(); d->map[key] = value; return *this; } template <class Key, class T> Map<Key, T> &Map<Key, T>::clear() { detach(); d->map.clear(); return *this; } template <class Key, class T> bool Map<Key, T>::isEmpty() const { return d->map.empty(); } template <class Key, class T> typename Map<Key, T>::Iterator Map<Key, T>::find(const Key &key) { detach(); return d->map.find(key); } template <class Key, class T> typename Map<Key,T>::ConstIterator Map<Key, T>::find(const Key &key) const { return d->map.find(key); } template <class Key, class T> bool Map<Key, T>::contains(const Key &key) const { return d->map.find(key) != d->map.end(); } template <class Key, class T> Map<Key, T> &Map<Key,T>::erase(Iterator it) { d->map.erase(it); return *this; } template <class Key, class T> Map<Key, T> &Map<Key,T>::erase(const Key &key) { detach(); d->map.erase(key); return *this; } template <class Key, class T> unsigned int Map<Key, T>::size() const { return static_cast<unsigned int>(d->map.size()); } template <class Key, class T> T Map<Key, T>::value(const Key &key, const T &defaultValue) const { auto it = d->map.find(key); return it != d->map.end() ? it->second : defaultValue; } template <class Key, class T> const T &Map<Key, T>::operator[](const Key &key) const { return d->map[key]; } template <class Key, class T> T &Map<Key, T>::operator[](const Key &key) { detach(); return d->map[key]; } template <class Key, class T> Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &) = default; template <class Key, class T> Map<Key, T> &Map<Key, T>::operator=(std::initializer_list<std::pair<const Key, T>> init) { Map(init).swap(*this); return *this; } template <class Key, class T> void Map<Key, T>::swap(Map<Key, T> &m) noexcept { using std::swap; swap(d, m.d); } template <class Key, class T> bool Map<Key, T>::operator==(const Map<Key, T> &m) const { return d->map == m.d->map; } template <class Key, class T> bool Map<Key, T>::operator!=(const Map<Key, T> &m) const { return d->map != m.d->map; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// template <class Key, class T> void Map<Key, T>::detach() { if(d.use_count() > 1) { d = std::make_shared<MapPrivate<Key, T>>(d->map); } } } // namespace TagLib �������������������taglib-2.0.2/taglib/toolkit/tpicturetype.cpp��������������������������������������������������������0000664�0000000�0000000�00000005105�14662262111�0021153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tpicturetype.h" #include "tstring.h" using namespace TagLib; namespace { constexpr const char *typeStrs[] = { "Other", "File Icon", "Other File Icon", "Front Cover", "Back Cover", "Leaflet Page", "Media", "Lead Artist", "Artist", "Conductor", "Band", "Composer", "Lyricist", "Recording Location", "During Recording", "During Performance", "Movie Screen Capture", "Coloured Fish", "Illustration", "Band Logo", "Publisher Logo", }; } // namespace String Utils::pictureTypeToString(int type) { if(type >= 0 && type < static_cast<int>(std::size(typeStrs))) { return typeStrs[type]; } return ""; } int Utils::pictureTypeFromString(const String& str) { for(int i = 0; i < static_cast<int>(std::size(typeStrs)); ++i) { if(str == typeStrs[i]) { return i; } } return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tpicturetype.h����������������������������������������������������������0000664�0000000�0000000�00000013404�14662262111�0020621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_PICTURETYPE_H #define TAGLIB_PICTURETYPE_H // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header #include "taglib_export.h" /*! * Declare a picture type \a name enumeration inside a class. * Declares a picture type enum according to the ID3v2 specification and * adds methods \c typeToString() and \c typeFromString(). * * \code {.cpp} * class MyClass { * public: * DECLARE_PICTURE_TYPE_ENUM(Type) * (..) * } * \endcode */ #define DECLARE_PICTURE_TYPE_ENUM(name) \ enum name { \ /*! A type not enumerated below */ \ Other = 0x00, \ /*! 32x32 PNG image that should be used as the file icon */ \ FileIcon = 0x01, \ /*! File icon of a different size or format */ \ OtherFileIcon = 0x02, \ /*! Front cover image of the album */ \ FrontCover = 0x03, \ /*! Back cover image of the album */ \ BackCover = 0x04, \ /*! Inside leaflet page of the album */ \ LeafletPage = 0x05, \ /*! Image from the album itself */ \ Media = 0x06, \ /*! Picture of the lead artist or soloist */ \ LeadArtist = 0x07, \ /*! Picture of the artist or performer */ \ Artist = 0x08, \ /*! Picture of the conductor */ \ Conductor = 0x09, \ /*! Picture of the band or orchestra */ \ Band = 0x0A, \ /*! Picture of the composer */ \ Composer = 0x0B, \ /*! Picture of the lyricist or text writer */ \ Lyricist = 0x0C, \ /*! Picture of the recording location or studio */ \ RecordingLocation = 0x0D, \ /*! Picture of the artists during recording */ \ DuringRecording = 0x0E, \ /*! Picture of the artists during performance */ \ DuringPerformance = 0x0F, \ /*! Picture from a movie or video related to the track */ \ MovieScreenCapture = 0x10, \ /*! Picture of a large, coloured fish */ \ ColouredFish = 0x11, \ /*! Illustration related to the track */ \ Illustration = 0x12, \ /*! Logo of the band or performer */ \ BandLogo = 0x13, \ /*! Logo of the publisher (record company) */ \ PublisherLogo = 0x14 \ }; \ static TagLib::String typeToString(name type) { \ return TagLib::Utils::pictureTypeToString(type); \ } \ static name typeFromString(const TagLib::String &str) { \ return static_cast<name>( \ TagLib::Utils::pictureTypeFromString(str)); \ } namespace TagLib { class String; namespace Utils { /*! * Get string representation of picture type. */ String TAGLIB_EXPORT pictureTypeToString(int type); /*! * Get picture type from string representation. */ int TAGLIB_EXPORT pictureTypeFromString(const String& str); } // namespace Utils } // namespace TagLib #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tpropertymap.cpp��������������������������������������������������������0000664�0000000�0000000�00000014344�14662262111�0021165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Michael Helmling email : helmling@mathematik.uni-kl.de ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tpropertymap.h" #include <utility> using namespace TagLib; class PropertyMap::PropertyMapPrivate { public: StringList unsupported; }; PropertyMap::PropertyMap() : d(std::make_unique<PropertyMapPrivate>()) { } PropertyMap::PropertyMap(const PropertyMap &m) : SimplePropertyMap(m), d(std::make_unique<PropertyMapPrivate>()) { *d = *m.d; } PropertyMap::PropertyMap(const SimplePropertyMap &m) : d(std::make_unique<PropertyMapPrivate>()) { for(const auto &[key, val] : m) { if(!key.isEmpty()) insert(key.upper(), val); else d->unsupported.append(key.upper()); } } PropertyMap::~PropertyMap() = default; bool PropertyMap::insert(const String &key, const StringList &values) { String realKey = key.upper(); auto result = SimplePropertyMap::find(realKey); if(result == end()) SimplePropertyMap::insert(realKey, values); else SimplePropertyMap::operator[](realKey).append(values); return true; } bool PropertyMap::replace(const String &key, const StringList &values) { String realKey = key.upper(); SimplePropertyMap::erase(realKey); SimplePropertyMap::insert(realKey, values); return true; } PropertyMap::Iterator PropertyMap::find(const String &key) { return SimplePropertyMap::find(key.upper()); } PropertyMap::ConstIterator PropertyMap::find(const String &key) const { return SimplePropertyMap::find(key.upper()); } bool PropertyMap::contains(const String &key) const { return SimplePropertyMap::contains(key.upper()); } bool PropertyMap::contains(const PropertyMap &other) const { return std::all_of(other.begin(), other.end(), [this](const auto &o) { return contains(o.first) && (*this)[o.first] == o.second; }); } PropertyMap &PropertyMap::erase(const String &key) { SimplePropertyMap::erase(key.upper()); return *this; } PropertyMap &PropertyMap::erase(const PropertyMap &other) { for(const auto &[property, _] : other) erase(property); return *this; } PropertyMap &PropertyMap::merge(const PropertyMap &other) { for(const auto &[property, val] : other) insert(property, val); d->unsupported.append(other.d->unsupported); return *this; } StringList PropertyMap::value(const String &key, const StringList &defaultValue) const { return SimplePropertyMap::value(key.upper(), defaultValue); } const StringList &PropertyMap::operator[](const String &key) const { return SimplePropertyMap::operator[](key.upper()); } StringList &PropertyMap::operator[](const String &key) { return SimplePropertyMap::operator[](key.upper()); } bool PropertyMap::operator==(const PropertyMap &other) const { for(const auto &[property, val] : other) { if(auto thisFind = find(property); thisFind == end() || thisFind->second != val) return false; } for(const auto &[property, val] : *this) { if(auto otherFind = other.find(property); otherFind == other.end() || otherFind->second != val) return false; } return d->unsupported == other.d->unsupported; } bool PropertyMap::operator!=(const PropertyMap &other) const { return !(*this == other); } String PropertyMap::toString() const { String ret; for(const auto &[property, val] : *this) ret += property + "=" + val.toString(", ") + "\n"; if(!d->unsupported.isEmpty()) ret += "Unsupported Data: " + d->unsupported.toString(", ") + "\n"; return ret; } void PropertyMap::removeEmpty() { PropertyMap m; for(const auto &[property, val] : std::as_const(*this)) { if(!val.isEmpty()) m.insert(property, val); } *this = m; } const StringList &PropertyMap::unsupportedData() const { return d->unsupported; } void PropertyMap::addUnsupportedData(const String &key) { d->unsupported.append(key); } PropertyMap &PropertyMap::operator=(const PropertyMap &other) { if(this == &other) return *this; SimplePropertyMap::operator=(other); *d = *other.d; return *this; } #ifdef _MSC_VER // When building with shared libraries and tests, MSVC will fail with // "already defined in test_opus.obj" as soon as operator[] of // Ogg::FieldListMap is used because this will instantiate the same template // Map<String, StringList>. Therefore this template is instantiated here // and declared extern in the headers using it. // Suppress warning "needs to have dll-interface to be used by clients", // in the template, TAGLIB_EXPORT cannot be used on the members, therefore // the private implemenation is exported too, but not accessible by clients. #pragma warning(suppress: 4251) template class TAGLIB_EXPORT TagLib::Map<TagLib::String, TagLib::StringList>; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tpropertymap.h����������������������������������������������������������0000664�0000000�0000000�00000021033�14662262111�0020623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Michael Helmling email : helmling@mathematik.uni-kl.de ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_PROPERTYMAP_H #define TAGLIB_PROPERTYMAP_H #include "tmap.h" #include "tstringlist.h" #ifdef _MSC_VER // Explained at end of tpropertymap.cpp TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE extern template class TagLib::Map<TagLib::String, TagLib::StringList>; #endif namespace TagLib { using SimplePropertyMap = Map<String, StringList>; //! A map for format-independent <key,values> tag representations. /*! * This map implements a generic representation of textual audio metadata * ("tags") realized as pairs of a case-insensitive key * and a nonempty list of corresponding values, each value being an arbitrary * unicode String. * * See \ref p_propertymapping for the mapping of the different formats to * properties. * * Note that most metadata formats pose additional conditions on the tag keys. The * most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of * length between 2 and 16. * * This class can contain any tags, but here is a list of "well-known" tags that * you might want to use: * * Basic tags: * * - TITLE * - ALBUM * - ARTIST * - ALBUMARTIST * - SUBTITLE * - TRACKNUMBER * - DISCNUMBER * - DATE * - ORIGINALDATE * - GENRE * - COMMENT * * Sort names: * * - TITLESORT * - ALBUMSORT * - ARTISTSORT * - ALBUMARTISTSORT * - COMPOSERSORT * * Credits: * * - COMPOSER * - LYRICIST * - CONDUCTOR * - REMIXER * - PERFORMER:\<XXXX> * * Other tags: * * - ISRC * - ASIN * - BPM * - COPYRIGHT * - ENCODEDBY * - MOOD * - COMMENT * - MEDIA * - LABEL * - CATALOGNUMBER * - BARCODE * - RELEASECOUNTRY * - RELEASESTATUS * - RELEASETYPE * * MusicBrainz identifiers: * * - MUSICBRAINZ_TRACKID * - MUSICBRAINZ_ALBUMID * - MUSICBRAINZ_RELEASEGROUPID * - MUSICBRAINZ_RELEASETRACKID * - MUSICBRAINZ_WORKID * - MUSICBRAINZ_ARTISTID * - MUSICBRAINZ_ALBUMARTISTID * - ACOUSTID_ID * - ACOUSTID_FINGERPRINT * - MUSICIP_PUID * */ class PropertyMap: public SimplePropertyMap { public: using Iterator = SimplePropertyMap::Iterator; using ConstIterator = SimplePropertyMap::ConstIterator; TAGLIB_EXPORT PropertyMap(); TAGLIB_EXPORT PropertyMap(const PropertyMap &m); TAGLIB_EXPORT PropertyMap &operator=(const PropertyMap &other); /*! * Creates a PropertyMap initialized from a SimplePropertyMap. Copies all * entries from \a m that have valid keys. * Invalid keys will be appended to the unsupportedData() list. */ TAGLIB_EXPORT PropertyMap(const SimplePropertyMap &m); TAGLIB_EXPORT ~PropertyMap(); /*! * Inserts \a values under \a key in the map. If \a key already exists, * then \a values will be appended to the existing StringList. * The returned value indicates success, i.e. whether \a key is a * valid key. */ TAGLIB_EXPORT bool insert(const String &key, const StringList &values); /*! * Replaces any existing values for \a key with the given \a values, * and simply insert them if \a key did not exist before. * The returned value indicates success, i.e. whether \a key is a * valid key. */ TAGLIB_EXPORT bool replace(const String &key, const StringList &values); /*! * Find the first occurrence of \a key. */ TAGLIB_EXPORT Iterator find(const String &key); /*! * Find the first occurrence of \a key. */ TAGLIB_EXPORT ConstIterator find(const String &key) const; /*! * Returns \c true if the map contains values for \a key. */ TAGLIB_EXPORT bool contains(const String &key) const; /*! * Returns \c true if this map contains all keys of \a other * and the values coincide for those keys. Does not take * the unsupportedData list into account. */ TAGLIB_EXPORT bool contains(const PropertyMap &other) const; /*! * Erase the \a key and its values from the map. */ TAGLIB_EXPORT PropertyMap &erase(const String &key); /*! * Erases from this map all keys that appear in \a other. */ TAGLIB_EXPORT PropertyMap &erase(const PropertyMap &other); /*! * Merge the contents of \a other into this PropertyMap. * If a key is contained in both maps, the values of the second * are appended to that of the first. * The unsupportedData() lists are concatenated as well. */ TAGLIB_EXPORT PropertyMap &merge(const PropertyMap &other); /*! * Returns the value associated with \a key. * * If the map does not contain \a key, it returns \a defaultValue. * If no \a defaultValue is specified, it returns an empty string list. */ TAGLIB_EXPORT StringList value(const String &key, const StringList &defaultValue = StringList()) const; /*! * Returns a reference to the value associated with \a key. * * \note If \a key is not contained in the map, an empty * StringList is returned without error. */ TAGLIB_EXPORT const StringList &operator[](const String &key) const; /*! * Returns a reference to the value associated with \a key. * * \note If \a key is not contained in the map, an empty * StringList is returned. You can also directly add entries * by using this function as an lvalue. */ TAGLIB_EXPORT StringList &operator[](const String &key); /*! * Returns \c true if and only if \a other has the same contents as this map. */ TAGLIB_EXPORT bool operator==(const PropertyMap &other) const; /*! * Returns \c false if and only if \a other has the same contents as this map. */ TAGLIB_EXPORT bool operator!=(const PropertyMap &other) const; /*! * If a PropertyMap is read from a File object using File::properties() * (or a FileRef object using FileRef::properties()), * the StringList returned from this function will represent metadata * that could not be parsed into the PropertyMap representation. This could * be e.g. binary data, unknown ID3 frames, etc. * * \see File::removeUnsupportedProperties(), * FileRef::removeUnsupportedProperties() */ TAGLIB_EXPORT const StringList &unsupportedData() const; /*! * Add property \a key to list of unsupported data. * * \see unsupportedData() */ TAGLIB_EXPORT void addUnsupportedData(const String &key); /*! * Removes all entries which have an empty value list. */ TAGLIB_EXPORT void removeEmpty(); TAGLIB_EXPORT String toString() const; private: class PropertyMapPrivate; std::unique_ptr<PropertyMapPrivate> d; }; } // namespace TagLib #endif /* TAGLIB_PROPERTYMAP_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tstring.cpp�������������������������������������������������������������0000664�0000000�0000000�00000036637�14662262111�0020122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tstring.h" #include <cerrno> #include <climits> #include <iostream> #include <utf8.h> #include "tdebug.h" #include "tstringlist.h" #include "tutils.h" namespace { using namespace TagLib; // Returns the native format of std::wstring. constexpr String::Type wcharByteOrder() { return Utils::systemByteOrder() == Utils::LittleEndian ? String::UTF16LE : String::UTF16BE; } // Converts a Latin-1 string into UTF-16(without BOM/CPU byte order) // and copies it to the internal buffer. void copyFromLatin1(std::wstring &data, const char *s, size_t length) { data.resize(length); for(size_t i = 0; i < length; ++i) data[i] = static_cast<unsigned char>(s[i]); } // Converts a UTF-8 string into UTF-16(without BOM/CPU byte order) // and copies it to the internal buffer. void copyFromUTF8(std::wstring &data, const char *s, size_t length) { data.resize(length); try { const std::wstring::iterator dstEnd = utf8::utf8to16(s, s + length, data.begin()); data.resize(dstEnd - data.begin()); } catch(const utf8::exception &e) { const String message(e.what()); debug("String::copyFromUTF8() - UTF8-CPP error: " + message); data.clear(); } } // Helper functions to read a UTF-16 character from an array. template <typename T> unsigned short nextUTF16(const T **p); template <> unsigned short nextUTF16<wchar_t>(const wchar_t **p) { return static_cast<unsigned short>(*(*p)++); } template <> unsigned short nextUTF16<char>(const char **p) { union { unsigned short w; char c[2]; } u; u.c[0] = *(*p)++; u.c[1] = *(*p)++; return u.w; } // Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into // UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. template <typename T> void copyFromUTF16(std::wstring &data, const T *s, size_t length, String::Type t) { bool swap; if(t == String::UTF16) { if(length < 1) { debug("String::copyFromUTF16() - Invalid UTF16 string. Too short to have a BOM."); return; } if(const unsigned short bom = nextUTF16(&s); bom == 0xfeff) swap = false; // Same as CPU endian. No need to swap bytes. else if(bom == 0xfffe) swap = true; // Not same as CPU endian. Need to swap bytes. else { debug("String::copyFromUTF16() - Invalid UTF16 string. BOM is broken."); return; } length--; } else { swap = t != wcharByteOrder(); } data.resize(length); for(size_t i = 0; i < length; ++i) { const unsigned short c = nextUTF16(&s); if(swap) data[i] = Utils::byteSwap(c); else data[i] = c; } } } // namespace namespace TagLib { class String::StringPrivate { public: /*! * Stores string in UTF-16. The byte order depends on the CPU endian. */ std::wstring data; /*! * This is only used to hold the most recent value of toCString(). */ std::string cstring; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// String::String() : d(std::make_shared<StringPrivate>()) { } String::String(const String &) = default; String::String(const std::string &s, Type t) : d(std::make_shared<StringPrivate>()) { if(t == Latin1) copyFromLatin1(d->data, s.c_str(), s.length()); else if(t == String::UTF8) copyFromUTF8(d->data, s.c_str(), s.length()); else { debug("String::String() -- std::string should not contain UTF16."); } } String::String(const std::wstring &s) : String(s, wcharByteOrder()) { } String::String(const std::wstring &s, Type t) : d(std::make_shared<StringPrivate>()) { if(t == UTF16 || t == UTF16BE || t == UTF16LE) { copyFromUTF16(d->data, s.c_str(), s.length(), t); } else { debug("String::String() -- std::wstring should not contain Latin1 or UTF-8."); } } String::String(const wchar_t *s) : String(s, wcharByteOrder()) { } String::String(const wchar_t *s, Type t) : d(std::make_shared<StringPrivate>()) { if(t == UTF16 || t == UTF16BE || t == UTF16LE) { copyFromUTF16(d->data, s, ::wcslen(s), t); } else { debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8."); } } String::String(const char *s, Type t) : d(std::make_shared<StringPrivate>()) { if(t == Latin1) copyFromLatin1(d->data, s, ::strlen(s)); else if(t == String::UTF8) copyFromUTF8(d->data, s, ::strlen(s)); else { debug("String::String() -- const char * should not contain UTF16."); } } String::String(wchar_t c, Type t) : d(std::make_shared<StringPrivate>()) { if(t == UTF16 || t == UTF16BE || t == UTF16LE) copyFromUTF16(d->data, &c, 1, t); else { debug("String::String() -- wchar_t should not contain Latin1 or UTF-8."); } } String::String(char c, Type t) : d(std::make_shared<StringPrivate>()) { if(t == Latin1) copyFromLatin1(d->data, &c, 1); else if(t == String::UTF8) copyFromUTF8(d->data, &c, 1); else { debug("String::String() -- char should not contain UTF16."); } } String::String(const ByteVector &v, Type t) : d(std::make_shared<StringPrivate>()) { if(v.isEmpty()) return; if(t == Latin1) copyFromLatin1(d->data, v.data(), v.size()); else if(t == UTF8) copyFromUTF8(d->data, v.data(), v.size()); else copyFromUTF16(d->data, v.data(), v.size() / 2, t); // If we hit a null in the ByteVector, shrink the string again. d->data.resize(::wcslen(d->data.c_str())); } //////////////////////////////////////////////////////////////////////////////// String::~String() = default; std::string String::to8Bit(bool unicode) const { const ByteVector v = data(unicode ? UTF8 : Latin1); return std::string(v.data(), v.size()); } std::wstring String::toWString() const { return d->data; } const char *String::toCString(bool unicode) const { d->cstring = to8Bit(unicode); return d->cstring.c_str(); } const wchar_t *String::toCWString() const { return d->data.c_str(); } String::Iterator String::begin() { detach(); return d->data.begin(); } String::ConstIterator String::begin() const { return d->data.begin(); } String::ConstIterator String::cbegin() const { return d->data.cbegin(); } String::Iterator String::end() { detach(); return d->data.end(); } String::ConstIterator String::end() const { return d->data.end(); } String::ConstIterator String::cend() const { return d->data.cend(); } int String::find(const String &s, int offset) const { return static_cast<int>(d->data.find(s.d->data, offset)); } int String::rfind(const String &s, int offset) const { return static_cast<int>(d->data.rfind(s.d->data, offset)); } StringList String::split(const String &separator) const { StringList list; for(int index = 0;;) { int sep = find(separator, index); if(sep < 0) { list.append(substr(index, size() - index)); break; } list.append(substr(index, sep - index)); index = sep + separator.size(); } return list; } bool String::startsWith(const String &s) const { if(s.length() > length()) return false; return substr(0, s.length()) == s; } String String::substr(unsigned int position, unsigned int n) const { if(position == 0 && n >= size()) return *this; return String(d->data.substr(position, n)); } String &String::append(const String &s) { detach(); d->data += s.d->data; return *this; } String & String::clear() { *this = String(); return *this; } String String::upper() const { String s; s.d->data.reserve(size()); for(wchar_t c : *this) { if(c >= 'a' && c <= 'z') s.d->data.push_back(c + 'A' - 'a'); else s.d->data.push_back(c); } return s; } unsigned int String::size() const { return static_cast<unsigned int>(d->data.size()); } unsigned int String::length() const { return size(); } bool String::isEmpty() const { return d->data.empty(); } ByteVector String::data(Type t) const { switch(t) { case Latin1: { ByteVector v(size(), 0); char *p = v.data(); for(wchar_t c : *this) { *p++ = static_cast<char>(c); } return v; } case UTF8: { ByteVector v(size() * 4, 0); try { const auto dstEnd = utf8::utf16to8(begin(), end(), v.begin()); v.resize(static_cast<unsigned int>(dstEnd - v.begin())); } catch(const utf8::exception &e) { const String message(e.what()); debug("String::data() - UTF8-CPP error: " + message); v.clear(); } return v; } case UTF16: { ByteVector v(2 + size() * 2, 0); char *p = v.data(); // We use little-endian encoding here and need a BOM. *p++ = '\xff'; *p++ = '\xfe'; for(wchar_t c : *this) { *p++ = static_cast<char>(c & 0xff); *p++ = static_cast<char>(c >> 8); } return v; } case UTF16BE: { ByteVector v(size() * 2, 0); char *p = v.data(); for(wchar_t c : *this) { *p++ = static_cast<char>(c >> 8); *p++ = static_cast<char>(c & 0xff); } return v; } case UTF16LE: { ByteVector v(size() * 2, 0); char *p = v.data(); for(wchar_t c : *this) { *p++ = static_cast<char>(c & 0xff); *p++ = static_cast<char>(c >> 8); } return v; } default: { debug("String::data() - Invalid Type value."); return ByteVector(); } } } int String::toInt(bool *ok) const { const wchar_t *beginPtr = d->data.c_str(); wchar_t *endPtr; errno = 0; const long value = ::wcstol(beginPtr, &endPtr, 10); // Has wcstol() consumed the entire string and not overflowed? if(ok) { *ok = errno == 0 && endPtr > beginPtr && *endPtr == L'\0'; *ok = *ok && value > INT_MIN && value < INT_MAX; } return static_cast<int>(value); } String String::stripWhiteSpace() const { static const wchar_t *WhiteSpaceChars = L"\t\n\f\r "; const size_t pos1 = d->data.find_first_not_of(WhiteSpaceChars); if(pos1 == std::wstring::npos) return String(); const size_t pos2 = d->data.find_last_not_of(WhiteSpaceChars); return substr(static_cast<unsigned int>(pos1), static_cast<unsigned int>(pos2 - pos1 + 1)); } bool String::isLatin1() const { return std::none_of(this->begin(), this->end(), [](auto c) { return c >= 256; }); } bool String::isAscii() const { return std::none_of(this->begin(), this->end(), [](auto c) { return c >= 128; }); } String String::number(int n) // static { return std::to_string(n); } String String::fromLongLong(long long n) // static { return std::to_string(n); } wchar_t &String::operator[](int i) { detach(); return d->data[i]; } const wchar_t &String::operator[](int i) const { return d->data[i]; } bool String::operator==(const String &s) const { return d == s.d || d->data == s.d->data; } bool String::operator!=(const String &s) const { return !(*this == s); } bool String::operator==(const char *s) const { const wchar_t *p = toCWString(); while(*p != L'\0' || *s != '\0') { if(*p++ != static_cast<unsigned char>(*s++)) return false; } return true; } bool String::operator!=(const char *s) const { return !(*this == s); } bool String::operator==(const wchar_t *s) const { return d->data == s; } bool String::operator!=(const wchar_t *s) const { return !(*this == s); } String &String::operator+=(const String &s) { detach(); d->data += s.d->data; return *this; } String &String::operator+=(const wchar_t *s) { detach(); d->data += s; return *this; } String &String::operator+=(const char *s) { detach(); for(int i = 0; s[i] != 0; i++) d->data += static_cast<unsigned char>(s[i]); return *this; } String &String::operator+=(wchar_t c) { detach(); d->data += c; return *this; } String &String::operator+=(char c) { detach(); d->data += static_cast<unsigned char>(c); return *this; } String &String::operator=(const String &) = default; String &String::operator=(const std::string &s) { String(s).swap(*this); return *this; } String &String::operator=(const std::wstring &s) { String(s).swap(*this); return *this; } String &String::operator=(const wchar_t *s) { String(s).swap(*this); return *this; } String &String::operator=(char c) { String(c).swap(*this); return *this; } String &String::operator=(wchar_t c) { String(c, wcharByteOrder()).swap(*this); return *this; } String &String::operator=(const char *s) { String(s).swap(*this); return *this; } String &String::operator=(const ByteVector &v) { String(v).swap(*this); return *this; } void String::swap(String &s) noexcept { using std::swap; swap(d, s.d); } bool String::operator<(const String &s) const { return d->data < s.d->data; } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// void String::detach() { if(d.use_count() > 1) String(d->data.c_str()).swap(*this); } } // namespace TagLib //////////////////////////////////////////////////////////////////////////////// // related non-member functions //////////////////////////////////////////////////////////////////////////////// TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2) { TagLib::String s(s1); s.append(s2); return s; } TagLib::String operator+(const char *s1, const TagLib::String &s2) { TagLib::String s(s1); s.append(s2); return s; } TagLib::String operator+(const TagLib::String &s1, const char *s2) { TagLib::String s(s1); s.append(s2); return s; } std::ostream &operator<<(std::ostream &s, const TagLib::String &str) { s << str.to8Bit(true); return s; } �������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tstring.h���������������������������������������������������������������0000664�0000000�0000000�00000037771�14662262111�0017567�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_STRING_H #define TAGLIB_STRING_H #include <string> #include "tbytevector.h" #include "taglib_export.h" /*! * \relates TagLib::String * * Converts a QString to a TagLib::String without a requirement to link to Qt. * * \note consider conversion via usual char-by-char for loop to avoid UTF16->UTF8->UTF16 * conversion happening in the background */ #if defined(QT_VERSION) && (QT_VERSION >= 0x040000) #define QStringToTString(s) TagLib::String(s.toUtf8().data(), TagLib::String::UTF8) #else #define QStringToTString(s) TagLib::String((s).utf8().data(), TagLib::String::UTF8) #endif /*! * \relates TagLib::String * * Converts a TagLib::String to a QString without a requirement to link to Qt. * * \note consider conversion via usual char-by-char for loop to avoid UTF16->UTF8->UTF16 * conversion happening in the background * */ #define TStringToQString(s) QString::fromUtf8((s).toCString(true)) namespace TagLib { class StringList; //! A \e wide string class suitable for unicode. /*! * This is an implicitly shared \e wide string. For storage it uses * std::wstring, but as this is an <i>implementation detail</i> this of * course could change. Strings are stored internally as UTF-16 (without * BOM/CPU byte order) * * The use of implicit sharing means that copying a string is cheap, the only * \e cost comes into play when the copy is modified. Prior to that the string * just has a pointer to the data of the \e parent String. This also makes * this class suitable as a function return type. * * In addition to adding implicit sharing, this class keeps track of * possible encodings, which are those supported by the ID3v2 standard. */ class TAGLIB_EXPORT String { public: #ifndef DO_NOT_DOCUMENT using Iterator = std::wstring::iterator; using ConstIterator = std::wstring::const_iterator; #endif /** * The four types of string encodings supported by the ID3v2 specification. * (plus UTF16LE). * ID3v1 is assumed to be Latin1 and Ogg Vorbis comments use UTF8. */ enum Type { /*! * ISO-8859-1, or <i>Latin1</i> encoding. 8 bit characters. */ Latin1 = 0, /*! * UTF16 with a <i>byte order mark</i>. 16 bit characters. */ UTF16 = 1, /*! * UTF16 <i>big endian</i>. 16 bit characters. This is the encoding used * internally by TagLib. */ UTF16BE = 2, /*! * UTF8 encoding. Characters are usually 8 bits but can be up to 32. */ UTF8 = 3, /*! * UTF16 <i>little endian</i>. 16 bit characters. */ UTF16LE = 4 }; /*! * Constructs an empty String. */ String(); /*! * Make a shallow, implicitly shared, copy of \a s. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ String(const String &s); /*! * Makes a deep copy of the data in \a s. * * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when * used with other codecs it will simply print a warning and exit. */ String(const std::string &s, Type t = Latin1); /*! * Makes a deep copy of the data in \a s, which are in CPU byte order. */ String(const std::wstring &s); /*! * Makes a deep copy of the data in \a s, which are in byte order \a t. */ String(const std::wstring &s, Type t); /*! * Makes a deep copy of the data in \a s, which are in CPU byte order. */ String(const wchar_t *s); /*! * Makes a deep copy of the data in \a s, which are in byte order \a t. */ String(const wchar_t *s, Type t); /*! * Makes a deep copy of the data in \a c. * * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when * used with other codecs it will simply print a warning and exit. */ String(char c, Type t = Latin1); /*! * Makes a deep copy of the data in \a c. */ String(wchar_t c, Type t = Latin1); /*! * Makes a deep copy of the data in \a s. * * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when * used with other codecs it will simply print a warning and exit. */ String(const char *s, Type t = Latin1); /*! * Makes a deep copy of the data in \a v. */ String(const ByteVector &v, Type t = Latin1); /*! * Destroys this String instance. */ ~String(); /*! * Returns a deep copy of this String as an \c std::string. The returned string * is encoded in UTF8 if \a unicode is \c true, otherwise Latin1. * * \see toCString() */ std::string to8Bit(bool unicode = false) const; /*! * Returns a deep copy of this String as a \c wstring. The returned string is * encoded in UTF-16 (without BOM/CPU byte order), not UTF-32 even if \c wchar_t * is 32-bit wide. * * \see toCWString() */ std::wstring toWString() const; /*! * Creates and returns a standard C-style (null-terminated) version of this * String. The returned string is encoded in UTF8 if \a unicode is \c true, * otherwise Latin1. * * The returned string is still owned by this String and should not be deleted * by the user. * * The returned pointer remains valid until this String instance is destroyed * or toCString() is called again. * * \warning This however has the side effect that the returned string will remain * in memory <b>in addition to</b> other memory that is consumed by this * String instance. So, this method should not be used on large strings or * where memory is critical. Consider using to8Bit() instead to avoid it. * * \see to8Bit() */ const char *toCString(bool unicode = false) const; /*! * Returns a standard C-style (null-terminated) wide character version of * this String. The returned string is encoded in UTF-16 (without BOM/CPU byte * order), not UTF-32 even if \c wchar_t is 32-bit wide. * * The returned string is still owned by this String and should not be deleted * by the user. * * The returned pointer remains valid until this String instance is destroyed * or any other method of this String is called. * * \note This returns a pointer to the String's internal data without any * conversions. * * \see toWString() */ const wchar_t *toCWString() const; /*! * Returns an iterator pointing to the beginning of the string. */ Iterator begin(); /*! * Returns a const iterator pointing to the beginning of the string. */ ConstIterator begin() const; /*! * Returns a const iterator pointing to the beginning of the string. */ ConstIterator cbegin() const; /*! * Returns an iterator pointing to the end of the string (the position * after the last character). */ Iterator end(); /*! * Returns a const iterator pointing to the end of the string (the position * after the last character). */ ConstIterator end() const; /*! * Returns a const iterator pointing to the end of the string (the position * after the last character). */ ConstIterator cend() const; /*! * Finds the first occurrence of pattern \a s in this string starting from * \a offset. If the pattern is not found, -1 is returned. */ int find(const String &s, int offset = 0) const; /*! * Finds the last occurrence of pattern \a s in this string, searched backwards, * either from the end of the string or starting from \a offset. If the pattern * is not found, -1 is returned. */ int rfind(const String &s, int offset = -1) const; /*! * Splits the string on each occurrence of \a separator. */ StringList split(const String &separator = " ") const; /*! * Returns \c true if the string starts with the substring \a s. */ bool startsWith(const String &s) const; /*! * Extract a substring from this string starting at \a position and * continuing for \a n characters. */ String substr(unsigned int position, unsigned int n = 0xffffffff) const; /*! * Append \a s to the current string and return a reference to the current * string. */ String &append(const String &s); /*! * Clears the string. */ String &clear(); /*! * Returns an upper case version of the string. * * \warning This only works for the characters in US-ASCII, i.e. A-Z. */ String upper() const; /*! * Returns the size of the string. */ unsigned int size() const; /*! * Returns the length of the string. Equivalent to size(). */ unsigned int length() const; /*! * Returns \c true if the string is empty. */ bool isEmpty() const; /*! * Returns a ByteVector containing the string's data. If \a t is Latin1 or * UTF8, this will return a vector of 8 bit characters, otherwise it will use * 16 bit characters. * * \note If \a t is UTF16, the returned data is encoded in little-endian * format and has a BOM. * * \note The returned data is not null terminated. */ ByteVector data(Type t) const; /*! * Convert the string to an integer. * * If the conversion was successful, it sets the value of \a *ok to * \c true and returns the integer. Otherwise it sets \a *ok to \c false * and the result is undefined. */ int toInt(bool *ok = nullptr) const; /*! * Returns a string with the leading and trailing whitespace stripped. */ String stripWhiteSpace() const; /*! * Returns \c true if the file only uses characters required by Latin1. */ bool isLatin1() const; /*! * Returns \c true if the file only uses characters required by (7-bit) ASCII. */ bool isAscii() const; /*! * Converts the base-10 integer \a n to a string. */ static String number(int n); /*! * Converts the base-10 integer \a n to a string. */ static String fromLongLong(long long n); /*! * Returns a reference to the character at position \a i. */ wchar_t &operator[](int i); /*! * Returns a const reference to the character at position \a i. */ const wchar_t &operator[](int i) const; /*! * Compares each character of the String with each character of \a s and * returns \c true if the strings match. */ bool operator==(const String &s) const; /*! * Compares each character of the String with each character of \a s and * returns \c false if the strings match. */ bool operator!=(const String &s) const; /*! * Compares each character of the String with each character of \a s and * returns \c true if the strings match. */ bool operator==(const char *s) const; /*! * Compares each character of the String with each character of \a s and * returns \c false if the strings match. */ bool operator!=(const char *s) const; /*! * Compares each character of the String with each character of \a s and * returns \c true if the strings match. */ bool operator==(const wchar_t *s) const; /*! * Compares each character of the String with each character of \a s and * returns \c false if the strings match. */ bool operator!=(const wchar_t *s) const; /*! * Appends \a s to the end of the String. */ String &operator+=(const String &s); /*! * Appends \a s to the end of the String. */ String &operator+=(const wchar_t* s); /*! * Appends \a s to the end of the String. */ String &operator+=(const char* s); /*! * Appends \a s to the end of the String. */ String &operator+=(wchar_t c); /*! * Appends \a c to the end of the String. */ String &operator+=(char c); /*! * Performs a shallow, implicitly shared, copy of \a s, overwriting the * String's current data. */ String &operator=(const String &s); /*! * Performs a deep copy of the data in \a s. */ String &operator=(const std::string &s); /*! * Performs a deep copy of the data in \a s. */ String &operator=(const std::wstring &s); /*! * Performs a deep copy of the data in \a s. */ String &operator=(const wchar_t *s); /*! * Performs a deep copy of the data in \a s. */ String &operator=(char c); /*! * Performs a deep copy of the data in \a s. */ String &operator=(wchar_t c); /*! * Performs a deep copy of the data in \a s. */ String &operator=(const char *s); /*! * Performs a deep copy of the data in \a v. */ String &operator=(const ByteVector &v); /*! * Exchanges the content of the String with the content of \a s. */ void swap(String &s) noexcept; /*! * To be able to use this class in a Map, this operator needed to be * implemented. Returns \c true if \a s is less than this string in a byte-wise * comparison. */ bool operator<(const String &s) const; protected: /*! * If this String is being shared via implicit sharing, do a deep copy of the * data and separate from the shared members. This should be called by all * non-const subclass members. */ void detach(); private: class StringPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<StringPrivate> d; }; } // namespace TagLib /*! * \relates TagLib::String * * Concatenates \a s1 and \a s2 and returns the result as a string. */ TAGLIB_EXPORT TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2); /*! * \relates TagLib::String * * Concatenates \a s1 and \a s2 and returns the result as a string. */ TAGLIB_EXPORT TagLib::String operator+(const char *s1, const TagLib::String &s2); /*! * \relates TagLib::String * * Concatenates \a s1 and \a s2 and returns the result as a string. */ TAGLIB_EXPORT TagLib::String operator+(const TagLib::String &s1, const char *s2); /*! * \relates TagLib::String * * Send the string to an output stream. */ TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::String &str); #endif �������taglib-2.0.2/taglib/toolkit/tstringlist.cpp���������������������������������������������������������0000664�0000000�0000000�00000010033�14662262111�0020774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tstringlist.h" using namespace TagLib; class StringList::StringListPrivate { }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// StringList StringList::split(const String &s, const String &pattern) { StringList l; int previousOffset = 0; for(int offset = s.find(pattern); offset != -1; offset = s.find(pattern, offset + 1)) { l.append(s.substr(previousOffset, offset - previousOffset)); previousOffset = offset + 1; } l.append(s.substr(previousOffset, s.size() - previousOffset)); return l; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// StringList::StringList() = default; StringList::StringList(const StringList &l) : List<String>(l) { // Uncomment if d is used, d.get() is nullptr and *d behavior undefined // *d = *l.d; } StringList::StringList(std::initializer_list<String> init) : List<String>(init) { } StringList &StringList::operator=(const StringList &l) { if(this == &l) return *this; List<String>::operator=(l); // Uncomment if d is used, d.get() is nullptr and *d behavior undefined // *d = *l.d; return *this; } StringList &StringList::operator=(std::initializer_list<String> init) { List<String>::operator=(init); return *this; } StringList::StringList(const String &s) { append(s); } StringList::StringList(const ByteVectorList &bl, String::Type t) { for(const auto &byte : bl) { append(String(byte, t)); } } StringList::~StringList() = default; String StringList::toString(const String &separator) const { String s; for(auto it = begin(); it != end(); ++it) { s += *it; if(std::next(it) != end()) s += separator; } return s; } StringList &StringList::append(const String &s) { List<String>::append(s); return *this; } StringList &StringList::append(const StringList &l) { List<String>::append(l); return *this; } //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// std::ostream &operator<<(std::ostream &s, const StringList &l) { s << l.toString(); return s; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tstringlist.h�����������������������������������������������������������0000664�0000000�0000000�00000010356�14662262111�0020451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_STRINGLIST_H #define TAGLIB_STRINGLIST_H #include "tstring.h" #include "tlist.h" #include "tbytevectorlist.h" #include "taglib_export.h" namespace TagLib { //! A list of strings /*! * This is a specialization of the List class with some convenience members * for string operations. */ class StringList : public List<String> { public: /*! * Constructs an empty StringList. */ TAGLIB_EXPORT StringList(); /*! * Make a shallow, implicitly shared, copy of \a l. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ TAGLIB_EXPORT StringList(const StringList &l); /*! * Construct a StringList with the contents of the braced initializer list. */ TAGLIB_EXPORT StringList(std::initializer_list<String> init); TAGLIB_EXPORT StringList &operator=(const StringList &); TAGLIB_EXPORT StringList &operator=(std::initializer_list<String> init); /*! * Constructs a StringList with \a s as a member. */ TAGLIB_EXPORT StringList(const String &s); /*! * Makes a deep copy of the data in \a bl. * * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when * used with other codecs it will simply print a warning and exit. */ TAGLIB_EXPORT StringList(const ByteVectorList &bl, String::Type t = String::Latin1); /*! * Destroys this StringList instance. */ TAGLIB_EXPORT ~StringList(); /*! * Concatenate the list of strings into one string separated by \a separator. */ TAGLIB_EXPORT String toString(const String &separator = " ") const; /*! * Appends \a s to the end of the list and returns a reference to the * list. */ TAGLIB_EXPORT StringList &append(const String &s); /*! * Appends all of the values in \a l to the end of the list and returns a * reference to the list. */ TAGLIB_EXPORT StringList &append(const StringList &l); /*! * Splits the String \a s into several strings at \a pattern. This will not include * the pattern in the returned strings. */ TAGLIB_EXPORT static StringList split(const String &s, const String &pattern); private: class StringListPrivate; std::unique_ptr<StringListPrivate> d; }; } // namespace TagLib /*! * \related TagLib::StringList * Send the StringList to an output stream. */ std::ostream TAGLIB_EXPORT &operator<<(std::ostream &s, const TagLib::StringList &l); #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tutils.h����������������������������������������������������������������0000664�0000000�0000000�00000013222�14662262111�0017402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TUTILS_H #define TAGLIB_TUTILS_H // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header #include <cstdio> #include <cstdarg> #include <cstring> #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(HAVE_MSC_BYTESWAP) # include <stdlib.h> #elif defined(HAVE_GLIBC_BYTESWAP) # include <byteswap.h> #elif defined(HAVE_MAC_BYTESWAP) # include <libkern/OSByteOrder.h> #elif defined(HAVE_OPENBSD_BYTESWAP) # include <sys/endian.h> #endif #include "tstring.h" namespace TagLib { namespace Utils { namespace { /*! * Reverses the order of bytes in a 16-bit integer. */ inline unsigned short byteSwap(unsigned short x) { #if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap16(x); #elif defined(HAVE_MSC_BYTESWAP) return _byteswap_ushort(x); #elif defined(HAVE_GLIBC_BYTESWAP) return __bswap_16(x); #elif defined(HAVE_MAC_BYTESWAP) return OSSwapInt16(x); #elif defined(HAVE_OPENBSD_BYTESWAP) return swap16(x); #else return ((x >> 8) & 0xff) | ((x & 0xff) << 8); #endif } /*! * Reverses the order of bytes in a 32-bit integer. */ inline unsigned int byteSwap(unsigned int x) { #if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap32(x); #elif defined(HAVE_MSC_BYTESWAP) return _byteswap_ulong(x); #elif defined(HAVE_GLIBC_BYTESWAP) return __bswap_32(x); #elif defined(HAVE_MAC_BYTESWAP) return OSSwapInt32(x); #elif defined(HAVE_OPENBSD_BYTESWAP) return swap32(x); #else return ((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) | ((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24); #endif } /*! * Reverses the order of bytes in a 64-bit integer. */ inline unsigned long long byteSwap(unsigned long long x) { #if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap64(x); #elif defined(HAVE_MSC_BYTESWAP) return _byteswap_uint64(x); #elif defined(HAVE_GLIBC_BYTESWAP) return __bswap_64(x); #elif defined(HAVE_MAC_BYTESWAP) return OSSwapInt64(x); #elif defined(HAVE_OPENBSD_BYTESWAP) return swap64(x); #else return ((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) | ((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56); #endif } /*! * Returns a formatted string just like standard sprintf(), but makes use of * safer functions such as snprintf() if available. */ inline String formatString(const char *format, ...) { // Sufficient buffer size for the current internal uses. // Consider changing this value when you use this function. static const size_t BufferSize = 128; va_list args; va_start(args, format); char buf[BufferSize]; int length; length = std::vsnprintf(buf, BufferSize, format, args); va_end(args); if(length > 0) return String(buf); return String(); } /*! * The types of byte order of the running system. */ enum ByteOrder { //! Little endian systems. LittleEndian, //! Big endian systems. BigEndian }; /*! * Returns the byte order of the system. */ constexpr ByteOrder systemByteOrder() { constexpr union IntCharUnion { int i; char c; constexpr IntCharUnion(int j) : i(j) {} } u{1}; if(u.c == 1) return LittleEndian; return BigEndian; } } // namespace } // namespace Utils } // namespace TagLib #endif #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tvariant.cpp������������������������������������������������������������0000664�0000000�0000000�00000023406�14662262111�0020246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tvariant.h" #include <variant> #include <iomanip> #include "tstring.h" #include "tstringlist.h" #include "tbytevector.h" #include "tbytevectorlist.h" using namespace TagLib; namespace { // The number and order of the template parameters must correspond to the // enum values in Variant::Type! using StdVariantType = std::variant< std::monostate, bool, int, unsigned int, long long, unsigned long long, double, TagLib::String, TagLib::StringList, TagLib::ByteVector, TagLib::ByteVectorList, List<TagLib::Variant>, Map<TagLib::String, TagLib::Variant> >; template<typename T> T getVariantValue(StdVariantType *v, bool *ok) { if(const auto valPtr = std::get_if<T>(v)) { if(ok) { *ok = true; } return *valPtr; } if(ok) { *ok = false; } return {}; } void printStringToStream(std::ostream &s, const String &v) { s << '"'; for(char c : v.to8Bit(true)) { if(c == '"') { s << "\\\""; } else { s << c; } } s << '"'; } void printByteVectorToStream(std::ostream &s, const String &v) { s << '"'; for(int c : v) { s << "\\x" << std::setfill('0') << std::setw(2) << std::right << std::hex << (c & 0xff); } s << std::dec << '"'; } // Print a possibly recursive Variant to an ostream. // The representation is JSON with hex strings for ByteVector. void printVariantToStream(std::ostream &s, const StdVariantType &v) { switch (v.index()) { case Variant::Void: s << "null"; break; case Variant::Bool: if(const auto valPtr = std::get_if<Variant::Bool>(&v)) { s << (*valPtr ? "true" : "false"); } break; case Variant::Int: if(const auto valPtr = std::get_if<Variant::Int>(&v)) { s << *valPtr; } break; case Variant::UInt: if(const auto valPtr = std::get_if<Variant::UInt>(&v)) { s << *valPtr; } break; case Variant::LongLong: if(const auto valPtr = std::get_if<Variant::LongLong>(&v)) { s << *valPtr; } break; case Variant::ULongLong: if(const auto valPtr = std::get_if<Variant::ULongLong>(&v)) { s << *valPtr; } break; case Variant::Double: if(const auto valPtr = std::get_if<Variant::Double>(&v)) { s << *valPtr; } break; case Variant::String: if(const auto valPtr = std::get_if<Variant::String>(&v)) { printStringToStream(s, *valPtr); } break; case Variant::StringList: if(const auto valPtr = std::get_if<Variant::StringList>(&v)) { s << '['; for(auto it = valPtr->cbegin(); it != valPtr->cend(); ++it) { if(it != valPtr->cbegin()) { s << ", "; } printStringToStream(s, *it); } s << ']'; } break; case Variant::ByteVector: if(const auto valPtr = std::get_if<Variant::ByteVector>(&v)) { printByteVectorToStream(s, *valPtr); } break; case Variant::ByteVectorList: if(const auto valPtr = std::get_if<Variant::ByteVectorList>(&v)) { s << '['; for(auto it = valPtr->cbegin(); it != valPtr->cend(); ++it) { if(it != valPtr->cbegin()) { s << ", "; } printByteVectorToStream(s, *it); } s << ']'; } break; case Variant::VariantList: if(const auto valPtr = std::get_if<Variant::VariantList>(&v)) { s << '['; for(auto it = valPtr->cbegin(); it != valPtr->cend(); ++it) { if(it != valPtr->cbegin()) { s << ", "; } s << *it; } s << ']'; } break; case Variant::VariantMap: if(const auto valPtr = std::get_if<Variant::VariantMap>(&v)) { s << '{'; for(auto it = valPtr->cbegin(); it != valPtr->cend(); ++it) { if(it != valPtr->cbegin()) { s << ", "; } printStringToStream(s, it->first); s << ": "; s << it->second; } s << '}'; } break; } } } // namespace class Variant::VariantPrivate { public: VariantPrivate() = default; VariantPrivate(StdVariantType v) : data(std::move(v)) {} StdVariantType data; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// Variant::Variant() : d(std::make_shared<VariantPrivate>()) { } Variant::Variant(int val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(unsigned int val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(long long val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(unsigned long long val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(bool val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(double val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const char *val) : d(std::make_shared<VariantPrivate>(TagLib::String(val))) { } Variant::Variant(const TagLib::String &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const TagLib::StringList &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const TagLib::ByteVector &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const TagLib::ByteVectorList &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const TagLib::List<TagLib::Variant> &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const TagLib::Map<TagLib::String, TagLib::Variant> &val) : d(std::make_shared<VariantPrivate>(val)) { } Variant::Variant(const Variant &) = default; //////////////////////////////////////////////////////////////////////////////// Variant::~Variant() = default; Variant::Type Variant::type() const { return static_cast<Type>(d->data.index()); } bool Variant::isEmpty() const { return type() == Void; } template<typename T> T Variant::value(bool *ok) const { return getVariantValue<T>(&d->data, ok); } template bool Variant::value(bool *ok) const; template int Variant::value(bool *ok) const; template unsigned int Variant::value(bool *ok) const; template long long Variant::value(bool *ok) const; template unsigned long long Variant::value(bool *ok) const; template double Variant::value(bool *ok) const; template String Variant::value(bool *ok) const; template StringList Variant::value(bool *ok) const; template ByteVector Variant::value(bool *ok) const; template ByteVectorList Variant::value(bool *ok) const; template VariantList Variant::value(bool *ok) const; template VariantMap Variant::value(bool *ok) const; bool Variant::toBool(bool *ok) const { return value<bool>(ok); } int Variant::toInt(bool *ok) const { return value<int>(ok); } unsigned int Variant::toUInt(bool *ok) const { return value<unsigned int>(ok); } long long Variant::toLongLong(bool *ok) const { return value<long long>(ok); } unsigned long long Variant::toULongLong(bool *ok) const { return value<unsigned long long>(ok); } double Variant::toDouble(bool *ok) const { return value<double>(ok); } TagLib::String Variant::toString(bool *ok) const { return value<TagLib::String>(ok); } TagLib::StringList Variant::toStringList(bool *ok) const { return value<TagLib::StringList>(ok); } TagLib::ByteVector Variant::toByteVector(bool *ok) const { return value<TagLib::ByteVector>(ok); } TagLib::ByteVectorList Variant::toByteVectorList(bool *ok) const { return value<TagLib::ByteVectorList>(ok); } TagLib::List<TagLib::Variant> Variant::toList(bool *ok) const { return value<TagLib::List<TagLib::Variant>>(ok); } TagLib::Map<TagLib::String, TagLib::Variant> Variant::toMap(bool *ok) const { return value<TagLib::Map<TagLib::String, TagLib::Variant>>(ok); } bool Variant::operator==(const Variant &v) const { return d == v.d || d->data == v.d->data; } bool Variant::operator!=(const Variant &v) const { return !(*this == v); } Variant &Variant::operator=(const Variant &) = default; //////////////////////////////////////////////////////////////////////////////// // related non-member functions //////////////////////////////////////////////////////////////////////////////// std::ostream &operator<<(std::ostream &s, const TagLib::Variant &v) { printVariantToStream(s, v.d->data); return s; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tvariant.h��������������������������������������������������������������0000664�0000000�0000000�00000017410�14662262111�0017711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_VARIANT_H #define TAGLIB_VARIANT_H #include <iosfwd> #include "tlist.h" #include "tmap.h" #include "taglib_export.h" // Forward declaration needed for friend function namespace TagLib { class Variant; } /*! * \relates TagLib::Variant * * Send the variant to an output stream. */ TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::Variant &v); namespace TagLib { class String; class StringList; class ByteVector; class ByteVectorList; //! An implicitly shared discriminated union. /*! * This is an implicitly shared discriminated union. * * The use of implicit sharing means that copying a variant is cheap. * These Variant objects are immutable (have only const methods). */ class TAGLIB_EXPORT Variant { public: /*! * Types which can be stored in a variant. */ // The number and order of these types must correspond to the template // parameters for StdVariantType in tvariant.cpp! enum Type { Void, //!< variant is empty Bool, //!< \c bool Int, //!< \c int UInt, //!< <tt>unsigned int</tt> LongLong, //!< <tt>long long</tt> ULongLong, //!< <tt>unsigned long long</tt> Double, //!< \c double String, //!< String StringList, //!< StringList ByteVector, //!< ByteVector ByteVectorList, //!< ByteVectorList VariantList, //!< \link TagLib::VariantList VariantList \endlink VariantMap //!< \link TagLib::VariantMap VariantMap \endlink }; /*! * Constructs an empty Variant. */ Variant(); Variant(int val); Variant(unsigned int val); Variant(long long val); Variant(unsigned long long val); Variant(bool val); Variant(double val); Variant(const char *val); Variant(const TagLib::String &val); Variant(const TagLib::StringList &val); Variant(const TagLib::ByteVector &val); Variant(const TagLib::ByteVectorList &val); Variant(const TagLib::List<TagLib::Variant> &val); Variant(const TagLib::Map<TagLib::String, TagLib::Variant> &val); /*! * Make a shallow, implicitly shared, copy of \a v. Because this is * implicitly shared, this method is lightweight and suitable for * pass-by-value usage. */ Variant(const Variant &v); /*! * Destroys this Variant instance. */ ~Variant(); /*! * Get the type which is currently stored in this Variant. */ Type type() const; /*! * Returns \c true if the Variant is empty. */ bool isEmpty() const; /*! * Extracts a value from the Variant. * If \a ok is passed, its boolean variable will be set to \c true if the * Variant contains the correct type, and the returned value is the value * of the Variant. Otherwise, the \a ok variable is set to \c false and * a dummy default value is returned. */ int toInt(bool *ok = nullptr) const; //! \copydoc toInt() unsigned int toUInt(bool *ok = nullptr) const; //! \copydoc toInt() long long toLongLong(bool *ok = nullptr) const; //! \copydoc toInt() unsigned long long toULongLong(bool *ok = nullptr) const; //! \copydoc toInt() bool toBool(bool *ok = nullptr) const; //! \copydoc toInt() double toDouble(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::String toString(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::StringList toStringList(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::ByteVector toByteVector(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::ByteVectorList toByteVectorList(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::List<TagLib::Variant> toList(bool *ok = nullptr) const; //! \copydoc toInt() TagLib::Map<TagLib::String, TagLib::Variant> toMap(bool *ok = nullptr) const; /*! * Extracts value of type \a T from the Variant. * If \a ok is passed, its boolean variable will be set to \c true if the * Variant contains the correct type, and the returned value is the value * of the Variant. Otherwise, the \a ok variable is set to \c false and * a dummy default value is returned. */ template<typename T> T value(bool *ok = nullptr) const; /*! * Returns \c true if the Variant and \a v are of the same type and contain the * same value. */ bool operator==(const Variant &v) const; /*! * Returns \c true if the Variant and \a v differ in type or value. */ bool operator!=(const Variant &v) const; /*! * Performs a shallow, implicitly shared, copy of \a v, overwriting the * Variant's current data. */ Variant &operator=(const Variant &v); private: friend TAGLIB_EXPORT std::ostream& ::operator<<(std::ostream &s, const TagLib::Variant &v); class VariantPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::shared_ptr<VariantPrivate> d; }; /*! A list of Variant elements. */ using VariantList = TagLib::List<TagLib::Variant>; /*! A map with String keys and Variant values. */ using VariantMap = TagLib::Map<TagLib::String, TagLib::Variant>; extern template TAGLIB_EXPORT bool Variant::value(bool *ok) const; extern template TAGLIB_EXPORT int Variant::value(bool *ok) const; extern template TAGLIB_EXPORT unsigned int Variant::value(bool *ok) const; extern template TAGLIB_EXPORT long long Variant::value(bool *ok) const; extern template TAGLIB_EXPORT unsigned long long Variant::value(bool *ok) const; extern template TAGLIB_EXPORT double Variant::value(bool *ok) const; extern template TAGLIB_EXPORT String Variant::value(bool *ok) const; extern template TAGLIB_EXPORT StringList Variant::value(bool *ok) const; extern template TAGLIB_EXPORT ByteVector Variant::value(bool *ok) const; extern template TAGLIB_EXPORT ByteVectorList Variant::value(bool *ok) const; extern template TAGLIB_EXPORT VariantList Variant::value(bool *ok) const; extern template TAGLIB_EXPORT VariantMap Variant::value(bool *ok) const; } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tversionnumber.cpp������������������������������������������������������0000664�0000000�0000000�00000005057�14662262111�0021502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2020 by Kevin Andre email : hyperquantum@gmail.com copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tversionnumber.h" #include "tstring.h" #include "taglib.h" using namespace TagLib; //////////////////////////////////////////////////////////////////////////////// // public methods //////////////////////////////////////////////////////////////////////////////// String VersionNumber::toString() const { return String::number(majorVersion()) + '.' + String::number(minorVersion()) + '.' + String::number(patchVersion()); } //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// VersionNumber TagLib::runtimeVersion() { return VersionNumber(TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tversionnumber.h��������������������������������������������������������0000664�0000000�0000000�00000011206�14662262111�0021140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2020 by Kevin Andre email : hyperquantum@gmail.com copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_VERSIONNUMBER_H #define TAGLIB_VERSIONNUMBER_H #include "taglib_export.h" namespace TagLib { class String; //! Version number with major, minor and patch segments. class TAGLIB_EXPORT VersionNumber { public: /*! * Constructs a version number from \a major, \a minor and \a patch segments. */ constexpr VersionNumber(unsigned int major, unsigned int minor, unsigned int patch = 0) : m_combined(((major & 0xff) << 16) | ((minor & 0xff) << 8) | (patch & 0xff)) { } /*! * Returns the version as an unsigned integer in the form * (major version << 16) | (minor version << 8) | (patch version), * e.g. 0x020100 for version 2.1.0. */ constexpr unsigned int combinedVersion() const { return m_combined; } /*! * Returns the major version, e.g. 2 */ constexpr unsigned int majorVersion() const { return (m_combined & 0xff0000) >> 16; } /*! * Returns the minor version, e.g. 1 */ constexpr unsigned int minorVersion() const { return (m_combined & 0xff00) >> 8; } /*! * Returns the patch version, e.g. 0 */ constexpr unsigned int patchVersion() const { return m_combined & 0xff; } /*! * Returns \c true if this version is equal to \a rhs. */ constexpr bool operator==(const VersionNumber &rhs) const { return m_combined == rhs.m_combined; } /*! * Returns \c true if this version is not equal to \a rhs. */ constexpr bool operator!=(const VersionNumber &rhs) const { return m_combined != rhs.m_combined; } /*! * Returns \c true if this version is less than \a rhs. */ constexpr bool operator<(const VersionNumber &rhs) const { return m_combined < rhs.m_combined; } /*! * Returns \c true if this version is greater than \a rhs. */ constexpr bool operator>(const VersionNumber &rhs) const { return m_combined > rhs.m_combined; } /*! * Returns \c true if this version is less or equal than \a rhs. */ constexpr bool operator<=(const VersionNumber &rhs) const { return m_combined <= rhs.m_combined; } /*! * Returns \c true if this version is greater or equal than \a rhs. */ constexpr bool operator>=(const VersionNumber &rhs) const { return m_combined >= rhs.m_combined; } /*! * Returns a string with major, minor, and patch versions separated by * periods. */ String toString() const; private: unsigned int m_combined; }; /*! * \relates TagLib::VersionNumber * Returns the version number of TagLib in use at runtime. * This does not need not be the version the application was compiled with. */ TAGLIB_EXPORT VersionNumber runtimeVersion(); } // namespace TagLib #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tzlib.cpp���������������������������������������������������������������0000664�0000000�0000000�00000006177�14662262111�0017550�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tzlib.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_ZLIB # include <zlib.h> # include "tstring.h" # include "tdebug.h" #endif using namespace TagLib; bool zlib::isAvailable() { #ifdef HAVE_ZLIB return true; #else return false; #endif } ByteVector zlib::decompress([[maybe_unused]] const ByteVector &data) { #ifdef HAVE_ZLIB z_stream stream = {}; if(inflateInit(&stream) != Z_OK) { debug("zlib::decompress() - Failed to initialize zlib."); return ByteVector(); } ByteVector inData = data; stream.avail_in = inData.size(); stream.next_in = reinterpret_cast<Bytef *>(inData.data()); ByteVector outData; do { constexpr unsigned int chunkSize = 1024; const size_t offset = outData.size(); outData.resize(outData.size() + chunkSize); stream.avail_out = static_cast<uInt>(chunkSize); stream.next_out = reinterpret_cast<Bytef *>(outData.data() + offset); if(const int result = inflate(&stream, Z_NO_FLUSH); result == Z_STREAM_ERROR || result == Z_NEED_DICT || result == Z_DATA_ERROR || result == Z_MEM_ERROR) { if(result != Z_STREAM_ERROR) inflateEnd(&stream); debug("zlib::decompress() - Error reading compressed stream."); return ByteVector(); } outData.resize(outData.size() - stream.avail_out); } while(stream.avail_out == 0); inflateEnd(&stream); return outData; #else return ByteVector(); #endif } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/toolkit/tzlib.h�����������������������������������������������������������������0000664�0000000�0000000�00000004306�14662262111�0017205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2016 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TZLIB_H #define TAGLIB_TZLIB_H #include "tbytevector.h" // THIS FILE IS NOT A PART OF THE TAGLIB API #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header namespace TagLib { namespace zlib { /*! * Returns whether or not zlib is installed and ready to use. */ bool TAGLIB_EXPORT isAvailable(); /*! * Decompress \a data by zlib. */ ByteVector decompress(const ByteVector &data); } // namespace zlib } // namespace TagLib #endif #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/trueaudio/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0016221�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/trueaudio/trueaudiofile.cpp�����������������������������������������������������0000664�0000000�0000000�00000020051�14662262111�0021564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "trueaudiofile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagunion.h" #include "tagutils.h" #include "id3v1tag.h" #include "id3v2tag.h" using namespace TagLib; namespace { enum { TrueAudioID3v2Index = 0, TrueAudioID3v1Index = 1 }; } // namespace class TrueAudio::File::FilePrivate { public: FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : ID3v2FrameFactory(frameFactory) { } const ID3v2::FrameFactory *ID3v2FrameFactory; offset_t ID3v2Location { -1 }; long ID3v2OriginalSize { 0 }; offset_t ID3v1Location { -1 }; TagUnion tag; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool TrueAudio::File::isSupported(IOStream *stream) { // A TrueAudio file has to start with "TTA". An ID3v2 tag may precede. const ByteVector id = Utils::readHeader(stream, 3, true); return id == "TTA"; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// TrueAudio::File::File(FileName file, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(file), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties); } TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } TrueAudio::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle, ID3v2::FrameFactory *frameFactory) : TagLib::File(stream), d(std::make_unique<FilePrivate>( frameFactory ? frameFactory : ID3v2::FrameFactory::instance())) { if(isOpen()) read(readProperties); } TrueAudio::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>(frameFactory)) { if(isOpen()) read(readProperties); } TrueAudio::File::~File() = default; TagLib::Tag *TrueAudio::File::tag() const { return &d->tag; } PropertyMap TrueAudio::File::properties() const { return d->tag.properties(); } void TrueAudio::File::removeUnsupportedProperties(const StringList &unsupported) { d->tag.removeUnsupportedProperties(unsupported); } PropertyMap TrueAudio::File::setProperties(const PropertyMap &properties) { if(ID3v1Tag()) ID3v1Tag()->setProperties(properties); return ID3v2Tag(true)->setProperties(properties); } TrueAudio::Properties *TrueAudio::File::audioProperties() const { return d->properties.get(); } bool TrueAudio::File::save() { if(readOnly()) { debug("TrueAudio::File::save() -- File is read only."); return false; } // Update ID3v2 tag if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) { // ID3v2 tag is not empty. Update the old one or create a new one. if(d->ID3v2Location < 0) d->ID3v2Location = 0; const ByteVector data = ID3v2Tag()->render(); insert(data, d->ID3v2Location, d->ID3v2OriginalSize); if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->ID3v2OriginalSize; d->ID3v2OriginalSize = data.size(); } else { // ID3v2 tag is empty. Remove the old one. if(d->ID3v2Location >= 0) { removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); if(d->ID3v1Location >= 0) d->ID3v1Location -= d->ID3v2OriginalSize; d->ID3v2Location = -1; d->ID3v2OriginalSize = 0; } } // Update ID3v1 tag if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. if(d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; } } return true; } ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(TrueAudioID3v1Index, create); } ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create) { return d->tag.access<ID3v2::Tag>(TrueAudioID3v2Index, create, d->ID3v2FrameFactory); } void TrueAudio::File::strip(int tags) { if(tags & ID3v1) d->tag.set(TrueAudioID3v1Index, nullptr); if(tags & ID3v2) d->tag.set(TrueAudioID3v2Index, nullptr); if(!ID3v1Tag()) ID3v2Tag(true); } bool TrueAudio::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } bool TrueAudio::File::hasID3v2Tag() const { return d->ID3v2Location >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void TrueAudio::File::read(bool readProperties) { // Look for an ID3v2 tag d->ID3v2Location = Utils::findID3v2(this); if(d->ID3v2Location >= 0) { d->tag.set(TrueAudioID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); } // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(TrueAudioID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); if(d->ID3v1Location < 0) ID3v2Tag(true); // Look for TrueAudio metadata if(readProperties) { offset_t streamLength; if(d->ID3v1Location >= 0) streamLength = d->ID3v1Location; else streamLength = length(); if(d->ID3v2Location >= 0) { seek(d->ID3v2Location + d->ID3v2OriginalSize); streamLength -= d->ID3v2Location + d->ID3v2OriginalSize; } else { seek(0); } d->properties = std::make_unique<Properties>(readBlock(TrueAudio::HeaderSize), streamLength); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/trueaudio/trueaudiofile.h�������������������������������������������������������0000664�0000000�0000000�00000023276�14662262111�0021245�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TRUEAUDIOFILE_H #define TAGLIB_TRUEAUDIOFILE_H #include "taglib.h" #include "tfile.h" #include "trueaudioproperties.h" namespace TagLib { class Tag; namespace ID3v2 { class Tag; class FrameFactory; } namespace ID3v1 { class Tag; } //! An implementation of TrueAudio metadata /*! * This is an implementation of TrueAudio metadata. * * This supports ID3v1 and ID3v2 tags as well as reading stream * properties from the file. */ namespace TrueAudio { //! An implementation of TagLib::File with TrueAudio specific methods /*! * This implements and provides an interface for TrueAudio files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to TrueAudio files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs a TrueAudio file from \a file. If \a readProperties is \c true * the file's audio properties will also be read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory (default if null). * * \note In the current implementation, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a TrueAudio file from \a file. If \a readProperties is \c true * the file's audio properties will also be read. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs a TrueAudio file from \a stream. If \a readProperties is \c true * the file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * \note In the current implementation, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average, ID3v2::FrameFactory *frameFactory = nullptr); /*! * Constructs a TrueAudio file from \a stream. If \a readProperties is \c true * the file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. * * If this file contains an ID3v2 tag, the frames will be created using * \a frameFactory. * * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated Use the constructor above. */ TAGLIB_DEPRECATED File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. */ TagLib::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * If the file contains both ID3v1 and v2 tags, only ID3v2 will be * converted to the PropertyMap. */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Creates in ID3v2 tag if necessary. If an ID3v1 tag exists, it will * be updated as well, within the limitations of ID3v1. */ PropertyMap setProperties(const PropertyMap &) override; void removeUnsupportedProperties(const StringList &unsupported) override; /*! * Returns the TrueAudio::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. */ bool save() override; /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is \c true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the TrueAudio::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the ID3v2 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v2 tag. If \a create is \c true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag <b>is still</b> owned by the TrueAudio::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v2Tag() */ ID3v2::Tag *ID3v2Tag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \note This will also invalidate pointers to the tags * as their memory will be freed. * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * * \see ID3v2Tag() */ bool hasID3v2Tag() const; /*! * Returns whether or not the given \a stream can be opened as a TrueAudio * file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace TrueAudio } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/trueaudio/trueaudioproperties.cpp�����������������������������������������������0000664�0000000�0000000�00000011107�14662262111�0023043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "trueaudioproperties.h" #include "tdebug.h" #include "tstring.h" using namespace TagLib; class TrueAudio::Properties::PropertiesPrivate { public: int version { 0 }; int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int bitsPerSample { 0 }; unsigned int sampleFrames { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// TrueAudio::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(data, streamLength); } TrueAudio::Properties::~Properties() = default; int TrueAudio::Properties::lengthInSeconds() const { return d->length / 1000; } int TrueAudio::Properties::lengthInMilliseconds() const { return d->length; } int TrueAudio::Properties::bitrate() const { return d->bitrate; } int TrueAudio::Properties::sampleRate() const { return d->sampleRate; } int TrueAudio::Properties::bitsPerSample() const { return d->bitsPerSample; } int TrueAudio::Properties::channels() const { return d->channels; } unsigned int TrueAudio::Properties::sampleFrames() const { return d->sampleFrames; } int TrueAudio::Properties::ttaVersion() const { return d->version; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void TrueAudio::Properties::read(const ByteVector &data, offset_t streamLength) { if(data.size() < 4) { debug("TrueAudio::Properties::read() -- data is too short."); return; } if(!data.startsWith("TTA")) { debug("TrueAudio::Properties::read() -- invalid header signature."); return; } unsigned int pos = 3; d->version = data[pos] - '0'; pos += 1; // According to http://en.true-audio.com/TTA_Lossless_Audio_Codec_-_Format_Description // TTA2 headers are in development, and have a different format if(1 == d->version) { if(data.size() < 18) { debug("TrueAudio::Properties::read() -- data is too short."); return; } // Skip the audio format pos += 2; d->channels = data.toShort(pos, false); pos += 2; d->bitsPerSample = data.toShort(pos, false); pos += 2; d->sampleRate = data.toUInt(pos, false); pos += 4; d->sampleFrames = data.toUInt(pos, false); if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/trueaudio/trueaudioproperties.h�������������������������������������������������0000664�0000000�0000000�00000010026�14662262111�0022507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_TRUEAUDIOPROPERTIES_H #define TAGLIB_TRUEAUDIOPROPERTIES_H #include "tbytevector.h" #include "audioproperties.h" namespace TagLib { namespace TrueAudio { static constexpr unsigned int HeaderSize = 18; //! An implementation of audio property reading for TrueAudio /*! * This reads the data from a TrueAudio stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of TrueAudio::Properties with the data read from the * ByteVector \a data. */ Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average); /*! * Destroys this TrueAudio::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in seconds. The length is rounded down to * the nearest whole second. * * \see lengthInMilliseconds() */ int lengthInSeconds() const override; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns the total number of sample frames */ unsigned int sampleFrames() const; /*! * Returns the major version number. */ int ttaVersion() const; private: void read(const ByteVector &data, offset_t streamLength); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace TrueAudio } // namespace TagLib #endif ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/wavpack/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015654�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/wavpack/wavpackfile.cpp���������������������������������������������������������0000664�0000000�0000000�00000015471�14662262111�0020664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "wavpackfile.h" #include "tdebug.h" #include "tpropertymap.h" #include "tagunion.h" #include "tagutils.h" #include "apefooter.h" #include "apetag.h" #include "id3v1tag.h" using namespace TagLib; namespace { enum { WavAPEIndex, WavID3v1Index }; } // namespace class WavPack::File::FilePrivate { public: offset_t APELocation { -1 }; long APESize { 0 }; offset_t ID3v1Location { -1 }; TagUnion tag; std::unique_ptr<Properties> properties; }; //////////////////////////////////////////////////////////////////////////////// // static members //////////////////////////////////////////////////////////////////////////////// bool WavPack::File::isSupported(IOStream *stream) { // A WavPack file has to start with "wvpk". const ByteVector id = Utils::readHeader(stream, 4, false); return id == "wvpk"; } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// WavPack::File::File(FileName file, bool readProperties, Properties::ReadStyle) : TagLib::File(file), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } WavPack::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : TagLib::File(stream), d(std::make_unique<FilePrivate>()) { if(isOpen()) read(readProperties); } WavPack::File::~File() = default; TagLib::Tag *WavPack::File::tag() const { return &d->tag; } PropertyMap WavPack::File::properties() const { return d->tag.properties(); } void WavPack::File::removeUnsupportedProperties(const StringList &unsupported) { d->tag.removeUnsupportedProperties(unsupported); } PropertyMap WavPack::File::setProperties(const PropertyMap &properties) { if(ID3v1Tag()) ID3v1Tag()->setProperties(properties); return APETag(true)->setProperties(properties); } WavPack::Properties *WavPack::File::audioProperties() const { return d->properties.get(); } bool WavPack::File::save() { if(readOnly()) { debug("WavPack::File::save() -- File is read only."); return false; } // Update ID3v1 tag if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { // ID3v1 tag is not empty. Update the old one or create a new one. if(d->ID3v1Location >= 0) { seek(d->ID3v1Location); } else { seek(0, End); d->ID3v1Location = tell(); } writeBlock(ID3v1Tag()->render()); } else { // ID3v1 tag is empty. Remove the old one. if(d->ID3v1Location >= 0) { truncate(d->ID3v1Location); d->ID3v1Location = -1; } } // Update APE tag if(APETag() && !APETag()->isEmpty()) { // APE tag is not empty. Update the old one or create a new one. if(d->APELocation < 0) { if(d->ID3v1Location >= 0) d->APELocation = d->ID3v1Location; else d->APELocation = length(); } const ByteVector data = APETag()->render(); insert(data, d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location += static_cast<long>(data.size()) - d->APESize; d->APESize = data.size(); } else { // APE tag is empty. Remove the old one. if(d->APELocation >= 0) { removeBlock(d->APELocation, d->APESize); if(d->ID3v1Location >= 0) d->ID3v1Location -= d->APESize; d->APELocation = -1; d->APESize = 0; } } return true; } ID3v1::Tag *WavPack::File::ID3v1Tag(bool create) { return d->tag.access<ID3v1::Tag>(WavID3v1Index, create); } APE::Tag *WavPack::File::APETag(bool create) { return d->tag.access<APE::Tag>(WavAPEIndex, create); } void WavPack::File::strip(int tags) { if(tags & ID3v1) d->tag.set(WavID3v1Index, nullptr); if(tags & APE) d->tag.set(WavAPEIndex, nullptr); if(!ID3v1Tag()) APETag(true); } bool WavPack::File::hasID3v1Tag() const { return d->ID3v1Location >= 0; } bool WavPack::File::hasAPETag() const { return d->APELocation >= 0; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void WavPack::File::read(bool readProperties) { // Look for an ID3v1 tag d->ID3v1Location = Utils::findID3v1(this); if(d->ID3v1Location >= 0) d->tag.set(WavID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); // Look for an APE tag d->APELocation = Utils::findAPE(this, d->ID3v1Location); if(d->APELocation >= 0) { d->tag.set(WavAPEIndex, new APE::Tag(this, d->APELocation)); d->APESize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APE::Footer::size() - d->APESize; } if(d->ID3v1Location < 0) APETag(true); // Look for WavPack audio properties if(readProperties) { offset_t streamLength; if(d->APELocation >= 0) streamLength = d->APELocation; else if(d->ID3v1Location >= 0) streamLength = d->ID3v1Location; else streamLength = length(); d->properties = std::make_unique<Properties>(this, streamLength); } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/wavpack/wavpackfile.h�����������������������������������������������������������0000664�0000000�0000000�00000020043�14662262111�0020320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_WVFILE_H #define TAGLIB_WVFILE_H #include "tfile.h" #include "taglib_export.h" #include "wavpackproperties.h" namespace TagLib { class Tag; namespace ID3v1 { class Tag; } namespace APE { class Tag; } //! An implementation of WavPack metadata /*! * This is an implementation of WavPack metadata. * * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream * properties from the file. */ namespace WavPack { //! An implementation of TagLib::File with WavPack specific methods /*! * This implements and provides an interface for WavPack files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to WavPack files. */ class TAGLIB_EXPORT File : public TagLib::File { public: /*! * This set of flags is used for various operations and is suitable for * being OR-ed together. */ enum TagTypes { //! Empty set. Matches no tag types. NoTags = 0x0000, //! Matches ID3v1 tags. ID3v1 = 0x0001, //! Matches APE tags. APE = 0x0002, //! Matches all tag types. AllTags = 0xffff }; /*! * Constructs a WavPack file from \a file. If \a readProperties is \c true the * file's audio properties will also be read using \a propertiesStyle. If * \c false, \a propertiesStyle is ignored */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Constructs a WavPack file from \a file. If \a readProperties is \c true the * file's audio properties will also be read using \a propertiesStyle. If * \c false, \a propertiesStyle is ignored. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; /*! * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag * or a combination of the two. */ TagLib::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * If the file contains both an APE and an ID3v1 tag, only APE * will be converted to the PropertyMap. */ PropertyMap properties() const override; void removeUnsupportedProperties(const StringList &unsupported) override; /*! * Implements the unified property interface -- import function. * Creates an APE tag if it does not exists and calls setProperties() on * that. Any existing ID3v1 tag will be updated as well. */ PropertyMap setProperties(const PropertyMap&) override; /*! * Returns the MPC::Properties for this file. If no audio properties * were read then this will return a null pointer. */ Properties *audioProperties() const override; /*! * Saves the file. * * This returns \c true if the save was successful. */ bool save() override; /*! * Returns a pointer to the ID3v1 tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is \c true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag <b>is still</b> owned by the WavPack::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasID3v1Tag() */ ID3v1::Tag *ID3v1Tag(bool create = false); /*! * Returns a pointer to the APE tag of the file. * * If \a create is \c false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is \c true it will create * an APE tag if one does not exist and returns a valid pointer. * * \note This may return a valid pointer regardless of whether or not the * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag <b>is still</b> owned by the WavPack::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. * * \see hasAPETag() */ APE::Tag *APETag(bool create = false); /*! * This will remove the tags that match the OR-ed together TagTypes from the * file. By default it removes all tags. * * \note This will also invalidate pointers to the tags * as their memory will be freed. * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * * \see ID3v1Tag() */ bool hasID3v1Tag() const; /*! * Returns whether or not the file on disk actually has an APE tag. * * \see APETag() */ bool hasAPETag() const; /*! * Check if the given \a stream can be opened as a WavPack file. * * \note This method is designed to do a quick check. The result may * not necessarily be correct. */ static bool isSupported(IOStream *stream); private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace WavPack } // namespace TagLib #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/wavpack/wavpackproperties.cpp���������������������������������������������������0000664�0000000�0000000�00000025545�14662262111�0022144�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "wavpackproperties.h" #include <cstdint> #include <array> #include "tstring.h" #include "tdebug.h" #include "wavpackfile.h" // Implementation of this class is based on the information at: // http://www.wavpack.com/file_format.txt using namespace TagLib; class WavPack::Properties::PropertiesPrivate { public: int length { 0 }; int bitrate { 0 }; int sampleRate { 0 }; int channels { 0 }; int version { 0 }; int bitsPerSample { 0 }; bool lossless { false }; unsigned int sampleFrames { 0 }; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// WavPack::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) : AudioProperties(style), d(std::make_unique<PropertiesPrivate>()) { read(file, streamLength); } WavPack::Properties::~Properties() = default; int WavPack::Properties::lengthInMilliseconds() const { return d->length; } int WavPack::Properties::bitrate() const { return d->bitrate; } int WavPack::Properties::sampleRate() const { return d->sampleRate; } int WavPack::Properties::channels() const { return d->channels; } int WavPack::Properties::version() const { return d->version; } int WavPack::Properties::bitsPerSample() const { return d->bitsPerSample; } bool WavPack::Properties::isLossless() const { return d->lossless; } unsigned int WavPack::Properties::sampleFrames() const { return d->sampleFrames; } //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// #define BYTES_STORED 3 #define MONO_FLAG 4 #define HYBRID_FLAG 8 #define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM) #define SHIFT_LSB 13 #define SHIFT_MASK (0x1fL << SHIFT_LSB) #define SRATE_LSB 23 #define SRATE_MASK (0xfL << SRATE_LSB) #define MIN_STREAM_VERS 0x402 #define MAX_STREAM_VERS 0x410 #define INITIAL_BLOCK 0x800 #define FINAL_BLOCK 0x1000 #define ID_DSD_BLOCK 0x0e #define ID_OPTIONAL_DATA 0x20 #define ID_UNIQUE 0x3f #define ID_ODD_SIZE 0x40 #define ID_LARGE 0x80 #define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) namespace { constexpr std::array sampleRates { 6000U, 8000U, 9600U, 11025U, 12000U, 16000U, 22050U, 24000U, 32000U, 44100U, 48000U, 64000U, 88200U, 96000U, 192000U, 0U }; /*! * Given a WavPack \a block (complete, but not including the 32-byte header), * parse the metadata blocks until an \a id block is found and return the * contained data, or zero if no such block is found. * Supported values for \a id are ID_SAMPLE_RATE and ID_DSD_BLOCK. */ int getMetaDataChunk(const ByteVector &block, unsigned char id) { if(id != ID_SAMPLE_RATE && id != ID_DSD_BLOCK) return 0; const int blockSize = static_cast<int>(block.size()); int index = 0; while(index + 1 < blockSize) { const auto metaId = static_cast<unsigned char>(block[index]); int metaBc = static_cast<unsigned char>(block[index + 1]) << 1; index += 2; if(metaId & ID_LARGE) { if(index + 2 > blockSize) return 0; metaBc += (static_cast<uint32_t>(static_cast<unsigned char>(block[index])) << 9) + (static_cast<uint32_t>(static_cast<unsigned char>(block[index + 1])) << 17); index += 2; } if(index + metaBc > blockSize) return 0; // if we got a sample rate, return it if(id == ID_SAMPLE_RATE && (metaId & ID_UNIQUE) == ID_SAMPLE_RATE && metaBc == 4) { int sampleRate = static_cast<int32_t>(static_cast<unsigned char>(block[index])); sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 1])) << 8; sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 2])) << 16; // only use 4th byte if it's really there if(!(metaId & ID_ODD_SIZE)) sampleRate |= static_cast<int32_t>(static_cast<unsigned char>(block[index + 3]) & 0x7f) << 24; return sampleRate; } // if we got DSD block, return the specified rate shift amount if(id == ID_DSD_BLOCK && (metaId & ID_UNIQUE) == ID_DSD_BLOCK && metaBc > 0) { if(const auto rateShift = static_cast<unsigned char>(block[index]); rateShift <= 31) return rateShift; } index += metaBc; } return 0; } /*! * Given a WavPack block (complete, but not including the 32-byte header), * parse the metadata blocks until an ID_SAMPLE_RATE block is found and * return the non-standard sample rate contained there, or zero if no such * block is found. */ int getNonStandardRate(const ByteVector &block) { return getMetaDataChunk(block, ID_SAMPLE_RATE); } /*! * Given a WavPack block (complete, but not including the 32-byte header), * parse the metadata blocks until a DSD audio data block is found and return * the sample-rate shift value contained there, or zero if no such block is * found. The nominal sample rate of DSD audio files (found in the header) * must be left-shifted by this amount to get the actual "byte" sample rate. * Note that 8-bit bytes are the "atoms" of the DSD audio coding (for * decoding, seeking, etc), so the shifted rate must be further multiplied by * 8 to get the actual DSD bit sample rate. */ int getDsdRateShifter(const ByteVector &block) { return getMetaDataChunk(block, ID_DSD_BLOCK); } } // namespace void WavPack::Properties::read(File *file, offset_t streamLength) { offset_t offset = 0; while(true) { file->seek(offset); const ByteVector data = file->readBlock(32); if(data.size() < 32) { debug("WavPack::Properties::read() -- data is too short."); break; } if(!data.startsWith("wvpk")) { debug("WavPack::Properties::read() -- Block header not found."); break; } const unsigned int blockSize = data.toUInt(4, false); const unsigned int smplFrames = data.toUInt(12, false); const unsigned int blockSamples = data.toUInt(20, false); const unsigned int flags = data.toUInt(24, false); unsigned int smplRate = sampleRates[(flags & SRATE_MASK) >> SRATE_LSB]; if(!blockSamples) { // ignore blocks with no samples offset += blockSize + 8; continue; } if(blockSize < 24 || blockSize > 1048576) { debug("WavPack::Properties::read() -- Invalid block header found."); break; } // For non-standard sample rates or DSD audio files, we must read and parse the block // to actually determine the sample rate. if(!smplRate || (flags & DSD_FLAG)) { const unsigned int adjustedBlockSize = blockSize - 24; const ByteVector block = file->readBlock(adjustedBlockSize); if(block.size() < adjustedBlockSize) { debug("WavPack::Properties::read() -- block is too short."); break; } if(!smplRate) smplRate = static_cast<unsigned int>(getNonStandardRate(block)); if(smplRate && (flags & DSD_FLAG)) smplRate <<= getDsdRateShifter(block); } if(flags & INITIAL_BLOCK) { d->version = data.toShort(8, false); if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) break; d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); d->sampleRate = static_cast<int>(smplRate); d->lossless = !(flags & HYBRID_FLAG); d->sampleFrames = smplFrames; } d->channels += (flags & MONO_FLAG) ? 1 : 2; if(flags & FINAL_BLOCK) break; offset += blockSize + 8; } if(d->sampleFrames == ~0u) d->sampleFrames = seekFinalIndex(file, streamLength); if(d->sampleFrames > 0 && d->sampleRate > 0) { const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate; d->length = static_cast<int>(length + 0.5); d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5); } } unsigned int WavPack::Properties::seekFinalIndex(File *file, offset_t streamLength) { offset_t offset = streamLength; while (offset >= 32) { offset = file->rfind("wvpk", offset - 4); if(offset == -1) return 0; file->seek(offset); const ByteVector data = file->readBlock(32); if(data.size() < 32) return 0; const unsigned int blockSize = data.toUInt(4, false); const unsigned int blockIndex = data.toUInt(16, false); const unsigned int blockSamples = data.toUInt(20, false); const unsigned int flags = data.toUInt(24, false); const int vers = data.toShort(8, false); // try not to trigger on a spurious "wvpk" in WavPack binary block data if(vers < MIN_STREAM_VERS || vers > MAX_STREAM_VERS || (blockSize & 1) || blockSize < 24 || blockSize >= 1048576 || blockSamples > 131072) continue; if (blockSamples && (flags & FINAL_BLOCK)) return blockIndex + blockSamples; } return 0; } �����������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/wavpack/wavpackproperties.h�����������������������������������������������������0000664�0000000�0000000�00000007656�14662262111�0021614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2006 by Lukáš Lalinský email : lalinsky@gmail.com copyright : (C) 2004 by Allan Sandfeld Jensen email : kde@carewolf.org (original MPC implementation) ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_WVPROPERTIES_H #define TAGLIB_WVPROPERTIES_H #include "taglib_export.h" #include "audioproperties.h" namespace TagLib { namespace WavPack { class File; static constexpr unsigned int HeaderSize = 32; //! An implementation of audio property reading for WavPack /*! * This reads the data from a WavPack stream found in the AudioProperties * API. */ class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! * Create an instance of WavPack::Properties. */ Properties(File *file, offset_t streamLength, ReadStyle style = Average); /*! * Destroys this WavPack::Properties instance. */ ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; /*! * Returns the length of the file in milliseconds. * * \see lengthInSeconds() */ int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ int bitrate() const override; /*! * Returns the sample rate in Hz. 0 means unknown or custom. */ int sampleRate() const override; /*! * Returns the number of audio channels. */ int channels() const override; /*! * Returns the number of bits per audio sample. */ int bitsPerSample() const; /*! * Returns whether or not the file is lossless encoded. */ bool isLossless() const; /*! * Returns the total number of audio samples in file. */ unsigned int sampleFrames() const; /*! * Returns the WavPack version. */ int version() const; private: void read(File *file, offset_t streamLength); unsigned int seekFinalIndex(File *file, offset_t streamLength); class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace WavPack } // namespace TagLib #endif ����������������������������������������������������������������������������������taglib-2.0.2/taglib/xm/�����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014644�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/xm/xmfile.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000040635�14662262111�0016644�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "xmfile.h" #include <algorithm> #include <utility> #include <numeric> #include "tstringlist.h" #include "tdebug.h" #include "tpropertymap.h" #include "modfileprivate.h" using namespace TagLib; using namespace XM; namespace { /*! * The Reader classes are helpers to make handling of the stripped XM * format more easy. In the stripped XM format certain header sizes might * be smaller than one would expect. The fields that are not included * are then just some predefined valued (e.g. 0). * * Using these classes this code: * * if(headerSize >= 4) { * if(!readU16L(value1)) ERROR(); * if(headerSize >= 8) { * if(!readU16L(value2)) ERROR(); * if(headerSize >= 12) { * if(!readString(value3, 22)) ERROR(); * ... * } * } * } * * Becomes: * * StructReader header; * header.u16L(value1).u16L(value2).string(value3, 22). ...; * if(header.read(*this, headerSize) < std::min(header.size(), headerSize)) * ERROR(); * * Maybe if this is useful to other formats these classes can be moved to * their own public files. */ class Reader { public: virtual ~Reader() = default; /*! * Reads associated values from \a file, but never reads more * then \a limit bytes. */ virtual unsigned int read(TagLib::File &file, unsigned int limit) = 0; /*! * Returns the number of bytes this reader would like to read. */ virtual unsigned int size() const = 0; }; class SkipReader : public Reader { public: SkipReader(unsigned int size) : m_size(size) { } unsigned int read(TagLib::File &file, unsigned int limit) override { unsigned int count = std::min(m_size, limit); file.seek(count, TagLib::File::Current); return count; } unsigned int size() const override { return m_size; } private: unsigned int m_size; }; template<typename T> class ValueReader : public Reader { public: ValueReader(T &value) : value(value) { } protected: T &value; }; class StringReader : public ValueReader<String> { public: StringReader(String &string, unsigned int size) : ValueReader<String>(string), m_size(size) { } unsigned int read(TagLib::File &file, unsigned int limit) override { ByteVector data = file.readBlock(std::min(m_size, limit)); unsigned int count = data.size(); int index = data.find(static_cast<char>(0)); if(index > -1) { data.resize(index); } data.replace('\xff', ' '); value = data; return count; } unsigned int size() const override { return m_size; } private: unsigned int m_size; }; class ByteReader : public ValueReader<unsigned char> { public: using ValueReader::ValueReader; unsigned int read(TagLib::File &file, unsigned int limit) override { ByteVector data = file.readBlock(std::min(1U,limit)); if(!data.isEmpty()) { value = data[0]; } return data.size(); } unsigned int size() const override { return 1; } }; template<typename T> class NumberReader : public ValueReader<T> { public: NumberReader(T &value, bool bigEndian) : ValueReader<T>(value), bigEndian(bigEndian) { } protected: bool bigEndian; }; class U16Reader : public NumberReader<unsigned short> { public: U16Reader(unsigned short &value, bool bigEndian) : NumberReader<unsigned short>(value, bigEndian) {} unsigned int read(TagLib::File &file, unsigned int limit) override { ByteVector data = file.readBlock(std::min(2U,limit)); value = data.toUShort(bigEndian); return data.size(); } unsigned int size() const override { return 2; } }; class U32Reader : public NumberReader<unsigned long> { public: U32Reader(unsigned long &value, bool bigEndian = true) : NumberReader<unsigned long>(value, bigEndian) { } unsigned int read(TagLib::File &file, unsigned int limit) override { ByteVector data = file.readBlock(std::min(4U,limit)); value = data.toUInt(bigEndian); return data.size(); } unsigned int size() const override { return 4; } }; class StructReader : public Reader { public: /*! * Add a nested reader. This reader takes ownership. */ StructReader &reader(std::unique_ptr<Reader> reader) { m_readers.push_back(std::move(reader)); return *this; } /*! * Don't read anything but skip \a size bytes. */ StructReader &skip(unsigned int size) { m_readers.push_back(std::make_unique<SkipReader>(size)); return *this; } /*! * Read a string of \a size characters (bytes) into \a string. */ StructReader &string(String &string, unsigned int size) { m_readers.push_back(std::make_unique<StringReader>(string, size)); return *this; } /*! * Read a byte into \a byte. */ StructReader &byte(unsigned char &byte) { m_readers.push_back(std::make_unique<ByteReader>(byte)); return *this; } /*! * Read a unsigned 16 Bit integer into \a number. The byte order * is controlled by \a bigEndian. */ StructReader &u16(unsigned short &number, bool bigEndian) { m_readers.push_back(std::make_unique<U16Reader>(number, bigEndian)); return *this; } /*! * Read a unsigned 16 Bit little endian integer into \a number. */ StructReader &u16L(unsigned short &number) { return u16(number, false); } /*! * Read a unsigned 16 Bit big endian integer into \a number. */ StructReader &u16B(unsigned short &number) { return u16(number, true); } /*! * Read a unsigned 32 Bit integer into \a number. The byte order * is controlled by \a bigEndian. */ StructReader &u32(unsigned long &number, bool bigEndian) { m_readers.push_back(std::make_unique<U32Reader>(number, bigEndian)); return *this; } /*! * Read a unsigned 32 Bit little endian integer into \a number. */ StructReader &u32L(unsigned long &number) { return u32(number, false); } /*! * Read a unsigned 32 Bit big endian integer into \a number. */ StructReader &u32B(unsigned long &number) { return u32(number, true); } unsigned int size() const override { return std::accumulate(m_readers.cbegin(), m_readers.cend(), 0U, [](unsigned int acc, const auto &rdr) { return acc + rdr->size(); }); } unsigned int read(TagLib::File &file, unsigned int limit) override { unsigned int sumcount = 0; for(const auto &rdr : std::as_const(m_readers)) { if(limit == 0) break; unsigned int count = rdr->read(file, limit); limit -= count; sumcount += count; } return sumcount; } private: std::list<std::unique_ptr<Reader>> m_readers; }; } // namespace class XM::File::FilePrivate { public: FilePrivate(AudioProperties::ReadStyle propertiesStyle) : properties(propertiesStyle) { } Mod::Tag tag; XM::Properties properties; }; XM::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(file), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } XM::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle propertiesStyle) : Mod::FileBase(stream), d(std::make_unique<FilePrivate>(propertiesStyle)) { if(isOpen()) read(readProperties); } XM::File::~File() = default; Mod::Tag *XM::File::tag() const { return &d->tag; } PropertyMap XM::File::properties() const { return d->tag.properties(); } PropertyMap XM::File::setProperties(const PropertyMap &properties) { return d->tag.setProperties(properties); } XM::Properties *XM::File::audioProperties() const { return &d->properties; } bool XM::File::save() { if(readOnly()) { debug("XM::File::save() - Cannot save to a read only file."); return false; } seek(17); writeString(d->tag.title(), 20); seek(38); writeString(d->tag.trackerName(), 20); seek(60); unsigned long headerSize = 0; if(!readU32L(headerSize)) return false; seek(70); unsigned short patternCount = 0; unsigned short instrumentCount = 0; if(!readU16L(patternCount) || !readU16L(instrumentCount)) return false; offset_t pos = 60 + headerSize; // need to read patterns again in order to seek to the instruments: for(unsigned short i = 0; i < patternCount; ++ i) { seek(pos); unsigned long patternHeaderLength = 0; if(!readU32L(patternHeaderLength) || patternHeaderLength < 4) return false; seek(pos + 7); unsigned short dataSize = 0; if (!readU16L(dataSize)) return false; pos += patternHeaderLength + dataSize; } const StringList lines = d->tag.comment().split("\n"); unsigned int sampleNameIndex = instrumentCount; for(unsigned short i = 0; i < instrumentCount; ++ i) { seek(pos); unsigned long instrumentHeaderSize = 0; if(!readU32L(instrumentHeaderSize) || instrumentHeaderSize < 4) return false; seek(pos + 4); const auto len = std::min(22UL, instrumentHeaderSize - 4); if(i >= lines.size()) writeString(String(), len); else writeString(lines[i], len); unsigned short sampleCount = 0; if(instrumentHeaderSize >= 29U) { seek(pos + 27); if(!readU16L(sampleCount)) return false; } unsigned long sampleHeaderSize = 0; if(sampleCount > 0) { seek(pos + 29); if(instrumentHeaderSize < 33U || !readU32L(sampleHeaderSize)) return false; } pos += instrumentHeaderSize; for(unsigned short j = 0; j < sampleCount; ++ j) { if(sampleHeaderSize > 4U) { seek(pos); if(unsigned long sampleLength = 0; !readU32L(sampleLength)) return false; if(sampleHeaderSize > 18U) { seek(pos + 18); const auto sz = std::min(sampleHeaderSize - 18, 22UL); if(sampleNameIndex >= lines.size()) writeString(String(), sz); else writeString(lines[sampleNameIndex ++], sz); } } pos += sampleHeaderSize; } } return true; } void XM::File::read(bool) { if(!isOpen()) return; seek(0); ByteVector magic = readBlock(17); // it's all 0x00 for stripped XM files: READ_ASSERT(magic == "Extended Module: " || magic == ByteVector(17, 0)); READ_STRING(d->tag.setTitle, 20); READ_BYTE_AS(escape); // in stripped XM files this is 0x00: READ_ASSERT(escape == 0x1A || escape == 0x00); READ_STRING(d->tag.setTrackerName, 20); READ_U16L(d->properties.setVersion); READ_U32L_AS(headerSize); READ_ASSERT(headerSize >= 4); unsigned short length = 0; unsigned short restartPosition = 0; unsigned short channels = 0; unsigned short patternCount = 0; unsigned short instrumentCount = 0; unsigned short flags = 0; unsigned short tempo = 0; unsigned short bpmSpeed = 0; StructReader header; header.u16L(length) .u16L(restartPosition) .u16L(channels) .u16L(patternCount) .u16L(instrumentCount) .u16L(flags) .u16L(tempo) .u16L(bpmSpeed); unsigned int count = header.read(*this, static_cast<unsigned int>(headerSize - 4)); unsigned int size = std::min(static_cast<unsigned int>(headerSize - 4), header.size()); READ_ASSERT(count == size); d->properties.setLengthInPatterns(length); d->properties.setRestartPosition(restartPosition); d->properties.setChannels(channels); d->properties.setPatternCount(patternCount); d->properties.setInstrumentCount(instrumentCount); d->properties.setFlags(flags); d->properties.setTempo(tempo); d->properties.setBpmSpeed(bpmSpeed); seek(60 + headerSize); // read patterns: for(unsigned short i = 0; i < patternCount; ++ i) { READ_U32L_AS(patternHeaderLength); READ_ASSERT(patternHeaderLength >= 4); unsigned char packingType = 0; unsigned short rowCount = 0; unsigned short dataSize = 0; StructReader pattern; pattern.byte(packingType).u16L(rowCount).u16L(dataSize); unsigned int ptCnt = pattern.read(*this, static_cast<unsigned int>(patternHeaderLength - 4)); READ_ASSERT(ptCnt == std::min(patternHeaderLength - 4U, static_cast<unsigned long>(pattern.size()))); seek(patternHeaderLength - (4 + ptCnt) + dataSize, Current); } StringList instrumentNames; StringList sampleNames; unsigned int sumSampleCount = 0; // read instruments: for(unsigned short i = 0; i < instrumentCount; ++ i) { READ_U32L_AS(instrumentHeaderSize); READ_ASSERT(instrumentHeaderSize >= 4); String instrumentName; unsigned char instrumentType = 0; unsigned short sampleCount = 0; StructReader instrument; instrument.string(instrumentName, 22).byte(instrumentType).u16L(sampleCount); // 4 for instrumentHeaderSize unsigned int inCnt = 4 + instrument.read(*this, static_cast<unsigned int>(instrumentHeaderSize - 4)); READ_ASSERT(inCnt == std::min(instrumentHeaderSize, static_cast<unsigned long>(instrument.size() + 4))); offset_t offset = 0; if(sampleCount > 0) { unsigned long sampleHeaderSize = 0; sumSampleCount += sampleCount; // wouldn't know which header size to assume otherwise: READ_ASSERT(instrumentHeaderSize >= inCnt + 4 && readU32L(sampleHeaderSize)); // skip unhandled header proportion: seek(instrumentHeaderSize - inCnt - 4, Current); for(unsigned short j = 0; j < sampleCount; ++ j) { unsigned long sampleLength = 0; unsigned long loopStart = 0; unsigned long loopLength = 0; unsigned char volume = 0; unsigned char finetune = 0; unsigned char sampleType = 0; unsigned char panning = 0; unsigned char noteNumber = 0; unsigned char compression = 0; String sampleName; StructReader sample; sample.u32L(sampleLength) .u32L(loopStart) .u32L(loopLength) .byte(volume) .byte(finetune) .byte(sampleType) .byte(panning) .byte(noteNumber) .byte(compression) .string(sampleName, 22); unsigned int smCnt = sample.read(*this, static_cast<unsigned int>(sampleHeaderSize)); READ_ASSERT(smCnt == std::min(sampleHeaderSize, static_cast<unsigned long>(sample.size()))); // skip unhandled header proportion: seek(sampleHeaderSize - smCnt, Current); offset += sampleLength; sampleNames.append(sampleName); } } else { offset = instrumentHeaderSize - inCnt; } instrumentNames.append(instrumentName); seek(offset, Current); } d->properties.setSampleCount(sumSampleCount); String comment(instrumentNames.toString("\n")); if(!sampleNames.isEmpty()) { comment += "\n"; comment += sampleNames.toString("\n"); } d->tag.setComment(comment); } ���������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/xm/xmfile.h���������������������������������������������������������������������0000664�0000000�0000000�00000011314�14662262111�0016301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_XMFILE_H #define TAGLIB_XMFILE_H #include "tfile.h" #include "taglib_export.h" #include "audioproperties.h" #include "modfilebase.h" #include "modtag.h" #include "xmproperties.h" namespace TagLib { //! An implementation of Extended Module metadata /*! * This is an implementation of Extended Module metadata. */ namespace XM { //! An implementation of TagLib::File with XM specific methods /*! * This implements and provides an interface for XM files to the * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing * the abstract TagLib::File API as well as providing some additional * information specific to XM files. */ class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! * Constructs an Extended Module file from \a file. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Constructs an Extended Module file from \a stream. * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. The audio properties are always * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Destroys this instance of the File. */ ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; Mod::Tag *tag() const override; /*! * Implements the unified property interface -- export function. * Forwards to Mod::Tag::properties(). */ PropertyMap properties() const override; /*! * Implements the unified property interface -- import function. * Forwards to Mod::Tag::setProperties(). */ PropertyMap setProperties(const PropertyMap &) override; /*! * Returns the XM::Properties for this file. If no audio properties * were read then this will return a null pointer. */ XM::Properties *audioProperties() const override; /*! * Save the file. * This is the same as calling save(AllTags); * * \note Saving Extended Module tags is not supported. */ bool save() override; private: void read(bool readProperties); class FilePrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<FilePrivate> d; }; } // namespace XM } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/xm/xmproperties.cpp�������������������������������������������������������������0000664�0000000�0000000�00000010060�14662262111�0020106�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "xmproperties.h" using namespace TagLib; using namespace XM; class XM::Properties::PropertiesPrivate { public: unsigned short lengthInPatterns { 0 }; int channels { 0 }; unsigned short version { 0 }; unsigned short restartPosition { 0 }; unsigned short patternCount { 0 }; unsigned short instrumentCount { 0 }; unsigned int sampleCount { 0 }; unsigned short flags { 0 }; unsigned short tempo { 0 }; unsigned short bpmSpeed { 0 }; }; XM::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), d(std::make_unique<PropertiesPrivate>()) { } XM::Properties::~Properties() = default; int XM::Properties::channels() const { return d->channels; } unsigned short XM::Properties::lengthInPatterns() const { return d->lengthInPatterns; } unsigned short XM::Properties::version() const { return d->version; } unsigned short XM::Properties::restartPosition() const { return d->restartPosition; } unsigned short XM::Properties::patternCount() const { return d->patternCount; } unsigned short XM::Properties::instrumentCount() const { return d->instrumentCount; } unsigned int XM::Properties::sampleCount() const { return d->sampleCount; } unsigned short XM::Properties::flags() const { return d->flags; } unsigned short XM::Properties::tempo() const { return d->tempo; } unsigned short XM::Properties::bpmSpeed() const { return d->bpmSpeed; } void XM::Properties::setLengthInPatterns(unsigned short lengthInPatterns) { d->lengthInPatterns = lengthInPatterns; } void XM::Properties::setChannels(int channels) { d->channels = channels; } void XM::Properties::setVersion(unsigned short version) { d->version = version; } void XM::Properties::setRestartPosition(unsigned short restartPosition) { d->restartPosition = restartPosition; } void XM::Properties::setPatternCount(unsigned short patternCount) { d->patternCount = patternCount; } void XM::Properties::setInstrumentCount(unsigned short instrumentCount) { d->instrumentCount = instrumentCount; } void XM::Properties::setSampleCount(unsigned int sampleCount) { d->sampleCount = sampleCount; } void XM::Properties::setFlags(unsigned short flags) { d->flags = flags; } void XM::Properties::setTempo(unsigned short tempo) { d->tempo = tempo; } void XM::Properties::setBpmSpeed(unsigned short bpmSpeed) { d->bpmSpeed = bpmSpeed; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/taglib/xm/xmproperties.h���������������������������������������������������������������0000664�0000000�0000000�00000006622�14662262111�0017564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_XMPROPERTIES_H #define TAGLIB_XMPROPERTIES_H #include "tstring.h" #include "audioproperties.h" namespace TagLib { namespace XM { //! An implementation of audio property reading for XM class TAGLIB_EXPORT Properties : public AudioProperties { public: /*! Flag bits. */ enum { LinearFreqTable = 1 // otherwise it is the amiga freq. table }; Properties(AudioProperties::ReadStyle propertiesStyle); ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; int channels() const override; unsigned short lengthInPatterns() const; unsigned short version() const; unsigned short restartPosition() const; unsigned short patternCount() const; unsigned short instrumentCount() const; unsigned int sampleCount() const; unsigned short flags() const; unsigned short tempo() const; unsigned short bpmSpeed() const; void setChannels(int channels); void setLengthInPatterns(unsigned short lengthInPatterns); void setVersion(unsigned short version); void setRestartPosition(unsigned short restartPosition); void setPatternCount(unsigned short patternCount); void setInstrumentCount(unsigned short instrumentCount); void setSampleCount(unsigned int sampleCount); void setFlags(unsigned short flags); void setTempo(unsigned short tempo); void setBpmSpeed(unsigned short bpmSpeed); private: class PropertiesPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr<PropertiesPrivate> d; }; } // namespace XM } // namespace TagLib #endif ��������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/���������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0014120�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/CMakeLists.txt�������������������������������������������������������������������0000664�0000000�0000000�00000005153�14662262111�0016664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/asf ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpc ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/riff ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/riff/aiff ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/riff/wav ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/trueaudio ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/flac ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/speex ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/flac ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/wavpack ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mod ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/s3m ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/it ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/xm ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/dsf ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/dsdiff ) SET(test_runner_SRCS main.cpp test_list.cpp test_map.cpp test_mpeg.cpp test_synchdata.cpp test_trueaudio.cpp test_bytevector.cpp test_bytevectorlist.cpp test_bytevectorstream.cpp test_string.cpp test_propertymap.cpp test_variant.cpp test_complexproperties.cpp test_file.cpp test_fileref.cpp test_id3v1.cpp test_id3v2.cpp test_id3v2framefactory.cpp test_xiphcomment.cpp test_aiff.cpp test_riff.cpp test_ogg.cpp test_oggflac.cpp test_flac.cpp test_flacpicture.cpp test_flacunknownmetadatablock.cpp test_ape.cpp test_apetag.cpp test_wav.cpp test_info.cpp test_wavpack.cpp test_mp4.cpp test_mp4item.cpp test_mp4coverart.cpp test_asf.cpp test_mod.cpp test_s3m.cpp test_it.cpp test_xm.cpp test_mpc.cpp test_opus.cpp test_speex.cpp test_dsf.cpp test_dsdiff.cpp test_sizes.cpp test_versionnumber.cpp ) IF(BUILD_BINDINGS) SET(test_runner_SRCS ${test_runner_SRCS} test_tag_c.cpp ) ENDIF() INCLUDE_DIRECTORIES(${CPPUNIT_INCLUDE_DIR}) ADD_EXECUTABLE(test_runner ${test_runner_SRCS}) TARGET_LINK_LIBRARIES(test_runner tag ${CPPUNIT_LIBRARIES}) IF(BUILD_BINDINGS) TARGET_LINK_LIBRARIES(test_runner tag_c) ENDIF() ADD_TEST(test_runner test_runner) ADD_CUSTOM_TARGET(check COMMAND ${CMAKE_CTEST_COMMAND} -V DEPENDS test_runner) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/����������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14662262111�0015031�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/005411.id3������������������������������������������������������������������0000664�0000000�0000000�00000113002�14662262111�0016161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3���+xWCOM���q��http://www.amazon.com/exec/obidos/ASIN/B0000024VP/softpointer-20?dev-t=D17H5OIRRQ5XUC%26camp=2025%26link_code=xm2COMM������eng�APIC�����image/jpg���JFIF�������C�  !"$"$�C�,,"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�9aQ(T|g'ZN = 9QHF1RR=i\d{}FaI3R(qG4E1?1F^jZP; 20 H˃H H@#ҤǦ(+AqRf:)vgA=F(o<?ZC!+(O^ o4Gր! :� ^3hzf{TRrOJ.!3ڝrzzb dRsJ�`ǯJ@;g"|P9A0!IF>Scڸ;RqALSm'#0G=)ʗ\�PF)�`!1h=| \GOtLBqPP_ʦry֛u} ?wJەFa f�/@Զ MȨz;Ʋ2WF(Vn"U\�ÿ5݇:Jj;w/M/'Mx5-7D ]w(�1�*�6G4nxis{bVM7�\f)7si_ׁ-~O iC8u6FyaH2r/R^a/Z}sӀ'}k?9nW8\}Q]#KqLj=MRƫ9uCK@?>3o ޿R�i r%>"?N�cT}{vS6Nz5-.ϯ�QG&vX*|=;/;׮x?c<�L4yRd>ׅ]1%*Ź9mnMh+�OͻTRq?ws3ݼ 8RRuX5ź! ݵ=O_֕<[U88{Q??=C7h?"8o}x@p[ZOK<< zi㩘g�E??=ɗo)GX~+;[4Ş*8г:G8�?Z?g߇cބmTނW'|6N?]y,}?ϵ4a4,:?>$��>gsnTಯ5>?tM"*EL1qG�9)=5@q?_ʼ;F�1�L9|w+�ė~E.߉wj滶QיT_ƣmGN jV ܠZ�qyrԋKcV 0 �])_4~',ڶ`|i_JOx~6 'tdn7:'쯵?O_ Z—WdY H=? >Uİ2$"Q& �$w8WZVIV�� $Baw$/^ר{㱮#!l�L )>707=xJ_a&8�G}=}hoW0́nSC\4�u8<?n-K@W2B&rxsLl& :ׁgoIk�zЌu]ޙ� YdgӦ3\a~K<Hː>EfA8'<S.Yh!O[:0p98G):⽤{s!w ^r7Zl+aw]wnā$o\2;W�幾PK{[X̨@]I n}\ƈMkI{�F[j!5K996=m�ik'WӢfeH Ն3mcՔZK,>>\\DqyYrQ NZ]׵Jy7 O@ �'<f5:sh馴uEl|k?Y[XwHN\-נ4y%4v[+lgqhzzԥJ/':5`ȥJC+d?jO{K|$<w{5)QC>n>rr8e" ^$4)M>Y>\@|;ehΔ=9w])iWٴno'UGo[.k8D.!*�n^Z+ XivE&X^i.YzXF9+wtu+TRk7 F| `vZ᭏J~!x`F�bHl٭�h\鐠6]YdV!*ry�gzǧi>=ԠB[%p;g{ŷMU\^ؓ{66pA�?g~Wpx+f]K̚zg W3^5 O>"^u;Tyݺc\xlɲo)8N de�'r:�:vxgEӭD APHʒ Tl^YZH3Gs!yo$ScUa<#�w:C0V+ӻz;gE"v#^Ic�&GIigKޕa5oږ&h헍*?SǠIY܈DksqA\X) Iy?g+߽űɅ w |+{<xF&X8�͏^<'KX"[VD׳<]V ty&o͠N)Dw��s da5LDYTmt'ZҮش/lcx|s֡cgsw"e>ߏ:jP />HoJ~B}ľ!Ҵue+ P8!'[5k:vrkȡ$S"cZe9; BV-7½kN^+�f4v_QpdCZ_xonmQ64%Ums[=EpG5 zKsH3 ?ɭ$uˈC.1<59,W}t^6)c޽J{6ofQ\ %z2r7cҡ3 .]i O�c+ܾjc宷}E2O\.Dl$=H+%æ[Cyg'$47 =C URJ ;]8DK$h,͵T $5W 5|ȳs'rHP?e%xé޸@b4 䵘}RL+E<2;JԫB;Z]zmKY<y][ ֺI4/Ѹ`pq 6XYG~t ~cדO;][F4Ų)B75aVVߖ ei$c<3>l�S5);on�JAil$;~m>jukʳy =J9Gt-i.&XRIZ5؀7ާ+7 Wru{嵆(mDCal1H;T"W0 fڻ=:>o*`Jq$j4]}Pr)۶ʒB_%Žm:4gwdH:g 묠 òZwsh2$VhgtUgmbI׵m_Mx7;ȈŶ ;%䡉WQ^v]j읓]ZXj2Zt Tx/\;e Bŏ�ZRJҒbZA$N,mi,{O_^{ >.[[s 2V\1'N+K m5m +oi4f,@*Ԓk((_=WwݴVv Z'M,o00 %}0:VF�ρ�#�6q=$mʟg}Td�9yo$%)VXՑЂ 8"j ^6�7P#\?km'PO0_�W>5]]OoxAoeHNb1�Y×|#]ok{�+˛'?5:cƃ=xy?g}_֫%KuW3Z{-1"6~�|OH׼Unquw͘q�~֯fouˏQh~1n=Ϧr kNvɤ S`}pӏ7:=�KcʬG ,+g �f$l$Oс0=Gq]7 W!b+cHV>̱`=zW5 K%G /40HAq@~a_OF:uSK;ןg.[]̲׃Vv1~'L`MtKri׺J< E7sǡ+oړj59BزX ˟ٿ�+.¯/+>�..i^8̎rNI8Oʹ�m> \ oCg|7Fw+*9�@$XҺO]ik$_=8_0|}=ëMPRO):[*r?"ZѤV?7dr>Щ>ײL?IuM58d =9|]ĺ.>@zFp8sF`}IqT+OF6rF81һ\K|Rm2[,zc(G~G�JBYxAfJ@9xzj4{+<!=2k4iԣ7-�kx/Z"_1~{rHϦAڼ'n7}]Ӿ )'DՂɓ2Σi6 ZRp79±K-[_<q3*pn )rܱz |׬$4~FᶫI dA99p4Լ}s yZt ha;*�s5κ!I$WBvUR#)בRUIKcoS]-%Ԡv5�VR?ŜA~_ӯ%V9x=Fvf5x2Ğ4HpV1hA<$tIDY>{ SMm6/u _e!=U=A8;cFygф{3bYPdcёVS" ? ӝIν`�*,zӡ 3űvՐ.�V-<3>mFs垹,cرv�fy*w$9ZUV Fȍq {TwM &%'[~l(yzW;:S/OlH{!M7Q[M^/-uPOM<An{|K�cUB;[f|x�mk_.L>_=12w�>9qji)5W ��z|0o.l-KɒY.En4X+:@�5ɊP{5%.xK7tP6:FAA^iV?-'bZ"Pp@8 I1֮EwlFwi:\򆍓ۆP�p+({0FcyP]`[P9:.jr{i[-+ʜD[m:{]?J_6<r0dyqU%וu33:B�;p˕lqXI24*4D9@vsBU5I/oQ\I)ATsۉ>,4J-?=4�3:JxMSп.qiw,7>`^A4i$3  VSu6FE2bF@?J n 7f(![(eI߸O%I=\9HhLeC?kJxSVm䖽uGbu)-[o,x_KˣcY.{hôjO !'@� c$EpO(ܤƜAzx ėYy2d+(/dv`6}H#$`gx�9TU*1)zk}_]μhK?w_BܗPĉ pkFrefq=+v1g(yX#B@�RNm\KkUIsi @uZ_o&ŶrH.UG^yuyU7Mv=g  ܂2B>xge uc�8t^}ZtiVf%Ң·T)f9z)KGL(ʵNXfvWᴿm<xY!(Xl $Btg::1l /mGZ(r-R�xM'�9]G>DTac$d[z`xSQw� XZe$0 TB-)}ȇJʛﭾˣ:t|y3\2 g q dq<g.N\ȲB݁�# z>ꭡZEƒ\~h2nـ;[*Wx-G_jꡆBh4QZmM:m?ƞ+I-T0(ܐ ø^6_mNMK7SXs@lYNlTڌxJ<G'��8Wf_O*te?.||m-nH/cVnXW*�8<g#d/*7\򥶟q][x3 8^MtDIqؑ#޻pUCjַ_z{)}6|p 188k/3<= ԭ`!ARJY`+pvM{1u*_'�(XC!VqtqK0GFX\J�RO➵ QѯP(ïpp 3ֽ|;}ZOJ\[]/LvH=}ZZ>jqӦj|"e#>.G9cW,R);0%˩}igY-fdYDy(<QJUyu4RXir;nykKMLX#NSڟ6l%<8enO/ Rs�"/Zk^y,*.SR=<k i� Cŗz՜ ,HDj$o 1!I\ڟv\i7Si,\eO d0=CGkW3tV:m@$. 1F<'Vuۧu |@kNWgyw73m ɀFepn|} 㕳Ҿ0BSvL0 gԒ+Q@ؓ2d, n3N=-�>Krħʃ<�ڻ/7 ܮ[A~Ĝ2�3C+{R-_Δ|!sK_vpO ]go~\t9"gx� aؓҵ<xKI,!6Cp0e'*ss^]/$v# |8扛X{N+ǽ%7�Z׽s~5{m( Ln$PI;NTt KPMӭሊ%y$']`oֺ]:YnV:Ds<Z4r\T$v�9+0A^J_fM$g8NתW^tOkֶG+k{v R!K\[O$.B iQ'ʐ#0;y5/{~!IQ b ƤmV$ I, s粺 KK`MH%jנc1uckiVhjV{\oڅ{z/&xLBQӠiT8B"yg|dB9+]|1mF)REkKxάRYrIj/=GRi=U$xC+Go R8T9˱׼oyh g${lıkKiK~JrTeaUl#b޻ZyiTMc]ĺN7VKT x[,5ce- ͸ pqd9 9~j0N%o5?%X,nw+NWAe#\, *:ʫ:ʕ�;8'":\d䭪]}.uzUjUh:U_M6/ _:~w h-m%lr00# 'ƫ[Yk4M|ڒL''>kmU*\^EsQ" {,[$Fd=�%[q9n xRރhmg:#U[~l$֕猃;M_j)[]VoOK_ԋQF RvAQ,dJr2{ut3\tm!yt ~Vmov*Ga7<5l�mmSR{p[ɸ nl3ԎlY#:,q99'>=ջKߞƭdmtVڄ$Y4^iCs!~`:V%|l͂0G>ľ'ؠ=DF ‡3tG uY]}�5.8>} gQv<>$�iӭ#%@˹ pUۗu KEr{}]N[j(UΉԬu8 <cK+g: 'vwB&?1 t8S4_uSO2lLp£R3nm̹\ʾhg";o`p�A2Iz} zR,\}&7e:tvZO %'h/ʻ'9\cP.|.aˁ�WQ)Ky'LMAx{漏 >JrWvrYUT&ADZv& "A9\08PreVjy{ݮ?ϽlU>;fJuR1uKF(H;8B~P^G׏jt$Hչa:mc ZH[M%"0GLV)фծ;YeP{E'SKed3IVN bGr8jss\�A4 �hnʍ'}KTe(c|`}:ծ:t9g+p쒏kJ}8LHdTv'6�ZglS,h]^9 (,u=VWʬq�SOSuot813ﶌhw�DqolvKPCD5sWmQ%FÍ#8:Tzv"b F9}A(8sŧ1.U%%ת2--%g}XdO8?J[Iλ9Uȟ^*UMʓĖz| Ry ǷWRwc/LTQUFWEZn+obvwm0\]Ml啘�#׃:RBn N3!?[!My+#z:�?jb%,CbaX¢q\v]kˠ/`D6q~ΠkdDeݿ*镔B>}?ƹ3W f&<e�4SNrvÇLξNt{.ݼ#E<#r $R  +B~ZגhሼK ҋ{{|كJ{st<yW?srE"\8�qֽ'E񆹣i-m7{YZS 2 SNO8\Fnl$#"8�A k,0Ӈk]?3ͱNu%eӭsƚψ|T蚝ZB㳷FXĒ] �ɦıs ŔL5u/ݰHD67œ�Kt5Wq?OY^!XYB8iEr'tz`52oݔE=CG3GK"=ˈx@dnZCrxViv{AЖ{->8[{x.@7�\ j� cQ6T4hde!lwm.Ѥ{@@\A[0xXXC[c퇯R_&Hi^k3J"1,Þ)@$C2H,gkǜd9׽?~q=S|�¹ӳ<ڗ2܆%UBFҤnSF#'RA$gN]Ci$XװE�wH8=*1aڑH#϶jwrFA<z9gŅ8m&WHwv1i!%gq.<]۝i^v,nu VD=qlݶG;rvgDr`َ�{Wk:7,-"HYIqЩԊ+ �#xZ4_כM<=[H=vCf;F-¨ʹ=xMq4vb֓; GF1d1 lxKF<SkjJ'lG;pz �HNw6m|Bu"98ăj9zcT-I{f }(<X{%G }_2g<g=+Yn~כB)ЛBx<DڻJ6TJ |}֪ At5:[Ma{UO 0\ak�֩Ȋm�g�)!V:06' ptA&݃j@`3r=?5g*ݎ0}o�w=LٿPm#p~w班S 7$�5~o@�~f {B:wnmMs?$w *'9\?jK-H;zi=At0?xN1ۈm)Xv#Ru{ڍV_aJWzld_YM rQw~]Ai[aO8C*ˍIIF8Q`ax�kA*Pi30dUlТh5+çMjpYH#?ʲ펷5Ԓ(63.=~^d ahL@<+kC֧Yk 5demT>ѱa} 1> vןU { c�<86]N$$tN?;[9Pd"@?3�ץBqEFv8l�jыC!9-}r�\�A:q忾~e�?tgA�~?O+Ƣs}[6U]O7.Ox�KFqn$H O ^�>!�izg�|4duN+_Tu�O"]y.BPxou֓xex8v?JaH?J¢?x_'nQ8waU% (8=?3u0 =Ġf'G5cWR�g 3zU`pp$�ZE(+#.¼ ΚwxOI#&]z�~}ju?i)#o'+Ưޟ.'8=Fss�֤�q�\MX�XHޗ@;p9#4ǡ鉈y4rN>)�g~ ښ@W5⼛r߹yM2Tgzs~(Gk芳G�һKt߼}ӝ�?d?n_yď_IAh(7W{#y-!t@�kR/g!#i"1l1Gx/^TtL7U\GE2Ij6siWϮ^ı޸‹bT�mFl@A5�i@pt*33Ldi$v%ݛ,NI=suZG(]npOG+reK(JRK[wN^Xst\_bݲ6GʚXM 2(+TO jKGU W! +Iw_低I@eHz*O !=_fi= Iģ�^iۃѳpT9U˚i]?4flk[DM]UNz`�zZė4Q4Arc$? ѡ)cv&qxx$e^L 9Gש)[tӷ**\ ѯC=/U{nBUǽU�y`#v%{z~5ƄiToyExba7Y-ܽ;/5[ķ,{h`}xYifZ9 #?_( {YX:3|Źe)E?yn]WNͿ-ntGYR!t Nh_}:I'KNHH �]qгgq,3�[<M)ܠlL#QUy SeNOH6P؅Qzgon"BNP{vbjŦ˹@L`~zƮV>gr}DkHٶRRX|ީ8`/8cɞ9?J%9^D<Wj7zd/nA9:{^(qJ[F55$Zx+ej2Āњ4%S' )OuIm]c}ۆ۵}qL6 V-7`V3vcBTaVW+)e1t?yliL'��c�SVݤ�2c*@|�e9z塮AW/oS �@\>}++)kH,=ؐ'>�NU<:41Jmr2>�T"�LN@ 뤵GKhdgjqನ(5qo#r>+҆vyO x}v[1}N8j 4/"\_�_r9$Ŏv|voWOcFA<ݷRr?.�WyF$pO\GGAgmݓ4 N#�/ZdU?^=GNPq�PxF1wkCۢ_CM]�\ϋ܎㟙xO;ANNpOd<WêGX5 TBJ<LN͡@_+ d57 ƧMf{L[iv' Y'AW�Zt%*umudMOKU[wݯڈG�zן[E%8 �<Ҷw/H'?*6]'r3c;VVXjdufMSr[Tq~IS'j^iJ#-oD=DѾGB=y�J%Q䟕:Uxu) }l+"̤w"ھMՎ[/}K2KErUۙ|޿zK.6@܋/sdU_ [NIQ�VtpLL;fr03 #Z�y]:Ӧފqz�$l;e~^8F3ko�0x?ԌUJ,%#ʤs?:f,,G^y?Npr28k&XεUxF3h�`矗񪚍ZJ�m'$�F1;by�cz\|ogBPE�랦LDҏ/sxOt[Zt[v]<{\Lf?y0NҠI`O/I8'Ssv?2H3<}5% B_Җ{DqCOҦEu3` d}8[9ecyS~̹<?Cڳ2>Bg'V݄&\A B0EunԹ%"ܷ;w�]{[Y#s4l =-אiqMrc(lX #'>:k-g4<, � w<r4']D DQ\cqvxW((r8Շש;dK)N|z##Mefp dci^-ZT~ <ҽe:m&FSlZ2tzUK&5#rmx#z"?|HR6Lc$y):XY|%LLO^jGX8+P;>Y0zOexkw*6l.Ҵ$~}ki�3+3k[-+`0r:($l$\~o+H 1,�Y+2k))fC'F6粖߶3C hRdkӑ?_U$T:P_A'@k۲�,1 ufg_?͋p}=0in{n '9qcb1Ӷ(ls#E41^x={hayܓrF8 21i9*zGҘdǦ2?s>,w[Xaw5tG^�?r,VmB"�GvCB}!L ʹii}<_//RnZ穯ST7p?u�xR77 ֖"- R ^}?Z.lpNOOfUg#>�?Z&nG$@#a^KGq//=p$o $+dI5�sSHTG?' ]2[:y4ᐆ\ i0_?@t ˏqߵr>m<tv#F T8_Tsa0+jƜ[i_,x̼KDc=V̇2x3&rr:E#'3tG_z5N +ARZ�M%*ct@`3x׍BA<9WNi2L6xRUQ$^#1g=y#�{uŋɶ|W5'nONH>b{]%qKhv)�s`uMNeո !`}A^ݓ‘Hђer~\?YŸ4O gg=Ϙ~]1zԼmZ\2cz�.kۻi[jEnH'�.;r~j2iڜ֓; LK 222oҒZX|A?]vr\)$Jw2;x'_]..vE #b=Aׯ [(m[�9t(۩ܑ[ [-6DmnIӎs֔{eJKT] ĞH?*=枰m?"lu#=_E蚎s54TiYg0HKO9n' �/kRS9RMjCi|ഖa<2N ݎ�@В3[oJV�'?)'g^W᩸R)}_JkGT_yd0cko"e Xcr#IrA8ְ4-Ŵ ~hmM|Y>!�?Y>`TpOz{d{PwۯjpUfq��^pGsֶɫ2�IG8_^檽,wAzzќU8>KejsJؠˏ5�gwn0>eoL~u_Lx~IdԡF+�ڿ$Ly3vq?,ң-;⾧I�)0Xz6לs}i3ܟÚdp1u=餜s84ᓁAU#?:]FV} Cc_?t cCҹ?6N0H< B+Qr7z�xr0Ioʾ qlb:NXQ::) �#O_Mvz{JdPUI;OПOjƛ"C<ݎ`G'^+Ь$??Yx?;0xִXd`% )D$#z/Xj^_s4" #t :j@#=WQ}۬Frsל>$/+좽|Ź]xnu03)E_}e�g }ri TG0Ii^G8I$kRsԦ7}qY̱?u<d^Ð~?T  vmRBHh'w֪?sy[g'U_ЎqvyJ[L9Np {,׏oW<r$IiK[YIn2[yN9?ZxOL"8=y'�,i62_y " g#H`$L`8#v'N/RRi2\>e9w0?.++:-\Is2Ydf_Ӯt  9 yyctQ\zq�*%h͹9 o- +y#ךg׵x'qqev0 Iy+"i.t ;GD;0:@jI1Tr# ?i(Nriμ[gxSψ%7E?5 zWO? M^[,2;cy^" ԖZ/= >Tߒ;E˷f>9<c+<|miJp]3'޿.raS$3\{DZV<޽ބگ[P?1L2pOׂFQWQ2wƴ5W_:Ё(=3[^ 0R$2܎?<V$ ci $hw;`шq~dq.R\|oRwW/qpA']U`zU;d@=A'G2lohʔrkEs8*Gݦ~.f̙ '~V?w>rX=OӃM}j&Mg?Uֵ(.59Yaמ& VbO*֧QE(GAbdž~E雕Q!Հ nan}jHd{+v$UsYc�] Gn 0xgqOKv\KsfҜ}Kp3 q t` ӊwVg s?t xO҃�Y<D lUg;?j_XtW$ǁ9{k�.[@=~fGyc j}d:'lx8�G[{1ox [;'k�~?!qku�kЌcNKk.bB6N�'bm|\�ZZ >*Icvֽ p).jA٥e� rc#|S1Ok+02m-OLry�?ʲl5mFEw ##|`OOҾj.viFE)ICIG_ uiYH)c&2??ҺU]cg;_HR@kMF_e) c"VBg~H�~̩HԲ@M8=NbW $?Ƿ^*BXq8<0;pM} fH[kg7L6I,@ (`J00<�/v. &%d�ň 8Gjz-֓\iӇ/o.mCr{Nkīu;xbc+?K?oѵ[KibF"Iʀw `nðSҢAlEE�ʌ`{?J]Rs=?GOo"sC)9㎕%htL0xoȎ(؂!=\S@[ q?v[&UYTǽpӒku.SSӴ˫rv#� 7cOn}~v[\<R#/cq^aKCӤ4ɡOؐA\uztpkn{<XBNTxD 98M8t8_iw+33yvtbe'�I :ͅYnϴm+arɩ$i0�y$5ڷ nP0O_Ϸ_]SV>KZR-Đ̭ щ-]/ s_+MNۚi"݌gk) `ʾz-e䛒7�ݾEڥм1%Csr2&1<"Y ,܎~k"Ȑdk?]vO=cxrП_QC/ht^'# ZlvXƐ�Ӡ 0<1s$E*U:\`w;G/M6Y:.�$W5﯒H!EbrW r9MqiWYPv9`ŠT}?8f:fשSۇlA\vU8lRxkgvO}9Du.I>cU30Ẁ<ҕѽ47/ H` B=kxgG:1ʹ6d] 3` ZJ.S{i箿֧F&RVO]{)2ۥ|29�(E%qnYhXVRrT#5Ťia?3�:dc]u$;Y] hҖ+'׾[\9*0'dˌq y5n.--TȞ8IE =<dU-ZHEU.K;'ϓ<�O z6ZxkM.Ov81[q,ʫV*֛/.Z㡖QTVC"QX6w""-bT@22G/99&E+IͬNn/78!A c=k]Zƙ+G#SǸʮx1=.рF1#د:EkV{=}6M|~Qݻ}{S�-A�MҼ^z0~|ld|S3O}>ㄥFqoV�Ec1*�9�<<9툸{Z^lkp .3O~Uky~h. |8" mV'x㝤c58G�iznW<#cgs:-ۙh~mu\$+&M4tzߧܫ 9�eb/7H&�W##TyHm_etٛeIZr@s7 :K 7~R9С(u%pFG'wf&2F�=:THZ3\$} FN}ow4܌c�1�)JrN9dqӎZ=5.s }g N�ɖJџB|9&۲R7sVIei&vLLɒ9=ۜnX_covcH d{NJ|yyf8cRy<vjTo,C5+%&ףw_5~mwa5?hTaݷ+"A�NHO^_6Mw ][}O9;tz?eyvWJ I$ Sۭxx$vSr3PÓUIH_u]Eq 2x9?oº趗oo1yc^UHHtv^k%_8\g:Z?(,bgϲ&xjK JW0o1V<t沴OϦ\MubmeU w$�FQڥEu{]LD%8Wc XCIoOzbs[SZSTA9U🊵k+OUլŲ7 Da X}Kӥx3˪IJ qn{?5 rNgJ5;/Ho )2Is|4^^<^i\{rX^ +E~_:kjhJ79ߜ~UxZ[l298:;?*"!rO݅we D "11+7+*K՚qg[ߚ^WVQ֦͜⬷;wEmj{q#�X~!V(PX4\C[gO qbk:/1S?/*G)|cOJ@{{orPutoZd(nw`u;/;�$h4''1lGxHϷҬXX[GzLjZH\>Ҳ$υmT%UfP@/Azw/HԦmJ^ٛ1t860�Y IV^[..FXynԚWkⅿ.sN ى$TrHsT<55t6z"-]G$m,:F8Ďxݳm3nW |>¼u)rFSNWk_Om[.V9FQz|^DP:n;(Ђ3Ж-wy%@pNTFz;R,eY\e[{t/!N$A+ve#GpQrO}k�uùÖֵm2_E~r0O; ]!O9#PNۖ+Q5E =6_Zⴭ7TӵI,-e#goD;2A O^M]ִdx[k-q�@F2NAU#^.q--﫺z[}3 RmkY]%^]�0Z<5uOI "qٱ^/?Zot14P#q)#`WWWzx#C^F\!hwp�b.Kztohpvr y<r7'<IZݗשҜGi ~7_1 n_>>/^x3FVhF;[[DUV ǖ=j/P߂2[F0�]enlm4yo\;)rdgBfni6--}:u*ERm4MyGEuj<-e#!GcYE^6xVܭA?ֽ/EfY.+Fc q,[qi6=᳔:Ѻ7>Xda#RٿMonUum뛕]}ҵ@wPOrx'hã~_n{W"\ף&'tA/wL?gϊ}S/ySNZ_5)/~g1ϧݸ?6oqoL2Χc諚_e.dOb?ES1 zVK6/W}՝繋�jH�<}Af; H9*/Gn5[�aoj\EuGEjʐ?�8xcN{I[=|6iC 9|2Mٯ5wLRKk6AVyµ|wm<ryN6tk* N:ygOP؝cyԖw3Zx)b7+wG8Ƙ `RX;K%yu~<FC+xѣ @"%T`3zVV46#XcOj({vA N/Wښhzut{ےr"cG#Nx<Wwu�xcJY&hlCu<%ͅݽ[\KnU2$~ OsĖ<_=徭iH|@+13qgt<u0Щg;Xk#~*ni̻M+@ed}kPX֮:=xUK9<<_n_g4fo_1 L/%ǂܸ@:cWti._qL<exsL=&yϟ4X Z݋T3k-²9>WuU[6ꑓ,ao"=ws>/}fOh&CA)e8 L5~osl% B7oԞ!nk-mYE1he,9U 9|fGO�ZY^JLg\Y50*)b:;z(7Ua!�[� dߥ tpSx˻6#�+K{-"C9UO $|�8�m'ܜ�� Se�~kRXj꒼~|ɻ}s6c?tZ�m/~׀�7].G}aN?�Z_=oY¿w�H<25�ewd]no%b? cm>SA�u?.%�?kp~gş:�?$;Z<1x$,Db(1g~Z*9;#x6;{ I@-\ gQ 2p0FG=k?dOx(D*XF99S~cT1Qny "&BrpqcԪ+ߧGqPJnw͏MxSM �T%T3t#AŴ!GH޼825qoﰒ��sIVuZkf=daC&} |Q^>/ %%=<fj?{W{âKq潤qcWw !䌆 yMSL 6l&^\{-=Q3,G?_e-Rx7HHh = aSW}/nY6kZZ7X.,].kI-/DdVB=c[ABHX%9*8PKG15򃫪r9G8]G4Z,l=돹,놎{*o=LEeFIwi|=a5te`Iq?Zڧ9tsZ6ۻ>=p_kyDGV85F46˨Jۧu$3s6?دP񵥾]& J+g ;2HOӀy^A4W>hmbZH hNnT8{%$jKGV#U�$vQ|HD/e(`>iF;Uh#00iA;o+F%v[vNXw$H|w�98cm1Lk㯭y4q32/BQ%u_zcT&#�_}|:>\d0<Sapy.2ÐqWsr GC Rn)r9̠�*|;oSx9M师~||Oj͝2{v& W Ӟk?ckV�[�QycyЗ,HuXRO۟�֭APA w`J  �5oΌ[Q"`˰~�Ҍ!R&b%ߜ{[FOͷ{wի]ٲ!R?]h�lMaqehD0G\j֥Z;^bOےd~^9x( \$`~ǡU> R9.0͵匦Vr7/n_C Gsm.(ېHǦqozh7koٓ$s(l/9Wo/֊~W+^yngSNWki[Fnze/vvK %UBB^ (p{m}\k0jZ1ۓX*ƮpUT` .<.=56 ڨ-'~PQ :3h#U z~[,9'Njw9VIV?H5ߞ?.iZq dyۀ@d1kH5MBBy',NכZmirCeSy-8<:̒=Ēͻ;FI$H$x8ERQMﮬ4J&W|�vLk+iy7e1#>P;s{ב7ͨ^Fg26$~>n~Q*m-exgh#p2N3>=A40X?1[ʭ<-YF.oiK+|-.k|M[kܩ>,1Fx}g�p?s-Zksr k2ۤX}O_āڲ<`m;XFI<Yի'V=ԭ+Q]#aFt{#7gf9<Z֚L)rNWF*FrN3qúV�סG(%wM~;KvnUWI|sjTs2s|7ATm8k}t��r~lQy]OZSM�/xDpp?U%|Z�[ $vz7Lr4[R14{IϨPd.>k{kKɵۀ'ֶor<Im.g�,syMH}RLLJܱۨo_lj^oJ˫ٯ Oe' =:)0w(c&Si 8[X5 LIrpe 6G&Y$Bݱa,w1-Bw1ƫ*6fcedҦKmɗQc�C# ~)3'Zw}emlao~̉'P۔ v9[^f=29/Bu F[�NN;O |8X֯?9Xb;U9%9/:q @Uh�`�:^UJе~5Jj<¶sRkmJm/TB𲝍<d-2LOF<I<bI$ri-llv"73 -$�,N��>G^+وTKwD83v G\}zCYj:-|$e�ݺ|ǎ_֞$:}ܓ'KSfI�R89<5KZT纽KSSiK`B81L3ϷJ>;\YjpEZ{PyIQ$xk`"b�\_C`[nZxۙ7&AF}Ř<W]*8XUw+ӭu0_Z%{Y~= >xz}!1 El4sYFۑڕֻMCZ<0G;Z,es'5kJvMHPeHdž';J?'ֵRZyk4M[7%c˓5{ʬԖ}~+Y7}YiԔT�'XV�3R^I}^}{5ܰ}ƊhTU{ڕBQ?<I*qO^�<[ōʘ`Ԁr?*Ϋ<qn̞u1C r�@Y~+vwH٘: \<^)SXI-uu}o4Qn@[g;Q= +Kl7=On;TH’d=G<jwzťʫrKdt4RnJmlȖ"ztZ7ٟ$�t'ݯҚ>%ɑ@y \q$Vx2p7 (ϏGTَ4T*pHMv^z|6/K $Mq롫 [h_E_þn-U[kiYiOV#<~K="ʁUnj1t=*p͓˨Q9'&ݺ^~Ec ?j &x̂FA#'k�*w,:g3oOjif rϿ%9Ƶ*~Υz=aүyŷk[#SQ+X<YyI A֬[%j $ tTۻ470mݞ_shZ^[\ۤ<w3{^+VW%ޫ{t`fc>y3`f+Kҧ+�A�Vі7RIEURw#] Yǔ8ktyRv|6GKG{(;BQ}we2h<M;Fr5S1NGvGk3N rp=ys4֓$70"*`g?T`I'd'N 6pū[M*e�Y *ʞOBr3 .lt[RU늡ᦆpd�d JQkuek慾鉯a`:\~ڶ+byʎŸVqk. xJ"Nq˚ћQaudBxsN9ږDaW]�93 \P8.sv 6:+FqL8_ĎxOS񞙧^,R2>ۆ{1߿Jê^iVKM&v. K$qpZ]rp\to=^%+RNWVI},atKqeoջ"KjvS{95FMm~4Ah"fmm% ǧCyI7#JۥæfcDI<zס�zqsx俫?y=~!VMMVSŅyޒ@AFWv^Ơ#r>+hFkxR"U#dM$uq775.񇉵 6IUHJpHa{v6w=W>%4G_*BY97WZ%#m'KI]V3#IE-I?/:$pm>X"cq<98!6W_'Vdc3ʚhcNnԉTҟ:tmLŬ@s< [W:M6ʲj\Wq;3�ni4_ mwrq #aOj!? !ڿ 8'֦C:I{oċl4KA۷h$WzGtۑR䠑-c$?2$(bRI~%OPDG8[{* ]ϬJ*|s}+VFӳپ-�#أB.6Z^zGxԗF&5]n!8`ؖih8 \ּ1-֩ 6Z>BL1 <g4Vm[X;ssw(#7Zƫy$%vvgfzkB&2QqvztՕӲW}}8<S~4x- Sԅ*p\'VEB:-#8rdC$ -ıd|Rh#Jt'\,ovT$yk' AWYYi܁q( @o.џm]Æ9TG*W'hW^碲pU~#Ŷpd l/Db< ]3)+Z٤t�uSr>2ƃ,OѼ'\A17K(U;A1^2YiW= Y'J{1+UqnnF{IQm${X+DCxb (9\*S,qǧ]g(51Bʭuuw!TfXm!?@<!mg w},{0? c洉\.dm�RI}#x= g<MUy}8D)w:S{xRRVDѣtq@8H.{^S A ? Ş (K$Ӷb6I2p 8QX~+:MWKl}'6E34F^V/6Uk*R7/_kv̲$6rSN6=3ּHVσJKƟDcrpr{k֮>(j[Z%9%2`>䃍p7 <K*i9|DdiW+2;LqMPh[^Gmz E7*r��umE]ly1 5zM;M嶷u_ Rt `N'uW^gmN J Mk>"PJ6wtk<9wtH.4}!~\!pcsc*_SgN߽Uo2Fz.ze[C{<+<P@v_zjk;V Otj탌A<\|Guچ-k{pK따cdp3ʯ=;֯tI 6Яkۻi\ `b|ng_ ZU*’^e{tZ^4k!ͮh1Z ߆pۂI%a9}φfUi|E{"+'Ě75ӮEY"C`k2`Ԫj̕u%ռ+pa < q*yJԆM&v{z i[mR| 2'R<6މ9_֨\|)Ynۧ2RT$drrv5牥RPɺ;t<,Eʎz:Ӵ; gG9 (&EI8ZOdkq�I~N�xv@z`L>m $9R˓t=TEyV ІՑ?7X-ݢ4q s[ 4Wt1e3÷؈û0Xq;c,O XMY6CT)T[ El-⵼ɘg�k?iyv3BPX�{t'zmX_jEYP0�3cT_/.g!ү#A9LO۔pG^4]L7g|y��|8-5i^v1_pHL�.}zwcnCOHl4%eˆeQ[n+ᇉE:Ǥܳ}v϶yϽw> m5�ϥK<w8xk7T+bs) eqF>Vjo337.:%m֝÷MNøB4g\�A'j60G} ŜHUXg,ɷ�ӽz{c]4|~dH�n' (<wyruyS˥nSD �2I8� OƼ[o/Bf/<ij<Z9%*@p^T] ]umR Oiv]c}uX1n2Ǥ#Z%xG{;,ֈn>2хE[�_LYf-$1C C�1n9UYUHw[Ky6D3{:!}Nsf ,dAoq9euoцӑ۶#($x]ˎEpCd#{{Ta%%o&s&ETAxzZsGgglQc|꠩4ENI({v s[IGE<ҁiCn;/UImx@2b4k׫7ruz3"M#X�,@$p2@U~/B~ؾ 1 RO+#E$m}$~dS :"2OS>?3(=4n?y%tЃѿ9_E/pXxN]=C%ż!pom E;IaTk|S]jL_ZjwYOeLFumYw?^գ݌PyI;C^(Xy{9Ti.ߩ*y}~G-7NVs\ bمͼq}gIk#Q<4VgP�<8Rk1mA|i$*9TuyUi2? �ɃQ:SWn\d(ÑٽZ]3ÚMy&<y;ս xeV6mˁՉ'e_p@.su?zT'8blk,p!ך,m ӯ]6g}cLG”-mzomKvm<?цC<鹲4;JhూK4pB~zdbn'_ plw *ؑ{ӎgބ}O=azٷ%f/$p-ޟlk-<Ah+Iu jsyEad'T >oIk<)_I�fQ'<b ؠu'tr,^eD1۹ �$z/j0yZWI|[aUq!`=Cz)a0wm_[ZmcXXWe/|1gy=Fqq9sBLWqWOcW5 %D1oe ̍=8QO5j-)-;XOv1gTg])Y)]n-;f\LK:}wW<:^v~47Z|pU眅U͍+vh(.% ]؍N �kIԭlE(UUr0:MմGTkBPm{3wϡ *8vվp7/9xB+ [}fsFIaY '[_zn[)y*'flV{S82BoHN9޴5.WӠ-o-.i0im9<sJ[CNsm<Q$MC| WsyjZ&&㳝%c93?&]3q0,mv;x.QƷ2DEXI;�:ƍjrZ/t]W� J乲bx``8}cJtY,l넒eVA 6qqIxe;/o[<YQsMgv⼞C>,GG$*G� ROI+YуO T[u:_ɣ4OvV WA 荑ƴ#x{E]X[-aBi= 4uPw 0Fk;K}Y$͈K[B3N9y1q\[^j=Cr(2` gsOF˞zZY;mNxV߶[m&uH#xfاjO'USOmF+ 8泍L@\+t n{c Mr%ܜ ?^1ҳGg.<i$hۄg®8p8q_A<N[鵼V9yZ-|5εiiDҨe{2L,t}vk--;}=d^v@Fb:;J۵m44-g$UPs?:_[",J&X_D1I9ۜ˖8=9QWu<Zy)SkggGuc^DŽ�XvDR`_{r 6Ma"`Y!kFw`䦓c[}L"? ܄S$u]MSN7[ݽȼ((E;pXNkl6B}~vx)ͫ 5uVwyvC Uv1{�E`x7 ՛Vvdq#cWa u;GJ5K+RN.$ES߸Pn>k2kdb{gn8<և5X-rSs)ۑԞ$;qNIio�J7xn7ʺ-`0  kmZݠ Ip\"7OH$];XA`rWO_&I%}伮ij;,|ē� WW<bmϏ4Y*n.#Qgڄ,0Np:4-b `"'`TO1y*v88[m?ٞ"S�`;RI@<s]P'5>*N!H<:-kE[�JDy~Rl(s֥^& )YnI' ׭s� w?{ۜy_Oެï ^x:5߯^N T76`8<gƭ�$i'z"Vɳ׀Ѯ{sAOT4O%.X#~| xU d8FP{TdtJe9$56 =Q`<{DO\~܏#&}NTKdK.@rq\c# )^Ja\g-߿ʍ4\@~-PHݜw% !RH�Z\[qLu4�L L!s0.G-�<P\dI� :ԩ �wRT3�M$sV̞M2o#s=BR�<qGӟ­dmYJʇ⃃emԷV#,_НOOoZ\r�52#m?#'Im5 zi<gR?J<�= O1 \1'xzt*�M u?�!a}y0;Ӗ0X{۠$z☈u�8Q'\_ZOzVzt 61Ҥ1�77M\n�4{�ƍ'S`u,ln��3'H7LsBX'4ƈFbGVaMFs n.Y8t=Y�:�TIT2������Sunshine Superman�TPE1��� ���Donovan�TALB������Sunshine Superman�TRCK������1�TDRC������1966�TCON������(80)���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/64bit.mp4�������������������������������������������������������������������0000664�0000000�0000000�00000000125�14662262111�0016401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������moov�������M���udta�������=���meta�������-�������!ilst���cpil���data��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/alaw.aifc�������������������������������������������������������������������0000664�0000000�0000000�00000003542�14662262111�0016605�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM��ZAIFCFVER���Q@COMM���,���V�@D������ALAWSGI CCITT G.711 A-lawMARK���J������ beg s loop������ end s loop������ beg r loop������ end r loop�INST���<������������AUTH���Prosonus(c) ���Copyright 1991, ProsonusNAME��� woodblock�SSND��^��������SFI]ㅆ:--#$ h?837 qS3159$&&%65걺`0%!-,&07  6?%&# <b ?;89=118&&>�8 -#$0 626A5%"(("<Q`ug49!,,!<釃63039!/)-%5̵ 8&&%>>;%%86h ')*)#%24i🷧>-**. :3hT 46<',(),&<g69::%&",- ; 6!,/"&%:%%:33$ "" ':=7 R> /)."%5ar62?:' ""&<19%$$'''$>68'!&%9<054?$! !%<7�lY529%& !$=j7<8::%$$;3�7?:%:8>?=6 c6>$&!&$83 6<:''':=563=8%$$;= k3>8;::;?1I464  聈m   ֞Gk�577i̖f� 54 e  𞁁q�r횀X㒞��`ecmZy iY헅Yg �cZꔒUxj 465xᘂ�5665�se5 `앟wetd  �lv�� �m턂M5445iYꅂe 41337 ~ᜁg40237×51=?<6܄T46667774ߟj477745nㄈO 6=<37 I`44 i듙a�  d욂tn46165 Pk� 45 �B  oᄏe bdX㟆c�  �`p據v  wᕑJn��K蜇glڕNn`~Cᖟ^dhxCVZwvL@qdca{HTGNLADAOIL@ZPQ^[[XY^YY_RW��������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/alaw.wav��������������������������������������������������������������������0000664�0000000�0000000�00000157032�14662262111�0016504�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF��WAVEfmt �����@��>������fact���n��data��>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66::!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸UU 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UU>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 UՋ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!%:66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!::66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!%%66==$$ ## %%0000%% ## $$==66%:!!## ''?? 㴴ss5588&&####&&99 Ջ>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸U 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66:%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' ##!!:%66==$$ ## %%0000%% ## $$==66%%!!## ''?? 㴴ss5588&&####&&99 U>>&&####!!;;44cc<<'' ##!!%%1122$$ ## $$2211%%!!## ''<<cc44;;!!####&&>>󵵸 99&&####&&8855ss ??'' &&;;44jj77>>%%%%;;== jj7733220044ᄄrs bb띝PPaabbccddrrBBSS������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/ape-id3v1.mp3���������������������������������������������������������������0000664�0000000�0000000�00000020343�14662262111�0017145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������d�u�=�@��"`�1 @�?˼N(|rrDO xKإ2Mw3ŕQv. sR܄$dyA�� 0�#����4�`��� ADYa ^O-c7 EbJ!SwukrA%5:=ʹjw0l%RPn@MD] uؕd oA`�� 0������=� ���[[6mes| !)V+ -.Xڤ4ϹR[" !mdj���0�����<� ���=|TVYrK>͎pu1D{-eRa7tfTb)JT*vdkB�@���0����=�`���PTdZOz SGm=F*=)2gK̘Y +ci7bpynd dA@�� `0������0�a����U^!62z)}H:]X7NqUlλcjk)h{ech辅*kTd(jA@�� 0������8 ��{T:TVuw0QlKJ48'ދ}D9<!cW=vEFbJd/z��� 0�����9� ���\E7i*of81CQIxD1,Q,<_/m u,V)JȆݕZkU d6SB�@�� 0������7 ��o*ikP)U1u(1wR+TQԠt뻋<޻ j29N؄[Jd>lB�`��` ������5 ��� صĨ3`:4\ȴ"bY Ӟ"0-d/uҥmguΔB(tndEyA�� 0�#����4� HG*P̿b7xxrmCMMm*̺t A"(BT.?8EdK`�@�� ������: Hm~˩2 NPrYЪ1E叼XRZa饔ƠmV߲-C8غdQz��� @0���P@�8 ���In͠*Y#V$.9sgnF̛?ԺbR\Vko*U(VIi{dXXA@�� `0�����/� ���,`rј MvX*<>YLzJ1ץ/;  ]&up+C dbz��@��@��t���:� ���:D\=XYzSsWN}x^w]!F U̐FY׭$FЁte Q;1F!-UPYoLdgt�A� �����4 ���j6VӔ*{۟8^(rm&^HXRFKA"Դð"j#:* dmv��� 0������2�`��̯ar-Xʱ+84ΕT1XqǸQs醘–ݩKe+ ]S*cRduuA��@ �"����- ���|Au�Oހom61W�Ϩp9 Xѵ9}$KS<Ӹ啇Bd|`��@�� 0������5 iud5'2haµ2$!/&p4  vZc9frdf�@�� �����;`@��M93bRqό]͹(u2"*+bխǘ?C\(tsKըV6']9[h;s=PdqA��  �����.�`��B2ELv<FT!ϩ$%smNwzj ` L$`SqcbdoA�� �#���= ���0F?xņ^aW+5B.cHyV)Bބҩ4U sYz!㏋]ja{ֳ`6pd^A`�� ` �����9�`��pKPlw4i= [qUJ1*rv_\VjE."ذ쐆+ lґn`ddA@�� @ ��@�=�`��ƺ#kQqU)8YкQI4 wUk.M R 5;Y;Rdz���� 0�����; ��� EzQuVTqW9v ^ر}N~HX|\6JyyNg0Udu���� `@������+�!����E5Nb2@m 9HFG.9WlutBihU K֭3N2 1o - #{SdfA���`0����3 ���zǦ^Tk QMI(M@@εxs 4uk=DP~53FW`Tx[<dzA��� 0�����1`���ʣ#AMoc.ʪIKn5P45+8?emלYww1q@$T)q.d\B�`�� 0������;�`��MBp*Pɫ֊iN7*d^D| aҟ(F~h[zQHͭHiy"dǏsA�`��  �����7�`��Ezv*S{^[byU k C'-[VHVQO?UBQQW̉M (7fApbŲ1w Yd͏iA���0������7`���04B4U[d7&?%sْ6|G|1/k{&]i˥)Ghl6>6dgA���  #&��<� ���(krqt))G yN Jlz C)Ňʹ3*79xdrA���@������3 ��h(Uƚ,r ET7_۰}L)!!I;Ifw=p^S _do����0������-�a����mWIn qѭ =gy+0V̪'L9 (ֵHsoZkklh0,T>d��@���#�A���9`���Y̎\fmy$'@WHbFbVyၢڌqel) g jd�����c�B(���G�`@�z'8jF@, ZY&&BrVI킡g?]}vwo:-[<R`dyA��0#8�@�8`��dI*T ؒ@τ(!rP:tTPU[аu~&$>8VJd� ���A`�?� @1i2iy/9q]UB Lː8bF@SX2r˫w~xyd���� ������C�`���wlx:iL)_d(fcSIKi&=A׳mR_|(ed@����B<@?�`@��t ;L&jVy#<Njkߒn_h=V瘦LcZ$Cd}��� #&P)DD`@��R]sq%k֢9KݗE~ A<t֨A䅥/4ZzMK,z2dA���#8@�? ��˽{<l|?hu>3|2mbb6STÙ vm <e(d6>*Ld���� ���B\�@Hx('p( i^ZÝNF 9<o!d}T\e-BdA��` �"�`�A�`@��qA!ր(}y V4<Gt3yM{G׻k+:lg%M&dwA��� #8\�L� @v틨>5 ^A)xf.pnJk*X|ukh}&dj����  �#�P`H`@�?l5=F93/`tIi&4͇aQr__{Pd� � #8A`�4�`�,1{o:+ile.Yn B)VdV2R.y˸^X-sgʿQd�`��A��:`���krFRN;vuHe8C-6*IlxH<+lqL|貔p9rKd@�7#$ �E`@��;}LPF͇ cLcZFVS:Vޛ :4*&⬏m@d@`��� ��C`��.]P iUM2sT0q  i00+&ctdC4dYhQ" d@����B ���G�`��Feڻ̿UU |[,aRmj"l 2<7H[2w2Ċ<uJd@�@ #8���I� @��N*Ne<}^qG_Q./My};(Bs.&Rd~��@�� ��8��N �c]Zx)jm6Dle(At: w)SEt4\95h{uOAQ !dmA��� #8h��H� @ :IgނYY;* }ܶlIr.;Ww{F=/ =MCdA���  #$ ���I� @��eG&/j7 (ʚ_](y9 b(.hRS⡋ k>,FXjqPldA��� 0���T��J� @��&Tִw&igE2l]Hkuu%ԉ45Լx,Yd����` �"����F� @�NJ(ʞPmUF@*vJC7wl;x`v'.X"eg81d@�"8��D @���.o&Sdɭ"+]"'סyh ...lP)C>AXSjHtXd��A�#&B$���F�`@�!aκ^Y%ϵC([uB7kCb]Ej)Zy iA&=*+isOd~����� ���D����I� @��R<6N-/ۀ;,?'jYMʃBBŦ^OJViMdA�0���\�J� �qnat9B QzU .*4Ir9<uof~ub+<ɔC-);Od~A��@0����?`��HB\)KmUE m \XcU)RJbQ"\(<:>X(Y_ed���` �� @�E� s-^8ysK!qVd:@(BR)`7,s8Caf%&\M ҙd����@��c�B$���<`���R[O*Y+T9<�v)iWj+nyuk1朗k8;,kv}?9_ldA��� ���$���E r>Ol]ڹ~ *zXA׵.JE񖔧LBjԊm,L> G{zM37d}�@�� ��p`M�`�"o㯘wș6sz}ۈ Kf( SIsݼ`vlYA&P]UE.d���`#&��@ ��mF m|MmQ7R;A-EkDEڻ5[tjwShCbI8_BdA��� ���B @�D`@�s+;)3gӭD o\kxFaeE`PjtikFiJr hqqZUdA������P`J @\t-{onAŚ[Zzw])SL"0_:y?RƤpo%owxRid�� ���(@�@ ���;o-)dy2U zBfQ ԲH׹'Զ0,fG6R̙TTR7d@�� ��BD��H @��9 0vPW6ANufm( r-x5ZuY#nʽ̎b@dA�@�� `���8�L�`@��၃d�bD-|r$$XJP-Yȓ29>IT7bkȏ)J:ud�@��@��$���I� ��jA/._Z0XY\J5*ck qΕk73}MUߺǨQ X duA�� ���A@�H`@�=k!g\/d,fg19ԭ <H,!y!vp"v1bInMsw>׿td������"�B @�H @�/Mc2G~M Pj]4iu`pAPB]1/75A�R%Qހ<xUdpA�� @�p@L`� pv8b*ӲyÈ NCOϣRc;ooAԤ}8sg^Xj_dA��� �#�`�?�`@��#i2Vt2KڅYas$Aık9ʠ)g/0wG_5ta c6d�@#�B$`�F�`@�U!.=j]| w8[e)l^lIDpUUrӣ+Uѭ35e)r76<d���� "���E� @��yOXing����B@�B@APETAGEX��3������������������������TITLE�TitleAPETAGEX��3�����������������TAGTitle��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/ape-id3v2.mp3���������������������������������������������������������������0000664�0000000�0000000�00000022175�14662262111�0017153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����TIT2������Title����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d�u�=�@��"`�1 @�?˼N(|rrDO xKإ2Mw3ŕQv. sR܄$dyA�� 0�#����4�`��� ADYa ^O-c7 EbJ!SwukrA%5:=ʹjw0l%RPn@MD] uؕd oA`�� 0������=� ���[[6mes| !)V+ -.Xڤ4ϹR[" !mdj���0�����<� ���=|TVYrK>͎pu1D{-eRa7tfTb)JT*vdkB�@���0����=�`���PTdZOz SGm=F*=)2gK̘Y +ci7bpynd dA@�� `0������0�a����U^!62z)}H:]X7NqUlλcjk)h{ech辅*kTd(jA@�� 0������8 ��{T:TVuw0QlKJ48'ދ}D9<!cW=vEFbJd/z��� 0�����9� ���\E7i*of81CQIxD1,Q,<_/m u,V)JȆݕZkU d6SB�@�� 0������7 ��o*ikP)U1u(1wR+TQԠt뻋<޻ j29N؄[Jd>lB�`��` ������5 ��� صĨ3`:4\ȴ"bY Ӟ"0-d/uҥmguΔB(tndEyA�� 0�#����4� HG*P̿b7xxrmCMMm*̺t A"(BT.?8EdK`�@�� ������: Hm~˩2 NPrYЪ1E叼XRZa饔ƠmV߲-C8غdQz��� @0���P@�8 ���In͠*Y#V$.9sgnF̛?ԺbR\Vko*U(VIi{dXXA@�� `0�����/� ���,`rј MvX*<>YLzJ1ץ/;  ]&up+C dbz��@��@��t���:� ���:D\=XYzSsWN}x^w]!F U̐FY׭$FЁte Q;1F!-UPYoLdgt�A� �����4 ���j6VӔ*{۟8^(rm&^HXRFKA"Դð"j#:* dmv��� 0������2�`��̯ar-Xʱ+84ΕT1XqǸQs醘–ݩKe+ ]S*cRduuA��@ �"����- ���|Au�Oހom61W�Ϩp9 Xѵ9}$KS<Ӹ啇Bd|`��@�� 0������5 iud5'2haµ2$!/&p4  vZc9frdf�@�� �����;`@��M93bRqό]͹(u2"*+bխǘ?C\(tsKըV6']9[h;s=PdqA��  �����.�`��B2ELv<FT!ϩ$%smNwzj ` L$`SqcbdoA�� �#���= ���0F?xņ^aW+5B.cHyV)Bބҩ4U sYz!㏋]ja{ֳ`6pd^A`�� ` �����9�`��pKPlw4i= [qUJ1*rv_\VjE."ذ쐆+ lґn`ddA@�� @ ��@�=�`��ƺ#kQqU)8YкQI4 wUk.M R 5;Y;Rdz���� 0�����; ��� EzQuVTqW9v ^ر}N~HX|\6JyyNg0Udu���� `@������+�!����E5Nb2@m 9HFG.9WlutBihU K֭3N2 1o - #{SdfA���`0����3 ���zǦ^Tk QMI(M@@εxs 4uk=DP~53FW`Tx[<dzA��� 0�����1`���ʣ#AMoc.ʪIKn5P45+8?emלYww1q@$T)q.d\B�`�� 0������;�`��MBp*Pɫ֊iN7*d^D| aҟ(F~h[zQHͭHiy"dǏsA�`��  �����7�`��Ezv*S{^[byU k C'-[VHVQO?UBQQW̉M (7fApbŲ1w Yd͏iA���0������7`���04B4U[d7&?%sْ6|G|1/k{&]i˥)Ghl6>6dgA���  #&��<� ���(krqt))G yN Jlz C)Ňʹ3*79xdrA���@������3 ��h(Uƚ,r ET7_۰}L)!!I;Ifw=p^S _do����0������-�a����mWIn qѭ =gy+0V̪'L9 (ֵHsoZkklh0,T>d��@���#�A���9`���Y̎\fmy$'@WHbFbVyၢڌqel) g jd�����c�B(���G�`@�z'8jF@, ZY&&BrVI킡g?]}vwo:-[<R`dyA��0#8�@�8`��dI*T ؒ@τ(!rP:tTPU[аu~&$>8VJd� ���A`�?� @1i2iy/9q]UB Lː8bF@SX2r˫w~xyd���� ������C�`���wlx:iL)_d(fcSIKi&=A׳mR_|(ed@����B<@?�`@��t ;L&jVy#<Njkߒn_h=V瘦LcZ$Cd}��� #&P)DD`@��R]sq%k֢9KݗE~ A<t֨A䅥/4ZzMK,z2dA���#8@�? ��˽{<l|?hu>3|2mbb6STÙ vm <e(d6>*Ld���� ���B\�@Hx('p( i^ZÝNF 9<o!d}T\e-BdA��` �"�`�A�`@��qA!ր(}y V4<Gt3yM{G׻k+:lg%M&dwA��� #8\�L� @v틨>5 ^A)xf.pnJk*X|ukh}&dj����  �#�P`H`@�?l5=F93/`tIi&4͇aQr__{Pd� � #8A`�4�`�,1{o:+ile.Yn B)VdV2R.y˸^X-sgʿQd�`��A��:`���krFRN;vuHe8C-6*IlxH<+lqL|貔p9rKd@�7#$ �E`@��;}LPF͇ cLcZFVS:Vޛ :4*&⬏m@d@`��� ��C`��.]P iUM2sT0q  i00+&ctdC4dYhQ" d@����B ���G�`��Feڻ̿UU |[,aRmj"l 2<7H[2w2Ċ<uJd@�@ #8���I� @��N*Ne<}^qG_Q./My};(Bs.&Rd~��@�� ��8��N �c]Zx)jm6Dle(At: w)SEt4\95h{uOAQ !dmA��� #8h��H� @ :IgނYY;* }ܶlIr.;Ww{F=/ =MCdA���  #$ ���I� @��eG&/j7 (ʚ_](y9 b(.hRS⡋ k>,FXjqPldA��� 0���T��J� @��&Tִw&igE2l]Hkuu%ԉ45Լx,Yd����` �"����F� @�NJ(ʞPmUF@*vJC7wl;x`v'.X"eg81d@�"8��D @���.o&Sdɭ"+]"'סyh ...lP)C>AXSjHtXd��A�#&B$���F�`@�!aκ^Y%ϵC([uB7kCb]Ej)Zy iA&=*+isOd~����� ���D����I� @��R<6N-/ۀ;,?'jYMʃBBŦ^OJViMdA�0���\�J� �qnat9B QzU .*4Ir9<uof~ub+<ɔC-);Od~A��@0����?`��HB\)KmUE m \XcU)RJbQ"\(<:>X(Y_ed���` �� @�E� s-^8ysK!qVd:@(BR)`7,s8Caf%&\M ҙd����@��c�B$���<`���R[O*Y+T9<�v)iWj+nyuk1朗k8;,kv}?9_ldA��� ���$���E r>Ol]ڹ~ *zXA׵.JE񖔧LBjԊm,L> G{zM37d}�@�� ��p`M�`�"o㯘wș6sz}ۈ Kf( SIsݼ`vlYA&P]UE.d���`#&��@ ��mF m|MmQ7R;A-EkDEڻ5[tjwShCbI8_BdA��� ���B @�D`@�s+;)3gӭD o\kxFaeE`PjtikFiJr hqqZUdA������P`J @\t-{onAŚ[Zzw])SL"0_:y?RƤpo%owxRid�� ���(@�@ ���;o-)dy2U zBfQ ԲH׹'Զ0,fG6R̙TTR7d@�� ��BD��H @��9 0vPW6ANufm( r-x5ZuY#nʽ̎b@dA�@�� `���8�L�`@��၃d�bD-|r$$XJP-Yȓ29>IT7bkȏ)J:ud�@��@��$���I� ��jA/._Z0XY\J5*ck qΕk73}MUߺǨQ X duA�� ���A@�H`@�=k!g\/d,fg19ԭ <H,!y!vp"v1bInMsw>׿td������"�B @�H @�/Mc2G~M Pj]4iu`pAPB]1/75A�R%Qހ<xUdpA�� @�p@L`� pv8b*ӲyÈ NCOϣRc;ooAԤ}8sg^Xj_dA��� �#�`�?�`@��#i2Vt2KڅYas$Aık9ʠ)g/0wG_5ta c6d�@#�B$`�F�`@�U!.=j]| w8[e)l^lIDpUUrӣ+Uѭ35e)r76<d���� "���E� @��yOXing����B@�B@APETAGEX��3������������������������TITLE�TitleAPETAGEX��3��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/ape.mp3���������������������������������������������������������������������0000664�0000000�0000000�00000020143�14662262111�0016217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������d�u�=�@��"`�1 @�?˼N(|rrDO xKإ2Mw3ŕQv. sR܄$dyA�� 0�#����4�`��� ADYa ^O-c7 EbJ!SwukrA%5:=ʹjw0l%RPn@MD] uؕd oA`�� 0������=� ���[[6mes| !)V+ -.Xڤ4ϹR[" !mdj���0�����<� ���=|TVYrK>͎pu1D{-eRa7tfTb)JT*vdkB�@���0����=�`���PTdZOz SGm=F*=)2gK̘Y +ci7bpynd dA@�� `0������0�a����U^!62z)}H:]X7NqUlλcjk)h{ech辅*kTd(jA@�� 0������8 ��{T:TVuw0QlKJ48'ދ}D9<!cW=vEFbJd/z��� 0�����9� ���\E7i*of81CQIxD1,Q,<_/m u,V)JȆݕZkU d6SB�@�� 0������7 ��o*ikP)U1u(1wR+TQԠt뻋<޻ j29N؄[Jd>lB�`��` ������5 ��� صĨ3`:4\ȴ"bY Ӟ"0-d/uҥmguΔB(tndEyA�� 0�#����4� HG*P̿b7xxrmCMMm*̺t A"(BT.?8EdK`�@�� ������: Hm~˩2 NPrYЪ1E叼XRZa饔ƠmV߲-C8غdQz��� @0���P@�8 ���In͠*Y#V$.9sgnF̛?ԺbR\Vko*U(VIi{dXXA@�� `0�����/� ���,`rј MvX*<>YLzJ1ץ/;  ]&up+C dbz��@��@��t���:� ���:D\=XYzSsWN}x^w]!F U̐FY׭$FЁte Q;1F!-UPYoLdgt�A� �����4 ���j6VӔ*{۟8^(rm&^HXRFKA"Դð"j#:* dmv��� 0������2�`��̯ar-Xʱ+84ΕT1XqǸQs醘–ݩKe+ ]S*cRduuA��@ �"����- ���|Au�Oހom61W�Ϩp9 Xѵ9}$KS<Ӹ啇Bd|`��@�� 0������5 iud5'2haµ2$!/&p4  vZc9frdf�@�� �����;`@��M93bRqό]͹(u2"*+bխǘ?C\(tsKըV6']9[h;s=PdqA��  �����.�`��B2ELv<FT!ϩ$%smNwzj ` L$`SqcbdoA�� �#���= ���0F?xņ^aW+5B.cHyV)Bބҩ4U sYz!㏋]ja{ֳ`6pd^A`�� ` �����9�`��pKPlw4i= [qUJ1*rv_\VjE."ذ쐆+ lґn`ddA@�� @ ��@�=�`��ƺ#kQqU)8YкQI4 wUk.M R 5;Y;Rdz���� 0�����; ��� EzQuVTqW9v ^ر}N~HX|\6JyyNg0Udu���� `@������+�!����E5Nb2@m 9HFG.9WlutBihU K֭3N2 1o - #{SdfA���`0����3 ���zǦ^Tk QMI(M@@εxs 4uk=DP~53FW`Tx[<dzA��� 0�����1`���ʣ#AMoc.ʪIKn5P45+8?emלYww1q@$T)q.d\B�`�� 0������;�`��MBp*Pɫ֊iN7*d^D| aҟ(F~h[zQHͭHiy"dǏsA�`��  �����7�`��Ezv*S{^[byU k C'-[VHVQO?UBQQW̉M (7fApbŲ1w Yd͏iA���0������7`���04B4U[d7&?%sْ6|G|1/k{&]i˥)Ghl6>6dgA���  #&��<� ���(krqt))G yN Jlz C)Ňʹ3*79xdrA���@������3 ��h(Uƚ,r ET7_۰}L)!!I;Ifw=p^S _do����0������-�a����mWIn qѭ =gy+0V̪'L9 (ֵHsoZkklh0,T>d��@���#�A���9`���Y̎\fmy$'@WHbFbVyၢڌqel) g jd�����c�B(���G�`@�z'8jF@, ZY&&BrVI킡g?]}vwo:-[<R`dyA��0#8�@�8`��dI*T ؒ@τ(!rP:tTPU[аu~&$>8VJd� ���A`�?� @1i2iy/9q]UB Lː8bF@SX2r˫w~xyd���� ������C�`���wlx:iL)_d(fcSIKi&=A׳mR_|(ed@����B<@?�`@��t ;L&jVy#<Njkߒn_h=V瘦LcZ$Cd}��� #&P)DD`@��R]sq%k֢9KݗE~ A<t֨A䅥/4ZzMK,z2dA���#8@�? ��˽{<l|?hu>3|2mbb6STÙ vm <e(d6>*Ld���� ���B\�@Hx('p( i^ZÝNF 9<o!d}T\e-BdA��` �"�`�A�`@��qA!ր(}y V4<Gt3yM{G׻k+:lg%M&dwA��� #8\�L� @v틨>5 ^A)xf.pnJk*X|ukh}&dj����  �#�P`H`@�?l5=F93/`tIi&4͇aQr__{Pd� � #8A`�4�`�,1{o:+ile.Yn B)VdV2R.y˸^X-sgʿQd�`��A��:`���krFRN;vuHe8C-6*IlxH<+lqL|貔p9rKd@�7#$ �E`@��;}LPF͇ cLcZFVS:Vޛ :4*&⬏m@d@`��� ��C`��.]P iUM2sT0q  i00+&ctdC4dYhQ" d@����B ���G�`��Feڻ̿UU |[,aRmj"l 2<7H[2w2Ċ<uJd@�@ #8���I� @��N*Ne<}^qG_Q./My};(Bs.&Rd~��@�� ��8��N �c]Zx)jm6Dle(At: w)SEt4\95h{uOAQ !dmA��� #8h��H� @ :IgނYY;* }ܶlIr.;Ww{F=/ =MCdA���  #$ ���I� @��eG&/j7 (ʚ_](y9 b(.hRS⡋ k>,FXjqPldA��� 0���T��J� @��&Tִw&igE2l]Hkuu%ԉ45Լx,Yd����` �"����F� @�NJ(ʞPmUF@*vJC7wl;x`v'.X"eg81d@�"8��D @���.o&Sdɭ"+]"'סyh ...lP)C>AXSjHtXd��A�#&B$���F�`@�!aκ^Y%ϵC([uB7kCb]Ej)Zy iA&=*+isOd~����� ���D����I� @��R<6N-/ۀ;,?'jYMʃBBŦ^OJViMdA�0���\�J� �qnat9B QzU .*4Ir9<uof~ub+<ɔC-);Od~A��@0����?`��HB\)KmUE m \XcU)RJbQ"\(<:>X(Y_ed���` �� @�E� s-^8ysK!qVd:@(BR)`7,s8Caf%&\M ҙd����@��c�B$���<`���R[O*Y+T9<�v)iWj+nyuk1朗k8;,kv}?9_ldA��� ���$���E r>Ol]ڹ~ *zXA׵.JE񖔧LBjԊm,L> G{zM37d}�@�� ��p`M�`�"o㯘wș6sz}ۈ Kf( SIsݼ`vlYA&P]UE.d���`#&��@ ��mF m|MmQ7R;A-EkDEڻ5[tjwShCbI8_BdA��� ���B @�D`@�s+;)3gӭD o\kxFaeE`PjtikFiJr hqqZUdA������P`J @\t-{onAŚ[Zzw])SL"0_:y?RƤpo%owxRid�� ���(@�@ ���;o-)dy2U zBfQ ԲH׹'Զ0,fG6R̙TTR7d@�� ��BD��H @��9 0vPW6ANufm( r-x5ZuY#nʽ̎b@dA�@�� `���8�L�`@��၃d�bD-|r$$XJP-Yȓ29>IT7bkȏ)J:ud�@��@��$���I� ��jA/._Z0XY\J5*ck qΕk73}MUߺǨQ X duA�� ���A@�H`@�=k!g\/d,fg19ԭ <H,!y!vp"v1bInMsw>׿td������"�B @�H @�/Mc2G~M Pj]4iu`pAPB]1/75A�R%Qހ<xUdpA�� @�p@L`� pv8b*ӲyÈ NCOϣRc;ooAԤ}8sg^Xj_dA��� �#�`�?�`@��#i2Vt2KڅYas$Aık9ʠ)g/0wG_5ta c6d�@#�B$`�F�`@�U!.=j]| w8[e)l^lIDpUUrӣ+Uѭ35e)r76<d���� "���E� @��yOXing����B@�B@APETAGEX��3������������������������TITLE�TitleAPETAGEX��3����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/bladeenc.mp3����������������������������������������������������������������0000664�0000000�0000000�00000067406�14662262111�0017224�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������R�� p[P)~H(C:i i*LTM߷PV DD{J @19�xxp���>8s!hcL #kPH Dҙ�u[�|D- >"&d?)SCO���R nA3 j%�sP1acЄ1�#WA1d ?B@_7(9[/~��m�lpL�P,Da./̅`!  $`B F�@NTwr]BNѭӻB\���R /{"a#%utnaAa6f~{g5f`a*1IPk<#rkcϽ6h9f~_wԹuրux��q �!91([ɽ* , ۯ}v*{7OT}_z׺cṫ;[?R���znabFR� -"a%u ,FbFa.a?hN>F `Q :$~}6A/I ϡsS;a*JH��P : @)�7`g,,h&u_OY(UYl(4/scܑ[ԍKsnw/V9kط���n`R /"aeu 4 4a&K`8fpbf7& lXS@uxUɗZ+L>mVwV>ulEi���_l@ ,07`X1/357sp0{3A@ ߐ0Ő_A4~{zULG][dk#6Crh5o$=tKNBM���n`6R /"a%uhTagA `9i\ff7 #pu tYykmh c34v5*DS��/73!,8G!213P0,:z0&`vbm][:/P}TJ�T9Bk<!���n`4af; `R /"a%uhT9ae8 A B1N.h3®Lsz\r.܎|vKK�� S(EN\FM A>2LO8!_ɡj7+ YƓ1Q2oAʝk{I2RգU{⫱o���~n`$`�J``B`20aR� E_-Mᛢut!a`("@Q iکmGFl+hDi*"/��/�P30:C18ÇX8>C_0z@0�|� < _{zQ5u#nz_TٴWsou:3TWoB^_$���naR X#-"]%hT& Ha &3D`,>awal*`"(#�h;'-re,ri[_{.ڊtʋ;PUc۰=Tka,��!$9\b& nf"�` jc}n| _;EGg{]JRNSԥ5���n` `�J`R -It` J`&DaQ"aF4`. �8 A]_N^oE:pv'Ct#aZa6V!˥π��?�+00�#'1 x7X C0C0f40 S�@lԴ_MmVyBn?Yq^2մ/dsU~$���nR� @#-"\%hT`&H`IfH` Na(Za>`0� 0O[׺12:1Bt0sU=toZCVLjI���l@ 9<b. n"@x`&jbro}$C^z-.,%ʓyl{o2U-=:���^labZ`k`R #+BbulLa#'\a`j`�;EWHo~`_<NE55WX,ƽBGb( C7 `cCY ?��%`gÁ1B 8z A7$>0� :s"]LL&!KZ{s]{~$���n`�"�R� \-ItY1HiBك$)E �-@_[}.nɽDY*|jQTş)c>��� 00dM鍬E�  f: Ժ:mWo_߯%=rk~ճg5(ou>UO���vl`R #-"P嵯hLb�BPjP">E3( р8�<G\TWE>f{؇{b/*ev5)̩З���l<[Ab0Fn "NyF[p(.jNWB.+C94IUioG���_n`� �,X\ �#R � !-MP嵯hLM3"@'pL�ɀd%F3zGhݭzko1}k}+-MJ4D4���l ,X> �^e؁ %']2`zs7 u5-e @Ht"_/8?!U���Wl`!QPHFr/R ,!-"u8EF�&���sN _M^mZV]cu1*u0U˻,|`�� m@2@4 ࢈8È1?@40( `#k~~K/DdtdC=uTVk6DNӧwflgڋ-)E,���_n`���`�*R � -MO嵯hL !P `�<@jqF3zڏ߿O{ڵ[3:gu yIe��1/tdX3D02lB WL<dcyikʍsTU)f*PSo47ŧ?В���Wl`$�R � I+MK%hT�PFJ|@OFh` `A�?(Dɇg:tGJ\#Q ϣ[%MdG\'���i1zc7z00^C�.2J ]+4P6PX}=}g)NJ}z-���n�P%HR /M钫u Iw@Eɀ8Ȁ͎Qs^e_vOOgOꝷ~l]A�� m  `0�0\8<c0Lx Ƞ Do?|躯ߺs#|fZkl/7mL���Wl #R (!-",%uJ0�"mH"� 3�c��=gC`.:+_agyKک;<e)۵և?���)�� Ś]=x961T 4Zy.jZz7Yl_u7u5r���Wl "R !-"{51riI9b9N�pd_֚/(ڑR:KKOB:g?GԥZ��?0 � ` 1u -ـX@ `}vV4TM #GL_W���_l ! $R� #-"um9AhAL�p r6tk܆}XN,~mP[Wss`�� m�$4 OeN L �<`z`�pfFEoZoQ=vaw���Wl *RLR H!-"+uJSœDLb9,)�dT<5?xXe<V?IgSv>}U]"kԶ��A�C(12ـX2L|UybZ/j*KU?Ebj,eR].���{l*R /"a%uN48vpuB,PH;8,Ew^U8SM"?,׵>��?m0� 0>s<16X8>0bc�`0 ـH>�Y Ze}4ꔻz=ΉS[$����Sl �R l#-"uhIFiR�QGR~BvkjvqTq u1)$%oUҶ}U%��m0L@hLMD/ ĠL@D t`�@2֩Dt)UzҖ۪|ʍk=*z4^(;���[lR� #-",%u0$C#00М00 0L0 <>x-/cѷnMУc'U1}|��mq<a$ˉ1�`�LA0K^oU{+zztz3S#-N~[Y ���WR /Ba,%ul"00m4ng357pMP(ס^'! ;Xi_NLc鳾L衋ͳ^""��m�;hPȓy@ (U_gusSo[jJmUkej';kU���[l#R <#-"|%B͘(}AO �'@i_l~flFc1L\(E(2eJHQv��>0@8L0�GېD=@@ 0�d0&%kgzﯢ}?ڴ{=-Oi{i_#���[l(�b�R� -LKv0 �0 D4) 0M%00cp3�=ЮhzzUgoi}6w7JVQd=(FMw��m(` > D A!�P*03�`kԢ=?[ofJ22?E_{���WR� $!-"{e |l2050G߸4bc 0N&<Y=h-ewo#W)7X•,ĸ-}z��O0�0.SX0Sm6:I0 $~?ٶo*\ݽkW /���lR� /{Ba,%ubF-a6&h5qa3昑^N-aӵ~瓱!V}i+>nic=-���l`*F2`n b.1 n/F!�, F`@` lA^j&V䯐i7Q֋TJm}n'Yjw���Wl0R 5-LKU%hTБ0cO*4C 0T(0/ >TM~߫>ҲҚSMʿ)t?:Tl2$b{s��c4˔5JNCe6fY0t$1m6%V)59EE9>Qn=/z*LSEӞS���_lP aR � !-B%5vH`'Y<lal_8R^^JZզew븥]3췛Rb��c0&SB0z Q8f1"`$S800WRbX_g6A W;U,ʚ&k]T(]})j���[l@P0R u-LK=嵯hLP0U5: p0X,0-�FN&ij}]MovT1Pc*.,^WC��c<˖5 ξ6MƳb!y0@죤3CXNJi:/Ъ}9ez-k7[8ձ_mW���_l#74C q5R � -Ba,%uV0<֭5|*K0CNߦ߹;ݻ VDR�:I[غK,Ski*l�� (`8& x`n Ԇڣa `v�ihd߯rVi^y޷^Zwa<,���[l�.`H a.a?FR Y+MI嵯hLl?FF` p`j`4X,;t~'wE>^?֝lHں&['.G|��eMfGКʘbq>i1 6FK7MI/(ho-S=m���_l�(`N $a=R � e+MO嵯hLm.=<` v`d`2^Cիɇɦ[>DruKk)~'_}lZٴ՚?{Wkǀ��O}1 J}0\5# e4Q4 d$f˚,Hs*hۗw?Svմz���_l &`PF !a;R +MyuhL=m; 6` t`b&@`0X0 k"jV/zwFEFB.[Ot~L=&��{ !"̱ 5ٲ�ih[tbVv ŷSEIca'o'H,Z5 [ _e"���l�R -LKR嵿hL`F a2fǙ>l.2f�` `b`LPh v+twͭ_Ѝ[uVolO~5t��  bBthЦjcxaX tP)0k#`Z$ym6vJrn{bbOSޟT.tM���_l�`DR -HKeuTF a0F̸\lx0` @d`Jf)rh&}Vwn?&~d"S$^pf˭ut3��O}1!s&2dQ041~1t 00@0�0�4M>!zˬɒx݄ 0܇zb"AdZ؎yLLY���wR� m/DK|l�t`4Pn$\QL4T@h4pN?w^5'U}^`��m@@8@#L+Gp9GHp@H�& (`\x5jt҉].ײ_o-?WZo���R !/{BZ嵿dL_l (<p0C]5c Pc0T�(3)L !ߤ̡{=c~ޭտr&hwi^��om0D h )?+tLH=a/%>G&=OZPkOOU]U&���_lf@AV{9!ɌR � +{Ba'e|Y�Bpix򬲜J馾K嘅V,(l."cwgh˦T�+myo��'` IM:1@?8L @8 Rjoo]ލZw|̽%;ݡ!���_l @AdR A-HK,%P�Q? t  YeM6o;M=z59^/M5ջReǿ���n`f@`,fa,a$?&ht=!`$`Ld<#_ί/c-ۤZ~vW֤Dg F>���_l@R y-PIa% 48`Vf @b&&fm& @`jiaGh }Lu$TR뙿ս)^eGC_|D?���n`$`�F` `2`Db 0a`:�HP5"�/�g"6*5jJիEۭÝڷښw*: ���_R 8-{Ba]%Tl�c氱b#2Dm$F yfe`9t>r#i"vܷ\r SHP},FڻZNP+�� I"\рI|PXQz�/+3Vu!Zx:m*zW(C-߼KW7ύ���l@ 0-SR� -LKt%T`F1 s7 \0i3P50#0g E_A4~-wJ[zS?v^v^J]eh9m~)���vna"FFaI:`9 a!a `/*10'XGĠXkmlzN)oCݎK_̓BUVbטI2 ���_l<R t+{BaeuT?sLMp^DY�Ϳ2fHꣴTf@-Lb[0 IZHD'fm[k2��c0U2&S-0% `@*r^ӵsrMB+`pO%Bhtp3bH<DZIn���lP� I-LKke 4AH`^ �pb&Nnj!``j`$F�C4Rjw\~C=oD'/o+ft<S:9��&ۘa�ဠE'7fIq> �T5@a]WWZi˗E;2ϽK;���_R� -{Ba%uTlÌ@3 Z4DA,<4 {SIۨ&9~efvnm/X<DlMe=#w��`ɄR 8dia3W@;� dWG]YBqye[{d{>iC7���_l(IR +{BaqeTēj.ى) |bL`arjSyزi@ ϪSCfi)S7N��))!؉X ^>oNQ>ӖVΦ6;hUzF˂)HgxY B���l_#R� /{Ba%uTjlهCPᳰFw\eU` CMU~Go}' IrO>s [j}x��c 0RY1' :s H'� #�$PaNHJExYQ6-Ig^Rp [^W6=|USw0^���_lp� 1A R -;+LK᎘e 4'8w SB0y3P?0 �}aMKS?[oRm(yR[GWImS:u{KZN62/+$��m�P��@J0�0 B,0pR0lB$00�8�h}jzބ/k.kpm�)#o n���R� =-LKX%Tl%�\ "b0 hnF"b`@d` nf Ro[Z?ѿG^j? z:׹hKq;���@İ A4y‹)8 �<dp p3_ ʺhirR KEB81>)/���l(R� 4!-{B yhsdMTLE� L@4 ']Eo}Cdf#"bU}.r?ZQejbN��s� �@PT!�0�<0(0WN~ivvRJ~RgQGtIOmjaBn���lR� !-{BM%T4{@׎ѻ  q(.-U=؉bĴB,E54zj-��_I b`L]@P  78JqGxY Q:p({ì{y&bƣg���l=&!1E�ӌ@8R � a+M+  0us=��A@mE_y5,ԕ?dyҾu^yYV}n][]W[j`F��}�pPQT20Qx0*00@0�vu'juT=^_ӷ'UbO}qE4z���_lR� @!-{B3% kIQ؇i~� USq)(&-`|zڝ5+{W5wM-ތ{AeZ���Wn��<.� bT�!Q`!`�$0jqF3zYկU~zt~i?{'wWTo؟WE���l`��A R a+MteT Pa.E8y�p �m#_׮9[ttߧѺN孟KgVm_ ϱ���Wn &�F-EFhf0 5#q__FƌvE{~s#z]$ $IHM '+oQ,*jP���lR� a-M5dLp�Fޤ݀ Df D` j*  %�2}<gد_w͛\%wbr�� Tht `:<)b<fqnP8lrTf[6ҖE4Ԁ7/ ���_l`� A0?��1CR a+MveT#Dl8}@p0m07A�mҙ#LjՓMU}W%ǾJ3ofNt��7P1D0 ,0vQN0s&0@0 F0 ~ .㢿t_[gb6rksa!hЋ{L-25B���l`�` 20R a-MkeT4�31y7fb0Z�,*4$2967_zݝkrUUKs��ۂH)&L` Va"raJ`*D``*z~&њj=5NW\3'SM,}Azo1���l`"� `~R a+M:dLFNb~\p`&jB��˅2GO)m'gەܫN~euzzTofC1 QK0��PBAƘ тE` `69=_y=.zv:)kSO���l` & `|&`btR }a+MpeTgp& `F@d(�h%io) O{#d:{K:ȬJV:{ ]Sc)hj���(X !酂vYR�Agr~A ;ysI^qlpF\z|Zr"j) ͬL���lcBR d-{BaweTq^b n uH4 X T gTSi�gldmRVW��+} vfpCX `u>0(�p,p#/9o}* =tSADMI ZY<V4)@54���l`$f `x&`bhR � _+Me 4 1p`&%`0`F�\ �|0L~k{~[d_,Y}h7rU-���Wn�p�s� ! D!3  �6C�ݣ�HW4t~j-?}_4S%Hɭ|)ų$���R -{Ba%tlcD&̡zbP$n66sĀRW&�Jb|3m?ߩ].Rd(Ij,wE�� m  :`�`>`>$aԠa`DB`"�`�@`"`�L|rԿ=z_ۃ7jRo҄҃C���lR� _-M}eT`& ``& b qZn&`D`�"ih3L˯zz?/^wq{��_D8`fU8`Ca a`I<`$`Fy$| ~OӞvw.ٵƬsJZf\><=HՕy|YIP���R d-{Ba%tlcDb#Rm!V& B`V J݇m}I{n*Hs.s9wMO��-}�B�ـ^ف !Za(:途qT!f�lFCtOJM}WEyg2|{a3۲-Mu���l`R _-Mptf `\& `a%3m$& d Y1s] k?/u ;7_mGSgf���D�$`�a7f{g6`f`:`LVk]_Ӳ~kJS���lcR� -{Baz%t&a'Tmn&FrFtê2L@Jb̘&WoEkvVYL!b_i`�� m@�*&�|`nCiKFaZ& �`X 2`h�z�ʉ~4mkv<wmZl]���l`$`XR _-MJ%dT a)صm0(f,�4`& `v%_tM~_O򶹿݉wfY�� mT(@Ƴ6 $Ƭ X Fp A NeSKx _?N=Hok./:g߽L,zgk^���l`*F6`nF b&2R � A_+MT%hTYn0 DcB`"` lASu3#wSk"ROuk[$Vߪt=gxfI���[l`Hy^'Q>lLs0L_[mTdȽ_]vH/I%-_���l`(f8`h `bR 9_+M]%hT4znt3fD%0pj0& LD{ȍLjveȯӻ~Ͻ՜s:G{]��m \Т/GT#MJGT$A< bc˅f )r.IOUemKoҮ���lcbqaR � 0-{Ba3eu /Xlb.-Dh+m:I_ǿq>/05CHnI=+bUn��t0�0�B0@08 1N1 0;@0^0 60 +0�V0� ^d9/zTFD}5{4;gn1j5:uh&���l`R� %_-MS嵿hL0`P& a1yjl0f # `$&``"M6/ eKڏYkuJWW}Oz{k߀��/M�1SJ0Z5 ;jT2$4w=.4{*؛79n,gln���lcC&aR � <-{Ba%u 3<k2 "eJ&HKNWFNJ>lrrҌСj���- \BT|| >„ xh9ۥ&5$mKDCtdu\&g:Qvu[���l`&4`R� =-LKU嵯hLH a5f kl3 `P�!p 0pNO"{oCWɯ"זS}dW��eW0diMׄiL75lMBcSLɉi!g&3ytr$+Bi< $dmh���lc#a6ƵR � !-{B3%u k5F�`J� 3fD(KrS^7W֞ufK1C^hcjߪg��'!X EFrHL9ၠ2 �s;tWӾjuE}2?OB%R;׶̕wE���l`&`8`@R � I=-LK#% t&a8fj7& `< 0FO"#:k1Rmn޵gDq^[P7��,90Y8rI*IP?1iXQ(+Ph PO[u֕DzWzk~իa*o*NrS���lR� t!+Ba%hTbb,Nd>`k$c!Hb `a `$`��i&xJPzZX*U (aB z=szrodX7��ebDEgYP>wك%Vh>0L)fhxaLq�tkeu־O9-.���lR� (-Ba" ta&"<ca`;&=i9efA3YNs]MkWPzwUح]/��+�0'Sf0j i6y@v0e30"]}A[+SNu׵^M{mJ LӋUEdϱ: ���~lbc(RR $!+B%uhTdiN`aTb"b<`W` 8`�j&:Q'~TKcms n,cA?OjܖQ߀��/MC"$*=G0@|!`KMebi)Dc|U RY X&nZpK<.QC\I{���R !-"% tlaf@c&s$a<=?i<f` 9<6_جW:-aR'f՟}VI)HzK7�� m`�-}I4[Y63IHq0ϋ h o}IB^5j#U؆w/���~lb#$VR !+"#% tc}^`Wb#bvh`L2`H�dH gNQke Z8iBQBx$FV OhZp+��ʰf@8` lm<Btb & `Lb`0 @m.&iKꎽmmw}Ӧ351/kס.���R !/"uhLn`4a&O`7u|g 6  7:Tg7\I֣գz\LHšt~��%�mПk%bp1TfbXh$\vLxƒJj߽,^SY1X'k T]%@/���lb#R� t#+BtVc*Fij`Kb@%b0`AB �XSf054Vt=lK|]ܦ%mXHYFQRG �^4>pa܀f1l ��e ` 2I0 @! 0.rh-6}]֩_cQU6W?zJ.^ݎ��R� h#-"[嵯hL�znaBfDaO8`;aנa `1"�S`Wz]k}u]b"Y9Od-5 -ce&��$9"yDP,fddt.EpL0{]#lorva ܴ6Naէ8r{R%o���la"F R� #-"Q嵯hLFaE>`6(a!Ha`,JA@X X=lLۑpPpS ͡wc0^S8��d- Nkf!nFb ghd٘@B@p>Ed`VE\Vh|iajKz|_:n$���{n`�&`H`�`@`06aR !_-M`%hTa"`&$:h L�>b=?]4|ԢjUDj.zSiT ?���_lA D4ۛ6LC0@21:$݊+_P?+.R )ێE=J!���[na"J`1F`*<akR � T#-"eu 4!a`,`*�X[ឭ~u2Xav. ܳTPw[HSVR�0Cx��a s 0s8q`1;SH0(�2~�C(&_^M5лkmBv`-KEv^+س$���l`&H`&'H`R <#-"%ut$JaD2a96`, �8O[׺mciLT] RIД& %͞f���8L0N',2LO0 @ r14V7veufO~bu}vM: ċ2�sV)N֤6���wn`�R� \!-"\%hTJ`)FN`Pa"ha<`2`��h|G`⦮NAkmmKlii]jjn[::r���8)6CtX# Sݠ% D �ĵ�n}lȵ cԘ6VnIA,jg[TzsAdi<���n`�"��Y1R � =_-M|%t\Yi*р(q`LB\4c;"__֪쉺/&Ӻwo/PmEhIw~���0X $b.fn"d``f`o3j]OKSz76miߥ{Tm(72tfTO^���wn`R �!-"#%t�BPJQT0.a�@N�8+Ch[_`Ĉ]_ND} ?���l$@\ b0 &@n "V`F@d`@ngu~_kj)J6Jgɺ]n%M���n� R /"a%u²afC2n k &;F*;^_(lEh%!h.6��� z4@bgEq f'X`fz`p~eggbZf[r}Q5-붟7mV%S���[n` �BP R � u-M၏%u,(FR.EP(L` gD;:w}4okj:OKnYmC&��`0 @>9 b`)�@ rԷnTu VTyU*j[&G{u+C ���{n P� /"a,%u GGL"]M)e=F*3N9UO;wM&O֔I -˚YZa-g@���h0FA (!b6b|F`j`6`@!2@|(H>g0V˺ڋG۪rk_ʏkEkkjOӽ/���_n`��t*@R � -MN%hT`Q� @�,@ sD;9?oT}:'Yӥ)fSw�� mB ib0jn�"``@\jKsh}�lBdl]JQ]}|RFo<'l���Sn��4BP�FR -M遧|&.fxEf@F(f�4�DQWe7-t}DZ?o}Q4IX9Wx��`  Ab2FEhnF"@�` ^"AoGT?FozR">ݟo{ [-b袘���n �R� i/M+ua@ hH A_hE(4\Zzoeн:T"|Џ���(Aa(@qrg�'s(0? +OzdդY~+Gl};gŭV���WR !-"'|l "K0*�"s"s #�s�0=a ;ȿM^eZ-Ї)UKGȿsAcg��� L @�Eq t4 I�( :cb? }GoikvNv VQ/���_l R� #-"+u#%M9xQ&!dAL� P r6}}E;bjz;tTd}i Ѽ�6J\V��!?SQ9PKQ@ B�ѕWG]2ςoMP&ym{V{DrŭлYAN���WlR� !-"L嵯hL ! u1Ỉ9nIN�!xkr~KwuƖ5zNã㞪g@jͿ~QRX<n���ń3�͘ԔL)T&"u˘u#,AjX?m5|4bSʽXz}[>]HeR���7l *vf R � 4!+BuM yF`2�ߍ9(*M7u}~N-g*B3jf8^Yn4)T C^8��c� 0!�0}c8x31/�@0!�_v͟2҅Q ]5޵7u%:-veok���7R !+Bt+l $(0S"0Bo0ܓ?0i0!0[0 #7Î:0tY#"GY5J8kbcơu9y0R}n~zt[_���l`��`f �fbxnj @"`�`N�`k�TGO}O?oNпk7%;lU7-���WR h!-"+l0c00P0΂0@0�L0 <>[/wWwP:Ck!W!-Զ.T?��'�9�HY '�P� ^` �vk>{#/e]jj_Nwlۭ}]/O~א���_R !/"+ul"�A8癏t)x9A QEbO9\m̦wվ��``@&<` %Lp5Gb0bD& ` ɀ0 Q5_k2 %J_ze"t'oړeieK m>g{���wn#�R� D!/"u0#!0n5V3Fc0: :M;X3/wWѯǬc?j?߼b廳CۄSu9+E7��?m0�0:3pV1*ӂZW720X�@ ;] kr3?ڴѫ&h^u2G\ߎZVQC���_l"�@�R� -Kv\с9, 0ဖyVd�rxFsD]W_UMo^z-QĢROUe/��m@(` x?Đ"g` @�0*03�@rDt~Nދ}:Ӫ{=(٫ooV{���wR� @!/"+nB34233;A0S�B0"åU s1Rz;QuNN_sG=ۂGia���l`f `\f a'&ݴRm%l�0P`0�L~GY~Ԣ55ӷk_]_tX:b¯���_l00R !-"u60sI4w3 0Q'<=wn'%z,�C{éqFibVR��om@HxL@F Ÿ0ÂlFfd`@&*˩D>{Q}ץ^=oΤv^k'oGq% 1���{n:��0R -LKY%hT0L\4 00R(01@:nTMCmdɹwӷFQrQg"E-i:x��c4˒5N[6h!O0Ax1cZlPϯE}V-E,-[.JZ]W^*EU-C.ڦ���WnP@R� 8-Ba,%uqx(]՞kT'0SɒmZcA)ˆ؊v'63|ҸgITL0��m �遨5Ɉxja80EY jQץ7?ۻJj徎ߥY_}mn_0̲X���_nR -Ba,%QS:48 59Q0 38*5̈́#,F_v&iD{R$IHmmjf0 졬J-��}p P+Q 3ෘk6x4`\yY]fojԫ_{Z,u���Wl�R� )-LK,%0@0Vk5K 0Y-0,GDOzw[}'S_R7{URU��/q 8oAH +0 *�[Nq-~i/u%|kWm([-c}m,m���_R� -BaR%hTl&cFƓ a8F j8!vņe2y`86nQF־xKCZ_Ek[/KjjyKt ��mp*`3:} 6d_?M{4$*Q dD4lV[G+p_ Bc^lh{Kle���{l�@ `:f �aR -LKF%hT6[fkR6` ^`T3h }^_iz5mٺ::g^ww7 Ԧ-i�� <6iZ&#Hk٬4a R�i<i!,%vSNn:>oꩊ~]k���_l *`L "a=>R � i+LKS%hTXmL= !>` t`f&MnQ^ʚt?VfNԗCQQKE|�g{dV-jr @��/&LѢnse 93�TS0ʆdVYk_Ioto\9UCw8ԠskYjۿ/���_lf$e'&b;&R � (!+BW%hTnm;& 4` vlf\{ovYwaݴJ׍b^ļLFpu+TIkެ縮��'hAaw50 U$dܔkWmv R*ϔ핮Ђ45?^CX���_l`"`T b 9pR -+LKF%hTn9!*` Fx`^fHMn}RtSZ=__-i6=hU)=EKt\Ak��m0DG a.,MN¬ �4 �xɭe2Yv+ ڭ=+Z=u-E62Y���_lef�b6[rnZ7&R� !+B%uT"A ` fzkfLЛu;.cր{C ,Xe/R~]je)`9xY��> X :hCŽ8$�X�Ԝ7}-W#P1d JZYj=Z- Y as 6){ӝԔޣ���Wl @ R� -HKveuTø[n@ٰ\ < Lp0\eM6kd}-M36j|{:1tE$VBƑ��mHdJ , `ZOLX� 0# �p�Qw(ІW=vgթa /^";V%ᗻJOY)���lR� q-HKJdLWͭT<XL= @ |\A__KQۥWonhToM ЇV�� a2dfcThIƁ#D` f:�Mҳk"GGqo%:0|[Ҧ'A���_l @ R� I-HK7dL tSPڰT ?A �| 8YeM?Wݿ?d]6Ke7lM;=*.NgBB��'ۘa$njA Y6J ntܚM$NY=.y]u-]^3{���_lf@`j@bJ-oR� 5+LKY嵿dL~.&$`  ~`HF  h vt飻2Wެ53!F��s 03B3 s4DBq0|SNrL@\#l`Es޸yV ~HV} b+ =!RRE���_l`lbP+R � y+PIa9dLo+%`` F�b , W,GJ({A$ $X$k#D۝յNZ-x��s 0Q 92X#360a@bh3®LOZg)o W򶻺`z+OϽ$���_l@ ,07`X1/357R (+PI?dLsp0{3A@ ߐ0Ő_A4~{zULG][dk#6Crh5o$=tKNBM��ۘ(a YB4AW0;"D�|kB~Gavz^og}hG70mJ���?lD?4Ba1`960R� !)b;dLL2T0w( OnJ{Tbm] kgET(l>XҚ|zƊ(+� 3Xeۋ̜OH^��'ۘ iAَ/yF|9h<BP S /\޾|܋w#/`s],���_l<ܕ?RMTz5 lR� D!+{B <L+kr0i;)6־ODC%-Z9W.*o��?0@0%0H00B00B0� �@ FXTom?Vk}[{luJt#6_m UTT���l!̀R� T-{BaeuT86\p8 C<>4M 3Iۨsڒ!]]jvނ @{7mzoDI��s 0 W2& u4 &3D "t�JE{4nw~EyB^x0NKKu T=v-<���_l$Ah`vpR � <y+PIa8dLbzζpf'@` & Q dYh&?-@kŨY13:]U)tU0 SC 5���€xaѦ#dGc`X!I (4;:,WcvOv'EiGaZgTں���_l"n`x�bb|R � +LKeuT pF'`|`&�yA5-66$eZ+~۲iev } mY0���ۘP2Ɋ ;TɘRA6`Y�`CrJRU+k{wV=f!c(PUO-Mqt=]>LֆoD���lR !-{BeuT@ 9<b. n"@x`&jbro}$C^z-.,%ʓyl{o2U-=:��Xa=HFa8@Z@!DrQUg9|:MMU <;kqPQ(kw PX"B���_lR� (+ba% 4"Pv|8 8M1?x@2C A0!�ӧ0h> 1B-eBn!`阋(5׻?O��ۘ�@$�`L`Z`Т`J`8`r�az� B)l۲/ol%Fʪ;Zt7q1gX���lR� -LK]')`TDZ b2 &n f"\`f`f`oj]N~үoMz5jd?7:*��n_08 ) :CX€#P(A5,, �@\5�@.p> {߼sbt^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/blank_video.m4v�������������������������������������������������������������0000664�0000000�0000000�00000035252�14662262111�0017745�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����isommp42��moov���lmvhd������X��I��������������������������������������������@���������������������������������iods�����O)��trak���\tkhd����������������D����������������������������������������������@�����h�����$edts���elst���������:���������8mdia��� mdhd����������`��U�����-hdlr��������vide������������VideoHandler���minf���vmhd��������������$dinf���dref���������� url �����stbl���stsd����������avc1�����������������������h�H���H�������������������������������������������-avcCB�gB���<X�h<���btrt��������!���stts���������������stss�������������(stsc����������������������������stsz���������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���stco�����������$L��trak���\tkhd������������I���������������������������������������������@�������������5mdia��� mdhd������D���U�����Lhdlr��������soun������������IsoMedia File Produced by Google, 5-11-2011���minf���smhd�����������$dinf���dref���������� url �����stbl���istsd����������Ymp4a���������������������D�����5esds����'���@������v�����������������stts����������*������(stsc����������������������������stsz�����������*���������������������������������������������������������������������������������������stco��������� Z��$��udta��meta�������!hdlr��������mdirappl�����������ilst���gsst���data�������0���gstd���data�������1021���8gssd���0data�������B4A7DD704��������������������������gspu���data������������������������������������������������������������������������������������������������������������������������������������������gspm���data�����������������������������������������������������������������������������������������������������������������������������������������gshh��data�������r4---sn-a5m7zu7e.googlevideo.com����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1mdat��e =�0|u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u]u^���A '���A@'���A`'���A'���A'���A'���A'���A�'���A '���A@'���A`'���A'���A'���A'!������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0{!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0w!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0u!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0p!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0p!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0t!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0x!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0}!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0v!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0r!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0s!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0u!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0s!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0~!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0w!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0w!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0x!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0v���A'���A�'���A '���A@'���A`'���A'���A'���A'���A'���A�'���A '���A@'���A`'���A'!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0u!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0u!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0~!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0{!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0u!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0q!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0~!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0}!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0s!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0q!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0s!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0|!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0z!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0x!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0~!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0q!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0s!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0y������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/broken-tenc.id3�������������������������������������������������������������0000664�0000000�0000000�00000000620�14662262111�0017637�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����TENC��� �WXXX������TCOP�����TOPE�����COMM���h���engiTunNORM� 0000036C 000003E6 00000BC1 00000BC3 000186E5 000186CE 00004ACA 00005A82 00011170 00011170�TCMP������1�TIT2��� ���Take On Me�TPE1������A Ha�TALB������1985�TRCK������1�TDRC������1985�TCON������80s�@���K���� p����.��� ��%������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/changed.mod�����������������������������������������������������������������0000664�0000000�0000000�00000006074�14662262111�0017132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������changed title�������This line will be trun���@���This line is ok.���������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@�����������������������������������������������������������������������������������������������������������������������������������8CHN����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/changed.s3m�����������������������������������������������������������������0000664�0000000�0000000�00000001040�14662262111�0017041�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������changed title���������������������� �SCRM@}0�����������      �������������������������������������������������������������������@��� ��������������This is an instrument name!���������������������������������@��� ��������������Module file formats�����������������������������������������@��� ��������������abuse instrument names��������������������������������������@��� ��������������as multiline comments.��������������������������������������@��� ��������������---------------------------�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/changed.xm������������������������������������������������������������������0000664�0000000�0000000�00000012537�14662262111�0017000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Extended Module: changed title�������TagLib������������������������}����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����@����Instrument names��������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����Sample names����������������������@����are sometimes�����������are abused as�����������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����also abused as����������comments in�������������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����comments.�������������������������@�����������������������������module file formats.��������======================��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/click.mpc�������������������������������������������������������������������0000664�0000000�0000000�00000003064�14662262111�0016622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MP+���\gg��Йs4}>|n"vKd"墲*{eiy\=sy4g/cX@Occ{҄-y^g2<DGjQ(5 lYR1ElŤ.<*0ZP>;ZK'?I%.]$Q(w%3V�I9_x{zÿLvZhߞ0|6H }7ܤ>_I]Y-&D.;"0I,H)zZb>sϱ ӷ]g;l*,/*'o֕ 5H6P&C(jAon]~пB8 xqKrJy=A8p:P1(iĝ7X?ZE?l:яӼ1F8Tp=|'mX/D*H hrn@{j-y-B^ BN9qL6.[`[&dN"hG Nc].h>;R'=2L<N7nc͔K H^2{fUt R{:r2q%L&9$ws> ǽ>{E0织F6/""K{|̜ [ĞK~zw69UTUDDWvB}|*^l] wLxInL sGSTI*}{O nQi5urNl i�m;IZX\^&ЖV*UAޮhn ȇYQ|8VM3yll mS33a)IqeZ9[f[-+gրOE uG&WG5&6|_{;V]sϤL/O} ۚ\%1b- ZU̓RT}:pFg+i}WiY<l[.cSfZzglnw= <S*b<zPz_v[o۞*ӈ{z=vpgf*Sjg禮ؑ#xs{RYo7$ Dp29|շvZ!#^% yyӤmnM<<g~ΟӞ3sϟg39{ϺaI%|Lq! `Y^H> �y럻+)2n$pմs9doZ���W dǩdoz۶mےcpz_UUU]?Vt.\tپpkU`__G뼪j$ҤLT<UU~r�[U5]ţK BUUuVAP~��:����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/click.wv��������������������������������������������������������������������0000664�0000000�0000000�00000006150�14662262111�0016476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpk` ���� ������ ��1!RIFF$6��WAVEfmt �����D�����data�6��BWWGVH�tH�%J��������������, e�����?~��p~aD4fLA�Y)4�!�p =`ɞEyPhI=LI]F"��tCד% V�@ l�j4$8M  t <�^xt؁K�@'Ͱ3@?#,�ƅ>@0L&Y: 4Lp !�^A˿c倫?7_}֟9cyfOʊw<u1#R$2,ƽ] é2hd"+x /Y"U,:ZnT_dMA <v18P!@a�r X!4 ((5@A0x 됒*duBBbapaDQ67D>LqHwLP-kjY3oM~lZ0 &.Pӈ5Ԙ.11VO50`#AG>7)Ŏ h1ƛʊ¢CsBxf,7EzM9dGżj .AEc,p)pr `,<X:s`Z;љ%` $ p7`TIbaxŖ yW2<M!F2MжENLw͍a� ;8P$& JeAB@A5r̴XWV"O$]w3IVXu7et}tRE |x8cߖ׿c6 R褬C? L4OR@yù<zPdcU1Lj<LOX><>D!bGgñ;d %cˤ7R Bjc6{Wrqh0];pT~t=M0sМfCEʇJH bY5bA6$L+EA⇀Ep@6 ]#̂uM=Mefax؁`H2_xspA噙4"^DP(YC1L;�ð!chVXొsx)iLa�<%S'=O2SN F1 9?qpdq, ːG!DcF s#c!F2Āw8pA4Ԉ21"�όv/Fw9sR=$X6OP*98L@s[:W d0L}Pf5 ;3x8pYy#oyN%@$<A/m LȖ20<%th0$S m~ q BQn0yYЉ=A(A:!β7wNACa(B�E8 A縑XwCcp;2C<1Qo/w3@w�% a+qfѻ:ec{4,GW]pQMHuѢyAI){2 8Ĺ!Cɐq) 3GzBxDtL3.խ?H(U1�sSb}S-:a.pW_߮c%yP;, LIUSVE"')Apgyi86D&x)HCϑ9$EjHАEaY$�U0H<XVj 66X\RduFQ#X4x`rVfFZ"u@(03>"� eIs`̶$!`2mr`bXi ޿R";WGH`5B8b踡@TBuOrq9%fԁ> a@1e9dA^CJAV\>diQ \=_"+]�ZDrSX)h=X0 _DJSfΒG1VLxC)\a,S,#@=@,GZF|:D!2GPƁI$RW._)y4=- lASYQFT`W<;:4haʆ 0B)OA7s4Nv! 2eSĉ3kP_A#"TA,` 2<`R 3ZpA;r  5h(LG:o9|  v]9Ț]SYHp`ƴ4)]!v0e F8 Dž�hDp,F!+햍=U y!dt\3I=\" GB^䡈;~Vҁi8L$3)A 6$0P "&,eaMz@ʂhʷ3t%&"FABgSQv jQ"HgaGB",r,gB0�T(J{#n[v`$<0s#DI\ ,# e9 ;-#(ܪ6TC p|ɳ` փOyc�Z�2oD3 "t<2>X0/�!$BO`;ΙA=Ąa눒 ckȗrx4!FCC!1 wV<_�`!!o'Fjag c)$([n/ 0t4OuNZ<m0&KmbC!0K08 AP|8MO,p-(2gOO|r? h�p@" �pCGCu0EB* +h,pdID#�PFA��؃t��03 D OA<����3`3�0L˶ B 8 #4T3:J_h������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/compressed_id3_frame.mp3����������������������������������������������������0000664�0000000�0000000�00000011610�14662262111�0021526�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����,4APIC��]����x[W?m^Z[* *]EAn" \D.Q{*[ fhJfggə9LLݿk_\hђU[43^ixޢ?sK]-|._yC o w!.Ğ߅+v1ZiFb/Ś\5Bb/W^*KXU^! w]ٻ*t1{W7GٻI657>Z|^>N+bϯ="jkLrad*wm4.$VL hMd*yj3,({'@vjLjagoֺy 1ͤzoG(Y+<~xݬ,|woaNvltDz$w@ւg~(3e[+<~`c%vGp& fb]V܂kFs3*F6k!vk e aGk4u {;-XnBS ƚwg\Q9t3�xVgFc!Inmu?.vanW.PYc{ 6̮Ⱥ*=#Z>'ad"\E,?dro^3سpn.i|X–_j+U{ ΜY*a-Jf`fj+:<E|>֜,(i= O,ʭ3Q̟Zk Z>r]`2cϲKA 2zeibB6[MZ惉rBg4/pIRWĞ_{~EWĞSY'+bϯ="+bϯ="+bϯ={¯0{~Eٿ1ׯ^__|955 c[b>3@ ̓###Ϟ=;ֱ'&&el �Ӿt֖wuu~t1FGAw(=& j+**4Maa7^RRRVVV]]]__~bO<AGA@a>էJx8s쬫+..HHHxbdddDDޣbcc $/ Ư@f] 訪ZJu̙�__Ç{{{`?88ٳ)))mmm_a .\ ؾ}-[6oތm۶ٳ~~~NWHOOG\hhh@@2>T dL 566�ݻiӦu֭Z_r5k֬_ڵ= ,, ;"?@~Zك2vX-n<$'o_9ν7Y|ٲe6l8tɓ'#._~aɿOn>@HA )ޭ[T~gE/.]|knݺu߾}AAAV1@GgBH.@Ƙ-/>3�Gb(yA Xr# 55Ν;>ĸ_a@ϟ?v= ؏"~0}~7?/{FGG/8{+6nܸz{{]}~#e0x}www=}t,WH1$s=%۶o ht:WHA(qU#߿cV֡/^Ge_ty^lܹs777##R7gT:>^^^MMM^b'>$Ş^vs=hU|yGx_PP�ɓ߼$Y%7Lq}`` "veך=Bl(ږPܾ'R1M2O$׋< WTiiiab0s�{#rc]UUe^~ { _o\t: {8y:wvvίW8bmUA 2|$z(#8&g W4]]5'r>7IWǏF/..dFt`0{iZb?@ɚ>$~bd^`_]]H􉽰go4AWWܹİ /ٙ^^Xsb-(Y֏ufL72cֆ&vo6} "š{ F ZCCCee[al~3>b/,؃nҪ@zrD}vg3>b/72B/ O{FA?{RI�{6G+!c/D}{zzFGG`o4 &NEEE~~?>c[kKPBG?00099Ie سHj 477#7 {a9(Ͽy&}^BBl'7n&Mz`w ab/9t _SSىtojjjNA\ަa?22n<}:nppP:sM9=-l:ӯD/:M9̞= 0;o<TC`\x&;^Zy4)) n%/9|mgbbѣGWؾW\�{v6z2c>>|s2훐�}6|CGCCCpα}aAv!~7 }}}wMd{EjtMub;":zYYIߟ&&&555IboSse^Vr-~z^|v|Ңhbݾg;;`{zz3$5w۔x8s5|zRlǸc?bGi>۔=xv3ڜvua| 1z#8{Hf�ݍ0K)�Y^ss3< ޚCmJ= [p0kWGx>lX/rMYKtA#0Fqqqc>~#A7{Y.,,0v+**T*USx٧E\@z/HDmʒ}AA,7$n Kt1W}R<G"6e;Φ܃:Ą>dEJg35boS9 =v옷+XOr;Ѐ@ޞ۔%˗/;w@{i ҲﴷkGGGK3{d|󍧧Ν;w{g߁RU=gEMY<rݻ7o޼~ k]k0~n-*,#o۶mM->k^XhhHMY?ѳKŞė~FI<!boSŗ5 AʉD{{_ZvǏO%=c>:|;GFt~'(b/&{ʸFt2o$⚕}Ne6 orrR#"bgzBcc#{z\{K{1\]] o.؋&%o[N$J-I!0z^/Ǥ)b/&AOꦦ&|$9s eee]]]r'(b/&k1;rssz{{ ;'{1Yї`hǦi^\)f~0U逈SSΛ2|WS"be#3/.j iʜD rycSe^\_n{_lOO,ezأ4|PGgn.wo$b{?lƠds,zcjF0l�Fbϳ="+bϯ="s*q^"+bϯ="+bϯ="+bϯ=r=-_= w!.Ğ-{Z\WOAR������POPM�����������TRCK������TCON��� ���Techno-DanceCOMM������eng�TYER������TALB��� ���<Undefined>TPE1������MobyTIT2������Braveheart Theme (Techno remix���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/correctness_gain_silent_output.opus�����������������������������������������0000664�0000000�0000000�00000105262�14662262111�0024275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������m����",OpusHeadx�����OggS����������m��� +OpusTags���libopus 0.9.11-66-g64c2dd7���%���ENCODER=Xiph.Org Opus testvectormaker���TESTDESCRIPTION=This sample must be silent or very quiet. If you can hear speech without turning the volume up very high the opus output gain is being handled incorrectly. This file also has random modeswitches.OggS��������m���)ܘBCCd@ @k B\` (h}tO,++~( rNS __?jہč7/{oFe����������������9 ,Ze$>a/UAgp @ _h9sIpײڈ0^|E;dGW{??p}L%8zjFs83AO �U| Aa,/i9-;~%^,#qᜪ<u̯nИzh6WccudzA+i7_sX8@e| B4"뾘% V ù.P Xp:Qכ 2Xf:mqn/kъxKu~+`TG/>B,$:+)a8|9iˆpĨ/n ߚJm =H/N);afn0G6%KgA˩=M'9f8&wh.t^\Ņ_Ik(,;M.mmY%[f+hs39Nڸ|9]F ey1+nn媵/Xh¸lΒo6 .y �u3 Q<Zp ,`&HHra ?d@ܞFGKeN|T FE?:nCy;(!1+Dž+^+(u!,[G[7wX2w]8 P "y~!F1O[t M1Bd uImnrՈ4z{ Θ[&ҪZ/:$(# ?~*w3\)% tQ>&mw0U(x<\Df?s̕xm>чRɜJv2D!ަnxYpΤjfiQtUȢ:Fױir,W=LqaOWִzkMUy�)c̒VA@on5!-G[v9T3.BԴE-$ NvY;Dj0DtBZfa[ �-%&�61.I$B>2nsn5=H\,}bw1=*,Z6\pBVa  vl.afzƮ$ۜSN0ύonTɋ |3a#vGKoPj-T E0r3VIX+ Nj\�B =9;3A #ګHbql֍d)㳐*SEYi}Sn;oWTPI;[_PWJ�)r.)JrcPeN,5J\H TM!2KSǼkGi`&ըn{~\%" Yk}7о:;{ANײ}gx($]O1|uCGl~<?ߞt E7.T.}�;F0wѠ25αf"ه_z�#ƺ6:Xڏ(r215@[Uk`~P5K<Ѣ?}\k! jku齩S3\F3.QoF9;gbw}_Y.:^&I}"; -7(+@"ē}3Ee "B>^ܨ>N.lm/h)swAOŎ8Jz=~ ؛{g>0 n kUMkm3#Z%�"߀*ڸXZ9,&J>~!B_(t(]N`MtcƱUŽV<7}8<f3&GN?� GI?2[xAh]:&RۤtCus(G^3bYgaS,p6~(*[{(G$^K[2>�AXB` ; ^<k&2} O!VXN*4s‘]ugndTb;&OmbGL'.)ᆃ%Ŷuy;:k9 1}b%ßC_lMÊ> _># R\Z*K }yIBNJ9BYܯHg`ffԓs~X㌨8T_z*?SeCM1i.?\eZpgw]vƐ2MKi -$ 1 uj0>Da$#NK(Ɠ9yhˆ(;;�N nrcpqȚ< (JnOnCCh7<k dЙRvs+W7-Oy&ZIZQZDKIzVD~ǀz9^יZѠOggS��0������m���aCl xAWOggS��?�����m���:7 ιڪgCuFe#$1!"xhgGlLljKzۅŴEBDCF Հz.CLt ^+Qx5/fktqbkmײrY$0ffxڦ8ߢ`#i=Lb̔nhv~jhۨhHдZt ۓpkhif_>}j05d)!~ZR[;G7gsvFF-$v PDM x,W_:pQ~\;mvko$D&?\�i&$ ~ea]m' ]'ewdx_{,1pYP'>KBPC۴8Q/&بǯ}Jr3^? 3BSf 0q<>ˤDs4]ZlGyreŹ=b3)ǠS+Ĩ ffZsB{ 1Ì\\ܔ.g$-"jj' 3F]utG[urQ6 BX94#~WZΣ bm>+x(8#ÛDtjЂ W#eik)-fDn%p3E~tTKxT"V/t>eBҧ4ʺ0- �ek3aOq+ sk6 'fu|bw`3f{)ŗM%ۃRtI>2r5�xDfЉnhIPաɸW�Ÿ0AyDS6{[)ÔUQ5?*bY̔o+5cV 'kfRc/{?b\6;dLsfȃ0ڂ$^mbl.TD!jVL;M�ݱBc<ֻk:rJ_U&SK)Mݬy;ciA e~lCն4�c!`+3C30+OO@P�OQĴ9dS'a F8ы&d^EojitWY9W%F#Yod$d%)=؁&SmT*uzC~,Dz&W:ctxR-47 L9)TߵHLNl Մ Aq] 3œr, !2ݥ4ml!) 6gYXޑG:Z9hND8i;=L!$`- TGk�tZ /iĖ%X |sKYSo"Y92J`2pWbl)=OOm㚯 `O ]y^}/@;=#!<PEK:FՃ8�lh۝)zZ#< ǜK6#Bsb{0#Ƭ;pux(#F_A SfKl(puO :ul] j G=sG|wQAVB&ioҌixhMֆ}-~mr:)]h֯H@~G)+geUZ{B)ȖR()Kftj)'VanDCt76_1iK y8%~Tm ̫[ͦ$md@fcAVV h|UavbGX*:bڿj/5G=dPgm1Vl~IEpS[́To-D^PdC*Ω|+e {^o07yQPM=(M\\wvu}ψPJ|Rad0{7+k<bDI0.D(9Iݗcbd]Da\Er>~a=[s#^@We@GcF^[Ha0mYXޢo"vbWo Z,p Ҵ4"ƹسyOu5ʧKZTJK)F&ygἊuymQ\q'>Vƌ;earf8 ~IٳiLwy�$^&pn&AF Ogsմ!eqJ4McNf˶sE(yc}*KelpƮΏ2vQ}4s`{I (!PSϬPAEO2TqI wp:#?%L/zVzlK^ =$+?c1ߋ,P  S)=E8ɼ ЧI=FROv)�_] FLl 2(;8givu"M2ѐ.[1eF<C\~lm[Բ^\h\sbJh\8p & k.wZW�o}v` R75tcx*E,@%Ok1Z"qm\DyGLs%/*5ɰN b޼QQN~k:kRUu5<]E."@L@jŔv6⟬LjUad|:1}C`nBvwh7?=:Xrh:@έˊ%ʩIbw#bF'MB%]|nJS}cVlc2B*'PCXZ_n2'HFpYy<"ٛ/>>ei%W"4OǹScʎ!}g G;+Fgc `Irވ,)ʼf'7h]RjƎqHX0U!LAIĶ5c`?[K1H /$=3yƇ^2u?L|馭3G0KY9JQ^! șnAhjk(uh\LcӒ䖇H1.|3Md9_oJ|h%7f6^.^nG7%Tͨ E Bb eFo3bh{=jY*Oou[R1Gzh`hۗG&= ,�wV[?Qyi<*±b)k}~C\ҋT7A4iFC_VOݧ-II8o{TjdT4Q}ܲߞ\odu~xZѧ`$_NW7=efeo}@BiAQIs5^p05= *[gfKԺ3ðYJJyt` NZx?ܐCeo0߻{J3>lZ�4J9/{LTqƠV٤ NʃÒ 8ԫxo͸B5t Le8oI18s]+ٜAe)oT%zx+aboꄰCvj)Q1`?d3 eҠ:e ί 龛yG^kujQ+=nf\7+^pJy?:87e̗Z3 k5 E =>SwG嚲B>r�]Yr],JnFrf=[8Y HJ k4eo& [>Tp̏7o[+g2b %'+#{ᘨp'|@p]$h2zƈ̒ZtQWcY�_gN\{fF.-Yk&F}s*'ޣ3<0g 3E)_)?oj84nUwJĜ�֗=vxl$pJGOFfyDx{?Au[Uq}#ŎJ{YrGDŀvRM;hh y& :cn#ox[a`ch|8FЈoG*Ԣś%mQnżC{f!~uF5) c"}a~^eusQ]7jngz0C}31BSW, v[|\l8)Vњ+u7) lkjDwjn &PP健/`½&OV$;o;Ea4(Y҃_MՌ'파�6s2e@OI.j:=p$Uof2L[V3K$ig fLn\/ aDc O%GP5i>h7k G DZ+k.OggS�������m���u/fdbnD./6"!BCCAq҂ CFedrަɦtqppij5lQwINyȤCL]w^KQH}1"!r]KE*t Y[0T?yZ$R?#[[Rjqj|.=qVhVx{jM8 7|7Us2Oo3vSE^Lo?in qHRY*2.Knq a\V Mʍ|R7uUT3&Zfi!,^Dm_2,Ah;qWmU&O{<u pQL2Ϡƽ(xTɟ׃@t^KT"Loͽ"Ta*`H<_ ·ȉIg,x[*ꕒ71Kʄ\w_cn!aʂ(Ć8޵gqE8KēzYb<-H @NH?#sI߱J袉Tá!]k/cp;IQ}wNək/tW0ꌧˆRW!y?j7[RAoM^e d><{eÕo}h$p�  SͶ 7/ 'Ɨw^s"2t {z)[;?$퉅Da}Mʾ&$W<Is&Ci5Ms?X߈Rx6ı$@5<kb [dti3D^NґTzQ{bu^nP"ſi4ˌH v(Nr2.AꎪfR+LS ]@$AC rƠz +w=1||ߑMz{MM- {\ZZw$A(FN쩎PwkݢOC}�7w2's%^K,c:gJS h[5SJ Y1qDfj!!==9 I3]$ qpxxg0 C;;*!h v)|;~ {\`h5HJ=y=ҥB{jFcmG,ZyCh:Z~ [CVj]Y dsq|n,3:Hk# åzJ#4ův'/aE?xqغ  i`hMHqHtM|lsvWe$k+"U|_,JbY*b'oh .x/~"]ݿ/1>IewIT:(7N8OWLBVdZI8950V^{صhڞp9È ^(`E`NS}w0h %"oltwy2xO.;YO!#vٱ<tzn HclKr[0(od�<?$Xtɍ8rJdZldu6bV(gqzF^~5DS de\mxV G0%X�%*ݰqgEύ-D=4*Y Jn<58]JFV;Wަ #s~YSy; t 0pK`iM/ dmfcp>x.3)<k�"}mrpV<C~IjXvR#ܒˈhHUR,h.y^_jH*K݁-~F m\;.䗷�U=Ƥ_-'>e؝Һy|Ah ! ~ҟ 0A5ԟi};Eթ K!$1qUWcn~~fxE& ~k$Aut=MԿqnf;kĂ;$>u,$xtA~XV b^H0P_3ݔʕo0Ck$/ })R l950dI u"% K�w %Zz&j_aq]JTB[= zW̘7>O2N=m,g_ݵeBkPH蚧Ī@a U֊f2GBbj#PmaH'\P5*沦0JY`~D'piNKրO_'r' !رs>�֟E:UY,q=ԅ-'bſ @W+9V?xD.λySR?^{mý_@7 <TnrxAcj9@~g<pb ]Aj%<Ͷr@o'5 Dg&ց>٧1}YE!U6) wٸ5B*j_~}`+=~~k ӆ?\.#sdB|͌YBKz05zdM4Əp3YOz=^Eʀz˗^ohr4,��rl$YjY=>"^x"GyX Y�6[hTpm -j肅q\2g2}gQZE1:AOH*^;YUd||yXUe^~2JS\N-g_ڏm8Z,햍޼*<]yfeȋM\BIe8HlH|֩b\-𛚮p*7pCE8d5*zzc>V4:�$ L/y{9(*M #['p*|6H.\-Vp~x^! PG`T!vou`}~<zxЎ? {Ki9)?n z/[txmx+[kqS]OCz&ƷNƉ@Uis*UFgf4EK.GMh}uliR1jxe\ "9/,IE#j6Ǿl"3HGhfy2dVRM ؈p~T i(ga&?޴ K "!L3v@ͼU~݈cI0y 1xI PQ?/{ |v2/g%E.l) WZ9 R~ ), 1qJ?FB6; ӟaXdcq0 ݷUb+x" hדw&9Jaf^? ʹm Ťm~>LWD&#d<g-&RBN5ƙup@?%c~.,g*lɳ6 ם9 ]-ʝ=l_f~ëX΋\;aj,}+QD9 ̠86MUqF%Wd!څ/8vB2zz&m^ 0g5YEOgg\`.AYʃI,aj:6WA_jK'~,BR{^=L CdpEGBⲱ)'B ˜ -o%qvif%/9˾V{JQ� /3j-yc T3]iH�^Zv~o drq۫0W‹fW)Ek%blKY\?Nba1:>\YoHkE$L )O<'^ʇ0ko'( d$Ş %Uc#n[jVJ0׼l3=KfUgtzanS03Wxݵ!F"|"[A8÷;8zݦknq5${}ܱ�ҷBtBcϚsrꑁ!c)b*ĢH8D o)m NqMGj˜mRwf(=|N%iq Qb`3!=# 4�l4 +UTM#]-xFs`' MrCsکm` =)/"ɓT҅4Ub?ܨ7OqnXtY >Ea2vo|]^SwoOSWZ%_Q﯋,\Sq'F&o=ko ;TBMy.$7yE? o}E05[ IC |?1]rz1ݔ^RBR<aw "C kDrQ[F6&'@`w3yYlp.<GFϭ)x@ƻI,wZnrQo\WiTqYr>jCڸkѦ˱ U>L`]Z[ ʩ9]~ Q)}v$pB!b#Wf(̤&I7\�#(%Fc!)gcj|kY wةR><DH4QiOggS��X�����m��� Y:qՈզ} coAadbona@ߡ٭ɥ ,; wAgY۾BfB4|72~/eYq} Q h#z_>ے()?3H免mEvME-lfj2Q rnF>To|ԗ4֤ nI{G(YW:#,ץ力RUUu. 5T]1<真 HTl54%<3vF PgZj֏7 +O=7D _BK3D>w>" =1TbB/:d!oq-,؛{SH|qOK|euŗFDjSiyPp#~K. +PcʥҴ^dƐ4 4➬'$>Қp)j+#.^!nMqy(D_Kt?[xLLZ}Bacj31d^+?5f-OC4pùh= ?3&فڎ?īJaݿ2GL"y 0Y]I8[�b NkLod8Uؖ ^!<1?&餻M=8w:dZܫcA?!9+]plwݕ"]}-߄A +\CNTj Z2ŸX^a>y ԗ4 Cj{f5>0plJ2V VӪg5Ͳt:~a||,R5!=`ti:3f$%ޗlqq;xЅҼJ] q8�"s `փJWa4dA_B1]?Ī?Mu!=g`rZ,r uy�դOaDE۵wX c]9SD+ŦdX![IDՃuo( J=!^˸ [sM8Ĕ�שU9nY�v1C5fy  s4`@Ay v!rerF\w7;`eq r5ں݀qq"cI7=͗B'$+/0uyHnPqlKuxqHS N8u`ɽE{ʓ` 2R(CfҺ\ȱ^d,L3HrFX�"zLTAND8;|Z{V6Gb٘dm.hAx-e|ɧ%+mؽOڔ.]iX'Qx&d-YXy{`xZS]2|+ۢ40m O%ol[haX_=3Gbs 4޺tz s!)H{amd?GO\B<} "q.2bȵmʠ?ZM[<lH!zǢT!mȡe  Pc,ٍ d^0hb '@<TUT#yI Sl`l[$l1Cn)6> PG49s?&1jO 4c.07l8TaTuZ\T؞D`F9U1U>Z#?tLH9CKn1I 1|@߭ٵތGM>_׆XE.!CĒTe*pb R^@2i)zeh3yǵFd^x 2FcϓӜpڔJjټkꕢMC_b 5Z9r;n切FgFbYdj'h#gSix”&wxxj4(Fsiy1yc!*Sa" ؙVb0R)^,38S%B1h<rO)gIO4'hnRm 8'UkW\9dpxnQ]Ch`f e-E*k"HSi?o5;A1+əJd2Z|] [tu=} i]CT T+J:d2*O<jԿgD5&;$LLun:j]bqso@~sq|QmG?ҢdXWQ",56>Gɭ[`%!6M(:B ] ;6VտczLGl(zq-mbA)ބh/ǯSs�3:֙XFKѹ(;ǸTC;RuǠa}mqRp[7YSM #ēy4�AfЩ=#ԍR(xCA@MȞL3rV}}#vFU8t41Ė&?MbPݸ<ηCsm~#]h>Pѫk! Y"{a[Ry6CzWcK#j VS3KjvZW"> ɗ~]h-{ M՚(K38FIzˈsk/+\HdR\|J4iqd|1P㐽Z1yZ\bm~/Pi䉟ȜhՒq̶&$~߽ؠTv5QlOg1R^@̈́,y/fӀ]$0 %-m~[7 Qu5Y5YM !K:m#Q[.*Yu˧1R-2o=;!%2j1"N #_o䶑7I8~@1Å}D&LwOs:Ч+G2Hi7[Y݃x lÿU$ECD¡u4Q99Tm>>w16ul~={I"k+q k3fٶOXGe 5$CS0wkW]Isc7Z6g%ޏ,ۤJ2c8x.Jy~MZPjE9絛V󴅚bEG*zy<;KPiFQXDV</\0)Q =Tm?Bw`. ,=ogg?l"UH(L[�;OC"E,N�5�l=~&E�+Ч5\ʙ>~[XF?")ua1%,A%`c6hfȆi0e6Z<OJ3Ҡl2TfSjγHOm) Cn*rqk6;Ga뀉:0ėٷW`PBBD~f"Ǭ:nuЕP !"yNV rxZ}Fݥy>xحmP;U{|/VwP7 3҈Y 7*-,\D8龬 $@@2?@K%69 O%LA"WVO쉥]Y'CBmUIL.#-e-# m3#. ~ ?qK1A,<2̝x8 mccNkܔp嶧RXan8T+{bkأ/#2U2۔gsZ[+/x q27{̛ P@cnPL lɸ2"d{0ü_06 *!vp) K{_K3YwؕK?y$顎X>F>/~`ynJtn@sT=Z_Ncj ,~. =BE!/d (?֙~bbR"} W C}|e:~Zd :g vd:W7 }th \}d>{1|zkυ}é}n#|4*g+Kmn/ENiYdj[n!R0@JjYKlF#O]S<%lQTht,t4n AtkC9vin#-}p-`ilPbGX;B禨jў( {y@l,).%ڪTElNωqE=!ҧ9j. b>w2�>@f9*h> Wmmi$g}PvûZx3 =sXɣ'WI$g |$Kg._1/ho_Qiх: yF[6ւFn4܌ {KBQeE}a̐R~H=q4D!n%pb7i&yź7nu[npf`漞0@hΏWM?4Fcz]M,t!?Ѯ f&l "�D툢J+\yR:O97TOggS��8j�����m���tNί -  !5""!/sErDc#$$$7  n8W(@J|)_ƋEd2\߼#F\~3G!CYVr,сߵY&pijeVo< +QV$|j1#ͬzMI3bڄJB@X;U6F0a#݅,-V]ï(-H9slw|idڏE>$ +|MeD$Jˌ7?6V:rh{\!O2Vt c#�bL|LUeT字hA8׻g]S9PxV3~8/ʆF/L>$ɟjcm(3@THFmCi%~-l>u*OI>̩z˻1Ayi?"xdš-7ğDS0$zҲ8ysQd>PXB\L1�V5:Пs6-:0/ikkWL<+AF]yMX הP� &0K#JpdN'[z!Q/ 3 ]z; wq.MZJSELF@9 T{S郁;`"WCĽ|VIQ^3Nw Dgv\ތ68wzFVYj~ Vf+n8ee(c;iGڧ'~[ϯ84&U$H+<1ЃW9: /]tWʻxa%WfۨDڪkWCne8%vp?c�*NkwQWrPh DlIsܸMRsU]i6 F6dc}K6;G;g B yg~=<w^VP{|TᴭOOگ4ԭ]]?2Wh1-")|1y@Cb-y9.1]#<�+G9lE4]{$ `)^{07ܢz/V2gaC4c@oV"Ξ1תE>C1Hh|vDw8.(Bh'>+*zc$l~yyYٝn`~^;j4~,kxظ2MǙN䍀HSqU#r科 Vlr8 +g\zn޼p"6P#LuLG_V30*.ʧx'pj˺OCP9ąň~cfa][?:tŜa\˔hF #Wv՛C ?ˤ`;m_v6"Xw"6{&Хµճz288cvt GXsZ8cXgju4bj۬JK6R"cmmB+1 /nJe{dzf֜0g:I�gm`YŅ/`A.^|\^0ƷDYZ~pҖM=h+ lwiS3F$y�PQwhZ85o.ʁ!x΁lLq(qV @$AM<kjIk8iL]tih0sgɂ_kit@J!#Hkisݢ*|iitph%U7\#ʗ1Mmm,Qw�#cHN׷i.q${4g`䀬!#StܵSJ<DDPw.f 6nP+s!S1%ۑ+qk8J[O#�K`.(&04oj~6q~ @,[16@kEW ՕY/S'l[yv#Y3lgY!Yy[4Kͬ&`c!keGbdHNjC$&a}UT&FVm,\| H>(ĸ ]ZKG\ 1:ZuK�^ko92-T> xeE ߁g_*|�q&;$T9E|0{~0hKM1<cW?qf]]曙KilA C23R_j_y݌ZBx>?IJ2u[j`Aw$^ZN8:)@icpej5,bL9d;}-PJore/V\wQЉYuT~]+dvob@e3o)We@v!D?]%y$^&7 H1K;ۭӛ/bNKW_< vZ=b$4h""ԧsKoIP٘| QN.T4 UQ n~]CDB,pTPA ySij[Rd�[ݩF @Q@HqNjhOx((:J c%Ho a;idrI'kj.vn 2N8iGiF7K4Wjאۭ코\~;#aBXk>Uu/PGz!PS6 jǮ%4놂-ԹТA7Bc*iJ|F,H`%p_1:_PofL"<w~g$.bMf׀J\;x6cp_(#e9bڳɰZc]0Mvb,$u|_dݨF1Ŋh*2˖{C˚~yw"xeź!wo@NٝUrG?* 2fbU+ ) tsm=*C:Yb߆8 tBVVx#^b\ԔEhE$@[/3wcMk(I~ՋF>�v:e~b~Ź=`Z�I SAܕ7Hʞ hi!Sڙi)h0z�wmmkQhb*]'Kl=xv0@e\K k1Bաc%qqN|Ѳ{K񫩋Ie/f ;Ǭ دBîk6_@0gM9COq7ڠ&sOČk=ۻ#[fH$7g@M]bYBCʐk8㝁2xUйZl@EAȺP'n-U<(<Q\y%NӃ ٦>f d4"!R؇ܴ&G]sPזA[q_fibh![w%:F!#NOEÓP>n�ۚ_|z&ǹɧ%k7j=U!VFR2 Y֝-+De`YV!'pg3q w<kEo9iz:�E`wT$_ϵ@Oóz{@E?RF fJcUPR_ %uY &@k?YD cm!341qNk:;MPtn$Ķ{s9&R ٍk?VU/:b3?hܼ1O)kPL_'Hl6w-!lvە7jQR=4fȱ&`Kӣq#iXS_oD4qșk>@wzj)[j9 ܤZHc%LUmYI|0wQ[?a+Hd2*cQ{wÓP# |i0TgevJcrvm%=ǀu00vUɀpA!TiLqUDm,qOS q't�#qh</d)zZdB c=64wS/d9L \|%57fA^5Y]`ިbvN 1_gr\pQ31 t<.x zd}1a,xAjh$Q%xIv4JKrlCTBx? ?zq-a)zG-OggS��@%�����m���huIյ !" . 5!!cBDCB˶#" ! paapq1/ !!AcdbitKz@. ynL'7y[Ý|4W8:nNXS[2b/JR)$QB i N=2+NI =BShj! &wX<._Ϣg7ZؽV@LG3O|<WvKPmmm4TcVp!$@phLv~|\E:neTAr�o,Z@R.ӷ殥3I'JP$Uz|(-sx`Gf#o:9h'jvPAfؘ롸8X륷)>¼ )o3-bt].j,e1_EOZ#ڝp:q^isx"�H&AtwfCjv5<%OxA#mO>v< S"o)<IQK{BvH 1NMlEfh+'XMh<0smR \:W<{1 -zQ$*C0ĬX&?Yi4n;gcU'>M:e F*k?�ن;\._bjW䂐qS ;[}sf!Ij`qEώa+uT<BmqJ|׋2"n:%zxr:qt�!3Mj!lQ EFp <y_e%J(Ȃg4*q/! ͶcgCMQq?^SChɗqNO2�QTÕc7c,pՆ]-S ִO;s{~ q*k MYyjpg&uǎb|g"[S'@QpOfC; I."KG�{\LMUKo ,iRD,7<$G6Ô[(fzcCބ�wcutfkg<S\c@"a_YphmͷA!jBXi3dfvv+SJV(J;k K_� ړ xbdX@sj&jf%F 1Qįd/RP 8tw2nw"AwxzG#r{+pz& kԢofXЊ͉_T+ѱ Q0o8{Vk *+2f`9Ce>L�a+A6WM}~CG_{q7d X'o+hv $i8}ߞ s/,k6YmCiX꺜ˆU־[z:%*;FR3Xwh'/34xֶah?>-VO~hXhx \\;^-ni_NG]zZXۧ?$fYZw`3 diҩvݏs'@既5@<.gUTn>BrdqV53=Wl*\x(n=i-F2%I:Z=J-hQz"K"N;+x#G#„D֐{v,ymӄe@t.+MS>(s 3&cc1Ws:%cD;:eb?&ȐޙA̠wWr \n1TxA`M� @a"r@_VQoƖeTW;+RO9gbuU׭ٰz޲nJҼ@ ]k�:z[CeUITn۴~)!TQ $K0[ȁ=5% q ܘ CYvg1eQ]Pl1.A�v݅'kތ;DB:;}jx̑mzyq,Q}bs}fFÒebXxO||<J(JnG8|ɴ'58 s_=W% l+xbrtYNr=>ؘDw[ [U:Yh #UgW<u9 aAKVX�71["twQ}rlM*v3ݭYa^k܂=L]SA lIBZ:P漎&G*`8 lr;4!4$il6*@_1锇:j,HigIH0M)Ԡw(܌OTGDyvu[sv3R8:E͟$5ahZI, 3nlAx+IfjkÀH^2(Rf0ryP_4�բ0\C\bjL~#,Lpf~P$=YoWᔸ"{*O@~t-80hx!\a m bYѰٴy3f3)!dC=ډ)|d c3F ƯDh6߉~l-(@YkflUd퓸R2vDjv8uLAoVcIHNO.ȏ;s jso, <9t)+!C9~CV{wཉS/o`e/P"oD >r[#*эEqhd~tA^7-M09|ˉ,G_|LHGOj*?[� ?#Kg'iP'ړ(4,djaQMQ Rw_??c#jki1mH/9HAAdӗZL'ښcf\z(0qId�S70~)4' =֤3g?ntq {_S`Sݹ6EWGލ6BI)XQl�7%P%6M" S`k{}9(?u/l덷z(BbvyƊaͽW3*JFDo071ծϔ8֤" Ϸ|#gq$*x[ % 0RGB k#2ZY7 R?nŷO3B9cL8%r%ׄgB//ǐ0 ?sX((L;?qD_§8`~Skfaē)g~+pLS*z=w9W职CXk-,5O`j5)v*JV={nQtHr5HqeVU@ZL.c^U{Ey3*}Ev^fu .*J|B^!MZRbmޅcY|P8OwS0�ǺD.k0FQE3q~{kI1_.)nD Djz#EOj/[=v$Sbiܛ{>]Q.V _jU&iƀ#Rg͉JNh{H6;i3-l�tjX2QwH-ۗ*/xɀ?M1</zîkQh. 9JB@)+4j94uBh]|:;a;ыS7e yVwq:] u9'7eZ$&f\#53+b`5SyQ-@E2_!RGErknisVYgB?GFNװ.AZyj`[lW!{0ihe,2?kHDw.Omh=P+)QisAGQh67ڇoh|@ߙơ~khCL3`B.kP~g2RI2v ޸w\hbY {+ij&KM}8+j¨.q8yOECq]4|0;": DV�Diy_M@auBȥjMD=sWc 7(�urY[gE:&Xa {o5S,\ ;A4q]H0[f>7"S.{U@‚"3[*r$4 H/ qqN@�`aĺJU POggS�������m ���V]Ap/ !. dftsvstscrBbpof"$""" cKeiedeithsduFf76""6"1#"$  pe$h0(/#~tT`*W!P"?G9Mb>W@蹻4TzM2n(KIL- Ǐ?3Ā@rO<Gu.41"(BYs/cl2 l;@@wioZzO{ـTH ɋW7̙/0ZKn)kb4|3iZH?f ۢuy=Fio m;275w;̼tiTi$c@>NrusXFVM ~)K1XCCuI<a6bWϩ;=<_kJbG isr1y�qi1SVrZ4qgI2uFĀpk<E~'n6;9Z>bhI6GzcٶIqڵpsjXiL-bud9�ŧ*Jte\rI $gwld-<: )'`EC 'g*{:"$;Fb,j+5xp&h+`hmAupp<lD�फ़E12߆|4cwDq"VA {pOٸWԜ:<ʷu]vIzF�BixgPTЫ9XpukpkGo[,J:wʪ Ô).8>_[ofn :h2] żc_ߖH>{``jEZ# ĜRz叧ok gN-@rUǾ1 ZN7KoU~3sx]Z`sVB4|Fd3|ۑۅrň4xl K J\s f9p4xC'(EH$L@=1)?<FfĶ�8jOzWwF$ +wE X/Jk]ƖT^p3<HAbǠ?\HԟAMX397­5JDF�Pz,'gAIAC.ML.@~&}|x9fANZɊ�Lqju  B!�,_<`N؁BAؒ5ۍ}DyDPXV5Vç(N[Q<~*iN~IR ~a 疤M7Wҡa~^2>!S.LJ_VH.JZLMKSX:?1 %n�/kÜH ߋf1 L5deFWriP|u@#x"O N&I2R$g> dSܯ8X  4Y *vuaDf`>.zcZ~V(X)H~L E Z"hn0BKGܿinܰ BL)RAY\in' k?s²Lԇ/k6X!g$C!E:b<-D.W>9>OZY,=Цd@41yfw4B#bIPLGc-Q2 bs~v"S1yVnagCzv uNNxPłnxgxX(C dmVٶrn7@~îJ9w7aCS)D擀CC~ thtT$bV{tlA%]sg덱HZ|&uq=FC.F@8AIYhZ5wA[(~ 8=88xrz8lWg~~-⟯4#RGl7p/// jdždOGDفݛ K@944jNlf&h q+3y*`ȐFt'~UKPS;Xu.,<^ަ,@f= 7ɟ%.M8/5VFZ^zbtWck؟AևЯ՗bTjJg\jeF?H-eOlWZ}Q}tsU 47!gw ,0(o> $41!h1xa k0�j=@2* }ό sBpq kG�BV3Z"{6K=I4>{fT_{(j}g{;y"ewB ۀ{@aڀ{We6|061}ȥ(qƍQy̶-By'M;�wTcey:qYj{sB{"QyJ́@l$"o$ȷ"ӨRs &XzHb;tp,Sȍ'gF`ݣ =  jcM]%mU|';b\ϩG�I: ʬqn%JekIqK>IVn.$*yM Jl !ൔT]<HUoOPG4;w?RY}%;PkZLM8*̣B&~0hfVmm%XoϯFyiA2;Wڿ`YE- W+_*9VUxpRtx,py/ ,2$�{9 1C֞mm-zRWc1w:-5|pisn<�sILA5z؃W &S:jBi3IKn/mR9/Y2[}0!kw{&FyAl �oeq@H"2q;a6lϏ8 ]Y)ÈwФn^ Մ?BDНvRեV^ۛfL4Xt|wqosJU6DvP~<'[D{*/hd0K~oub7h@6ʝR`cVs.=0s (DɉOz?m3bETp!L[0XN_ PVty⧯~Y/o2ҙ.7ti)V+ h&U`1UůbDE?׋o)|x!019l@'E,upd|vƩ]-C_H8O0J$I/] :L�(SͼD`P/kG]G`br&z?^2Fd׼F}Pv3Ujup½b43\>2& q! N( Ɓ!7j:l!sn}pZH!:OdK%�BzJ4qβ d<fq}* X/J\ ~^JEO')۵ZF=]p %`gER4~ )˯_Z"� t<Ӫ)4B&U~O[s>^0F67܈pSҐ@] ӯ/EqI~|Z 3LHY[iN 2g^ ݾIO>McCS:}q޿]~ [~5|ȴT6). hP�쾅:RFtޗ^W!T`> O[FSutYxPbݭg`G1Lo|>o3oH3H#! J$r+K e>,/ew]5׊[h3T~gR~[$@92"ZRp~YJs)Ĉ$HrU81;eR:jKUMAbNb*!bx/ -㭵'6�1 ֵ0G~B;=fJ7F)}&o>#!!r(7KhJh:|,4UјYz_!mZMjjlA.zGۈnv覔x(/i1#jp0O$6Y9"II"Yt)ghu64N�R(HKcZQyKx,tjn[;;etn'}3@Rfϗv.*麘[{)wU)|T5ȷjjw ud�VӨbے3"rH"dG~fol wp9=4+`ݞ)d*IXTɡl}Gh~�_65yn«Iܨb J|9吻U|4(�tmp 3뽲PQ{�lpA,?BleOu&}+B1qq] R'><V<g�D wDh(E7wJ°d>?OaqN1@Usb|΃I$u5ƧZ\rv,OggS�������m ���HŲ{IcdsE Dpudd"//""fFgut!7 1" .655 nqpp`hMvMu|�cF[&j@ѐEn[{GPufiSM8FKhR-H0__F\rhGv:A9?"}^�qFTLKì]t`Qy삯Z@Uh?m)VSafǂiyl|a&kJ-+;5| �:Ӫ(e 6 &T]aj]fhZxY]ցCf3;Gib ov'98Bg+,gϵנh: ~g+N|o -'?L sSzBݘZ3qY5v(RVk }1z X%Sz/Xwy20,2wP6wm`[z\x \NR׀sϤU1Pz޲'wzx4rY.<JOzIDxOz[$Jswu3f{enxEuiHDBP]]11HqmkU@E`mꣲ@E/j~RDɘC@ mI$;OfdsshR.T"i\hV>R ,eOS:Rri@;~7o)~j- @>}"D 6];\f7H^~~4!%v7ni@yOk 2+6b*:ôxo5sZ󂿣F1/#W=[@bS3 E^w0buM<v# dW\p5-n &p35BϧS%el9܁24fvd9y/X G톜6$RnWh/HfAXфƷڿ{UX%IOy;~88v- 1I-UBimlMic* b7HqQKo}efFjxqNܡ|;,-[DsFn1,)BDk�J;&M$�Í^~X9s]X]jXjUm y2EA>W:xl}Xm`?@4`"3z͠wj3P^O`B120ruRm{2Q-}Tm[&ج#e*V z" D ?F/]+ Iyz)z 4v .၈j~n _=ZygȆMw8Rt+2ѳzxԔ]W 2\/hEchёؿ!<6{sUPCWx}Rl,~ p^.ܡ3R52i{wF#4cq"+ȜvC-ȟwJM\ΌUyZ'm^ԤBhM'%$#߾KCdk8 rP }H*0xQ=$ q󘥩t/uZڑ}')(j%w؏\[�~p@]# EZжO7&\QU- V0%Me6>L[|3c̮k0$Z[ 0jq@$.EkĻAJ}1tm/qeסE@a}a6]>?S~qO+q/6-W&Ou> ;g0QnpKe>pچ$~)ԙ8X;!.?@b6ves0IAՓ fyjL�is:EbVd`_]J/Α=Fdd$5#`t5iKY< ">+}vh'%ˢַ۴1qZҫ߽ϊ v7(ؒrlc#Fd!,mP qH.e~Ի*tv2i?~od:Rt22;mnHo~'1:ذ:)n k`;g.01 | B{uS` i%RGt1݇+Ie'3ɽRG ([i"m K/!~fj>r*pz6ρ=R[CiHYز׃=! ae45kA5:h1u':On~YD2;39 <lߊ=2Q�BR ;dҪw1DKa-6ދF|D N!duu+<oxې#~LF(Dı*i"(=\7 لYZÂzS�vA{Sn8#9t]-mRstvǣ:fO)o)Uz,Ш.>`5Ⱥdcc@rTHY!X++%eNɠQTI)kbRu#\ώ)JZs{y챬6b~^ۡW tU~7c=N~ 0a&x~nvt|ʳ_zZ:<;5xM7Ww Y,ez&^IRS?fT$;k㉫Xo~[XpE˘1 Yhƚh5pL0ﰶ#E֤]((y5_0aO $H 2Б1tqѷlsXA.;="HkÁ'=E#~[QU+;橊!EDki΍{ޔyf{#3[TJBXJr' &3Lbb.) T:hbo~j/*5^Ot"'Fk!hw[W m-e'G{D*BXFhc޸;v!7d N~s Ye�cŖWQT>19uZ)hj~tvHfz u֥8{ o0ZM>(W~A}*ەMkoOvوlb^t҅h ~rNjQ7AVn0?䟁4/gn@fy{G}E~h:c} )'%9γǖᝲ{/Wa[D}'2!iEϻCT˩G*as3hcb,]:W2d:Nmx3*EX3m5&}Wn&FhGY2.JvI@As}&ŀsd0]s48w AR~]gXǣ!L̵Nvon 1.)AĔ8ƤȀ'&� K{ ^bL̕D2Bs3ț1e:P2߸szz(MJ~ ꏊ ` 7'7׀arGM؝z唦;ϔ$}A{O"j-Ʊ/yRk|J䟒gA50TLb.H@NBS̉(dq5;M׏e~N@"s$>\o8 Ho91{ܘSNjh<zy@? tUAA7GlG4Dwމ tnz!kr@c ꀲ{ x-D[G(?ۡR¤AQuقVvlP*:qY/ h< |ZCˆ"fȄ 4L{%Tu 3z\إ|$Qn.2 csGECOW�VA É ˠ g#p;oT!5śYu+Va*pUPFĭx՝J_L �@ob1zፃ|X}SbpNլvbߨ3hTK ;헂=$ҪE6n+:1&Y(I y8hU)0agJ FÞr<,E01L#*vnt6&j'E|+8&n�xkR4#́Bmp>|P[ ~Y (!gj%K3ɮ`+?q4o_һ}LF9|9oROv]w>ܩZ 6^(zj T/qH]2m`u)�>ŎXxBdۭϝMڽZpZ5FQEn3VLbځ[М%Ky'}^53f:'_^\YKU<U.Ns1ߩ]70@63!>/D#fu[ɑ/i9 AMօ9wn&9$/7bh4�7A^qDoaQLa^p_bb�kb7W3?'7 w/0$PL-Vo c>7QJtWBc+ԧ:w q=b/-P rRfwڕ f(Ԥj1C]%샵$:3tOggS�!�����m ���˵hxmCamcob2tJΚQ XO?' H 2Ra5GbEP|> Hic>䪁9\*zF-D ?a-!robkv<w.@�.uQ#P$_* 6P1h!/F҉}P{eV2v[ƀ@G ?Κ|v7~];D>aZBT=a|I35+emV)�i#_Q+kKQO$"K"HɒUU>:u9PlNV:'VD(a)N&E@xm{(zˋD (4/]jfVyOHCoFlN#걬V%w4w@z'BiMq3 %srd4bVIۭGELXA# )&ʼn##OWo#^)U3$,hڙ"YEZsӢ7>3򛦩z`tqEۢ32|.Ksf>45nAW ?F����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/covr-junk.m4a���������������������������������������������������������������0000664�0000000�0000000�00000011764�14662262111�0017363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����mp42isom��mdat��libfaac 1.24��B� 2�G!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#��#moov���lmvhd����ELII�_���������������������������������������������@���������������������������������iods�����O��ttrak���\tkhd���ELII�����������������������������������������������������@�������������mdia��� mdhd����ELII��D�~�������!hdlr��������soun���������������minf���smhd�����������$dinf���dref���������� url �����stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���� �� b��� stts��������������������stsz����������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���(stsc�������������,��������������� stco���������� ����I����� ctts���������������������� #udta�� meta�������!hdlr��������mdirappl�����������ilst���----���mean����com.apple.iTunes���name����iTunNORM���jdata������� 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000��covr���_data�������PNG  ��� IHDR���������Ԛs���IDATxc|�� Xo����IENDB`��/data��� �����JFIF��d�d���C�    "##! %*5-%'2( .?/279<<<$-BFA:F5;<9�C  9& &99999999999999999999999999999999999999999999999999���"�������������������������������������������������������������������� ��?����name�����������#ART���data�������Test Artist���!too���data�������FAAC 1.24��Zfree������������taglib-2.0.2/tests/data/dsd_stereo.wv���������������������������������������������������������������0000664�0000000�0000000�00000146563�14662262111�0017561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpk|@���������"V���o%hdff�#AFRM8�����'DSD FVER���������PROP�������JSND FS ��������+�CHNL������� �SLFTSRGTCMPR�������DSD not compressed�DSD �����'@e��* ��������0N4\wg!v9nxIfS`/) j[$PlLwI psY�r,莠Y3K 156٥ZϪD81�7wv)F-Vc(NvG1_ u>$G!U?ykQ-iݩ)jNW)-Pe贚 :1 cVM咽>kb j- ,d'$Y節7<]؛H˄^S5vܽ�6:ݢgG:vQAg6-"@7ŒWLYUr {؋h<'z Ȧx4Y|r|R<3$�<t).l"jZrW!tdLR_w݃wݬ{[x3 L,%p0y[Җں7MåRF�/WGcyףD\)ٮ7i]{{Q J< vځQqhl%V8ox&/UIl|mߕWF0Kt`r,f:Q@iتXXhډsӛ-ٛt8donUŒ!˟r w ~9/{[!g%edXsGEGRd%s敃ط,yZBY"X=[՜Nzُ0!^OgXEXz(]&;eu z"Հ~"ᔛ짞 Pa/fIX�49'{R@a (r9PVU8q q+oӗy;<C|vn_K9F5+|ﭟ#9 Pp1G3;.ADP"!URna9v�D 9znXh^AIVO"vCtxO>)8D֬h f.4L2ĐɊü~4Q &En^CK5NҼ=OUtgKLiiTkPtv_M3\^O{;kn;) E<)T]H8"0:[x%W0/%Tvl+.zG{lmvrN,P!~>ĄxsFt dTȴCDzLM*=h?OuňmlDg<oV'W|, : RȾdb{p8ULhn֡t"!3=7E~6[7Qe <^GW$'1H_*wq&O~C蛂LUw}pC4䕲6ޭ7aTB yg)v<>sɰ5 5!/k$чn!o: l -WܼIk`a0uë` NݞR|;/n%aΘyb-ӶUʼ}R]5+ եAP z^8a5S7 eIW~k2_ƵpɝMӆŞ\twvOՑ/e3WXJQsBfj7!Sƌqiȓ cNJwЖβFY}AzCOLG3 K Ld(bp"75h p[V)r,[yA"y1'ӛԻ N4mwu M!}Wݯڢ69f_C}f7K Е\=la4Tj.Uq:zܧQ 5G*#~Ne:7qQUP!2tk֕2j]X#NT$ZD7fq� of&&][kzypBHSʪHM֨jhcrL$ڈ \0?7.@=h<0_(|mT-͎g$I83zKb OeMݥ)Z[/lx?tAv<htQ۩0(]LeEM͕36bA+r6VyV ݔ68o=+-j;@g Л(HOwJE !Wl}1"׷1t)ģe]ؽX$*)!.G0(N4\;LIԹyQz=ӡ1ᩑy#'%@Vi{Պ5_[jR>w_zBztVSxsGob+72C;� Sv;ezmʭۻT\⺥tf6٠#L}1 y[:RHs3IaڎGT9FH۞\CC=wɫ C;y8mճ]]+"$thKnda<|DR kUpkb$dCO<Roh|'-!nGIXVl >ye2"+_; !>ZX q|q�&$NpaC$J23 zO{eB./'9S3^XD}ȧ՞mւ:ښe{@{~u#s }1Oӓ[lm4˜dN;BK\Ukɲ&-`jȺb+eO%k_1~lRlEHCԙpo 9tzNy%s+ ^ucY\7 z>@W`JGy�i^x ?f(Z#y@?/q#zAũ(?KŽSrے!wsISPm^$ʾDh9e:1K=[p06]USeC,([gTϗ±y1sQ=di4ϖ[BWC>FqU|)s?m(B^E&*S8  !NWOVm -y禣VRs+;R\8q9օQV_#`*hl{cD[s̋Se M ' h#:-l&@_i[eu) 9cBڧ^ba6"Ր:ukHg 87jr|I`%Qת&k "Ev񭲓؏ޡj RtHlt~}𶳴jۜYqK~jP7R :sq=Sԣ[Յ)e^Ʌ1oQ s4;-! e$-5G(eJOwiNw*4FUC^VN z"> X>|/xySyF6la;įتm4}Z@/H 㐉eWzOxCc ;gI+"hlvDLO5ts$ $6 )zCL@ߩ2SJuAhҊU5*KDwlu^b7sVkP+u**bP(wfG?eƕ&Fmg߻ǷD&9yڸMP6ɽt@:E߂}bʗl߭}>۷F47tUn!WKi0۔akQT|`a }oqr/_gW8Gsh,ƺߗB-Xs8nriʸ<o6 )vg4Sy�< 5`|{ikT2$L˲ CƼz)hABu9>VlZ5K kt+=\oC282AsMmjŖuV@$,24MᒤU7G2) LHjh9>+1 gW[V3UN@QÈpܠEaϳ#+>"&y>9j~!;:re C|^Vg@B(u$ko zpa4e=UQf4Am0^<�<P!?swyi$YQvb >b-xR :!Zpszk苦 :!_K17bsQ>v*k;)\sG3^a.ˆ+r8Pl "6G~ J"aWPQD'"WAZ7G &glSuW#k> $Tέ0c<sL+A֫5n~/P9&;l ?o&/@Jq]a[jd/{!,I!io}D#mz:q.Z.LO`#-#  jX#k(qUgG3˻wLMRO'||"|?:!�iVXeHR ;9/ISg;9-2?Yttfd}ԏ˗SFZ)1@[ݷE l2ŴgAdܟC�sU_z)}I8?W /57TVþq1ڭ>PQD}i[oTiK#mu틐ƮO}TVӒ[ ؔ\ Fqi7mcmڪ(C}<=}d<#7?8p7tkoB}!'-FP#j2[[u{g+ + mI6 /9/\ܩ Z܃L`kOAtj /Rt`fQlFT֞By_|oD,⾇ny]u' =qcY �O 9~Nt@K"u> œѢ {2aD\uآ F"JB3ډVgv%yLf6A E,8Ȏ__5>/ԾЯ!ID# 0Gz[&I㎠bƪ~̰Ut;*I64]d.aGE5tOgg>~</K:Vwh~jEV%[Af4z+=R W k]"Kx&ȉfS pS"[xՏX4x `]?>&rQߞSn(|Ɍ?^Ub,|2_B Cl Ϙ xL9׹@K?'Kq ;xRF)#=tIO / r%Son6@C6eA ]׉'~:KX:HεFHʩ"xNw!~xpMc٩tr'mVԷ;#MP/8I7{FX0kS#d6N :R~'6!/pwqpEEoQJmD"1m`658Pژt�$jW)q@u@bJ߇3GόW򵹐 %߷zz :Cܿv)ӗkޔ?4hGB 8Ks S%EcHkۡuVp H/aWUv�?<&mС\+u(ߋ;.:]" :٬ |TZe<T , #;1-ŋW1fݼuU0㸠O&WRF5Iħ"m~ݫP s,]Ѳ>c>�WWKzy?_*KnID{(fJQ}DOCjTբlj1lC{t­.;\{3PD9ָ>rvEokf$@"@j"&IRdr'E DR-#ar&9qZ*lNYIv] p_T@~4Q�3E܂.=^Y2#0cr 3_ՔS/h(>DȲz Y|D]J*T%EH`Ixht0+\0mJ XXh69\&ĞG}0MVl?p3Q,_JԡiPmGOoQj�Hی7HM]�#$bSs>O-qA|4}@N4>e {qCbpXgO^h-*!<O*Y_ZV?wY4N/;2<CߑS`Y$df9P9ځS~|CN6ڭ[B;]U"iq*, W`E�?iBj`L4M͕C5nӕTMF(_bɇ[IZ*Sps(<}#CvO5K. I1T\Sۿ?r:-sܖJ_`ʬN ?l[Ba-no$:6yԃi}iHWX6^1:%ã eKB%e\[jZmη?hYh!~=S?-bsww O/c3\Ou(ҺqPѮg�pPgm{/nܯ>(Bv0N.[i 2ގmXI&5WB (&Mc-)u9t(RDGdl8if8ԐX8NDO.Q`Ʋ4L릥Rah/qnLzJ娮c0KhJe9P ̺8#7RR9~K*6X) ``^"p#ЁE'd4l{HuEad J^(hHrG,?K)K):IaAQ4y 4K5z 9%#%D۷3s]mnby2u0`/rr?�g>"Ռx<lRTkIf;A{f8+uQ  "8vBh諫&t6\6A *e@YLw~jcWLeQf.V!&)ѫo<L X{: He vZN@XPuWPnY]zRH%|N۹|qbKGMÂ3-O:=hS z{ѴD%DךZ"Nb.3@W`?ޑ) WPpYl5,?3;iGկ|~I> ]5qC/q8%�.C3yBG05L!Qňj1Uj;P:_^[~vx|Ho }ͧ7!<A̲!α3 R'd8U0hHAMaN9./ݍcɣ2Bv"B4u/"pE//A5ݻB qZPa".iѽZ[pTGlvi[ ̱H5bqwvx`yDF}yJg7�qpXg%˜_WK1G5?B;A:sǐJRgſ'VF�H<eԈu 4;zzEN K/ 'G!$]"#m.5OL#Ӝ鲖Dj4kR 6213c]j,9TJҀnK N" U0Ss,:b)|Isu,1t4[tp5@9cԭx-}5e⁺M.4ˏIK '9U9#7l MiDrM }#f�?Ҳ̅A ZqriBpP9|䞎UѴh{N.\YOv:d1J#Qs=,wۉi׺Kh7 W$u>Yh o~by4}NUSBܭ4%^2]n="H5~35+V|HŠ>J%~p^USwZgm? W=k])[Kmb8n2JӘcáZ2FT)d5iYÜrGv58?wp f%orޭnp&vɝ"Ki4""ݏ*ubo;1/rNӜfq>1'mBO01 {{#Jiu6Uᓭr:\=qڡ"GD�fpͣq#=0U+e woQW>N p  ՝^~2勎vRi,&D>J c=`<y=R6B 8M ڟ\_/4DdN#�d'K)\M.̲`R!9DnP^jL*3<KYsnV#S!nԹl*ɟ7VI_w%{LRI&ϯ+JWb-W-Fv&m,�͐A08';cRȕo_?,3 v1gh9$6T0)hс)q[.=Ѻz\">oDI(h Cj E{8ƊXnh-<%KǴN[oZqYVp8&g7u$ AtT#ǃ0Z~ZG7c{6D2 Zgq1K=mX{?ZKr.AeGݺw @vyhR_qZ% kd@  /9?궯dI&9,BDW=ou#ȇk `1<»V!Ʋђ50n롁×/kRQCV3LOĐADU?.ÏSa Uikfd=%45~h1s&2lխtaD{Wu⭢pGe'~rjIweD[1hʪ#W,hM�U&r12W,LqPx7ǚPŪgm:1)v0IcXW3~ @Y).SjCczB-|GC"/2ɰ|?gBMا~o|$�֤B/H(4\wPM|X^HǚQL" aY8bwۮZ(Kj΄%珆_枞cWZxIIRȞ+AVY~wzPDuWtG בJīY )z跬OUϴ:۲tiƘeb1?Ҵ]xbmtPnI<Om*gat< :4ܡ;a*jB:X-)lA 8<N.B+*TE]kގDD@J'ۖ0ԛz~HK;rJ1e@-�FUD-T&J5KK-*jBrO8(IO 1cّE\,([ݾYx(|`2#FwK(!]%H$.9#:3j%{"Q"\c >EV.WL'KB,52ܓTK$,53/tNUW(kR)[)IPV0_k8X6JJ*ZavqǎBYiP4ӓcGͿ&d:dx<2jl#=;Hq`aI6TE>JMxϔ#g \Vbk;I NCho-^޳}7_ĤUЪFD+yδ�l9޾28v2KGzA"Ď YeV,!r.&\plA'6yC=Xůe^UW(0m4 /(Tm8ws&>NzflaSSo]լ]hl8֨B<e|&%V8HѮ,T(r25W>t\50ֳ]qʳ2#HIi`jR6ҔK P1 &2:_ ~,ܒWπB -5Nj Mn-+$/e+0ÈLYIe<4-^ںoœoNB}vEgFy3h(pD3膍GZo0DFM]fViS5UϐjȞN#ߗEuuoc""-M/}jܵoV?ˡ|F7sFD, RD|V1Wj< rW80ߓqҩ$~ƯT=O[:hfx§# wM ݤ: -qKX[Lw3ڢ@X[h2tѳ /QxE~G~fp|QmU\:�D I~8ބSSQf}*5RoX@:B-) =$>%1g7o@}T_j5KБU l*cc'R �T羥^^dȫ~BЁr80MiʵC3YxP].G<ga m,9{ֻCQ>D57db滶 5:r!$"Q䐼̫u iЧ]V;s>$߁xPkH$A>`{Oʍ)聑6L|tDȻy\!3l:HQ?ʁ χŌB5z!>�)@];M8 !=N6O1kCNp4>qr7H~>)a)ؕ伣 >!()`DZy6}5Zu~u= 4 Ӥ,SPj*;SüPN9H`aגUs:&$ӲCRܺ߾GvS@L*zܜjIyxnLq8l!o=x6&NqfxݼTX~ϦYH`s<yo[^$mӺR.:UCJ{X^pޅR?<chO VZZ42*"2$)7V߫hf!#qw;[Ŭk4!n[=sx<5W  c7 2O/apҟY"+L/^1gb3TNɣeI( cj|3j|XSrrF⹭[<C--3 [xJ*VmfTLu)H3;FJO[zԫhoĽpf^W>[ʴV'V:x1k @C�e14n㡅;krfQ@HD)(4j(tnڌ,Wo)'0eTף1 #L fĶ*PʥW0$nV|!XbD\'3Pjx 1)!Pg^"$N9Ye3&{fF)u)~ow 5gp*.ks+nj!Ox_ SlO43E3"ۉa'o$?勦6LEt.J9)_yk#~U͹'.'@$>H# D,$H;8zˍPciAieg(jR1aOM X$᪣'NO\*ţE }[Pܷχk0kB;)4pQ7r"%PBdshA+ I'f08R k3i/KEp6x)B;O͜9[#__h1?e>Yh,cT¼xUڈV d9ԋuf;-!K '@eA%*s> ?3~yu@ w%.>S+vcgxaCΏ=Aqԍ̮~iw> KF@tv"bVhX#(AǡB}X6EPi|KxUNh663k €C'sՋV?KTY 1%Mf4MeNHefbC€Dtf|�L [m86mZ*ˊ=TEvS5ä/QCQms5UPg0#!-qh|'n>dx Q%MJ2 6 ̞MBR{j8r*,ܶ/_xS {x۵{)q:Q̙L^9.,g+v{&s˃�<5ƫL5~5 C}Yble0F(@G_D]0F)hZ(9)=o}[�:Їh3 Fh'A_3H ܇TTR4*O,^m +ˑ{,T2IVg;Ƿ]Ÿg?a3x7Z{cU:H VF˧չj(%j_[ aOŽH X[q{+BGg�ͷ yiBwzD6M4W1nb@ULW 7^&SwGOJg:%+՟3[I9X\f]2/j+wO!x<fM6$Ӵ^0z kԧ>R{agu7뉬[a!gR^}CQZP}䟲'F\٬�R8]|"_Bg]: 8YN`KU6Df tJ6WL?$F3z~ͧ5h/zt zJjSR~݅(Eomʷc5 N HJy2f>t9] `kb<[ N\f ζ{:md:uN4`3](Wͺ]1(Col=P}g\m7O}ڸ2Ƶ  yo*7oY+[B_oVLۛ{{晆eUz`T(țc2bs<&b.KXFĬyЌ% Ik*si weȘ}3^N1'Zh[J=5f+kg mk';KcachA8ըVm㻵|3q[F,{LNH̘u)VL&x# y^TXGE :`oUnp/̷ We=5k4s/RRS#tQ7 й|bOeм!h)i˖sqG0֚֫=r/jtlb"FW^. -NѸ8nف7<@o7jXČיl.|%ƻaά-bڼB1evFw]'raΒ3U0!whPƣڱe~Ӌq.f8"KOq)3 `I}Z]$˃nasګTLL쒥d:{SoYg2ҋHz\'n`D!)=?݌浌y5C:2 N<W={9cUnC@ 6U=O;iڊԷ>[-wF [g 2AxQ,h~'% g],B?;CXf 7tcFfveG?� v_7O[ouDؤ\cWnɿ< jŭ;FPX !t]0 q|sIe{Z mF0 $�#prROMJb2D ?qv 0}ݡ9~B|_ ]h/Mŝ(C~>;_Fw\diܺ6CMg٬DZW]a[S6.=q(e;$͙7E*Y5؎6J#œ Eu/|Տ|V^`u}tZܬ\UM +{?ViBWEq𲚧<D;jBe6ɦJ[?7`_QFX*uN7~`{Ȯt7tm'ʑ/p]#v7%� ||`#P=ٽT ]&sbɚE`Mf3W^F W`NE[p\ć=I8^> J?` ~)iP(X\YXLW͛-&/,Eߒd#('^\!Ó %(sXhbyvPЀӫx&I{Q[cvp̽S( */B톧ѾߧKu)xքXT:nJ? DEdew. dLGV)WXؿo=T%=\#| 61~2E1a[HeBTiЋ;4uB܉cv͖*Hr9R+/gDT17(*L?ݢDʋdVRC͢y 67ji;]Q3~ZC 9 IZWJŞ@rSq glo!&)ybf 3ъ uhBԨ^�1ˉ0L;nՅқ|?ᒤR1eSJ8#j^!"ǔi6' OLTM&Dχ^_2KG䕐!  xDfӌB뱸i(,f|qd|[]EdXͬ#x7:>:9_gLl±;5tnPdskqFq̢w2-7l2N2-s*Df D4aYSDGhíສ&"Đ#Ϯ۹\תufp;\sqL ϛi_"ߺ#0&D_M'=~̽|CSJiO :�vA4z~߽yq+-MEՏxwT 2U0.%uJ(*5F3!-JRM,S tܵ;0+WnR?J7 Y <z>?c _~XX ǙX42=rr+ '71qד֔$GXx|Sm!|=cѩÛat7RI^<Kw{kk�IQ~ꤧɲᔞ;7VigUh`DĄC_ԇ"�lÜ:";G*çшPUe@R6$dʞt޷{0 G*)C H 0 ߝhTiu i K a`3թk6.o7X($q 2?2g:Lh]*ts1 eܹ]ǙPC#W:+8<=}=kOP@Œ (;\)87K6p {DQE\{&66I2N\f$,0Z:w+@a6Ƙx"}DTz 1MzN1C\שqk)!.h&@+1q$W̅fXBE~iacI�7_Be̵Q4?nk}3߼6{8 I\IUl.6kmk=*a0z%FƤ'>tsS.eE3I�t+c�U,\')MD5VJF^=r>eoxdL 72P3kIOlv1^GTz~(*E>X2C]D]5GV-<.,+t7?4hN2ݔdy�6iUMtv�/)wvpk?�����"V��"V��� sd* � ywyyyl�wqwxxa�0[ry7٬ B�5緅a!c ۺȯU@ RB3ʑ!'I0~(J?YEk/("TԙG+Ԕ33@.fFx=$Wa^S*3K2wx<Բ8,'nJaf Y g־m#zofwAY2a^-?y!Ow>ʳ"U]D73!.A"P3 WZTnr8HꇋKXh%CZ wm Dgx}י)S[� aˑH5~Jt C&j\ӫ5_A�Ad8jM2"< ίc%&?.G! *H\^ۓ;A jQt8q~>[޷~EךZZŧ6-}ɪX,f媗k#L;4\*ſc )+lB9/K5~M_<E;*Io k*oBd4x$Dž r�[y`/cG{mq0UȺ{1&12\3cjcȾo9^o|,h⒳<dHĮaEG9uq{!>Kq=4 նhi>P�o8i~N$ud cM*e @$vjk<8zCTZtBNy~ݲ ܧ^ſ_2ACt  kG]Q_ڲ0fޔto~FcHXjtW+ ڥ~v0R87*~YٟdxplbW1ZB<rMI},_6桃߿;p(xVז^HdfZV^՘npv] P.V.= ,#U`L2XeAk _,hϡ)Q7 `k"*9_e篤0#ʐ6/uW^KMޗ ^LvkSz2Oluj*q8$<VeJkN=+B((kZLm-.ԅ�]!RЙ^vWzX}"o~SCn`w= U؜@u3o5Pmɩ<7>uN/ɇ ~BM/4lAœRQX:QjKbA8[h:?xm}โ tF'\f,M: `GC"2"%LLUaGq-FH?W8FH y ;H[n0` ǥA ľݚMvLq U3&^coޘtl#!й۸U{!6٪2$?"8bv8D 7CN epǩ.VwuUؿ3mg~غՎQ2>3vloGUfw&ڱrpz&D?u%!n o߶O.ў"4Kmm)feFMNX1p3E c1"Mǻ[lWb3dKCco| 9E&Na AvuHτIo2F 9cY̙*ׂ*G="3\L!K+iㆂ%5FOΙ `e2_P^O+F P FҪ55ۧ䤐sm1f/.pn:PҘthp]6Rufݰ3̺)FORGi;\&A*'W֪ {9nRd<i ,X3N+H$h?L Ib$ZGysjg\9܁ehւ DBgZu`Kq$^~h�u}vE jCgi<^bأ9�sܠ#uV:\cZ G(Φq?#rO*YAD-(%D˳C%c6Mng[6 m䂻iʬAozXd|爟js 4\ ͦ=1Vrz3⓺^wy>ѪvoD} /SkЬdjRf8'U=6 B24S%&u[ˆkZ۔b$G b;[T1' �\Oa.$~{�CUѺTu!knʅI\" [%_!_+A #Y[Xv8T$9pf EV>7&?mgkJj0&)؋W/c(,Ò`[087O0ƜvF""DPt)-|3d8J @;*`^wUKַm3QalsY_n#x  0aj7س1A.muTՠrН~>ː\z:$$)r뒟~al>(ΙyĢ%^S2M9mb( yr͛3C6'.'|X4% z�AXS:p5B G%k�˝XaEL!J-nDvF [" Su1G[NqV'&bdGJ‚QVwdY9l^))  g-#8i{bIs]>2C=}/h2\y>(qh]y]}n]J 4sg-g:?St >s&IzzLI\Tw%򾕍7i6S0FغNlJ<C!jbj =I%p0k[938Ls2u)Վ>ol0<S S4(sXf-Kf6%݀^y)+۶Fě׾܇/M Qo a gLHy+~`F` ' K6pDٍIq_WT&"PR@eVndMI$x{:Z^P·ѩ� <[.VR-zG~֝#a (Rq.V&BTJf^̃#<ՏHtr[�Hj1 ֛Syqؠ~r9ƒ6pzco;yj<d"`~ei HX0cPqM@7VL<(iҐnpzp}A(<_xE{^x"{m(+%р䤳&o.lIwV僇7,L>^7χ Rw;[eĄB"ķ!C|ۄO~I<0[q&y0dUJR1ֆئ$e:eUyWe?*tZUfhBBO^Gѱ*..5j'[>k2yV"}M[sfӹ7�aѱ4M뮈nFD!gmIa摑ߧ j!ۢtۈB'_76VB:5^/% w) ?00�@=38[}ve4"&C1]xjK!p_T7ݜa Żlp0(jhNkxss/ę.Ws"[*O{hGaFɋTj d, @=Ȏr:ɵ?C2'gX<܂G]&J0hjs2.Iw0wޖ~LbX"MuQ1`*gD;jXp>�{FTr0<W5bCFuJcKɭC.#xn{RzEPuĐcR$Y6kZ%n<v~rL!vcg]vU翳wHYJKom\я(Ib"f`2Zyd#ԓҟ#\B{L%X+ъAXu#N;9d Hސga[0E rO$gP: BJ!P*[@s2@@%ț2"w=4jfLT8ߋ<kS=Nv⬻rʤ=YR*좓6vnfQqJ8/x5wOȬ"`^qrynM|:8Wʗ-\:! TS3Y{DsM@m-vwқ3`7r+@p#)YosQtBV!w"~Vljga9"4?*NG|Z6Ooo-m0X>DF_钴/9 W#Mrl Op<MͯDd jd?DialwMG'bݱc|+MsIM:ØǑMa*gqd.TE&jX(;s<^BĽF:x|~A6r-1ݯ6^<i=!+donC* m& جxvmĭt;v[IZ}q& %2 1N8Շ`bVV Hjf�f@֘/"JNcCE7=4ao6KA.rTMr$`vK拻 1ֳ XAu{mcB8Z~WV⡄tV6K:4aS$~Ŀ"X6U!_ I dnINZjr+zh_j4K Rk˜I=j4f|*߄Vd欪N4 3zGIn{TS=ZAx<|Iwrv�Lע>. 3j&W*LZSmP]f9ے\A5+)ڴ.!6J#<6;Gy#M];;&.  ܅ZAd c5[Gw+AR#fj.hii\gzg>VQ=DZ3f?-gGXKH\|9i5gJi}.EMrE 2]9tVKd.~Knkr5nZ.bʢ$E\0&>ߍF6Xt�HA(44REE 'a1'N347RP(> .\}Q:2+f3TnGWAA'_Wpq?tP4|THlnNe-=l#-�qwM`Jt1sǃ;C)mJ!Is= SK9x%X(K(cieXΙ ASyyhLYrmƇ$? )3*x"/tR 7 4aGJT;Pds)U#L0*i|8?w"Fmpf׍LCqF;ml,n,'-N"qt]k`_i|%E)'t%¿@6}&1j`֋RR^RE. V 1auJ,-DhJ\qe7( vhG e'%hr6_㕹Ნ2rauʂ)Sm cG8+ ^YPx$_Nb8n < 6t9rbtIMAQC+N`n,Nnea=PYch=Dr/V,(l' Mx.UH> Tۤ݌ѝ<S*đ|dQ^*DesRO2P@s}i!yܗ`=ea<]~?8B A�Ic)ZIxa`Od9V͑ЛDR 邉r0"I;bIJ~[:jF&_F[ �Z"0|S/(*jH-C!i wF/#G+T<v7r7ҢY9[ȩGmu𓱯AvQk;o#G<݆3]�NB3dЦ BM_=YhߠUc{7 y^(ULú ^Sr׿i,6*ycԉe" 0$ A9%4tT);F-#\yHI"ю(,du { /SS+{X0rBX!٭)G`z OvT-hJP_:lH9uhFU!-20%#S 5ߚ\B"-hհx[!Ut卑15}lL{Q3 x&xCOtGnЈes~9|cmXE R)'#QzG@zQ_/%LQ: N֗匤n.?+S] F7!@\FX$RJZQ9MڽX~Na3&Ϧ `MpC 0P"BO`n}NK:z\=Ya]kܡw>\f:֧5w lu CBԊk`.z97$u@ Z:?KV瘸Ә`yVl_@tk <Ew@ev%z7{}/^ߓ88w!n7 FIH)A *$GO@G rLDaClJ1:m߃a�\+0zWY%=@dVBT%Y}sa5#G9!_#0ӣgeœr Wgյo&A`ZwkyR OnI1^\fs~U ӿu<yϣ29bYeE(YT!Dnq\6+qXCa7ATauв5t%&s,Z؃ ɍ6;c/cչaa\GIC KD"][?΍y~5�{Hq;a {Sv!}{?}6L/EljӮpĎs!"(][AgT{x2Ξ`Bb"&9�/k[ UF}ǭ'Z1;S3N3b\YڧE>?L9xhb<A&1СXod&Aa֭_TE:R( xDʦտu'aϠXUmy# !"O/KqVuGx, 71J%@}Z{dv'NG8#]l{_Ltv8KFߊcSqaCGbt,'bTO3ґXrt `O憧DeWEN©T"=@I!SMcR=`rI![-TwBMKv$4Z'^I<u~ғˍLvb{YߗZ|[ԕBV3$6'=z\`fQL3^{W]۔`׉>7FG"}HDַFG1#(MRRJ)D6;DO?f![G0,i wK{]acW@+iNij'8~$EyJ}L{JZ+8 lyDS�(\ϩ�f;X:mϹkq`Y9#�4asN<gqBl܎ M?S 2&1\$e݊/g/#?B&gjo [}ȏ~KUȇ:(a)X=pZ),hnފ<*o\F%,cxś�%]\{ϰ߶65˰G*E2&ڋ2]9`^}ԇ P-7 /]dH_?_g&Ɩ*@4@*Vb[ ϰh9EYᾘyk'}: L~WAgkESB$(,GjooCvͶlŪ#FL'w9yaxr-pNZ)Xaf,fWfTYܯmr}O \Ď�`TQqBXn#RT2HEȷ "CR3n4jLF G+4S"b? )vi7oq,qM lSJ5\8F0{L }:W�DeeP z)bSԭX+4&2 [5ٮl/HBic2͚_-*!콙H9m!z/�E k1 Lt\Zw&%C;/ҪB3cz xL/x$AbB@E+9pKn+(cSFng"J#L31nm'lX?%r,%iypF^4�?B +}aFIݬBصZh"Nl|9_=h+"tjqn*XXEwYibLwXV4⃗ [nlhn^q&D,Oںzd5MɗZ? a郣R:d AA[VۜMȖeᮒ^$5f~y$Xip刀)k-,{m�GEXDTZ3RyMǓ Яj "Wv&gݤ(̺w# : :y7LʢFkK o]gi^ؔ=* X*C"ҋ&$6X"eY$G:>Cx׎Q vf 3n8,1>'ZS L&;nwZLJMK|bEuT$6xi.#.?TeeQsg!@qVEWEI~Z~u24\ :u-dPh![`*`2|WE;NP:S$pZ*+WQ^Zd;B=GkFJҙU)g \ X.߽mbz:_BSOח~1hAK>Xap<�D-L'Dqo^5{xY A G_Hc1nSDuӫ\vvTrCwy#5 :K%- uA,ZmI4ִ;ɘ^tB4pqU3`>pI?Rj<2D&}j�tOnhF@#:!FLNP@XG^ �O %@vqB[A Iv`aDuWQ7㕴Ҵ01tp 2 h\nʙ!]E ds~EE#]ɟo=8T3p"3uHn\ ē21]q%wvޔéSO"؀ۂ."JLu;LY37Iձ S}K| (׋u5V5W5EԈ•:0V9_ˤ5 (MV,` N0?qd-`\`C`n j7_ŃoJa6P<32![K*~~TySZ>Nں DaxFQ[ 6|mKkֆ0U3;!Ϙ3p5%,j.v"٫7 ~+E5PLŲT;iC*oaK݁w`.ӫ1Şm,t5fl_B]: -)@TS&ˎѴgJu8 W?f%" KuN(Y˨yK% qZZ4 A F@Cvǎ"akk cPW,C ̟H4q0tSukA$>/oGXcV/'Fþܡu9`7 Q>E#Y')\'mW!-1=&Ͱ՛PMq~`-CDNHg}Ea4e?Vo�82O~* c5|$Y?q|?8 xέlVY#L(s8-bM�D=Xkz t|xmM8u3Ap5niY-GDąG,RaJ(\i!BxZy}/ppc*,_�ZPyVj7 x>{w2\;xH 6P\1:mXɝݶb; dDY0'(MEXG3+n V{']uL_8BV#LE[LWsRIQNP(a=QFF2](O.e}+| `ALjyN$Պ /|ELϬ7dx0SoW9PbQyPXQZsHVftmo*7jUzU�dfh<>,b>ZQr1d4;D:# nͼLC +6:gدAY߽54p_]V̳$9%Rhv$H3 +Gs`Hu1e%0I7v~o\< =rLeUFy$o]`AVi3!zRm]5d/y\k 0xY{'ݙ1͋5rs9/ ٸBj)SH"k-$ 1Q9K᫣Țq}otlGge~nX!Y‡j(+@$#[J+UqA9 9L9sM.-r`y]@8~#[r ;6#G7Fb|T#m7ʉt2p.Y3#Fj +y� Qtr5o~ƼjwqfXL_Ώ\_D7W4p$V!iSj^QyBǃ,Wrh{#y2EJAh�%3+޳kD,+Ȇb~c&dMʰu#H11kխاmJ81ًP` qOfsH#1~Ԟrm~,MLDM|IBZؐ[_*%+{=& 9[:_kׂ,ri(Bl]?1YBOc(C\o-hLװw5PZ]2 UP{b[w~'1U?fT󐬽e=XЩp"?@/Lɂ!JFIgQ.1&.loWx#`Zbn>YAu5J_Vx G;n㛮QI0?o3$Zp c$ M}Ɋ.vmNLdREg#_N"-e 'C|v#߳ 4pZ;OZw8p_m:\L mQ73܅SJ ?ŵZΉ /jjl!L#KzR`� .~:xVHqS0}0\;k{ gVS؟YSK`G~7|$. i4Ú5%:Fa6#Vk~Lmh60LXu)x۾i^p[$u3$N헱B_ujrzc*ψ ܛ* 3!j(k\.z=EsnFČ|3g0i=]W}$:ztkyj^馫YyB -GhTz֟a0�nVCbZ8B4} x dsqFVeDJ o.SqH{;O9O;I'6|ԯOw-I V׫ j'UQzd rz{8Z|~s:WûIyj`Ok`g0KH3-L&Eť'B9ط(|x>`6@0 Ө{`]֍x&~UTxCIQrfU3{K8^hjq%;2Ȏk^d]Čx$wm?褼d8nVpɇBu=|5B4CXC7 n\o[.8Bwiߥ@x^Qǡp([ŸH$j)TD@ 5%e>R7yi ,+@l&@oY.X-Z%qiwmM17q岴=D`vv) M=Wdd+_^j 3EiS2^lqrwq>-rijZZ:]_|MRcp"T@oRi:rfXTz;SE(Mg)ⴊK#WҲ]~~QQ,6uhSkt K"VK[IaOY=@]AguDWI2**}\,CWÃY9JȲ,;]o؋c>r:vn_h#.Xzc2v[^]u !3T^q෎%TG>}+AcU3UuQW"d{uGQ#n0`*Z%dDmJ}L9<A$}9[#n$<K%@oDmUM-1"1ԝk< K-*%A;2`Dur4Ԧi{CP!Igixl2)GB A?%"lo0"pwꍯV:=H[0�wiZgomt24|L+n4F(`# b`CˠXݷ�R#a!G1>ޝ#|xgCTٓ\Tuv/ٜʮzJ" 8j6#C.fH`?LMI0zZ^+kb vﳊCOP@u>L۞ E LFȏojj Ki@J ew.=B?dzDsebgK<YIB?Yo0�|S_kHo![/38,lNAW7emʻ̿Q$zn1U߉&3t"98#4 ` FWhNQ�$tz<�S7$'7fnI e<>EB»ZOT[<ExǨUZujVأ@{jT/jݳ*~b9#Mg2k(01'y'MgZ<xS|TQw_*Xm=]5%J]o(--9"yyV:w +2;ܲ Ȍ6띮aeqZ`M7Y2f^e?c#}h "~0,8h-_/!r@x˿{)~쐵3q:S }k@fuE!�$PxĆME1V9 |OY}+%6ƦS r$:\9qyK�PY  ݰ_Rw1[lgt]Yf,7 ϙi+yeO5BM|hOcGx0DaWϒJBxkIJUZ| 7nBH}o `%n1:oֲ+ B Oms%�h;qN:&g41k3|bJ zkyttV =ֺ8 LwUr%o_8C�'6,PF  O٦ڋ̳m׮68!]! lB>L$%jTkU`"(igjy92cF2 (C=DXzt AꣁXHN9^LP@OI/:_|ޔN c9k!01tIlR'zO2{q;2dUiת+Tm}mGU8/oIQ|&'J@)iC6hN@JB" \P׈fnC!i\ޒ|֙r2(YHX \ Zɇyzw2@5UHgDbؙdr:__/hazz7)G3[ѭ''rzu(Ou=N^ / rcGm.yl{N@Mx rev_/LbK'|:6Q;TVT| iJֆ YKZV$r\s&lSF5%J\# *&G'ղ]m*XyMu_66+RbTą#6G'*!UwG ahC`p4tlCF|+tZSaSId3{vMcz*6"%rکX oET)|ryRG)TRw{GRd$\d3WK&:Eٳp</+:-`W?vS ik-AV3�P/xU5 YyZiƓ VBnA(6L1.gi<e7Vy}}8({HmbL'펱chrvn0*5 +i!B>cMv<EZm譊nER\?J׊�% j{?@n,Yy-*jECv7IpED1y39]2 V!ߢfm= K7ƷHxe{h\1(q..D({R1dMY(B, dev ĂQĨH%4{^o Y('Lp�4gj\?>J* +Y.:׀ސ";ѿo3 wxʶ)"v}{KI" ?j楯c&A=//�u927 }#~Sw!`1j&ht$HX&k"˷&SP 1vA!8ƍ7b. j;ὭL>p=[#%nJgכ;Vʼnb#6o,2ӸR%L >Ӂrx2Sɝۜu{ jޣbyPF$ ,Q*}}"0~\^tHjz1iGkKgo+/ v<L?Ycx_O#Vu@^ꎴS~(y̿~ ܏z-r-/Je͓adcj^lU8'Wp[ė?KmF/IqwvpkR&�����D��3���sg* � ~}||�~{~~~y�[|mbqa10"a,Nj`|Q*aEL|ʤjN9ȑ[WG4dj[-e/F7q"i2r&cuQNwmc5<x=zXx4]Qc|55G5~ET {I)sR.mȆ2n #C}A=r^gn7M禨9Tj B+4E0٥2(mttGae?�%4po RX W99o1f)H$T㥅YmD>^T ڏdX#eٸ aTBgٲ2S> 9pb"t,H�7B=&܇>~59—� ./"G[&y�N_:Ŵ~n*Gqk[#ғ.Ub;\yOch7bLmEKPi~~�CW*dB"GدP H4Չ O8t7lh9d7O!HD[ 846CzH#tE/x<p (4 oS' .=Fwc X_.eM;m0`o I§G(jMj%<5k~F)R^{lLf275?!YhJpmt1")cԣn篵6U:h. zCSh*ij|VM(LVf:Q%,'g#IԺj.E-~?My[[|E,K>A MݏͺkBsy ݩ$*/]~WiG؟�D_-jd'}XCQY A;9'=Z 2ⱸ葸 IyFaL+`عEZfi 'iw'tzGx5QbyH%W|(eEI=kS"RBt eR" 0<* ߬[r�A2D?lS&Fկ޹!'fr9^{jd=*k}nQ[Q1nF4IwO\ft0vKSՒFExQ{xמXcbئrm'TVD"ѷK׃32yҞ�n7xou`sqii76iKe!Q]O981;$M$6K%*v{x]M`U!VKUB/=P֩ӮjOg GCJ*Ěѣ2CW~|^b_:TmnKd,NC]BZl,qy NT_oySȏIȚ`d%B V Zf~~ &�#p;Ӥ:ztKߤ:M6 NE 5x/]$5tqjV!XBr׎D:ʎNﳬqCqrURE8)?wiwg4zC8g>v *5''GL=cLwps ,gp3m5)># ur-# #\"4z-Iy\g]D`^ ė~pcR!Gi@ICZNVwڧ*؍P|3<ۡY`؎D-ݳn4h؟@丷)?/)jڬVz7p$VQH‘G__}B#a=Zl&, H̭܅O4q"z(<:s\Aq5* %;!][5yY|ߗKT:SBnpcۭl>u ߈s. �0%RU UHG O?Ѭ\gB8* [4ڕ>k-&zX7r, zw ,@ьSq?I5:!˺}CC=I}ej.[aj0w%ՓE |:CxĦց1p.4{+T.gh܌ʗsH _I7i㛳qĭExFXo,h8.У c [ /utʑ_𿝱}N_W {ᇮ6y 'KMMG^(g¬gD Qs7h '{zh; RE+�}뉗.҆0 qUFvum(mxE=jxcy9`z)' j ͸Hl5|YHbdP} ;ʗ&G@Ǐ]9:]_ˡr*2 '',S_xb\!ˍW!:vƗ=K^7^3n/L*]PRTOc-iI MТ /<wUQE}l%ݢ{B )kKfBcx׷0Q,-g^~ɓJD*Tb*kT["4$yk?dʵ@ & ME/}f&a J'#Ä'v<zk+dXRO~EU)4J0\5TK Jh7ZPoEI8@d,?u&w!F0 ^‡ƒ %h V^=tّcfHs5c.dS`N1Wq6jF ][85"XtK(i㘀LY-J)N/4ZnuI1SL{'7o?|VXk!`[aw[H_{@Q)'1/ ,Cx+O4J::nYg*cǼc)T|vu5Ae$4Nk +AN1u[FtbDB'ZqãQ謪O^�C'fjo4UUqI4KُiKDMi{gVx,Ta'NPj2E>arHIWvBK."WIdmUR4pqgbhk%0hڒT +Cj;11zP rz8AZPDp ɉxh.Bיq�Zɕ?}z1h0&YwߠU['F{y齶iiCWVCb(M G}.#67]eXn|Rצ8He%1iZуVmK߿M᪶ު%$ du+"WJ /b=2LM\gH> n+"ݑɘ1))ͼN'7%XE }]t]䆦1~[y%M9àǡ0݃)sX�KRey琾y9 {VWk { &\Ȝ`~ K#+MG_RTjzo-~L�QT馀 S1Zjƣ65U"57C5RV7+�PE~0\c_`3`9m5w/R[ \9~!}aDž`Ymn[*]#֡s jpsST^]*ϗA36#ԍ-f_rwJgn娗^N,t!eϏpT A6'J.,Zj@aKMCzu&Vl]bcըN9Bx[tJIRbOE(uW"ћ!̢zR1uڶáKY#U_Z͢/C#T?]�E~l4ۈ.҇"2|2YO'GNu툻VyЄ\-2{:дf l`[UZH0WLAPFhkikA~ZY~mH9Bp Sr3UtZUR6B؃_r;ҹu]"I09[5@=5g2r N<ӞdN<@\MXdU&hޞào}1'B4v]X3xS6LwTSy1mw}ETĒ"<є"I8'#C%@':=$jOa8~y1uoV혩JTW;WaMvڽIUT9k-IB}߱14з\gڒgLX[N4*U-*'kB:՝2#{"+R̈́3(|Oz:ZtON+9]NsQz70uaeH`rӈי|\?s 4<]6pQ~ܽ不tCײVѨ0=@g s+)3IϻeRb44QcFB[+FkkȳniڈPI eFVG sW0)!K!TՄe@{MI`We8SGH<*D1I?&Β4:7cX AaY][,݆RȺ h _I`wCxJ�іZH9�aPR:lOO`Tp;ۨ?S+jț �yj_fHPC׮yQ{CV"y^&~1,@@pP(nK b\3gj_=^:*!"OP i a{gc;W)AnG[4qGl^3$RkۨJP=ͰEpYJԱ-o˽A.e"pp20fPH$TaܑQp 5O/-5XSNH5%FW$$*f%˓6ly)<ʓ9>Glz_(/+r^VP*XܛlfzӑTb|lo0i}3Ѹ8ܩ< {jXCl[5`Slzr nD}x/}eu2ggہb7$hF}ޜ]"|fv %Q)Z83\ǿ 3aBw538NFrN@˿`2 _ qArAXNs//Y $ cVXΈ[/׀*<fٰLIs܅ン;�2ZO]Ww'(pY+bċwX/+"0j|}hK=ag2(wTTF4;`[d)m= *P+ &FV�-Ձd[ך=N; &~1AHvJL-y4:0bNv&BpAg3:uw~jƌ Ǒl/o] $I8J.Ue~6[1I=󪑷\&v DÛoΆ21>A}mpi 0#_@ZqvrQ#8m%Liy1<-5iJ\h"^WƑd"K,aVHqoVk;@ȠJnŀӹ&,jtx2v=]-?'1>Z 9k^nXr&bn{ˎƒYEDU]&sGen{�<|rO?ee^hӚA6w"ފrvb>j+'KYݏgmT4,yzt0(z+5-1a</tT pH^!}*pm04#R'|;f*mF㜑KcDLHڝگHS`Ǻ LҢ-�:Cdi*M2 t1Σ'n[z@(| `Y&dqOR9租rc U ᙆ׌vdZ9VCŐRRgXP#̣>1BZ0e˃/B ]7Py {i+)#5 KY[UtqHG)9㬈ٰ7Wm¨0uIҕ O:'cJ`{}揁j^hKK,! ==mʓk]]5pv5vUjj7_=xU`WG |#)5 >e`(GU׬c.-ٷ뜡k4bTnb3 ɢ:v} / ,.)=Kd<2wU=V%uNTuGwlz8d~I#yH!mr]Gl:^l44-1K = } ?&ċxz! u-*̵e#H/W^ͼy!\S5 qp]8cčz äEL;I] e#9Di͇ lzjHXaBApٓRryU5(p/u%Q~xA; .8qII@Mm-`7Alsc!;4{OȑzAǚ7<!KYxA3dIMd+ju9Xf 5LuH{2iJik@m@ڥue\%)=𺗜Cikf5.5A0/%C/ipЄM1v-@d9q@z `^]wA6L>Ur&1ݰ*j%z ^?ͨ|j<ocRN67'T26 1),{F*� y=J"$}32$1" 2a; }5[;_ 0tW,F}R{K}O})RX8h.T|}c!FJDD%7FEVy}='1[O_8Sn"̘:AH+?r9 dRN-O\{\^#4qI@^qGҩ$->g_vkioѿs@�IBpPU3r>-c UȔkL(pR&?C'$  dr)nOˠ>Ã>bfهMfJA8k'a�1m:nRS�1ٯZEJWR lsרk"2C[`9|RvQj?B_{:G֓F<5-Y1_G,< J&^ DpB5"|ӯ} #BBfУMraL]]3I0P'8Vr^tk_Wٗܐ`]tЛ4Yo�7OS C\Fkfj[,ep|!}0PJ,B&{^G+dP 1ZhAe_$KwbH(~!W|[h*ŀ?`몶՛ѣ:Zd䉨DEϭ2TW@nǸcK'Ce ~.K4<Dp "P&SM@g|-~T ú9 ۭfǢK" T 4cPH:aW&kiJ4,0ܙs9oح˨aq׹d>EX5(Aۢ{'I8zmPÞ!ȞMO~)-Lw~V;ˀQ)?\pFlf^HXT7.w;@4yw-wyL? d^FT;:DAr&gd:Z>&r0:4u߰jըoCynp!F aǟ $vi@9>}Yu& U~ 5kȄ' @p]<$&256k/U͗PkhWUfP?za!7ANt}ezՁku˯݁x[ޑIfٌ[Qx EF nPܹT-xLV=$25n!˄a[ S**;yR qDb5?εn;jI'=[M4C"MҞ>gx}=ckzꐁr7EYFkcMX&N띖8ZCތĞ}bͭZBY\os;8ddfSHL^9ppﵤZ)>/xC~pM9 (TpD+ߪ^~KgJTR8<ΣO!D~@_�ӱ.ɐ�"d''LS,쮬Sr#?Z.5IR, De>Gn +7@ |ű#'yGn7l4m|*TAl[^ȩ5k#SEw:}Z|q=CsXec`d(px=I%F2 4\XB4p9͞^/Q\jʈ)Zq P70�/JwRiP(8,6 /æ(5N %-ࡆ$fV:oP� f OM!➝EX4(ozYItoI>=яǎ?LEgzj~|gWvšȒ_If{%a`yd}p_{G( {6*btj} VyS 3{N:վF0:,5"6ek֎(->xZ.8,lLѸȌODd\gG7 BҸIBB۩?�M/]O]ȬƽA=EcvK& s +4Cu֔*ylhF\4;l=g bdtGw8\iQ.8@;;s7mK)|*rbpveL a0Nw5#aNv 70WMR?/(zpO6nd6*YF(?2"p#8<FkS,]Pe. ( E$>壅9%qNڻ%P2: (lߢVx'%$=44c .`o<Cp!6']zT14L6bHk+n6X�f'ֺ6Ezyհۡy@Rc|N �/,owvpk%�������3���ZI6.* � ~�}�Tums}h%2;&A<ÇD;\M/͙ ngg`ۋ9aR\{@RAy(qs7lnm .lS$6aAUfDnȭ^}N[fG)!MT |vAD߬;e|T0!ai=;B())sXg#tu:_4T " `B#l }U]sME i>u_eJ N4j HS>;+Ҕab9�*6 ˜M>oc0 mHDCEZOɐ'+~H-ʬ0`k͜$&@E]nEA/DfQl;X6AG=g@lISW}77|Ó:>(:vfGukFIƃ%M pbOv++X i�mn6čKfF&KL9ٸH$T*Ma'D3voXu mz@*n2%,~dWNU̓̾H%p/3%gk 8eQ52[߁jĻڦp[n 0P}+!Hj&C#c0c-Ff~:II3nyI�xe GIK2-t.7MЁBddUNU ]bf~6 h$ʲNgrjZӷ'we]|$.g,XQe XC _g[WZomQUtrn.HVZՋȆ# Ҵ_\:N&F<‰ ÏT)=_ `/ĭ^F&I P]O O}ڕz#tupH6 :\:ziYΓ՝| b!ha'`-ML>8C{tLD-9vW\H08 aoF943q fИbp0cC$PVJN_.QTu !ne :ST1xC&)#s<vQVJf!l(1V 5KQV¥Z#@x7ctxSq>5۝[d/ur:C< V 񦻩ukY/ @:ŞBAȚ4H>>}a*p.-uSf:6JC7~|?4Idh=2!u]9Qm&oFѫlsiW@Ya@Cz?ھ&( f9m+gJg/նYoxhi刣i~W|ʭ9<�EEdx 杳|_lG؃ىR?W9|3Ðn[g#.,E DpULyĶs%N YUAYIΟ7pUc"8ռ[N6{gEe$Y8)܃.�zR(\pz�2QXyI Ε. Ru(r)twhؕWhB-?^%t[!^}+Vv,OlI� im|ФCuP{2u5⌿ $jA i1("o=HavpL] br[&ĬNU. :\NRwE-fкcVI8%I62�bc"=<v+vg?K>t;H^99mVۘR1 ~˦ o@#S5aD焞X4y:!|qZdWy!dN(( .JQf,~UCQFz#4a\ 8w9CmM.9mZ:3GF,ɗa Y6.al~r%+/cC8Ur�;UgWlpmg@S;נc{ ٕK\np-G1tݬ{@ڻrqn:%)lfT:eXq}]P'u{S1K?eܩLnq؄xÿD4 f-yb|:ÔbY&X <8�;1;(>w*TNfuF)~!QEK}BQU 9wkum~9kLƦe@BYQGC2IE'ωW!ݰG@>5XiN8}-H0SeC=ʎ^PT]"+u7ZoŎ�AO5Q~7P?hS\MI^rMIZ$y/(8kz(p ӵ4d:;}ǘC-DL Fѭpr+Qڬ22J d.(g22$Ɉ|sLI(\xPM>zHԩe= _QOR*0}NzEFrEMWd"m2<ôN"S 䆉gE=CJT+mĸ{k'ś{Cuϣq (O5;]2?q`. +p/H:U}!pM7Ƨ&.onFˡ&F9pUr5cV sAjwջ?c;$ -.:A JxI4 +.J.m{RqZƔ!WڮL-Jg?D"@e>#}"(QW } i�L,ʐpwn5)TP)k6|HrKf7[ ~{#%K=CԸ. (!3ǙG&ImO+*JD Sd�hmhOUZkr y]47 iΐ}Dl6fwm55TsίD5ӽoH^䯆=V>#BS0?zH5);YtS0+R89!%XjK;9(2]9j3V&3>6dwM"jgrd:>j2 O!E c;Ƣz ?ljwf=[K!jvǖQqԏ!l~J5 -W:<c=QI\)+ySF塛Zq 1ΨZ\|b٪^ⁿ+{a;~@w'+ IQKW;/&I~`'R7ptoT`MB>d8,VR E^ZPiR?/P'Rdck'S1`։;iInйxOKL3,tTG Li}7G$t^'9Gf[ ӿ?#Jo:Hu,uN!̚65*?t[rԿ~)&a ŋo* E>Bdܔ!,Lv82Sǭ'8_ eӸN+TuV[w{C│ 5.)7A%4KʐYl;Dɧ Uvt&,L�dF/AEʸ.3W\i0}M;e}˿ ~CtǏ_Wk8vo!.F/�/s?;"1HC,>} ñψ. BTq[Y7RMnu_}!RD#0'SNp#섺Le}9[ΐ[JڀwJ{I5S-W5m PI: w٢o47<rV,9kǿ𼏓|zӄ.6;N뇃o  l!#8dno@Yr^q75e]Vl G2'xEJ�rsT)o.#V SPu ϳO#E2;Bfq< Y'r3Zهz�$k� ']o_'_q){o3=Ur;|'V[b^܈Nt`S/k["еpDQZnxr|Y �*Wփ9e9 e,)|( eT OSAehOb"lNn%.)zfzݿ8qkjqFV Iիv-'9; IakZjАxwD5Fni=>KH,/v0ƴ9w/MZn2:�?s ~^,@ZlvџJg�!ٶ>/5 t }_hk>Z2}]ժd*|PǓ[*Sza@IcAb`I}`BXM>p?sQB㟷@@UISI:uI{?dOF['-x6k"f+IŝI6wc/4` NYAOȾ:ύ:㞩$Kx%U)|Zt\R3kl s[y=*^CD6z& Ϧkk-YҹHP6r,CqX e1.(! Nϋ f(U+O?TKo/utXxə �mA!/us5/g dXEGV˙m$Jd2ɻFπ?S64$#5.+J7w2H8�i !(ؑ4Reqۜ/hD~b )0[朂\SD[Q= =ej#VѸ\ҳ{a uI8Xy¸WN̿a{`~]̌X L (pq2R˖3 S:ԣ.=Fpw}p �}S �.Z 6{ePX\Tձ|M*g W+szL#T,D{]7^źN/a@14^V7(}V/YOc9I!,#<< $^2DK,7 wQ+ŤQA8$U a:{t4co˩]ؽ<Mb3B4oIaoA'h6Dtnx>8QnYβkt<"63(Ig2kgxqLn<b:sH0(JH7 #RR -ݟ2ӽqӪR+8i;3qހ\Q ).+9 qnB7K1˞FGH&8DFh< *|׺/}toXIuCӦʟocWc:>Uִ82c)y ,S,'Nc$x~kpjv•h˧*]'SGmX_zd2/@w"xGsTWDڮXk["|'6>B/&Ő_8 9C+7f||wYAa$ Hm]Ҵ%:YA }[ޑԋ(jɋ"qcfC\!(FA17&R)<VY_;dR8)YEDrb G2C~MH%;E$H%dJSy@3_?F;2Dlc\:btv{e[cpxG&Y%^#tSݻk<`xz<#AD6W!aVNu 8x0ąReu*gr͞Ȫ~x;OVVA;WlAHeEfψU%k s'&Yg|`~lTWZkÕϹp)!]ݪxGChd6Ζ2O?[ފ')Xd * 7=~?Q&!?r8>l+psrc5Bm?$53|k=mÒQ8ִ Cԙx hySClSv'[fXǠ"i&R2iR+'x}J5L;id}i} QVt9elNq(-Wvq'X)YVYJ[nP3x-mdJ� ^vD en{$11ֺDO&=uK`J(W"ҘIMНhiJBLl~vTuC0:u* C^6}f=d: |7Oȍ)<M}5E&)A T9zi_}]MZu%SJ[">?ԳK5."#␥Wv`~ 'e5.4y,./Spw'0=l`;0!BhRU)5Ho )5QSyk;+ޟO߅\gT"7QFMf.TLyռɦFA:ץZ) t.@R6C8?9mwFQA:Hh{OܘG.^>j86^`ڵp8*]f #Sƪąo1( }DZLc5�%Z4+Yr 4q6aIDCsIEOOWg'.9/o)[= >jp,h2C=V\?>W=%im =uQ(|b ժ[Y0DT/o IC11.fy!O :P۾x�V8D>wX_uTo(Yro;m:iB]l`Wog 3yRA-i=qcd&[\|Ɲ` $)5|_:NrsJ*OLy7Hea]*łjvtEq0n1@L3Gbn4  :WOU.ۓo;@eqA f7Chi^m n;C+d^+@[_z0;owĴΐ_NƐJ  /sTo^qY2AFk;:Ǔ;hATN[TW*~؃cEeB1È-5vqm4{WT8MGyJ>`mjMVsfrJ0ŘM'*ZYFrNrmz/-ɄIg̓:>,y:(1OWy,S- *k3Z?bk}obugW1Q?GRz*F덡l~ MTZ1sQ1_Ϣ$E[6! 8pVzvf^$>6$A hwDh:ODX!Xq1:`|D2t1HŤ<ܿ?O?K.&bFܸdZ'og ȅ:Q{~ͭ0 !SMu\.3TR wJ$x68.%}αա)*OрM}P;rM wxxp1kYmlK5njlـ1' 8VE/2%?+U[;EρTRukN8jzTҤx;)ml6oZ`T z DdThM|7@Z8B£zg Y:Vse~Ms3Vnj? ȁ_GTGjO(&AEolLvLHMb-'1!4c.�BqRar/쑔ɟ0s{dA9:֙s(of>{ B&fvVFȴOf9&;`ӥe?[ȳ�{MP+7ٚ!-2"n®>y{&jg=Gd2S_>պ)f㹷LlԟNcgWWQq0[fJ7\K~z­*ݯ kBgA;1hz#{#O7%:zqpA&E20Cǭ$[Q= Q^kQۋmP�62y3H_֠0]HQ˔G @ "ڌ|e 1tx8He!/]@o*{ |UZ)W+'!<euw7ՖRDD¤6ҙI;d468s*&ʺMwo&nNUvTil|çPBm(:7='Dy?i>Y Z \q,P`K"iN^0Bdv!hpĩBr1K0-QXLiz5Q qԘ0Kw}*&I#r76 Py ,?7�Efd^SeQCcW1/ic2+He^dbN^lp#XMR9- 0.:(snU.[qlMI�Q~symA;̔Q0*5=^(m9ъ$z䪧CdyI<c!*sE;t 2i<[B&kx2‰p_d qJMipq>zX:kW B-[,ƅ_WQ}@U>k,EzR& u ^ݬzi\\4gźήX#i AEW$Eyoݫk.\34>xgcpG!C;Z|ƃA& P3!߱hO?~b~MCٌxc,1#RN1 G=Mk+�6?=S@yT>s!5CKd -j+%s~،ɵFb YHRS|uz�M<7wIQ)R ki i.h~Pҧ3xAV/w[Rwr 9ӕ&H3 %NJ/zB'тƥMD%[z?_#b?~FHu\qXrxB嘦0J]{QɋH#Ch0? cӍ~5j̸rb"~&(G/׈[r7} >=cobv\y/8 wvpk0���������������������)"[(5f/HAPETAGEX������������������� �������Artist�Lorna Hunt�������Title�Long Hard Road (excerpt)�������Album�All in One Day�������Track�1�������Year�1999APETAGEX����������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/duplicate_id3v2.aiff��������������������������������������������������������0000664�0000000�0000000�00000017674�14662262111�0020660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM��AIFFCOMM������ }�@D������SSND�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������TEST����ID3 ��>ID3����4TIT2������Title1TALB������Album1TPE1������Artist1����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������id3 ��>ID3����4TIT2������Title2TALB������Album2TPE1������Artist2������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/duplicate_id3v2.mp3���������������������������������������������������������0000664�0000000�0000000�00000023632�14662262111�0020441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����]TALB�����A�l�b�u�m�X�X�X�X�TPE1�����A�r�t�i�s�t�X�X�X�X�TIT2�����T�i�t�l�e�X�X�X�X�PRIV���'��WM/WMCollectionGroupID�mkW@d&`PRIV�����WM/UniqueFileIdentifier�A�M�G�a�_�i�d�=�R� � �1�0�9�6�4�3�9�;�A�M�G�p�_�i�d�=�P� � � � �1�3�4�8�4�;�A�M�G�t�_�i�d�=�T� �1�1�1�2�1�0�0�7���PRIV�����WM/Provider�A�M�G���PRIV���'��WM/MediaClassPrimaryID�}`#KH*(DPRIV���"��WM/WMCollectionID�mkW@d&`PRIV�����WM/WMContentID�6)$myIPRIV���)��WM/MediaClassSecondaryID�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3���� �TIT2���1��J�o� �O�n�e�s� �U�g�l�y� �A�f�t�e�r� �2� �A�M�TPE1���'��L�e�f�t� �W�i�n�g� �F�a�s�c�i�s�t�s�TRCK������7/11TALB�����A�l�l� �F�i�r�e�d� �U�p�TPOS������1/1TDRC�����1�9�9�1�-�0�1�-�0�1�TCON��� ��P�u�n�k�TMED�����D�i�g�i�t�a�l� �M�e�d�i�a�TXXX�����S�C�R�I�P�T���L�a�t�n�TXXX���e��A�c�o�u�s�t�i�d� �I�d���4�5�b�e�3�3�6�e�-�5�1�d�a�-�4�f�b�8�-�9�d�5�d�-�1�1�c�5�2�d�7�d�8�0�a�1�TXXX���=��M�u�s�i�c�B�r�a�i�n�z� �A�l�b�u�m� �T�y�p�e���a�l�b�u�m�TXXX����M�u�s�i�c�B�r�a�i�n�z� �A�l�b�u�m� �A�r�t�i�s�t� �I�d���1�f�c�5�5�b�7�b�-�d�e�c�1�-�4�8�0�f�-�8�5�9�9�-�5�c�1�a�c�8�3�1�3�c�0�6�TXXX���y��M�u�s�i�c�B�r�a�i�n�z� �A�r�t�i�s�t� �I�d���1�f�c�5�5�b�7�b�-�d�e�c�1�-�4�8�0�f�-�8�5�9�9�-�5�c�1�a�c�8�3�1�3�c�0�6�TDOR�����1�9�9�1�-�0�1�-�0�1�TSO2���'��L�e�f�t� �W�i�n�g� �F�a�s�c�i�s�t�s�TPE2���'��L�e�f�t� �W�i�n�g� �F�a�s�c�i�s�t�s�TXXX����M�u�s�i�c�B�r�a�i�n�z� �R�e�l�e�a�s�e� �G�r�o�u�p� �I�d���1�b�4�f�b�8�d�f�-�a�3�c�a�-�4�d�0�0�-�b�2�2�b�-�8�a�5�a�2�2�8�2�b�1�5�0�APIC��]���image/jpeg���JFIF������C�    $.' ",#(7),01444'9=82<.342�C  2!!22222222222222222222222222222222222222222222222222��F�F"����������������B��  ������!1"AQ#4as$23Sqr%BCRTb��������������)� ���������1q!#3QAS� ��?�AyP%)6f>5׈oJ Rह Z;hdN;vO\ڬU#cqef$)Ph]jRJ:E7`;)6Wx)XHRc�(qڬD\rz!UGFi8|k~>50FyL=;%/%%?1K30 J󀰂*6OuѲq_8OOսFǚ.Dy6IQBe;CZCNJ6P^ x RrF& Ja sMs}JGW/*CxG] <,:.LQvFYrOg .SwPKEiAҝU'=3!iѠ 6Z[oV 2̳ߗbB,U�QZG~V <,3vTwG]=Pky< 309Q}:qbEVx_aB"sp2-ࢇQ&PvWacP uR)t[qk{9WY=3Q1juBjqd�5*`7o@&bQq 5ŭCEЮG܂׵zǡ!+a-BhOP6Z};a26%ˌ!%bB!#[jIN$j=\&PiJ ٷ-dsIڀ6CF|(4g'ŠEtz oHj9 &z8\u$Rq};7d{xR9p Ų,-&S4p3:C݆E+;9+/(ǖʄT,*UQի[ܚNMN#,m% RRAV#;ZH{l*$"Kt.(<] Zƛp\ϖk%+h- (<8[ d}azm5l8U%cl' %O-k)[z'ZNb6oUjsT֙(duҮGl̹3l ,涒w!I؋ µnN<ɇ5 _BHb[6Sh":F?KAϘ9G,Z$L}r_q$R~m)>+ $V0S3Bz&M, v {w4LF}|(E#,NAt3,W*X'qN⎢ziD2FRko0\/8u)hνA"ÛzbcHWVE+ob+6:b^|!%K IIa9f|E[=}:JkŶ3On;S1$)8k "jUJmcpmqVxn39)*>;}R}y4]mL gR8$nj'qlAԬ9U8@ o적DwD|D̓yE6DM<nx$mW1,%S/IqIq:bBTF(mָǚ31/ͮQ=m�W|)md-anT>D|"?QѾǣqJ{E/7�E$:ErGi–̨+6eplv;b`|ݮmW )UiԊyIb|:[-EO-� D-7nsQZ+ gQ5r~q'2tCӅ)zo"~JO�hiy 65;+a'&#!"�з�(Z^Cr~q9%/[άZQ7'*\UFID���;��http://musicbrainz.org�a00754f0-f4f4-4e7f-9cf7-72800fb9a474TSOP���'��L�e�f�t� �W�i�n�g� �F�a�s�c�i�s�t�s�TXXX���w��M�u�s�i�c�B�r�a�i�n�z� �A�l�b�u�m� �I�d���f�a�0�c�5�3�5�c�-�8�b�f�a�-�4�7�9�5�-�a�2�6�d�-�9�9�c�b�6�c�8�4�9�7�e�7�TPUB�����S�k�y�r�a�t� �M�u�s�i�c�TXXX���M��M�u�s�i�c�B�r�a�i�n�z� �A�l�b�u�m� �R�e�l�e�a�s�e� �C�o�u�n�t�r�y���U�S�TXXX���G��M�u�s�i�c�B�r�a�i�n�z� �A�l�b�u�m� �S�t�a�t�u�s���o�f�f�i�c�i�a�l��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/duplicate_tags.wav����������������������������������������������������������0000664�0000000�0000000�00000041234�14662262111�0020544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFFB��WAVEfmt �����������datal9��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3 >��ID3����4TIT2������Title1TALB������Album1TPE1������Artist1����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������id3 >��ID3����4TIT2������Title2TALB������Album2TPE1������Artist2����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������LIST4���INFOIART���Artist1�INAM���Title1��IPRD���Album1��LIST4���INFOIART���Artist2�INAM���Title2��IPRD���Album2����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty-seektable.flac��������������������������������������������������������0000664�0000000�0000000�00000011000�14662262111�0020743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"�����<Np%6RԨRB "o�����( ���reference libFLAC 1.1.3 20070917�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������r��������F��������=��������N.����������������H���������������(tpL?{q@ �� @ A�#%$(Y� i! vEd)QVR#E$?f82xHdT9C)g7Ѽ \K#.i)+"Ø̹t*R:3Q7ixmr;jˈa[H!(&a"X)cZK1 %n$(z(Ȣ %:%]:$s*'KƊ!taglib-2.0.2/tests/data/empty.aiff������������������������������������������������������������������0000664�0000000�0000000�00000013460�14662262111�0017022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM��(AIFFCOMM������ }�@D������SSND�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������������������������������������������������������������������������������������������������������������������������������������TEST��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty.ogg�������������������������������������������������������������������0000664�0000000�0000000�00000010350�14662262111�0016664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������ۿ;����Zfvorbis����D�����������OggS����������ۿ;���r-vorbis���Xiph.Org libVorbis I 20050304����vorbis%BCV�@��$s*FsBPBkBL2L[%s!B[(АU��@��AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d���((  ���@Qqɑɱ  Y�����HHH$Y%Y%Y扪,˲,˲,2 �H��PQ Eq Y�d��8Xh爎�����4CS<GDTU׶m۶m۶m۶m۶m[e Y�@��if0BCV���0ĀАU��@��J 9ߜYJ9Hy9s19眢Y 9ĠY 9'yК*9q`9&y9iK9HyRK9s9s99眨9Oޜ9s9s9 4d���@a)h Fb2A0 Bh:%qRJ' Y���@!RH!RH!b!r)J*2,2,:쬳; 1C+RSm5Xk9皃VZkRJ)R BCV� ��BdQH!b)r *АU�� �����O%Q%Q-25SEUueזuY}[؅]}}uaXeYeYeYeYeY 4d���� BH!RH)s9$ Y������pGqɑI$K$,O4O=QE4U]Q7mQ6e5]S6]UVmWm[uۗe}}}}}]BCV���:#)")8$I@h*�@�@��(8$I%igy陞*@h*���@������xxx舒h+ʦ캮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮 �$��t$Gr$GR$ER$GrАU� ���1$Er,4O4O==SEWtАU�� ������� ɰM%R-US-RESUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM4M Y ��S--ƚ $bjc R쥱H*g1^Q{$cA-)&TBc*RR 4d�p@,@,�������4 <4�������$M,O4�������������������������������������������������������������������@4@<@<�������<<D�������,4<Q�������������������������������������������������������������������@4@<@<�������<D<�������,<Q<������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������`!"��pH$ HM4eAӠi0M����������$MA �IӠi4"����������AӠiEi4hD����������4!E&3M"D ������������������������p��0 "��p8e�8��X��X%��`Y(������������������������������������������������������������������p��0 ��p(eDZ,8$ɲ�<D���(p��ASbqBCV�Q��ű,MEi'$I<Oiyi<4!hEQ4Mi*0MU��P��`��B�bYy'$I<OE4MSUIy(i,M<QETUUy(i<OE4Uuy'hEQ4MTMUu] i DOMSU]u牢i.MTUUu]Yi2@UUu]W뺲 PUu]Ye�뺲,����*ф @!+(��S0&!$B&%R RR)TJ*%Rj)UR) B*%R��؁�؁PhJ� �0F)sN"c9'R1眓J1sI)s9礔9sRJs9)s9眔RJsNJ)%A'9��T��`#A�R� cYyh$iy(&Iy'<OE4Uy(i*E4MUU],i0MTUu]i.l[UUue꺲 \ueٖ,ڲ���VG8) ,4d%��@B!eB !R ��p��0 �H��Zk@gZkZkZkZkRkZkZkZkZkZkZkZkZkZkZk-RJ)RJ)RJ)RJ�U8�?ذ:IX`!+p��s B)T1tTZB1$ZlsA(!b,sB))VcQ)RRJ-XJRJX1ZbI)Z1#lMjck*-c_dl-j #[,-Zk0[1>R,1\��w�D3$� � R1s9R9sBT1ƜsB!1sB!RJƜsB!RsB!J)sB!B)B!J(B!BB!RB(!R!B)%R !RBRJ)BRJ)J %R))J!RJJ)TJ J)%RJ!J)��8��A'Ua BCV�d��R)-E"KFsPZr RͩR $1T2B BuL)-BrKs���A���3��stG� DfDBpxP S@bB.�TX\]\@.!!A,pox N)*u ����� ���\�adhlptx|������|��$%@DD4s !"#$������ ���������OggS�z�����ۿ;���f}[� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty.spx�������������������������������������������������������������������0000664�0000000�0000000�00000057355�14662262111�0016742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������'����ǑrPSpeex 1.1.12�����������������P���D��������������������������������OggS����������'���[0!���Encoded with Speex 1.1.12����OggS��n������'���+-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tN��mC�_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNkmE_mm�/mmomUՅ]]]]XUՅ]]]]\ֵtN��mW�_mm/mmܡm%UՅ]]]]XUՅ]]]]\ֵtNFS_mm�/mmwUՅ]]]]XUՅ]]]]\ֵtNq�m`3_?%/mm�mmUՅ]]]]XUՅ]]]]\ֵtNa�m}�_mmcmҵ0?omUՅ]]]]XUՅ]]]]\ֵtN<m�_mm/mmmUՅ]]]]XUՅ]]]]\ֵtN�mP_mm@/mmhUՅ]]]]XUՅ]]]]\ֵtN(խa_'@mmUՅ]]]]XUՅ]]]]\ֵtN�m}�_mmثtbmmUՅ]]]]XUՅ]]]]\ֵtN(mmB_jȯmmsmUՅ]]]]XUՅ]]]]\ֵtN@�ma_o/mm[AmUՅ]]]]XUՅ]]]]\ֵtN�mn�_mm/mmUՅ]]]]XUՅ]]]]\ֵtN_"mt"_jVm mmUՅ]]]]XUՅ]]]]\ֵtN,�mj�_mm/mmo5UՅ]]]]XUՅ]]]]\ֵtN�m^c_m@/mm(m[mUՅ]]]]XUՅ]]]]\ֵtN_Dg,_mUՅ]]]]XUՅ]]]]\ֵtNpm]_m/mg8տoUՅ]]]]XUՅ]]]]\ֵtNZ�md#_m/mm#mUՅ]]]]XUՅ]]]]\ֵtND+^�_mm+ m`mmUՅ]]]]XUՅ]]]]\ֵtN;�mk_mmٯmO#omUՅ]]]]XUՅ]]]]\ֵtN.�m~#_'mfmUՅ]]]]XUՅ]]]]\ֵtNPj�_mm�/mmrWڵ5UՅ]]]]XUՅ]]]]\ֵtN/ƀ_mm@/mm۠mmUՅ]]]]XUՅ]]]]\ֵtNt�mx�_mm/mmUՅ]]]]XUՅ]]]]\ֵtN1�mk_mm/mmѠmmUՅ]]]]XUՅ]]]]\ֵtN[�m@_[m/mmڎmUՅ]]]]XUՅ]]]]\ֵtNmFmR_mm/mmޔmUՅ]]]]XUՅ]]]]\ֵtN'�mW"_mm@/mmmUՅ]]]]XUՅ]]]]\ֵtNFm#_m/mmUՅ]]]]XUՅ]]]]\ֵtNC�mu_mm/mmXmmUՅ]]]]XUՅ]]]]\ֵtNm\�_mm@/mmmmUՅ]]]]XUՅ]]]]\ֵtNrp�_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNcmOʀ_mmm@mmUՅ]]]]XUՅ]]]]\ֵtN5�m_mmA/mРmmUՅ]]]]XUՅ]]]]\ֵtN8�mu_/mmLmmUՅ]]]]XUՅ]]]]\ֵtNz�mR_o@/mm�mmUՅ]]]]XUՅ]]]]\ֵtNPc�mU_�/mumUՅ]]]]XUՅ]]]]\ֵtN �j�_mmfm$OhUՅ]]]]XUՅ]]]]\ֵtNY�mV_m/mmߋoUՅ]]]]XUՅ]]]]\ֵtN�mp_mm˯mkmUՅ]]]]XUՅ]]]]\ֵtN(mU�_mm/mm�mmUՅ]]]]XUՅ]]]]\ֵtNq:kH�_mm/mmܠmmUՅ]]]]XUՅ]]]]\ֵtNp�mI_lm/mm mmUՅ]]]]XUՅ]]]]\ֵtNZ�mq_?m'/mmcomUՅ]]]]XUՅ]]]]\ֵOggS��������'���B-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tNH�mi#_@/mm`mmUՅ]]]]XUՅ]]]]\ֵtNP^ԿZQS_mgpmUՅ]]]]XUՅ]]]]\ֵtN1�ma_ikAUՅ]]]]XUՅ]]]]\ֵtN_oڵ@/mm mmUՅ]]]]XUՅ]]]]\ֵtNH�mv_mm@/mmҞlmUՅ]]]]XUՅ]]]]\ֵtN��mc_oow�/mހmmUՅ]]]]XUՅ]]]]\ֵtNK�mr__mۅmmmmUՅ]]]]XUՅ]]]]\ֵtNk�mj_mm�/mmmmUՅ]]]]XUՅ]]]]\ֵtNp_mm�/mm`mmUՅ]]]]XUՅ]]]]\ֵtNbI#_m/ٱWmmUՅ]]]]XUՅ]]]]\ֵtN+.O�_mmu/m mmUՅ]]]]XUՅ]]]]\ֵtN7�ms_mm@/mmԎ%[mUՅ]]]]XUՅ]]]]\ֵtNԛ_mo@/mm`mmUՅ]]]]XUՅ]]]]\ֵtNfηz_mm/mmޠmmUՅ]]]]XUՅ]]]]\ֵtN{_[m/dm mmUՅ]]]]XUՅ]]]]\ֵtN_�mb�_mm mmkmUՅ]]]]XUՅ]]]]\ֵtNE�mT_mjҿmJmmUՅ]]]]XUՅ]]]]\ֵtNsFs_mm/_mUՅ]]]]XUՅ]]]]\ֵtN*ԿR_mm"mm×UՅ]]]]XUՅ]]]]\ֵtN�mp_z'YmӬ5oUՅ]]]]XUՅ]]]]\ֵtN=�mq�_mm/mm߀mmUՅ]]]]XUՅ]]]]\ֵtNwmE_mm/mӭmmUՅ]]]]XUՅ]]]]\ֵtN<Xֶo_mkmUՅ]]]]XUՅ]]]]\ֵtN0jx_oѬ[텝UՅ]]]]XUՅ]]]]\ֵtN>�mo_mom:mUՅ]]]]XUՅ]]]]\ֵtNP`Կm~_omoUՅ]]]]XUՅ]]]]\ֵtN�m[�_mm@/mmmmUՅ]]]]XUՅ]]]]\ֵtNfη]�_mm2/m|Ӄ'UՅ]]]]XUՅ]]]]\ֵtN'�mq_mm@/mm٠mmUՅ]]]]XUՅ]]]]\ֵtNmX+_m[m�/mm@mmUՅ]]]]XUՅ]]]]\ֵtNb�m~�_mm/mmҗ[mmUՅ]]]]XUՅ]]]]\ֵtN�m[�_mm/mm�mmUՅ]]]]XUՅ]]]]\ֵtN �mZ_F/mm`mmUՅ]]]]XUՅ]]]]\ֵtNT,v_@/mm܀mmUՅ]]]]XUՅ]]]]\ֵtNtFO_mmmmUՅ]]]]XUՅ]]]]\ֵtN8οmp_ummUՅ]]]]XUՅ]]]]\ֵtN&_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNmOO_mm@/mm�mmUՅ]]]]XUՅ]]]]\ֵtN3mՐ3_m/mm`mmUՅ]]]]XUՅ]]]]\ֵtNl�mJ_m[m/mmՏUՅ]]]]XUՅ]]]]\ֵtNQ�m}�_mm�/mm[mmUՅ]]]]XUՅ]]]]\ֵtN�mo_mm`mmUՅ]]]]XUՅ]]]]\ֵtNJTӶS_ml @/mڀmmUՅ]]]]XUՅ]]]]\ֵtN~�mw_mmMmmmUՅ]]]]XUՅ]]]]\ֵtN2UI�_mm/mm,[m[mUՅ]]]]XUՅ]]]]\ֵOggS��O�����'���r&:-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tN mp#_mo/mm׳mmUՅ]]]]XUՅ]]]]\ֵtNB�mv�_mm/}WmmUՅ]]]]XUՅ]]]]\ֵtN'Կmp_m�/mm۠mmUՅ]]]]XUՅ]]]]\ֵtNpm~Vmm/mm@mmUՅ]]]]XUՅ]]]]\ֵtNP�mn�_mmCdmmmUՅ]]]]XUՅ]]]]\ֵtN7�mq�_mmomխmmUՅ]]]]XUՅ]]]]\ֵtN:Կ{�_mmѠmmUՅ]]]]XUՅ]]]]\ֵtNP\�moO_m3UՅ]]]]XUՅ]]]]\ֵtNvF؀_mm/kMmmUՅ]]]]XUՅ]]]]\ֵtN=Xm_mmݠmmUՅ]]]]XUՅ]]]]\ֵtN$�mi�_mm/mmѠomUՅ]]]]XUՅ]]]]\ֵtN/ _mmGm mmUՅ]]]]XUՅ]]]]\ֵtNv�mh�_mmomUՅ]]]]XUՅ]]]]\ֵtN*�mz_o/mmmmUՅ]]]]XUՅ]]]]\ֵtNh�mx?_m/mm mmUՅ]]]]XUՅ]]]]\ֵtNc�mJ_m[mg/ZmmUՅ]]]]XUՅ]]]]\ֵtNNmq_/UՅ]]]]XUՅ]]]]\ֵtNq�md_mm@/mm mmUՅ]]]]XUՅ]]]]\ֵtNukR_M�mmUՅ]]]]XUՅ]]]]\ֵtNk�ms_o/mm mmUՅ]]]]XUՅ]]]]\ֵtN{Կ_mm0/m[ mPmmUՅ]]]]XUՅ]]]]\ֵtN1�mn�_mm/mmї[mUՅ]]]]XUՅ]]]]\ֵtNFm[_/mlmUՅ]]]]XUՅ]]]]\ֵtN'�my_o@/mmPUՅ]]]]XUՅ]]]]\ֵtNrFKX_mm/mmm5UՅ]]]]XUՅ]]]]\ֵtN�mQ�_mmu/m;ͿmۅUՅ]]]]XUՅ]]]]\ֵtNԿmN�_mm/mmݠmmUՅ]]]]XUՅ]]]]\ֵtN �mE_lo�/mm׈mmUՅ]]]]XUՅ]]]]\ֵtNymi_?/ym֐oUUՅ]]]]XUՅ]]]]\ֵtNƿӚ�_mmٯNmUՅ]]]]XUՅ]]]]\ֵtN8 dg�_mmm٠mmUՅ]]]]XUՅ]]]]\ֵtNP9ԿO_mmj[mUՅ]]]]XUՅ]]]]\ֵtNyƿ|�_mm@/mm@mUՅ]]]]XUՅ]]]]\ֵtNNً_mm/mmڨo4mUՅ]]]]XUՅ]]]]\ֵtNfJE_mmkm۠mmUՅ]]]]XUՅ]]]]\ֵtNR�m~_2[mٯmkm@mmUՅ]]]]XUՅ]]]]\ֵtN@�mu�_mm/mm_mڵUՅ]]]]XUՅ]]]]\ֵtNu>ˀ_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtN�m@_mmͯmdmmUՅ]]]]XUՅ]]]]\ֵtNC�mj"_m/mm mmUՅ]]]]XUՅ]]]]\ֵtNl�mzH_mmmmUՅ]]]]XUՅ]]]]\ֵtNG�m\#_m/mmmmUՅ]]]]XUՅ]]]]\ֵtN&οm�_mm�/mmmmUՅ]]]]XUՅ]]]]\ֵtN9mkN~_mmUՅ]]]]XUՅ]]]]\ֵtNs�mn_omڗ/RmmUՅ]]]]XUՅ]]]]\ֵOggS�������'���ț-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tN=�mr_mmm+sUՅ]]]]XUՅ]]]]\ֵtN/�mm�_mm/mmmڵUՅ]]]]XUՅ]]]]\ֵtN�_mm/mmzmmUՅ]]]]XUՅ]]]]\ֵtN)�me*_ڴmM mmUՅ]]]]XUՅ]]]]\ֵtN#�mg_mm@/mm7mUՅ]]]]XUՅ]]]]\ֵtN2Կm_oڗm@/mm]UՅ]]]]XUՅ]]]]\ֵtNԿmQ_m)m`mmUՅ]]]]XUՅ]]]]\ֵtN=d_mm/mmo[mUՅ]]]]XUՅ]]]]\ֵtN"�mTc_mmQ@mmUՅ]]]]XUՅ]]]]\ֵtNb�mx_m_G/m`mmUՅ]]]]XUՅ]]]]\ֵtNJ�ms�_mmHmm!տUՅ]]]]XUՅ]]]]\ֵtN�m_�_mm/mmoڕUՅ]]]]XUՅ]]]]\ֵtNF][_m/mmvmUՅ]]]]XUՅ]]]]\ֵtNP�moj_m@/mmHUՅ]]]]XUՅ]]]]\ֵtNI o#_/mmCUՅ]]]]XUՅ]]]]\ֵtNzԿ^_/UՅ]]]]XUՅ]]]]\ֵtN�mX�_mm�/mmߠmmUՅ]]]]XUՅ]]]]\ֵtN.�mI_vm@/mm׿o7mUՅ]]]]XUՅ]]]]\ֵtN �Y�_mm@/mmޗ?UՅ]]]]XUՅ]]]]\ֵtNƿmH_mm}/mmmmUՅ]]]]XUՅ]]]]\ֵtN �mx,_oo:/mӠmmUՅ]]]]XUՅ]]]]\ֵtNC�mh_mmCZHmmUՅ]]]]XUՅ]]]]\ֵtN5ֶm_6m!/Ƿm͝UՅ]]]]XUՅ]]]]\ֵtN9�T�_mm@/mm mmUՅ]]]]XUՅ]]]]\ֵtNo�m@\%@/mm#oUՅ]]]]XUՅ]]]]\ֵtNj�mn�_mmׯmmѠmmUՅ]]]]XUՅ]]]]\ֵtN5�m}_mQm mmUՅ]]]]XUՅ]]]]\ֵtN`_mm/mm�mmUՅ]]]]XUՅ]]]]\ֵtNxI_mm!/momUՅ]]]]XUՅ]]]]\ֵtNW_mmٯmmmmUՅ]]]]XUՅ]]]]\ֵtNBԿO_o5@/mm֠mmUՅ]]]]XUՅ]]]]\ֵtNu6V�_mm@/mm'mUՅ]]]]XUՅ]]]]\ֵtN#㏀_mm�/mm�mmUՅ]]]]XUՅ]]]]\ֵtNi�mR_mm:@mmUՅ]]]]XUՅ]]]]\ֵtN:VB_mۯp WmUՅ]]]]XUՅ]]]]\ֵtN@ֿ_m�/mm,mUՅ]]]]XUՅ]]]]\ֵtN&6_Vm/mmܐUՅ]]]]XUՅ]]]]\ֵtNPX�m\_j呯hUՅ]]]]XUՅ]]]]\ֵtNdοV�_mm/mm`mmUՅ]]]]XUՅ]]]]\ֵtN_m H_/mm݀mmUՅ]]]]XUՅ]]]]\ֵtN|Os m|S_6m@/mmڗmmUՅ]]]]XUՅ]]]]\ֵtNP�ml_Q[mK`[mUՅ]]]]XUՅ]]]]\ֵtN�m\�_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNwmOʀ_mm/mm`mmUՅ]]]]XUՅ]]]]\ֵtNs�mE_mm@/mmmUՅ]]]]XUՅ]]]]\ֵOggS��0�����'���m,-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tN�mh_mm/ҶmmUՅ]]]]XUՅ]]]]\ֵtN>�mz~_o@/mmUՅ]]]]XUՅ]]]]\ֵtND,_m/mlmmUՅ]]]]XUՅ]]]]\ֵtN%F#_/mmԀmmUՅ]]]]XUՅ]]]]\ֵtNS�mI_/mmmUՅ]]]]XUՅ]]]]\ֵtNL�m{_mmkm-@ mmUՅ]]]]XUՅ]]]]\ֵtN�ma_mm@/mmҗ[mmUՅ]]]]XUՅ]]]]\ֵtN,~܀_mm@/mmmmUՅ]]]]XUՅ]]]]\ֵtNcoƀ_mm/mmzomUՅ]]]]XUՅ]]]]\ֵtNm�ma_mm/mUՅ]]]]XUՅ]]]]\ֵtN8�mp�_mm/mmހmmUՅ]]]]XUՅ]]]]\ֵtN|mY#_mڵ1mmUՅ]]]]XUՅ]]]]\ֵtNgfZ_jbگmmUՅ]]]]XUՅ]]]]\ֵtN�m~,_/mmmmUՅ]]]]XUՅ]]]]\ֵtNgS_mo*ȯmmUՅ]]]]XUՅ]]]]\ֵtNE�mx�_mmQ/խa?mUՅ]]]]XUՅ]]]]\ֵtN'�mi�_mm/mmܗmmUՅ]]]]XUՅ]]]]\ֵtN�mO�_mm/mm@mmUՅ]]]]XUՅ]]]]\ֵtNmO,_om/ZUՅ]]]]XUՅ]]]]\ֵtNX�mu�_mmmHooUՅ]]]]XUՅ]]]]\ֵtNO_m`mmUՅ]]]]XUՅ]]]]\ֵtNgO[_joA+mmUՅ]]]]XUՅ]]]]\ֵtN(ԿmH_mm@/mmmmUՅ]]]]XUՅ]]]]\ֵtN �mT_멿m`mmUՅ]]]]XUՅ]]]]\ֵtNBm�_mm@/mmmmUՅ]]]]XUՅ]]]]\ֵtN}GVmm/mmڨmvmUՅ]]]]XUՅ]]]]\ֵtNj�mU_mmm`mmUՅ]]]]XUՅ]]]]\ֵtN"�mh_mm/mmW[omUՅ]]]]XUՅ]]]]\ֵtNFm �_mm/VmmUՅ]]]]XUՅ]]]]\ֵtNMX�_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNr�mu_mڵ/mmUՅ]]]]XUՅ]]]]\ֵtN)mx_mm/mmUՅ]]]]XUՅ]]]]\ֵtN�mb�_mm/Ӣm%UՅ]]]]XUՅ]]]]\ֵtNƿVƣ_/mmڀmmUՅ]]]]XUՅ]]]]\ֵtN|�mc_om/moUՅ]]]]XUՅ]]]]\ֵtNJm_mmѯmmmUՅ]]]]XUՅ]]]]\ֵtNCƿcE_mmζommUՅ]]]]XUՅ]]]]\ֵtN.�ms_m/mmڠmmUՅ]]]]XUՅ]]]]\ֵtNaܿ γ_oo5�/mmԀmmUՅ]]]]XUՅ]]]]\ֵtNI�m�_mm/mm?mmUՅ]]]]XUՅ]]]]\ֵtN<m՞_mm)m mڏmUՅ]]]]XUՅ]]]]\ֵtNNX`S_mڴٯ-ٿ@mmUՅ]]]]XUՅ]]]]\ֵtNW_mQp mmUՅ]]]]XUՅ]]]]\ֵtNdF_mmf/ ڵUՅ]]]]XUՅ]]]]\ֵtN~mb_n�/mm׀mmUՅ]]]]XUՅ]]]]\ֵOggS�z�����'���F]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]tNZ�mH_mMmk�mmUՅ]]]]XUՅ]]]]\ֵtNucG[m/m܀mmUՅ]]]]XUՅ]]]]\ֵtNP6NӶmK_mmqf[jVڵUՅ]]]]XUՅ]]]]\ֵtN�mf�_mm�/mm@mmUՅ]]]]XUՅ]]]]\ֵtNI�mw_mm�/mm`mmUՅ]]]]XUՅ]]]]\ֵtNR�m}�_mm@/mmԠmmUՅ]]]]XUՅ]]]]\ֵtN6?F_mm<mmݨmmUՅ]]]]XUՅ]]]]\ֵtN�mV_mm?/mJmmUՅ]]]]XUՅ]]]]\ֵtN.mmӄ_mmUՅ]]]]XUՅ]]]]\ֵtN �mR_om`mmUՅ]]]]XUՅ]]]]\ֵtNc�mk�_mmïmKmimUՅ]]]]XUՅ]]]]\ֵtN�mU_mm/mmo7UՅ]]]]XUՅ]]]]\ֵtN�mQ_mm/mm mmUՅ]]]]XUՅ]]]]\ֵtNz,_m@/mm@mmUՅ]]]]XUՅ]]]]\ֵtNU�mH_m[o/mm`mmUՅ]]]]XUՅ]]]]\ֵtNkF|�_mm/mmI}UՅ]]]]XUՅ]]]]\ֵtN%6m`_mm@/mm@mmUՅ]]]]XUՅ]]]]\ֵtNe6mk@_mmmRXoUՅ]]]]XUՅ]]]]\ֵtN,�ml_w[o&/@mmUՅ]]]]XUՅ]]]]\ֵtN-ԿmX�_mm/mmmUՅ]]]]XUՅ]]]]\ֵtNZ�mt?_m/mV@mmUՅ]]]]XUՅ]]]]\ֵtN(mh�_mm�/mmߨoڕmUՅ]]]]XUՅ]]]]\ֵtN 6M_mm�/mmܠmmUՅ]]]]XUՅ]]]]\ֵtN֚J�_mm/mm mmUՅ]]]]XUՅ]]]]\ֵtNk�mK_V糯m�mmUՅ]]]]XUՅ]]]]\ֵtNRa_mm/mmڗ]mUՅ]]]]XUՅ]]]]\ֵtN"mKj_`mmUՅ]]]]XUՅ]]]]\ֵtNBp_ůmm۫mmUՅ]]]]XUՅ]]]]\ֵtN[�mo_mm/mmmmUՅ]]]]XUՅ]]]]\ֵtNh�m@_mm@/mm`mmUՅ]]]]XUՅ]]]]\ֵ�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty.tta�������������������������������������������������������������������0000664�0000000�0000000�00000233262�14662262111�0016711�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TTA1���D��z�)RLZ��RZ��Z��'��` _��������������� �������������������������������������@� ��������������������������������������������������������������@����������������� ��� ��������P���������������@����������@���(@�� � ������� ��������P@�� �����@���������������� ���� ��(����������(��� �P�� ��������P(���A��@!������ ���� ��� (�HB%�%B ��D���@��A����������PPP�`�(`�����P@I�P�P������������P(�@�������T�C��@�!PX @a� �����@A����&���� d(0�0 ��L ������������� ��@&" �E�(EaPtP`L���LT� ���E��P[������2`����0�������`������e����e��$@Y ��ˀ ���d���0lr1���I�&a2����`������*�� e������e��TI��LR6 �e�0��������&P �� �e�Q[����0 `�*��������&������`��eT�`e���0 ������` ����e�2�*��0 ������P6 ���0 ������e����e�@��� eP6������LB52���������P�L�� `R\!� ��&�@�`���`����(��� �&�P�2����jL`�ʘ�e�e� &�����Ap�0 ��P ������0�������Pe����L�� ��� �����$�e�0 �� 0WRI��(1a�(���I �dP6L� �C�� ��eS �����I������IL�0 Pm�2lj T����@6�L��A�  ��@5�� 0 ���@1���&(���I��&P0PF5&6$ ���(T������0��`�0�!L`� @Y5�������ʀ2�j��d0$����P)��ee!L2j�`V �@0P$0�L��L�e�3(l@�T���&e��L��ʀ2�`(cB����(eP �����L� L2 ���&@5 ���PV@5�՘���@P��( �2p'!M `������ �����I` ʨ��jE e �( � $����P�! a�`�P6\6'` ���*� �e@���eTC����@�T � ����P�@A�0����`k*aj�P6 C�2���� (����`!@ �0 CH`@(������ �&0Y���� ��2�P���L`)�����j@Ym #C1��($ �������(������ ��� �<���& (S`rɐ00 ��`�00 f �e���PF2j%L5&����P2��a�$@x!R2TP������`” ������'���0 �������&af�0�LA�PV6 C`�d���2�ef&$S!L�! L2l&I������T d& 0$�������0�����A�� a3 C���( aC �� (ʀ0����A�� abʨ`@�! �������� ��(��AJ������ �����*V-� ����� ��������x&a`Ն0@��0�����T �0�0 .� �����a( OQca��! A`L��`j ���� �@\m$�&a��A���(������0!yõ2@9aTl���\��0! Pa&a�L��������@6Sa��IՆ0e�I0$ a�eCLj#a0 � 6\m&eCjL2d�Ae����Pa(=̜ ����- y���( @5�0�� �a ���d�$ a A23��P�$Px�2&'I�� R6L2@! ��0WIx�P)0�P)0�՘dL�.d!L��0$3�Ax6u{�0��Pa����` Pa��������` CH$ Cr$Lep9 ddM��2 d0��pf�A L`j6'S[ɐ &$3����x8<$O5&gAI�PF5D0�2<'3  �����@6!L������eh 0d g&a0 C�A2C0��I1WIfH0&a�۬$2Hf��0eaA2C2HfB$3\MƩ@sj.r3a'dj�*aN<C���PL jC yS! @fN23(Y �mI"0 @$3d\$p2(0jF L��&0 rmd&��@$@ٓ!��� �f0 ��%0a$ aL&a83�&a83d3�� 0uIf!y<3'pm&C9 s5!L2 A��P69<a&gV d&��!Ppx8 �� ��(0S0IL�ʆ9g����LIL�9IF9I@"0��(0Ix8L&@f������A L$L����&7N A'0a83$ g& <O���`t>@pfRa298$$��\md& !0 3C<Pm$$$ 灘D���2HL�&arF rQ&&~֖̜@X��A2\M !g&���0 3�����&ŕ!e &C�&arf093PTd̄ ���0 3I�La&g&Lhd$393��d&g&PIQF&a+o)�Wb P Ù A2 j2uy��L 2L`|m d!L�d&g& ���0'$Hfr2ə �Pa @!A�0 3��2 3/<ʮf2d&咙s�䩏;9AJ4��F2$MKy `reG'�&ə��ᓧ!L232(f9G������`lO MNq!$'&��Ro9m9J���L.{5GT$Dd&'�������$393�����2yΙ !a�ZkkC!TPcAjfڮ�$393���I3��������D$Նd����0LXL'CjCxIjcΜ#!�fCޤ<M"cEC�����`LL ����(0\0\a�dx>v &Pae�sِ32O29 �3`393�� 3932C���(0 (sِ!L2C��`LL�L_z83 �1W{ ��0sfG� ����ˆs'A��������PA!��2��030sfC(0�&dR3?*V'3.0�`LL�0�`LL�������"!�F@PF�j 3a&g&eLL��00L0m0��@V M 3 $arf������d@ ?LL������03sٜmɑhE$ j$<����&��rf�e$<2sL"�2�����e3?G- �B��*Vl8<HxfrFF29�PFC� 393`<<L"��0jgC >[�T0_#P  O"@�&Ϝ?ơ���$#E@593` y'�&HxȄarf@8g�@ ZAC��ˆs~03`.{nYoMjbO1Ih`1HxHIyGʔSo �) *P[$Rf����0ayQA֖I����� 5C03'09y2sddp ��eEϜ3 3j@����`LL��ly8y+ 2,19����&kLq@��*sIms>A0wX՟)y&�9<�e31VE�BʂǾ��0fRTQE���|HF$p W6��!DTE`6syjD��L2GWA`p3g 3� 3k38���PFa,�2rxC�`91G@�*:L9�PV9aʨogY&y �`99@�� -'93���(#gϸVU��a''�(#0a愙9}Th ����@9<0 3���ԍ99 G�sp>!-a@mYTa1hc@f�M*"�`9H9zpH1V*"�0 V{sO*B��cA3g#3j 0s&��� 3��ϸH|8 �jTj�PFg8g&��f|au#HF|1��`朧�����@F�@2b 3gN0)?xUi10 �j��������Lxʮ8͢ 2B͙�L9̉����(#�fy&����@9<dv&y�F X5B�@3(bD@3'`̙��@9<L9�&|`@dppŪ!UG�Q,f<Kb 38"ky5;0 ����*c T��0a?qU#�@5u)2(ja='Cc$ Fx��fy*�șt;<P9<�P|C̜z �P9<��������v&DI3���`2·*.#Rgl;Ҁ` *,�d�Pԍ*(0C|mO>8����33@KX5B�B& -@�'̜3HfN��0a?:NَZ5 ���$#23���&68P6uC"F08<3ϙ9mcT8�������(#�3 sSC9aΜ�����&|15B����H|ꪣ1h`A:8U#77Ta@�,C}C��!���0gfU" ���fy*���X>YF�7=h��,H9<33kGc!����������9)sN�PU#Z/wi@��d+VBD GcPa@0V"3u�����@9<!���V9a�90ảap9Yi9$/w` 91�0F¤vvT!��PD̜��<�@F:Xe *زKPĪ *����F?gOZ=0R9fq]�b�BQ#9j޲ !b0mLErV2"Ӟ5�cPa@�BUqs �TPul€` %WI��f> �&Z9$����$#O VcPSvcaĪ)��aŪ�� 9c@3Ta@�� kTPFGle5HAԤɁ"T{$X 3, @c€�sx�����0X��PX!��Tg�*xS3��$0`3\X5B���:P���*X砊�� |  _�Tg���$|?V`;Ö:U#cmIP'Ϝ�8jy@�"\�<35|jLD 膧p:Aj����:j�����@OcU������|Xkݡ[vV �Lnha1Ta@��������1j � c,g>F,EDYT@��������@��UJM380S?Q��0bPe\!0*Y]u����z:kWX51t !I� |j"V��P9@@|j�����q,ޭ5z˂00b���8Y\8$����H,du1!2Ta@����RiF nIa.0 ԈFQN91joX)ӌ}*�����@8ͨªBTxge!e#Vh\X+}v1"���0.�����@���ƢbU:Jƈ߆~ْ,V!eXC%Ee왾# U81.=eF)[0bmg k}<֊*`vb@eBT1�F!rh=fע,H�BjCFa@c up:F L+1@����s!pp:F@1ƥǸjDc�������U.F����`q:Uq@08jfb@@#V�2I7U�����s9 �HVy@̧F�PI~V����Lx3.v*E@#VO9��P*FŚ~e*D���e3?��*v-!n3ŀB>�T_݁W|U)SU����dE@F`RMKc������*XuUA2ƨ �. hqa( :B����@c4PW}��0Zq@e:x4U¡8b1i���0����,p�bExY �����G]1q3wiBWLt1+-U:VQjBj���aq@�H8!uXx��@c|spr1jDS.<\ZA����@2QXc]z !��d#s�q.�񔍺!X5B�`CF��� ������u*~jE ��V�����R8" u#X1b1ZXcTXu#V0b)FĪ#��0ƫ>UG�������d.��T/ĊUhĪ#���@2Q���`1 �`QGisAxueP+����"vz�0b1 #FD:B!XuW}ejĢbغB4"ZCVbĪ#��cXWP+4Zq@Eօj�`6u<$��ª#c�����(ZǏx@��`q������1*jq fU 놮8bF}YUZpuQ8 h *Vz"b`ݿ^ǃDH(вA0Z11Պ��Ʒ ?LЪ1` ���1��PQGh٨/)F\uDcvWQ8[w>������@F)T#P->���HVX1ԊVQ1�w?E�P,���UG#C8PD#uSp@��0�18̙[!C-:úl}AěfRa8 ���P[X8V �,C[Q8 ��c Ke�EUG������q�$cq@@G#z Bj]UǨ9]j��XZq@��1 ���$bQu����@Z������ uiv jq1 @Oٳkǵ����������R85pu���c���QWjPxG\#pB(.A٬Q/:@����@(:ЈƤP+���cq-;_Hň=h Z eYX�@(V!���Fak;ή I���rX+��j1 ��������dG]PQnp@0��TUG4���ԭ~UG�� 0U#d !�ưVa8 ��R7 1B�Pţ�cUG(v׊�c9 � ,����8"����ԭO0Ckq@���W!� ���5j#�p�c��TT/��������?> @P֏ǶQI)aX+�p#c+~UG���PW!���Pţ������d����8����@2.U±�:<&Y,���@ l"H W!�1�����8����Rt^Z)U^g򺆩‚P:.]W�LkEP���� uG���� uO^:B��DUG kŁa2 Z6֗~DafUa`+N!k  *R0 ��cX+ uſǰI ��P \u�����$82g]-z6~{wĪGȲA�����@88 ��v=õƈ=/cmg*e/A+f&$$W!����Z�d<B ���PcE6~џ ���XEϰp�,h����8Tz xMl1Z�����4;��@Qq#29�6RƸen=HT#d֊�2EZq@�������@iF= ߺQW��nb@o Z��a8 ���8,Zq@aҳ8D�b8W$����Y X8i8в֭B �WcUX1p������ jc\2Ep8 ����@8F]u*:B���QqiZӬVP !�,:k!��G-L��8p8Z8d!v]r<`D������(Qu:B�����u#  ƈ^T�ٱȴp�an]rV!XA֊ !^xĊb \u�����Hሗ~K#qb0rX+�,ZcUGr��������:.qq@�HQ�ԭK?��QqS&��`֊`�����:C8f?_r"�`1&aR �� =&���JW!DĮƀP4ƈW �� fZ+4BZGq[D >A�í  :a8 ���Haq@p6T;a8 �ūZ Bq#8p���@E]<Ł���]z<$��a-z1���>^.J ��0!:B���dG]l 1�1x@eX+���><ZQN.v ����+EAe���c!*pu�������Qq�qFD#d֊Egz|�����������P. Xq@���:.���5QXqK?eưV`,^8uUG�_*:$Ue[I/6K$�p*Xqe@t_0�����0`)99@cXk ����PS֯gkAE\ ʀ�@(\u� ޺c2,�ZcD#֊��qiVkʼn ���L6;UC,Zq@���?`D^,H������jq88Mz\u*\uu?TRZq1G8pA����KCF]:B@Y ��� W֊a8 �@~PX,cX+�Bq#dY-㡫��!  P4U9d k0Kc#��� zv4'$a%nY %����������1 e!���N}���Q۳p8 ����@88 XDZ��Rqaή]C^H�Z1ezc$�){ ��,Zq*<jm@T�l+;] 1bhX/A��������@?ho@P€d z- +0]!��m6_rBBARk4 ��Z,Zq@�� u<-k!dbʪ^l������:q@��R4;+����|z��Lk�Ԩ :B�B0�� ���l ���Q- - �DNJ�RDZQ8𪃲:zj����HNJ)ƈWİV�2]��TW+:N1 hT ���^u��QqUG��������5c!��5«@ŁW8#�@(|z���Xq@���@E]|�� Y}݅z|UUh( h���`֊���F]|�겻B� a_X8D (z=rY"���:158#�@=;��b!P<5h7aPX* %�0�����:#�*/�������"x %`�*�Z,Z`֊Zƈaw c`:N5Y㠊 {`Y#*h6SfW=U � ���� uG!*F |zŁWfܸ.:he �������H����d<zAuU�õ� z^۳Zƺ�������G�Pu=BcV<@�z� ^۳x-z�Ƭ+| $�0�1,-ZGpx@���@Eu=B��cV|H^յ����W]9,Ӡ@K�KCsVh�������@dLC b%Z7+P@o{CxOt���\r&�@I+4�, 2R(Kf^l m[5' Zdi Jf0%Yl  6H��`L&ӲC^a ,6� i��MK@���%LPPvԶpM,6�1x[` ¶ RRSrk ����8G������Rk<�ŁW=Bt]������(QaVhN޶$�$hL^$���� 64K@������ 0-m ��@P/= $��0eI�0R+'+AѮFԘ0���� fв t]꺢gj�����BcDt�����PQ��������HqKʵ %M  ư,P0`��@PQz��1 u<OQp`8@��ٯ%�(-1\1+������:>(1�U ŁW=B���� (<ǵŁQtV=>BCFs&Aka� V@���0eRHXa E=W�aZ@� (  @ AErQi@���KNtDaZaZ� ���=ӿ.I�` [e Chia��������iIi¥LA0v2à0�HdV:B���p͎kK`xK ail��1P%='U�����@EU����d<>r2���r06,opƕؘ֖p=ņ 2&����� 2ò Qh%i�jg}g �p-i8UA8Z,ed!%$$������� _:WRþ3(P% ��8Z\.qZ@��������P'MKaZJ`iZ@���ƴ,iB++.d�`L˒Ro`�����ư,i` .7,[dH-L ����@09 �����@Ppѐ.e „K��XiNVf ,a����L1ZZ0ۮXW ��������*/cLж.6�( -&r$$� $���� ( MI-L ��@Pp)SJCPvG]q���K@$ ZBI@̘6<������ 0-npeP� .eRN'XYka����� 2%Mi �r �(eI�cZ! <P� #vl.6���m�����Dƴ ���D)M���� lP°iyii2`DqV E&֊��` 5����Jԋe"eI�Жa +oh2ٍm;L&�����ƴ,i���„Kw [!f ���Aa¥LA�����il `1\)xVFޭ4㤵0Y`p) � $����å $�nvL��TL���� 2*Vm逢ƈZMxX3I$���QW=B���@HK�,LpT ITs0�⤵0��Ŵ,i����@IK� }6|6|d4f ���\iJmAՆul���,eISh/K` $@Fl6�X Kxp{-1.p [vL��l`ih4�YbC(4f �ˆm7: $ U @���  i]mKzM z_ڐ�Hhr������ІPJ�@2iyii�����cX4A��cX4A�� l0b r �p(_VA[vIr��0ee1 ��ư,i���hNM�������Pb �� (CaY�������(Ulր%LJ+:B0@|hXD 4f � .6lia�����cؖ4A�\biT,;ކv57m7p" ��@{M`P{��( �����cؖ4AA9&����T/ 6e$��00�(a܊@h- m&۪9Z@���@aؖ0(m � (L`M$���0mI�a( [ ������ mK 2˵Z"`bl!Q]d2і; 8YZ@'K uܰ|=KXKzl8lia��@X1I$*m f^mGl[e ��� D��a[+[ bؖ4�@(/ i&����X ے&���������J*[8CP(W9j� wr\֦d��a E 6I!=H�������ʆK9!(Lv  n6! ���`1may9nw@0[ZLD�0ae �wU_(LR�\l"!.6�������� 2�������J(ndpr8d76bF⬁D 4&.6�������ʆ[90mI����Q-MH 0^�������(lC&ʁ���������%$��Z&�` ے&((LiHfP(؜l- �@ n6��0F{KtLpMH9Z@�H`6f ����1lK ��������B8NC�cZ0Q3(4�����c4A�ak0M$��„Wnh:/- ��������@ն>P�`1lK� vb ������ n�1,ჼD��,VR ukhp�[9LTa0`i)QM$@B3(4��Ű-i�X ے&��&\dwж0� 4%l"�����Ű-i -Lp*7,[a�8l- ���Jp+ ����� Es[APex]������PpXha`ia� '(�Y7' Km ��Ű-a��A:L4lY+ ���% &I .6���@(iZ4 wm)�H�Ts0��������������@T M� S{ne"8�,rR[9 l-Ll0&�P-/vDL @F\l"�bږ4��Ŵ-ѹA6������@T e [9���i[B ʁ�������@I!p&����bؖt���8,�@([wN-F0Le5\h`Zh[@������t _^Ʃ&K"XL"��&DbɾX-K@�����ö`H������iۆN � �% ����� q؆  l(g߶�`[4C i,i�0r ���������@Ib 0ʂԺpY@&H .6ٲAi+@@ .6����Bce; Lp Cm0$P,K@����� pM ����pM �@NK:�(%djQEs-i���@(ရb,CUs-i�������Br8$DBUlX~{Zx����@Cyi�eI#L�� N M"2��������p[2���@([9������8lá`XH �ŰmY 6 ö���B0IAscӥ���������,muϠQٲ���������LP(�aۆ99† |ML$8X4��Mp`ۛ2:% 2! % �@Fd"�@(�/2�b[H�����`1l0%"2l{`8X4����t-<A�Aۆ'y i,)lYҠ4X4�Mpsstit& �����bڮmK9e{ &D�eIdit& �������iۆ B @�������ӧrvvcö@q�@(H�ö���J8E �����(\Yl[�A �J8;\~a7wJD�P0esܮsTU2�����J86- "dt0\cpit&79Lp�'˒2& �="D˒�������()C���PR8E ��B {`=2ತA= Am)ZyR������%B nda�@qƆmK��bڶ4An2!M% ��%p+d"��Ŵmi=6H n2��� plY ���%"PhL$@U4(v Fу{Ke .,d��H,K0[4��5H+-rö`L$�������%Cbږ4Mp&x%s79ٰ]Nk7H�BjN[$����B @��,mˎ鶤AhrLZD��BIYl�2& ����`I�ޕئE>- eIDPŲ0��(NeHa/ Mr)d &D�@FDmIazyɎeIIn2MC{+)0+G|Lla 8/0����:&u����`i)6M$���XLmJa/L &x%+Ҷņ4ؖa���֒[#Jc P���������@(7ۗ'ى������tWXTm0؋L$���*Vm&)9Leܰc-!�����*m �IWH0�(NFA܏Mvq0ۙ!ܦC0!) E$"���������xaf n4@@q,��%" d͍]mI8 " ��Ul/e"vb&"���8Z8 " E2CF�������� ӢJNHF�$]4� &xqQd8X%/A@"!,qodI���`/2L�@qU9,���]/-f_`m�����bld/2�����xBIx���}$t0E&� K4b- ��@(7hya0(NzI0VH���@qrY����`1]1ݖa������Axކ#L,e [n6d[Np*0^d"!I`H,̮Zrk$[D�������,6Mb, `a6@����@ͽ ����fcn���( E&���� (Aod[������B7vx[B0Ds a%k68ФA"��.0( nixD��������� 6:2 `�������Z8mӄblJ:ȋLQ2 $2��Pl0Y5Ǝ$%jv6"!I^d"����@(ZY` C($/2X8ȋL$��e0���nξ VXT{)\ai ������ذmtJPA -L"�Eia$/22 7l[Za/d[Aa-1-��������@(餗v:U Z1/9Y8ȋL$��������� gܗ LI���%&`[������ i/ vXQa  ��Y8ɋL$��b-Cx, P�vކ�����p٦A؇(� IkYfWh��@V� d%T]g"; Ha����������"^`4dvvA$l$��������re æ)�����0M�X m:48ɋLFy�����,ձryDq- �@FrM$�E&{������`1\i��@(78}DqrKAҭ E �Y8ȋL$����������� ��jt& �,6Mbe\4�������jM ���Iov]LRWYD0��������D5N4��������(CD$r. ,A 2 b�d /0������� MOD�d /0��X caH"�����Io8Y %m^iӲd/b"��� y'2 ©\ 7H�����ݒl ��� a5H��t:6l}$h �d /2�����e&����blA������%v%p7:W]lJ8ؖvc"([8#�����Y8ȋ:m&)���tH�2 A^`"��%&�����������ZtT)p e��� tľ\`J"m77\.าPZd ���J: &�*$/0��� o ::D6݀3Ă #���-"uLnE`;~ @��m������pڦI,6:/U��������f)-}F����M򎛖] ����mnd���Iv�ti*/0����� t^a�% n0 ��,2 b���M� tW$-2HK����r$Odd�����*L%;&ݱr&%+)} IL×)D �����*w����@qRdSE���nKvLn0��P;~>@AѭMU\a��� e ��'*M x H$�Y0dd����`aTQ&������Io8.2Lb�2Lb�����������(NGFcr[2LPaS y/  ]0]\".7D-` �����bxLK{Jo6����Pҩcp[I ���,6Lb�d`/0!^ʳBPZdaoidaFeZ�������AI'"? nvIѢ*%@U0,&2��]6|}Y@����Xna{9| dQE9���������@VvA����@�@T0r h8!V2|/`"� { �@qpYI� d�����@(Tr& { �^`"��%7 #.0 �����%=`"�m& '{*.0 H]K2c/)hoؘdV$3f6IdRB��� `[b "s����S{[��`1ܶaS\a0þa(pe88Ld��2Lɾ2 p^nɤA*FCѠn$_(Efb��8T$bibFϵi [@qrYarYarYI ���, &8,1e& '{ ����� pd.p4W^)c_QR8Gb�������X m���Jz(_(^P(`"����+ E2��BIF)MD@N��� &2��* p7�����, hi 4zh{GU$BÙFXn !d_XE&AFE0,$e�����)Ӄ}#G6a �����T23�f :n������A Д2��bm : d�dt7��G"9 .6I$ ��������� (‹Md���vT| i-'H(Ⱥde&1����pۆILq>7C3�p^[$��������������� al LI @qrY Z80L$N� �^@������/}E ��G$LqLD`스(Cy1D 1:������/و"A,U4vMl!m@@B v022������a 21222��@P ibӊ;$zE%XKF@�ƙW(1����EeR`Q 4goi6 !������%</0�������v6,Q\AAF�2:Ҿ, KD��hb;H�hb;H���X6A4XtY&*6J $������B  $�Jx(_`"� & 6M"��dt7����� (| M�@(ipfs"dt77wK3a���d4 5 X₎jd@8XtY&BF{`U29nA`����+CE[R.i }QlA!%h@��d�a���� t-L`4(.h`\�ɢL$���8YtY&Ee"�Pam$6M"��� P����bmнVq}IMY0m%3f9<xS$P;-���ࡶœ���@%D$dI~ NaWHpmA`�U2BF���d4 de���, NEe"@FdD�Ja՛(�����������A[2 V]`e8VͩDL�ѨYMȈ�����06 i 0`"jEB������@ٻ(qAwQ I̊$I�hb$#�������� ��@&�������xx7�(6U2A H�&ԜeD@q6MNm&m&.Б]bH"���dt/0 ������C&�����������Pr[mY i_YD��������B a~M| ò|bo @y!/@I!��d2*.Ht$ڐuD���������J @�20m$�������, .cY&[e"����dP};H{Q$ P �(Nn]6e�P;%:ʊ��@qABg:` ���@,MF$ `.������"x(@qXuY&�����b0t$8yUCi�'mD���� P�� 3|," ��rwNJ_*IB�����:_Q$bEB�����'.D�������@{fp�(N6]������������PY4Xr&1��� ��S'@(ɾ .im&���4I2\/Ib@v$8tY&���@Ez*_@ ��X /4���@(} ����������Zةi$�%< (^i��˵[v or-</p QܶiP����p(_hn-) �����vH6m$Bʗ0CR8}A  ����������`1l�@ v8x(_@���������% N8z=M{GF@�d Q0P `D������� $����XL/pj���@2RxoI tbv ���'.D���������@]> 6Q$�� @����@Aإ Jqje"�Meb@/4PlA&q,q+��Pl,���`T| ��v<2�����d2�b3b(�� FŮ4opqİH(S� d3I!��@</#m<B7,D�������2�24���2;̚ ��� �(6UѨ`R\���p;8 ����UeJSl,���tcO_ N 2��T ݽ8<i*WPK&£;a7HHf"'[s!]UE[IIPܖP~a3 �нetL{[I�δ_Y P.4��������()<7( VUlUŎi/H����������,-]e"�@L( XU٠Yףr~ro$3`pQW%I����(N۬~hnHF"I�������� (}����]w/Ad"@"ݥiAG񚅈�@ �������� (AqC9VU ��P,��0 * |ShmKw�������������������������������������`@�������� ����������������@�����@���������������������� �����������������D�@�����������@��R����� ��� ������� ������(@���H � ��� ��P��P@�P@����P@� �P @��P @��P @@��@A@����@A@@����� � ������ �������(@���(��"��� @�P2@( �����"@����� �� P������@EP����<$�����``�(�������@ ���&� (�����I(�L��`.P&`P@� (@�L0�TL��$� `&�� �)&��((��L� L�����L���\���& �B$���$PHQ�I@(�BT�(@Q���I����D���2���$��0I���������������$e�P�L���I��������`������(�&��$Tc��&�$��$�����e��@��L��@Y��P$ )*!@ L0�ʀ2�ePm����I`������I��&T�2P&2�`0���I��2`�2����ee�C��(���2����������&P `B�����0I�@YCj��e����`B���A�������0 �0 @@Y��P&�0 ���$L\ �P�����L�����\a&A��2��(+��&)���������2��(&��@�@��`������$Pm����(c���(c� e@��e�����e�����L� ������������P� ���Q����e��P��2$����������������eL�j���P$ �0 0)j��e�LP�������P��P����PV���0�`�j�0(���@@�20������!����jS7 e����e�e�����@�����s5&00@��ˆ ��`�`C@5 &�2*Q 0l@��LB��&�(C���$���02���2�&�I�d������@P�LR���$e��Ap�( �e�$e�PP�eL���e�������L�`��I����� ����@m�0�0 L2���L���`2����&`�j���$@���(�Le\F2 A�0L @!�$�L`e .0 C��I�����$ a������IfHsjC��@5&@���������P��2�22��!Ce�������� ��&a3 C��tld��I0@�ˆ0� ���P$LS L�” <!�0��La�� ��&�����A!<02���PF0����e��e0@����@R[jL�� @�@e�e@������PF3 LIfr$ L`O�”A1 ��P�����������L&�0�P 沙Dl  @sd2!LPF�l�`I2C�(+� &�& ː& $@P�2��P �& L0ʠ����P�L`Ȑ!0x&a���������&a&j0 3@���I.@ epe&�0! ��������e�e���ee��@0�( @����&aȐB�`�&a0 C��(@L6� ��� Vʊ `�Ɠ̐2���2! 0 I������02�Pa&a���0 C�'g&a�� @m\mH2dH9:m��I:6 ���eTcLL2e<9$ ����eʘd&a(-a1AƄ TI0 C�� �Ax30!��&n2@P@F2Ԇ0���(0�C2��PaA��ʨ�(+3aC�<'L���ef������`$aIp� ����Pa(0O\ !$ I�0�ȀIxrx������ ��2�!C2! ���A����&a� aq51!ruI ����&aL'0�0!< !3''gd̤2�C�tpj! a��eCjf $e$Sa�$�� ������C�If������ef` a�1aP pe#=H2Lt&a'[0d0@��=-?\l <$@��d��LL C2&gjsxr����j#@0 ��� 'Ϝ@y0�� W{o$L0��L! Pa�'a �0! �A8 Ʉ2Hpm0epf��(Pm8<&@d�@f�0l83$L&a�����L L �C9 ̜ L „!<A53�(0��(d&���Tb p9 ���L&������@Y� �A2 px8L��������2H2��-��`&xrxB!LS a���L&2 3A2��(dd\mg<9HAmrMdo0&�� v0b C�� �`Bm�������@$C v 3 �0A�\6I�`CO<Lj#0aAB!�P[&�L`L2 &a@e2���23���fns0 � L 2��0!3�&aL�(0��& �������eCI3' A0 ̄2d&��L�3��Ifrf����� ���Ն���2CdgN3�&>-h�~83I0a3� ���2C����lIfNmpmZ4Yѓ;} ��� ���2ld& �L`rfe 沙 Qm8pF9Yo3Hǜ@f̜D�(0\Ɯk'5@H�����Ajeo<&����Pa&��@m Lj6�e _$S8sĄ0a0�� �@Ն\ouc& ���\6� R$M & ���L83$<93WdA ����ee!�lL2a&�0ɄI�@0������j<uc30[2s�Pa&��PL ̤l83ϟzC  0���L`8R!eE3`Ùp90�� ����d0a3sI � � 93&g&��frMadI0(3�`d&�L<EzHI����d&gd&g&���$393$39:e�C�(2HfSo$Zs'g&Pa&<GJ$ ���P!3O208 �AY ������2HfB$3- �fy�0a&gm� A �����A3<G�P!LL��0<S��0Af'yr�&p$C&<$DS6u3sI21`LΨA��s'̵6L2s13&3&$39� ��&03��`R6C� rmde���-z�Pa&L`rf��PT+K`323�mW |#��B0�(0& 3g&������� H`rfRa&ɇ���������frfef��Pa&���Lə09h$Cl>30�Ljxe L09l9g&�L$m���L5HjAx R3 !���|<Gx2HH  3930sF7&y 2 LL2�l>BxY�� .{8<*9(�Hf39Ph��� 3\7 <L a&' pGR[.#ᙃD��� ٧Y<s0LW� 393(#!�e9DE4bHx 393- σM"�0a&g <&$ 3j$<�rf��Ly9!LФO>I�������(#!���vL���0m��� <� <ժZ3?s&(#am'm A�2j 9r5&y`LL�Y[2,<9������P٪c\C�*C[z" ΐXɁ�0 I.)(;d��Hx&L��ʸZM6"frX[<g���LL�2eg��@y�(#g�ə 0\?��� r<��6rx&�m|`��PFkK0L�PVL�!|x>պ#(��`LL�03Cə ��������frf39303�0sݞ ��!���Lə ��P6̫}<0g 9h@��$#���& v<bDfI?9I��0sA0aL"�0\ۓ�I DM&<yK660 � ���HF PaXkE�����/{ҍI�fΙՀ2rxH9<2rxaP;<8VaHŐ2@&̜ə 3 3hEh�� 3&v08Lɡ-׆,w0x2H4�����PF����(#Lə @9<�&<Cۜ;x0 �����`朧�&<<|V48< 3 3k38`Va=#�ad83��Lx8X!�$#?3gI��`Ǣ<D`.6c` *E �����C 6!ik=U��ƠY-;Ġ€@2b!L&-|pU11v)b@U$`&1s&�(#PF?ayΧ.BDM"DHז<8�~p@��HF 1Aoc! d\Q:�j!$6 $#dD��fy&����PF(#9OE9<�����0ϳc,q@�Bs���*y/����`1c��@qU#�1X�kzjYlB���YA֖z<��@œ'!3����&3s3�������2𳎑fRa@ɈÃ`23gNÏ9��a9O�dd,;gR`1hp!�eeϵ�BQ(933<z"����ʸn59 &!30sL���(#'A93H=Gΰq@Ơ! 47"��HFa"B;59�IPa9dD0fA3gp28uÏF8j��@:ƪ2'd 3'��0m>�*L>GlTa@�����d9穠lɧֈŠD"3�(#_dD�f}Id<Μ����0sγ���PF|5B��`\ӬT6 -UDZ,BC����T#":@ �am$͉�!0aE|g%̉����� 3'393&1�fyXR&������*y'A#ԨAq@�@2rxO<$�0F .l٨_ !*eRa@���$#|���*RWx����T!̙�%QnMBH^��`g"ed;z0 �����P sΣ >bƣQ̜���0sΑr� F5 zX�y?8 4)bD Vb����$#s+u 10 Jpu<bUʤ€���!ÃP̣p@�̈B�ɈQ?�ٺ#V��!Pw\������(#����PVP~~"FȚ`& I�!����&̜C�z`,VcB5C���`4 ��QUt%U#��ԨF���:b }* @m)g=%:WijThEZ#!���j5B0yj�X��̙sX#$Ȝ#!0snxJ�̜9��0y?8�EDrxxfQ *�F��`̜T� Z# !�� IT*;-@(#$3|Pu"��PFϜ9s!S#(3'am8Pw .fqC�cPa@:ACFBdI2s"�|F2���0s2G:{]c�ˆ ����TCf�?W<V ��@2`�$c �TxuSPP?YAj@ ��cHwV%D@�,�Ro秳G- �����U1q� #0 �����Tn|fQC#**L(~TCk #N L4��P9<0x#V 2Iz߲aa��aAl����1`X 5���ֿ? e�` U`bzV # X:pkU!��` U!c+ V5C���@kP #����@" XPa3ҷ#�;aJ :5*1Ta[6=ò~ R*,C-5CڐA֖ ]!��bĪ1� Uf p�(Fs���q?��������ԨUXC!X5BA4P�S#'lV땞u!�- F,ň�����Pc *x[�੣F@8y<8 �� u+>u:T4b�1c[vCq]1jЈU#ˆU#�����(b]�����Pc\):["`@E@jF�����UX@2�� u1b8Dz�����*XuU:j���������Tpqpc�dPCd b)FЮB2ղ3ˆU#�0agX ˆUj!lG\5B����.�������)"{<HiF 0hW!���H8 c5j �*jj4p@�������jVQ��P Qq`����Pc!�@2Qj �`#,kN80@]a @cVDDCaЮ ��f:Ɓ�Quu(hbxD1E�����q#Vpb�P ��Q+V!����@A-qpA������j:8XZq@@T!h;C) \q@��XZ8 �T:SB��HC#b��10c \AW}lgc-������������1.�RWXh`U p@�,!Tƫcm3C`D]c@Y�0zq` jLQ1b�XZq@��Hg**�����:Ʊ>5��1b������3Oc #V!���d � u�Q+vvKqT%m]J �� ���*n-:ے(X��Bj Y#~]2E���01t C����$c: �1�uDž1 ���� 㨋6>4N3^C¡e Sj%0 � \z@�������P RFڍi6֊#F,ňXf9lN4b��R?4bHU� ƀ`j���P���Pc!�(:` .t@@T 4 k�*TkDDZq@�����HwUPl7o����C54Zqb�Wcc\6H������@z뱮#������d^{�G]eGe��5tx`Ɔ(�������㈇4h Mj�������cu5ja,CxfX+z PjňUG4zBqh Zq@�B�B0Wu!����$cq�*Vz` ���PS~`tG Z1e9ԊP ,Vqh����Rxʎv8,F:H�XZqC:B����� u.ԈW!������� LuY 1 �@TV���jx����zmZq�aB� c>KA0zmB\vʮ "V4@����0;tphᨷJ!0hU �cW! dgۻG, P+�����U:B���z#�Eg+*FV(k;kjѳxغ!RV&H"�����TˆƈjG]�nQW1�1kP+�u=��czgV��4S8 ���*Xxcu W:1v1.!<l ;E'Yai � V �Jahw;ꊇ4p�����*j#Zq�P(֙~ +vEAl�F6X-Ā���Z<� Ap8�ŰB� 4(G��� ucXIHSq3)D hY_k��Zce cuY6`A�!ưV<PDZ3XEȀ1B^Ȁft 8 ֢c���!:q���XƱ#��Xce c@8H]]֊�NqcGٸ~G0�������x#8-Ha�����@Ia;#�eV֊زQs?���X *9a۟=^nFe)Ŭ: �������P:YB b`]Ua8 �u=afvQl�زT]BG$���ưJCW~E ������"cHx Z 㸾qmcv}1D�ڞm8jHHqm@��$�ai�� Z _mTeX+���� l}i<BW!���1#q1���Hgk�eXQW<W!�Q#JJZ+��Dk=2v-U�� k���gV����*⺮xbBTZ1JZGa8 㸾C`bW_6% �Em)OX8 �����-Zq�W[������$4ӯmD��ڥL���ra8 ����:; Z,3=A`%!��,ZqUq:B��������;+�����㨋�RqEUDZ";qE�2vPvW!F,:ׯ,2J,���� jDTIcQqcUG�,Zq@��:n~(4GK\����R4;GJNJ!�0U+\uDŁ(:hT̪hPQ F( VPk3\������5q=mwE@n,Qqઃ�������QWЧ׆!A���Aqઃ֊����vH)],J!�Ł1��@t6V<Ƶ#T8/:@�����Xq@ mcK� p�:BŁ1ۭaD �������� Z+(Pa a,������6* ����HqE�$8%(\u����iv+ҁeX+�B` ١W!�����H�P.W}e QqUG�����������㨋�HZfkWC�����(:.��P.>gtwb@0W `Ċ:B�������8"�������jU?#PJ 8h+:B������H> QqUG@(1٨ ����Ԩ:Bau [p l��� *2Gx=V ��� uvX8 ��Q%�q<͎G%'A)@�*A���X���8����x]_G;&d2E lJ"���P€ $�*a8bŁW=B��!���������d1ٮjUh k��1 cXٱ^PB5B���ŁW=B@^�`,^>N1 KJX"��C[vʂƠq~ɉ �&r�����@T8QDZYC �������Q/u,, ���j -pUtI',����[n0I���% �ZcV ����uB��5,ú2+�2+��!�P.|sC5Tc8n���:6H����X Unh �ҁZ TW$mH����KNd���`1,a��ò � t UcZD@h���q��C[Ae4S�  $���bhK �����p(S2+`���������Qb1,K8ZW<䑖 :fMR6[2Qĵ MQoŕ@�����0UKjc[&���� n.,Kx@��Zk=ȅW-+EH�%C2i $������Aǰ]Tn@0( [H��cXp]6 ���` m%=V�Cka�������Y e0LA������a(XUCNk\le �Łz�1-���{Էch�P8* �Aq$��p[m ix���� E֊ x)T k1e-"�8qR>KMBDh)i ˅`CZ.4) J��h!H���R�` m ֊PLw$ ڄ"le �-a -|[hIX D^hA)0%Y,Q4|�,6��( BR e2,6 i0(A[hPB��@(aVPeX=cB@ 6H����X X1WhΖev:&H������, ��(Q^5u}LI^v���jih!�@TxiW=BzPx#��BqU�>z����ƈoݣ^񠢇,z켓uF8Ί)@֦A �^6Űqj]qð.z����MӲȉ*0���� 0�XLMpY������J ��$qGl4RA/;H�X\C{������� (iX)��ch[z HC 2�( ae���0e d,lm6IL $�kT0��������i*J 6����$r0�cZ0lBW9x��׿ ����� ]v)��06MKH&K�pH 6 @BB H�����UXC�,Yk4�€L ^�"�H,6$Xl �����Dƴ 2�^A}VVլ$ rv8tZ*/@�HP^$���0eIT 2$MRi����� ( )������%+>v0@ư,.6�1�A!].�[ *S��$q9,K X����TpQ�p!�t0RF-- � .e ���\*S` ו�WaY9(4n]`���HphW*/´@1,K@`  ����Aј]cX0A@PpiTJJ� ���*j(c}|*O`Z@ `@aX0$l ��ò ò "�ư,i` ˒&�� ( )��� 2Z6=L¤4U[y %Ե ǥ&T���T]8} %l ��XiJ 0q R'Xa0�p &��@B\l ���h./aR \l QUl� TaT���������%v,lK@0eI*6.�A6����; Bm�0l0�����dsr9 .FoS{q� B 6Hl6������� ��pJS������@d [�cXZ8 \l  ( vFsr#h41[[Ҥ40(MIkaB "!�� �����/c[ ���������\[pJ*a.WXL ������@ KK [RL9 V m"%)/x0i- �8A6bd}З $ s �������PQ?Ɖu�����Y<遊妵- 0-K@���+=^4P)H�e Ӳ  T= e�@aZ0 tt@���LZ{yRk&@h����� (L)0eI���Ș4Aư,i�cڭrVRiC�t]���QW=B�H;li2H��1+K@����҆AҰ���bX4A�����2-}*e ����������jLS[,i���d��PR8) jS�ò �4tBLu9L&�J[ � K ��0R` ��cX4A���% 2��������"K`]zٻp-i��++8^#xM$@.6���0! hHyCY����� fh]4A_2/Ia^2"e �������x�c|L)�������(^(SYP@BFlBIk�������Ԩ���RC_e �bxKy:.6���(N�������PF}[@��h���^ZXZ0�,eIB .6 ˒2l6��,a���B .e �KOt���JZC X }Lm��heul @f&h͆P(. 'Us0���@koa鶡�����1,K@0R$�0a[m6�� (L %MPPpѐD�Ķ7; &���1,Kò ��@b > D�iѶ ƎU Nat+Od3ZcsUaKaZ@@aZ )bH�@1Z蘶0��)kR)@qe ������7K fKcsڲ'D��������Y4-ј,i�Zm/`ka2H`&Г�27- ���1[[An6�[5G[ ������ʆK9���JVۮ��˫]$%p-/vL�������@P‹>F-- ��1lK ��Ll{+ :oiebH��±#N�@7yM$��0lK@��شB?M$��$0͞hSʣj_,1��T r.e"�����bcvL��aM斿52%��@B,,"P�I!Mf ���I5%<nd;mh[@Hh $�� p���p;.- �����8Ʀp���0mI9lK ��„[9&\hҾ}I�HhM$�$4&���c QVXh��� 24����0cMӃl"������34`/@7H����AT.SZhD��0mIAaMf cܴmi���������2L� ( hh 0\^������ ZyĕmiD��iyS}[Eˁͱ*6@HhʃRR&���Rp)L%Ll"�P,K6:Mhe2"��8d� .6( F &˒�H������ 0VA���@b = oi2� .2���a[a&���„Wlh]]6ٲ OJH��a�cےaFOP˒2̖% ����„[9�������1ڇm /6���( ےf �ľ}U!êM"AU�D����������rIƦoA\lK@˒" ��0C(4E&�����@da� 7������D0LPP^&�v-}H�� (p�H�Hn2��AရL$�����fa�m$�AရA�Qo3r˖�P$/rȰۦ$9ؖ!QxH!M&r,K4'ے As걹������Aa­&D��ö ��@P>ЋL$��@P8r9 In2( vKνׂPr,K&Ki& ����%Lxс�,mJA d[ ��``ۗhbn2�(N%mI���m@����������T豂%JBS�����5u4a�S{HxK˒����{`J"�,f{[A X ض������@ICx" �7q,)êe1I�/rd &D���@(z A~QOmgX,KD�����Űm$P/2�����$/Cf�@F$#PTKD�b48M$�@F#rp˒����������Qa �öM&DŰm�a��J8C& �������(\.p������r>ȋLHeI\'yhRm䲤�eI�����m ����@IKxBF#D@q,i������B ����DKyLbض4���UE����m �ö  pЋC{CD������������rI�^t ��ư\"wiŋDlͬ����rE&�2/CVX4`Y@&ɈeI���������"p&�`1l0A����ja+�PA/:!pЋL$UKAh N6- PhL0��PlKD ߮.K&���&D���@N ���Xl_$W`⠪Z\]a⠪V-&)�������p(:E&�x�_ ]Tym!�`Yd= 2M&���2& dF2As7\M"d�U//ТU/id[J0Л$F��AA�hRDZ &xM˖���p˒Pvs"I"���xс��&ꕷ4������p؆ -" .v~IOh8���2:M&����-;ے2a/d9,i����#v!�r56]]mIi0Mvgے2a/ Iے2̶% 8X.H 8% "��mI�������rыA���@I7���J8M �@(79 P@����z*KR� r:؋L$����+V�����J8bo��� pқ$��n���B 'Ɂ����ji,2|Jom(nNbO& (\nd !D pJ[Vcö �ё" ��@(iE,qpkOk&KJa/dcrY1K;% L$�����%&�������PB �������I_$ey.iYmwa ��P1[ \A2 "�����������j$ �iF\i p&xyQ �������������T.|!`ɶ 0a7 MˁK A����&�e(e{L�@q-ـ](2�����`anrIzY` A$+q54 L4H���PIo0Aar���pنA�`"������jit:6w۞/@B"(tH�����,˜xL������X.4IX-O�;H�]E"�����p6F\Y \AhH&yDq-Cٶ fےe��������`1<ZDdt/|ɝx!&ĐH,D d2rEѭK{D ���� ��������D5N4I����6,Z0p٦r,p3X'5e0J�ݱ.a#@WI|!3 R$[U `"dt7 U4hpՍI kT(T]g$� f[ ^ 0 L$@F' ϴ : wd3 �Q&+]E"�8ؖa�.0mnv&�ɶ.0ٖa�����bl$J8 6 7;L_\@FSuI����������M/t4@pH������@(7u.0l"94v �&��P1[ -l[A��������D5N4bM�lR4 "�.! /0���Ფd/0������PIo8 o-6J$�@Fy�I^`"���X e ^`" Y]ކbPZd ���X mD���@(᡽L;mv1II֮D+=% "��J8;nZ^ l[A�������MJ8 &PI^(c7v)���������B 'M��,6M%a_6: L$������%m6%�% }/0��� ʕX=L[ d/p8aan4bM�����@FyǶ_d������̶4'Q(m%:;�������`aXtQ$8t]@F8ն,’n0[.'ux` 17  LD o����0\a�X m N{aI"!����Wa'7I;Eـơ(17th&@0&�������XLmD�Ax8do����0]apl0���`1ݭMA��XLp SD�8va{9|0_@B����@qZT ��������hr[4StUieF��@qUQ$�������Z8m$eh o0kbCh o0X8LQ D�0�%[_`"���������@TM� �Tnp7 d �����0\i�����( ��B '},Wbѷ5_= B�'2 "������PC)-<DnzJ7H��, PA(I`"! y):I| eXl%�%`-- "�d o8bO⤫AA`"���Uͻs%&eqrYA {`0, ��beK߶���A`"e��������%÷ ��PI`"��������Q-<l$!tH�PI_`k +,mi.w%=�к5-$MTE B�b o0).3dA`P_T 1 I�P,,٢"����T}l`dVR˸=a B����@���J: L$Te30 8N@LDwL��������tpBf2D�����,$'*KaZ8L$���@Nc;So 1 ����AF@ ddQeYA&almi�� y ��,2 skpd em7~$9+(&n%!����'S*vxY>��r4U-i���%ze`o' 8YTY$B ���������JJ�m; "�m�md/6H�%[3 7ȂXafSr\i���n4����`1ݶ ����o/Cj0}E������D�������� e.E bmA��m; "�XNcso˧H�������������B-<)D����`cm ����JzX^NE&poBI2H,8%i}sae`bJo4������pjvmJ/2��Y% 8, ����m; 6cP$vl&V !MTE(Nt۾@ ��Y8L$����k 7cտV%$PݾYL�����Y0eEE"����E:s%"�����������Qa�StOlI(N.-&���P҃D2 "����B7vzs@�Y8e$����T҃QF`t.(#Qa �(.4L��r ��&�n- &2:ٛ:/4��(jA,i� jm([D������ 0I�����PFkui"ɢ���� K�M ���PC��@(| �tۦA��� t]`J"����������ڿ-2� Ndw(3U¢e��p7@@qN ����$–h���� P�������@&���j<^. ������Q0I%=/6Jv"��]qܒa 5I�����̲KyѪ8](U�@ 2��P,,������$�����ONbV~PFŦ(j ;TKЋb0@j�$$������rmA������^x:7]^N H���N 2 brY2 "�@/P�Tvh),MH lLvUdQѠLpP]D"��P1- .&ϩbLW$����d;H�������,L/ r[V-Ku3Ր,r��������U7D����`qM\yYFe9:7u#(w.;hT�TP"�do`"�����C ���,wk֞}LL(4o������A&4nnj&���@ɦ U&* Tޤs5%!������P e\'&r~3Nf" ��������]J;|(_`"���X_H�@ɭm��+K a5v{h@br[A��@䶌זi���������@ ~(Ŧ6 "���*"C2x~uo- br[A��TLn4����Mm1O*s!�Qn`0nI�����@І*C0- UmE$��TҩZ:7B �����1����� ^2:&/4�����^pjo0p*o0���rd2�Cx ���Yii��*T`t/lNDdo $���������ZzjoLd������ ԾDPCy ap[fW{JfC��SزAivvVR[ I*.C"; 2 bTҡt{ hAcD �������eB-d$QrI6p`S$m0��rP`b����������vT`9l �vIa ��×L@��TCyg���bL�, ������� sG7w(���* ;���Av0����If22"860H�B:طY���@Ex(o0mbp[AJxn"1μtݕD ��T ^mi�*᡼0- ����X-݈FWWXNnK �@KDTCy�������BKO Ld�@-=7�����E[:���Tl1yH T n4����lb'D ���@"1Ld�����^0um)0$//0�&j�����boB< NP������*` ���XN/4‹Ld���������2&2��b\u�t*o0ܖi�*oAb �������������bho T�2 b�����e;Lb�����eHO M%T\0- bD"#"x(@F��@{6HTRvE(0PXn$!Fukae 1��@EP 5A@"sͿ�������P<o! 6U92eݖi2:-D'P-Mb�������{iM$HyҲB �� &2��оM 0��m9:7�@qp[I ����˅o'"0UL3����;7,L @r Meb����*xEHR,6 1��������4,"_.Cy�tpX= 9+ e1{YI,-<7������%=_'<{R Zxho`" MeJ�����C{*^iP,$��Te0Sb6$! $�,xho4 SaB+ D���S{e&1�B Fe/47dj""����� (S{Ú/4�ĂQ"�������������P` ����ծEl�����iyC`We������X޺A&e���m&[ʇI8XUYY�@Ly$b���8XUY�VM2U;ʅٞK\0���@en' %hi+X-ip_P������������0Ɲ]F@�ȂQ0[8ٮHЩt 0p/Q` ��*E4l`Q$5k! ���@e&1P1xYI ���`9|UFe!5&mFnO ��������r6Mb���,i"������, oU0���XvU���X(������� lwܦ������������������������������������������������������ ������������������������������������������� �� �������������������������H�����@��������P�@�P�@��@���������(@���������� ��������( ����( � ����������� �����@����(����@ ��������������@����@!��@@!@@AA����@��!������� R���(�PH�@�D���@��������� �������H�@@(���@E� @��$�� ��$`(�&&)` ��`���0�s P@a��&���� (��LB`(��$�(�������@������(�I������$@ ������I���d�((��$��$@�@A)(��@A���U��(����L��P�� ����`$� &��0��E�������� ( � a. ��@Q������ �� �eL��(c��( ��L��� � `!� ��`(�� �� � &I��\6 0`H�`$��@�L�����@��P���e�������0 L2��\�����f�����2���`&�� ��l@jT���������0�����������������(c2� �`��e�C��� ����1 �e�T��l.0�0 �2`������ʨV� �$�����e�C�$�L$C`r�P�$C���P $��s5N 2���$��ee@IA�� L�P�e�L ����2��S &������e�C��ʆ0�e�0����A5�&�I!���0!� �����C����ՠ��`@P0 O�C���� 0L��� �P @�T�`�0&I�������!��P ��I(������!L�Հ���@�Ԗj��f$�����(�(j� ����LA���0��$���T.d��@5���`�&(C ������`1 ��e I ����0 ������Tc��!��LB��Y0�����PT& @5���`@%L ����&��� T!@�2��( 21 C�`B�����e����@�����@Y�@`@@��������&�A ��(clSap��(���`P6L�`�������e�e��P6ɔ$ e�P[P$ a`���e@2������ �C�&��`��� $$� PI�2��������&�0� Wd&L��PPV �՘�P@YI`��(``��0@՘Y L.�@�0 �Ԩ�0'`�&)Ce��&a�xL�C�j���������Tc @���2��2���Ȑ2@�3I$ a@�e@Y5������eՆ�����&a�0I(������Q 2�d&$ a�&aH�����H0P\!$L�'aOZ� ����IeC��`:Lm!L�@��&aH�Ld�� �(��L2�2��!������B��Pj @WR �(`T՘d�����L�`((T ��0�e�e������� ��������� @&�aL �`p̩m�s5e&��0�����LI�0 CI���@m ɓF `�YTC  a�����0$�����L`TI�Lf0�����L ����6C2$�����0&a��������0ɐ a�21� 0 @ʀ�`0�P&KIP6�&A`��P��������P)3�Q!L@���P��e0�0e!Tc�������2��II����0 C�If��0V L2L2 e00 j �@ ��0 3P 0�L0���@!L\m&, ���0���2�(0������0W0ɐ @ddP6jL�2�C\�� <� 0 dAL&�Lp�����������2�P6 CU A�ʨa'ala0bf2j@f`0�Pa� R �����A��A&a������0'a��@��(0$ ` f��d���0\3Ղ! �0! e$e�� a���2JH�d�LRF���� �# $C.{Nf8A0 Cf������IP dQ T<  e0��0l8$ A ef���`0���0 C�d(M5� KjLʆ0�0 Cj !���`rِ uj3��@2kb$ L“3O�����P6��0 C���� Ս� A2��& gr����2HZ 23����@��A2I y(.0 L a C& A LII($ ��@C1@��\ə'@s2O$2HF ea L���(̈́ˆ!L2df���� &0 ������# 6fee�0!L ��0���pjd,�L``0E$3��&a6d!g(d&0&0l42Hf����L��`3� �j2IlHfBڞ$B* ��I.{0 !9L�j3Ld�A2 5Hf$f8L�2Hfpf�LpA2��&a8$ g&L<j<9 弶sF'� @m!nL0&����L`8eIlsC2b02L��IL�2ȵMj`BI L0��j#eL$Y-/�pəa@̓0g Ù $ gb<s0sVb09<rdb\b�ɇ��@mÓ0sf�A8LT9̨A2�(jL!y�����3y>Mb ���`n3 �@YY W=(��(\$ 0#$32 3_O?X4R e4 *"�����&09V$0C`@ A2I932pfa0WӜ3P|I�Pdre'����e@fș 0g0 lCle?GBE �P1r!@&L�(:y ���P#ၜ���0ə 2��b2&e3'arfL&C6Hf�L`H|3)Cf.O"�@$3��(\$ ��� a8yma̜gL(d&0і(Hms&&GՐeL���� 3je 0��h��@HXF>�9��\L����-LL(dF < � $<$0�����2Hm0���� L2Hf2əPo L�(d&LفkSK6'D0��0�&frfPL�2\''3՘���@\�!<�Ty0(b&'gp <Ԁdmf 5ȵM�����f}jé a&LI����� r��L23jf����gIB�L23�@Yy&�!3O���efB՞`c.1�T0$2+Km8yLb ������!<L y(H$����60����(02ə!ə ���ep̤ C�&<$@PLL���� �03*921Lp0 393����������@�AxJ����@1��0mPsID������������L9'0 "LL�����*9&032چ3s0���032Ho3 93�03��Ifۣ<�dB��ee]H( D�� ��(C�����'|͠ebp귌r}dL��0M-!�Lə LN"�0a&g&��������@YY��&hˇRF%93�`LL`�@kg~򟪀�0QD��-X'b� e����P28ć&&���� uE���`T\y֨��C��03� rf���L3!�Lə epxepx�epx�� g&�Lsf<Q;C�2*B���ȩ[(,XŁQC8Њ���j!�� FU<� NC�\|5*NYh d1px�!Ƀ�ᇡ#PE09��P�����frf.C ��������&<|Аڒ3<x3IC*Yj ܯ ���ʸ~ ��|U�PF 39#Mn%s rCźЊUD��� .-=Xu2"4�Px6"��(#_WTE@9X������dfrf�����(#vdeGDM"�LI0���&\g`&'`LL�`u{h2rX��`¬vfa&g2rxees$ e\c!F*t � EX#VBL"�� 2rx&eL�f΄9HL"�ᙃG8CtĥA6UcD!b&�a9yp�U@G#1Xƈ �0sȩ7 3���e<MDTX"���@2b� 3'<L>k1��j L�0s0<&̜90ả��&̜D�&̜"fN��ۃ# ?k=bcPae51P5BrsdD0 IA���1F:RW!���Lj8 2X+���@0 �PF\s@E&̜9�eOīC�����(8V�$F rU#4C %1X��(#9p0`F������2rxL9����(s[<" 9<5PsaΜ@9s"�0 c����*(Gd5F]Z#d * XGZcj"$3'���`.j@3'f| jO<5B|dD����2ag�fy~C,rZ\Z(�!b]���! #FFTQg T����fy*�����p*"930dmb@�̜!f3B�PFO5rx%93���&̜30s>+|<#H<ՠ�0yN��If 2rx$3sAޠ fWۻ��B,6 С5B1 JҲb:j�� uMd1'f 0 �9D ��`LBfes>q}PZ# !T8�@9<\FBPF�0a朧��$Sy> 9‘vҀ \�@Ö�2IŨ Y5B dўp �ˆA�O<p BUNd AG=F������PAtj d`1Ygu!¡Ck���Q!B�š ���T1Z6!�Q5BD!̉������2�� 3<������Pc<J̙LNnym@0vxLc8<ھvP�T9 ��� '?q �������� >g1gn@i@�����Tht0vUPAam3 #?g` * �8PJ1V1s# �����`9OڒC����.#_eɈB��Tft:{H` * d`@M]XF�ACq1*��R1*)U#����| d���j#FڸnW83j#��9 `ԛ^]ԊJ�1+{ĭX��.k ��@B����@cF20 ���������#,�@��59穀9|u eÃ�9$L-?*@ˠ��0sN!��s�������r v&yC�gU#d `<s"��0ᙏ"aD~u ���T6 `#G8$3'�����@m�d@m|�����f>cձuQ !2If1hp ���� 3T��@I~>c�@!/vUqY�� * �����@Uc0@�U#��@n:* - H1XjA ͟+U#� *,�QU!�0g1!���TP?,^��!D١W}B(aĪ1 )Uk;*�0bU? ����` XT�8j��!X5B1b)"̉��˟��@2bp)K�@M݊F!�Ѳ^3;dgѰX5>Ve"f<5Ơp1.$E1Ҳ3€�FݵPEYaP)B����� y׸jR&-;xU#������$Kq1Ts! ԘpWЈ[7pa@���R8 ab- �����*Xu��ԭ:TB@o64e8$�����Wja@��c�Ht!Q6k<Z$l $')B� :Ƹ1!�1€��j~*#�!qc\lԅ>5��iɲcq5B���� "u<$�0*Fa_a�ˆU#`lRz˂�@v ui iĭb@!X5B��0b��*xfV����Hx.]q1��1.s+�TQkǐ�0b!X5B��,!,ˢ}) ��:*C���PA뎣#!����dczF���\X1JG�!ĊU#oguрP �����qE�PQU93����ΨzUZ8j� zjgZtTQ pMP#V�B͡Z#�@#V!^x# �0Z1ܵxˎX$��P+aĪ2*9JgP#V����@B��&[#$u<D�ˆUL���fc`� V=ZJg+>!��cGll>VLL���PUG�� ���HFuկW!��VP#V!�!+~Uf/CD1ԊF:B���������@Oz|=XYd B�V:FaĪ#����$cuA@:B�����$c\DKg#  j,|UG�ˆUhĪ#0bF:B����1 �`QG@+nݤK�k=Ŭ!Zی" eXDT6aEgX+!۳ �����!8�j�1u��aĺ=RؚKua2[P+s14FĪ#���*X�cc Vj� T0ba8 ��d#��������PQKgG ^4B����PAkab#4f8a,V���Zw|` YG!d+�����*Xu/hW}k$�!XkT+&jb@�`bx:FaZG���!tW}dc����cq@aZG���"@Y^!�0b#C8 �Ƭjq!ec]Zq@��������H8"dVU0q`8p@�PV#b#�����@Ez::B�����$#:ȡ #uA՞a-{p@�,C-{p@����q��j|zvA*R^0c���Zu#���Hac< V��;{E3a���18 18 �5bĺ=]8u$�����������P��$#:-[ d mp@���0F<#uFi_4EP+X4S8 �������H8"�H8"���H]Ǡ�, !c!c:T ۳Xl1( Y4`Z�C8 _=;$���bZG���q+��eU[}W;) @e'@ AXU����@2Q/iZG@(Fu�DF}TPv4jC,CV ��aĵP@DZCdUigNd$D�PVYJbDz�( jŠ݊C��au{k5k���PQ+u��@Eq����q:B&N�����H8”]HgV��� z` =[ b*=]8YPSvb 4bbZ+4-*h k1����� .��*x��z��!\B��� uS1*B�0'xZ���gZF����*غ c@���Tpc=B��cxE,��k=B����$8ꂌ11+ o!m K9H@an(#X#z�Ъ-{Ԣ SvG�PuH5etw@ʮ3q3zC@`����!#����� z������_xQ zL`+n(��2-펺@cX+��������@2^ �HqX�pŵ>#1=Ut���������@��\8^k2Q(Akc\rV���!qG0egkD�Z-����i '\-sA KahESV0 C!��#oG]���0k=B�����dGXz|,DKHa0h2U����k ���� 2!< kr KH�����"c pV)lw!qG!qG֊ ׊Z ע������HYըǡk=Bm}i! A+9Ly͵}l)H$����0������$8E���P`\!�8xê٨?ZjFh`Y<l � m3ى*J a)G1n( ZF񺤩V � S^1C[qbZе>@)\ P [OBS�H(Q+Ҡ:@�Z�֊xhі( $�chٺ4����@8V<qc]�)\uHe*9Lye@��������VQ8CZ9H�����@W\gqx@0UI*=+@G\A�J5eZ+������dG,�HfGE�qc`Z��p����PQW=4;qDU$�8Z#���c8nl]z ]ABfU��0��T҅m@cֽ+⠻�ሷC_,%㤊�����@2#z3z;` ������zGax@��X�)8pZ�����\Xq@�B8kA[H���Zx@tc~MA+2 `eRM#M���� Cx[ͦA����` ^ &@azLZ%¥ $������1&ʖNb6 maRtP% ]Z!R(tABB:H����a]Q:>i%h�� (ڃȁqtYҁd)Ҡ,e ܲAZB $������&q\le ��bhK̡-��ᶋ�� 8+|xA�,zG}@���Ю�������n'-aB1.$��-a������Qa X bk=ƢgR(4誃@hUG lU ��1�8W:H���������$x  �*xh[H�����"���@E]!��B!Xw\HB8 a@ahKx�����@ź Cĺm9R � ڔ l( $$«0DaPZ$,A0jD 8e:z!0&�6���lYjbҲР4(p&b^_u�eucq&�� (x)V< ������Pz��€MA�������Z�kfk)lw+ԸQ=L K7p˥:pFP ȰriJI}$BB 2�( J ���jnxp[7G4XQQ�����`kSPc%O$B 2àl F@/ �������6!(P���@deZ M 02*`) �� (Lڱûf'X@���[H0`kS` m RzƣV��Bq!�1+欻Ǡ%C[hNˎA ULۄ5@���`1AWCkð�'@  ��(:_SN'�BF 2�������ʂC�X   q6 ,u= %1���`���Rq���$쏯1H-L �����@(qզ �������ԥǠ.q\��HeXo^cX@����������@q<`҂òLA����,W���jK>e G|Y :H\6q uŐ1_����@xNk$�DucZu���,qØ޲QN1YV4n6+ HJ"� [H���X,޻JlUri��� ( Dm ���A!c%��������@dLS @ NwWXx@00hk ��@P)T ���e9 �$aV���Aa֦�#?h+� $�aY%L��+ u��!\xG1\ 6ŁjF�v5kZx@�@bB�����#< ����ԩ_Ѡ4h���% �ò^��8Z���,u��ڲW�������� ?.C0= -0ipF� (LDch[z $���&0jS����*z����������ԛ-L����Ș0A * $�� [; e\d[H����U찴)% KH��0e �����i ����Pq '-YL[; Mm4g/a*p[@U0�(NZ�����Ű,a�����՘rS p) ��lm ,e +X] �bˎ_@@qr¶=L[&e Gm,z ThFCk ��(B8mciZh����R�����&sk K_]ɔl) �������&���D4 e.Lے usRaYŞ`I$%�%X@�����B 6��X zIk,e ����BI)Tm=PH ���ikhNFQeB����PT>ԊNRrMf�������Z:6��X,a@�iYdNB�X4cf۵2HI$���mK@��������j |?pWB��,V0@vB'K�`1-K ��� qLȲx]Lڶ �%X`,e ����^4PNBX]q@�8ZWQ4ઃ����1�����Ԩ-ugz-a���@81M%փ�� T<Z^0,-Zt( Hj��>WH���BG]q]Px��Qk}�S*ҶE"- @�������(lڔZ�����% [&lm"eI1v4)�-*Ĵ`mc4Z+@ `MKH��B&*4b)X@�����`1<Q4_.6PJ- İB)5��% !a+���������DGyA:Wx@�`Z#�@l��%<'��bX�X ������Ca{X ��&2���ð( ��� 0akSB #vQJ @P!et) ���AaF1kp, AZH2uAZ8ZA.e �P @P%L ���ڛ>L6 n!h0à5@�XaX@$4R2���1���P6lm ���_d��ư,1mK@` ˒&����StsER��` &Aa֦ ����i 9[[�@ �ò����ҝ]y1y)T�����X4!��jaZ!Q@�������(^|)�@ Q7ֵ0"^L5(z������������T6m ,eI���՘4A����%6=Х $0h- �c`) ��[t9ZZLX,�K9HJ >ѭ $��$4RPh rL������ 6òIiXa0`ilNZJZ�����U}0Pn5@��0&� jSp`��cXa 6�`L˒&��� v.-Yka ����zMm/vL�e ���i 56zKH@qR�2�������%MP(aUmT.`0V2�Dmt*ȊHZq`8W$������B_# CvP Tt$�x૛��� LC%M 1,K �@bכ|�`+9 Z � (LDqeel+ �� v@% 0R`+Ҡ0( �L")V&����&\ 9r9:f$4 viD����������Ș4A���@\����T6\cᲄ�����QiIaY�`1,KٶrxM$�BV&� .���aYdKabj �%<p) ��i - BB-4jCTaK���������� ni&OT�(f9mkaB+P`�Q-7; &�� yZ W4@���%M,eI��`1m �Yֶhr%d-=�������%M��nlZ.a�����D5TjS��%M��. ��@���m/4����,eI�ò �X ˒.eBph[H�����):*Akq��Xfka��Gڶi].6����%L�JxR|@��������(^!��|C@ʁ$xM$��Mm ���Ű,i����PRp) ���@Տ4֠ (Lؖd[5G[ Ҡ0����,eI����T/|C� (]C �Ll{+�M$b$ m&l"���@(a¥aY�A/H��P2|�b XI$$4%<-o� �P������QiI�Aa tle"������� 2t� 0[t,eIX ˒bX(RX68a!2qV|LR!2,Gm0\D@Ble"�����Aa]mj0�IԵ%$Jp e � Qv�;nX0Q2��������@dt������"0&\!�a[� (LC„jUlX0��������A��C&r,-L �0&�&\VhkaP-KC�[ ��öY-JH` ے ����¡\a[&��ư-d28[Z@�����[K`[H�qE>PP,i[ ��������"㰤 (L7,00wHE%D���diltdž//�@8YZ@hL$@Y2YғD 'ud 4V&¤4Y D@Fle"��XLے % %@�HhL$@B,0ٕ7-L ��2���%@ˆ.h,KzK;Lv) � _OMR�2r&KP(4 p!$��a��@(a­H������Dp�iPpkŴma�������J(t(2t0�4'_. �����AaM0%=m_������ rIM$���0n6���a�`,\ȩ ōU/m ����3hŴmimas-a����PZ#�����PQ/h ���������A/me `Y$a[H�ö�2���@PBijIL$��!qn g˅1ٖ0`Y¤4X0��� wYeK�H$Lj@ D1l[J4M$�������`1Ea[!UzB6�����㰥Sѡ\´@hKBv8iYfcm#$,K@�Hh]lD����cض!Q&Da>&wk]@ٲ �HhMH@a`-aBƆr..���@Bl"Q,Ka,aB\l .6�����~&��@*vNm5G&��������`ÖNEl"¥Ka,a���ư]M&*4&� \l)mK2��J+:̰M$������@ [9D���� 2gcvnm"��0f 7xƕeasڲ'xdadd.76������p i,i%+2��Z貤f �( &0f Im0ŦPh !&�������0) C(4kMNeI�� v>Ћ8[4���������rML-a,� .6!M%M ���0m&pp&�� eI = EEg˒&����1l01lWaD&pyK�����~<������Aရ4f @P8&��XY^y[=] BΛږ&����@P8VlKaröI��������r4&'y@abNSn27H����� j`0 t�� D8k@Զ4���0AHhP 4: p0YI^l"������[6`4+oj4dY�$4Fm/^,f˒&����� /6bڶ4 aZ  pno��eI�eB��qI.I^l"�� vD������cڶ����Y8l����@Tˎ] Ҁi   n65Hd1[4lY���ưmi�����rK9ö 0�`1,[axyK0P]iz :5'T�����'Ւ&0����B t0&2���@(�����ŴmW` �<E{�_Ҏ{o8Y4A0D ����b-&2Pmov��'˒&0����J8VTqV $0I`br)Ci&���B x ���@N[:Mp '˒&I :F%M`�8Y4��XLۖ&0�����B C(�T=6'Q�Mp ���@Qm%KVږ&0P,Kf `7����ö f`itf7�^K-A<[H _.1��˒&02f n6!Mv� M`@Fl &D�d4./nm"SjD@ d����%)$xM6|;(,[eI�Mp*%M`�������Z ��aZޖa����m8�,mP��@(ဗrar`��N=`6|3��P,KtmK���aۆ ��@(tN=w\�������%›~;�� (ˍ$˒&1�������ʎ SF% P@ öID`ah7;� Mpgے&1�����cض @=V~k(;|;|hzỳ��@BVf ܶ'�.l����1,h F rifh` ����\w8̄,LD ��PtU(9vM,̺*@H:M#Ka ���P,Kے&0��%ف ���`1\4�Nb8ٖ4�������zeAZՆ6L`P^lk R2�P`3\B{y%h$&8ٖ4)N%MŦwe ʅo ,hE0 K�ɶ , T@8ؖ4d /sR[X���Mr ��˒FmK�ے&0�PAn2An6����e`����������J*Tz, T@�/BBܪctm{1Ku��bկT@d4M0{6�������@(ဗr fh�B ؁hے&1����%|XN7@d��8vcvI ^iӲl] nf"# n6�������mM��������P$傗rhyw7lŒT4e(ed��������P$o!�eb�����+LtxyNb��������J8BB ف ����������@ 喞K9��p7%Lb��X m�/2A3haٶD䲤I �h;�˒&12HmeI�LOf2�����X -Mb���������~z3�P1-I0AI3ha����`1\ pYi��J8;,K+/-4���������ʨw%|��eIr:؋Md�������ʉ ��,v ov ��PAj6XՓN!v+Cu@ TlK`[D؁ hR Z���,˒&1�.K���B irIM"Um_pH>H������6LPhdۍ %4�PlK����B D�����@Tˎ]A]Lm��������ʨwy{aXhizyN`�mI�amA0 Yii ��@q-i�6abf���鲤A^ A^d"���2LdM7-!oQA^d"�2:ȋ;F%Mb��������������� a8��`1<-2Q{%%8ٖ4䪗dd/չ֞ś#[aIln$b�i)Q<&0 D��eI��tٲ@������N4Io6��XLm8BIy ��������PRMD����`1]����e(�J:͝C׶4N d�T0���b����o,j4D¡H,DB ���Ȃ$#SafUp V"3M$`�����(N. &{ �����tL3nl���tr)������Z8] `~le1�d\c/h6*diB$6p����������%SZђȃRm&j / /2ٶ 5"����D.Oy Rn ']e`�����d"����J:M2�@_ޢRUd �Y8kVXZ.1m���d /2ٶM;Fm�������%zL"{Jwaf),����� Le&8ضa�͞"3�.DF��������`UD"#���"M&2������NjOR�p dۆ Lqm��� 1i ;'oz㢮 `���������.P0lۆ ����������4PB 'D`1\.ácqm8t9F$�]M`�@&U],2# &mn*0ضa�p7��,M&2�Pڇ22�����`a\ AdB],2Pt]���;P�j38 &]8(St]4X8ț;F*&&b������XLrE-& Nd�@quQ04ٛdd�������`a\J:���������_ʳcvulܢ @qnm3��D8l���@(/2�����()E1۶app,a� y;GvnlLB � f"E`Gp $̂ a�����tZDY84Tȭ)HDX8ț8M&2�Pl0D@ɿ&Ʉ)N.��������AI'}��� t9� tD!Ѡ^Y$Fm&1Pl[v ڇ;YRl!+_)������J:5Z8444D�8ٮ 0! ��'6Lbd o0!Lm��,䥜N���(Nm=I���(f6Lb����NA`"4}[fA^&J.&=w݆U^[7��&&D4�bea`Wp &2������� t DF~ )5Il`,,Ҷ&C����%&2���vɎm& Ȓ&1��ۗ �Y8Ld�����,6;vuH�Xw%"/Bq-ZZ@$HDTcQ!!a��M!dܗ L Z8K9H �, Tqrن%tLl- bi(oeM#W`F�wŜ1���=&D�����l m9���@qmS$FU,6!!9 .0��(Nֆj-0v禗߂���������� (/r{vW`�������E2-.$ ���T[D H76Uܸf$a�����brٲcrۆI �{M\ vIe ɾ,o7g7b ۠ZOUM) H���������Mr&Q"RA^M�TL.0�P y)p i*o �nl/* "dV&"���ParYP@R `Č��������KӦ &b2� QvqM�P da+1 Pat XP@Ą���͕"3�Pabde��0jmRbr٦I ������v&IT #����@h$_`"* _@����bg$*&c6  Ld�@% d,gm���J8E|+O]oK[^@ ���P1l$�����vm������o_)̀Ne^@ \a E2P 'ۀ2vFc�Q(♞ 2d: ������v&brنIanm+2$1����@(ط/I ����`! L5Qd ���͕"3����I&JarI ������VswɨE sm+$WZwf#&("��ParaV|.$2ف ����������etY&�������tb�,ep��@MP 'wOlιk[!žYHDl@B �����"BP '{I '{�e&1���XV_Av\"pǥ2Lb������rn[xHRl!,dor �P~Hdo2 "MA 6xfi)F ̂˖6:'^ZJ���������V{ -/1ui)  'b����������@҃~ ?m`���P<Eeb�������o0dv ) ("�" d���kB/6 "BE ������.@cG7 (��)Q.sWl���v0 `b;)Nu�����������E|\٤NJf*ɄP,J,)ڮ ���������XZHN������x*�`9\C@�����^eE6(do0 SapI �Jxh/d�[:wjob@�������K"���BK @wE"3km-IL>$&j2P '{������������d���Q�0ISr ��������) ���@h|S{[!",}���������D=.��������Pd@&V^uI" `%0�*d_@��������X&P �d_`@�4- m2������BKT nK8i , b����Ndbpۦ Zxhw73����@%;p vI����4@2���t깣͛`&D$0��*}b�����*L^,����`iz 21�������()|9r154 5P^2Q 0tqj]0p$tdlEc�����}Qm]!C��*d_@bpۦ Z88��P d������������^tԹC TM����Gؗ1չ喒E+[H d����@-<8P d���*FV,xu !lXL C D����ij-/Yt<hRUl%= ��@M��������0X/|\D9��������⚭�g��������������������������������������������������@� ������� ����@� ������������������������������ ���������������������@������������$������������ ����(��� ��� ���� ����� �(������@��������� �P@����� �����P��P��@�����P !����@@�����������@A"���@���@ �������A���(��(��������������� P������������"BP��� �P�P!P�d@ *��*H H(�����H@������P��� ��L�„�X�&(@�(&����`�0T �(��&��  0IA���� &� (������������P�*&LB����(�&�����&P�I��`���&JQ���� �$�P�P�s5$�\m�&a�(��RL2���� �� �(�PV0 �e����e�Le��C�d�P!e�L��2��C�� 2����I� (I���� I*�elB����� ���������j#������L�$���0 ��0`����C� aL2�� I�`(!��e�e�P����ML$����m�����L����(`F����(If�������@�P$��Q ���2�l C��`e��������P������l2����L�I��@�����:���e��e�0d�&I� �����s52��&I�1!�C������0��!�Tj!<Wc0l��e�P 2I�����������0dH�P ��ȀI��!�� ��d�P �6�����I��I��!0 ���! ����C���020��QP�P�L�u����`R��$ 0���0�������P6'dL�@�� ���@�@���0 ���@Lj0T++c��jL��L a��j#�$<I�T���0 ��� j����e C�������(!e�`!�� 2����!Cd �0 �$������ ��`��������ʘ( ��`Հ2�ɀjȠ��PV �� ������L2���������T*�L(������L@A@�`ʨ6@� ��`@j aPV������$@ �����2�(2Y2���`2&��e��e��0W9  `���T �L������a2����2� �2A2����&�a�`2WZ `�2S ���aL���e������Հ0 ����������((���d��0LjL�(��`2�2Pj�P���L�� �����������(+��I(P��P[�� �&�a��`�����YQ�e�� ������(��e�C0������0 lrm�a�5������&���e0��ula0�@������� �L�Uj�(�!C ����`0�0 ��&Z �1l�Ls! �!<I$ �� aH&Ð ����0 C�d � aA�0� a�b��(@V����՘d! T{8lڐ* a.a2HT+� @!<!� ��&0j0�����$<9 A @$3����� `��Mj$CjL��� &a$ 0�����0�� 0 *a�����&a2�P0��0�ˆ0ePc2�01%�0 �e�`6O< $<9L.ņ0��eePp��� cl�e0a Ա�05'g&a�!e82CjL���0\6���\mC�� L5�0 C0 #`C`C�j0P,C�! 4��@ )00����Axrf�� 2�a! �C.C �����e���!����dpAd& �LkeCD�`Cjd� aɐ<''a�(0OO�@! e@jm0 C����&a��Ld.�C �j@�\1BmL2SA̤ ���C�� ���0sL��e0ls<t2�0��������2\m̄! A! �$ I����T0� a����Pa`j �0 g&a�` a a0���P � � a0[M6jL2 )#Wl0\6d''�C2eC2=���&a0�(0��0 OO��0\$CuZaa�`��L2�j A�L( ��ef��������IP �����$e$ÔA2SVn2e��2�d�j3L����` IIR$�@ < C��'gVZ- e�� 0!L���\6eIA2p� �������A2 0 PL(d&�L`p�����00���ʘ 1'39L,pmCmHfAmHf��@$3�� |ِ3O&F&A2���p���P1B$3�Nm@f��A2 �Lp����������L`8 0�ʘyr\ ��e<9L`CPO5HfB$S$3����� �PL���(+`�&0&eTc &Sd3S@$3aR6e8L��&0��fN �@m!SG<%O$2Y����� yÙ 0<O0pFdfAp�&0 apFr̜H&2lҊ#ck{@2HfLZ<L�`×MP0a�3�`ep���2H LL͜|F��&arf��Am 6I2��Pep6F`ԀL63P $31'j2s1HfPL�����`&g&��0LL��d&g&�L2���"I><3��\s���063��&ə �eTc!����2Cʘgrk<M"ed&'�՞9 0�d&mr @mM=8@2��0LL�����d&g&������1'ϓmr^`�2jp$3�������(d& � m<I&_$���������`3C`rA2s':6<WD��L`rfesʞ|p @b#g��@s<קlD�Lŀ3399"L��&093d% ��e &093���� ~$P<LN"��@ulr!���PL(|ə A2���ʨ$z;dԘ0ə A z$BH ����@2|fș xyμn( d1c7=8"��PL������ ��� _o3~#I!�e̗=5�鑰hr0H3jGR-XAк8R"1 feh 9arf„Q;7<+7<:&Pd&@$3d<s0jf���ef2a&!Cy&'Ag&g&Y̘������ 3|x2ar���&k{fB8g.L`LN"�ԖdHmf�����PA�PV3( 0932̐3��� 39[NfH�frfL23��93&SFY s0�(��0C7m'eŕMa&2�&n9Ι'��� 3Ifrf$h9����IfrfB�@63s23���`$3dyL"��eqO5 3<ЃO-zr1��0L���eԍI!YDZ*B�����`dYLeǜ0⧹!)AIa&'�28<28> |4B�� 70 HI4Rd@.I��� pS3�28<jpx*�2$�Q32cTE��Pg�����\6W`B�`����H<yPk($B$I@L|3������@ �&yLCfr�� ���&�&<jՔo̙)>ƨ( 0E����Lf9REIfr%RIe&'GʙsfNd K@@DA=9PF��euN\oGc9xqxLЖBFr�&g&C�����`<��*ə ������e&< 39$px"4ehL͆ A!B94 3@0B#"fNŐ�`gLUǨm@ir+��d��TP5FUe3:Bdd1'uky����@ g&Lf��M>CcĠY`E!b&�`Wg{7F("��jT"0ả2rx0 y"<KM:�0a@9<yD(#ޢ@jQ�PFg=*\ZAL@��ƨ, !I�����0a朙�e(#�C3L>k*"`2QK>�ə aC�PF�2rx���̣K���PAU������N`e�F~��������@m,"?qT��Po a��0gOULKDpc G���`,;G�ar<9<��S_#TjƠVPD�A2�z yr`y7��P̙#�`��������PF&<IgByΙ �sf.F�٨€""�sf<5ƪI @9<2rx�ԖdL�0sL2<$9[fp �q e!y&�q5&y&����3���m bPa��0a朙�3 sL��0sL� 32rx��Lv>T��������|�*U8”IƠ€�Q#cU#�� C9 ��0sS����� ���/{"����C0axҡ5B��c5*vͰx`��CoQ5ց""F�`F����C����l䇡cv+a]X`Ú6&R 0F���d`U����@S"�PA9@9<s1rX[rDO�cЖQk<j1`tvX11 C!�1kG-I��!FE���@2R˧ޞӳ1P!�����j#g��!C-96Ül,š,g= J "DHf���0sL��ʸڐ@D���L _ã}"B�� 1.l38y#���Lx XYbz\L����!�lN'>1 9<2rx*�j;pٲ48�2r7�������q5z"F�@R;0so҈֠���FϤ99L@9G����9nZ"bb#0�&̜)Df�������@fyj2oQ� �(#9?cQ5j��0szCU>Zi&1I9g~��*X h�0aǨ[KaY=x�����T95 ��� 3糊`<TvD�Uf<@ɇ ÜTMb<xC10B�gFr����ec,bY rD�(#gfr������� eL�jKœy�sm(�@UcM��$#(#g�����eo^1 �` *RD1��^:�� 1Bn< Fy1!��B<u4(!Dz#!�0Vҁ�L9 ��)kCF4Fx&]c!Ds!D��� 3* 33B���*6��0Ft* ɈB���)1K!�� &3s��~Rv3$ϣt�ˠ�ٸ(����+(0j �@F-h������FUc-@F#95d 1X!@ ����������*jk$#W��@O���fg]VZ#cP�c*P("j��!QزCg<Z#�!D1ny ����q re2p1İ3Vn����j?Oc(D팪 � !�TPX�j2r��zՕfR��'[Z8 aq�ˠ+�@ňk!X\k!����� uન[Qk T$# ~? T8T8����qh]ˆZcd~*���TjA$#?�5~DET8 �!&)'!��Bx2! >P39<4@ xj���T9mkz&!YxPU������������ ux�� ]T���� 1�H1v3, Q4 1 ��\q>LmCF@Ly�nm;0P:n£k}�������>u/|T�BfRY12eqH�زH𐂺EpQ:B���#!X8p��HHk,Yfn=Y2c\k ����@\�@ꈧ҅#���H1Ƶ��RGB�@Zjc?���� c��@ꈃ+ u/)bX ���Haˤ#"rЃO�g�L3)C:P:t!���b1�b1 Xk �������T8�UˆP#C��0;p��� ���PXC���8֭B: �!k<ngc-P ��d���T8�T4;B: �RtXǣ4b1$�xC���@+1B#C��X(�L@PG\_nЅ` e�����W@k]q\#b1������Hxk:C�U`Ł2FB qA+"@ !�1ͮ+UP )օ1B!Xk+"TB[aZ����PBv+B4!6wkC������ "�����1.U:��:1 XnЅ������:k�PC]h p@�ˆ!�aĺp@1S-c1�q`X1c ��P/]�C4S-����\z:C-Z?�Pb-������H",,4w񫝆 ���XZ,mX_i�p�ˠ#1CUͤ+>Ƶ L"j0 pCkC@#:��0Z8 XZxHq\G`�1:Buܺ � ��Ef_1k !�!�aY]40#ԮHt�ˆU1kC�@ ��:1����@2E�$c\Xǐ1�����VZDZl dx}I����A*Fĥ����\8(א�� xq! ��c�QǡJ@ٸ0V{���R1c2ղgX ���XUR ����ԨUq ��������RG|׸4S-eBTX,C-<kC���QCb�P`b[Wz!b-h pC-��늀0VHd bңc����� B"JFu ���q,=tˆcaD])9JgpCDa`=C���0Z1"ふ!DcC_ѭ@T G@#:kC0f*1L_.5B��� 1^j( jP X*FZ���u|H�06CKzE ������qFu X5:FcPD��Hx]8baZF1F]C�F@W{c�����T8���Խ5>u �F07k}J=Gʔ2H ����@#u$Z1ƪ�"Bm6֊Hw` p@@#u 0b] hYѪ������`գFD\Aw+J9�B1JX�������@@<>j@ĵP ��1h67k|H�` 6;2J+p����cfQ/QG���@6����P-%:Ƹ1�eJz{vڽ h@a ��� Ċk0=0ejW ������@5Fĵr+>k +0 ,h ��� 1q/|��aĵe*zlw^x@��4 [QF\1�����@d#]륏Z CYx@�0Ƶpu](+8@����ak!C-< öXz�8> �P&1)~?RHGX��!����q���$c����1Exz!*+J3)S-< ��` @ ���V=+9dC? X,5>V$6Kb]I����,0A���PRiͅHX�� *Ƹ1����H#vW�bԭ<b, 8V _x\K dj����ZcFX����Pָ1Zf*ĀFF\k*%5��@T1���@1ZW|mJe�e���jԪ:� Ύ*@FZIA���TX-0VUv/< C-<ju<p#��j+F\HUz \B(F\ax%P *0��(0AA1Zhĵaa,=@ňkǂW�BH G]#Dau���BXqo#G\u(i_�!1C-< �����&����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty.wav�������������������������������������������������������������������0000664�0000000�0000000�00000034630�14662262111�0016714�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF9��WAVEfmt �����������datal9����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty10ms.dff���������������������������������������������������������������0000664�0000000�0000000�00000016022�14662262111�0017352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FRM8������DSD FVER���������PROP�������JSND FS ��������+�CHNL������� �SLFTSRGTCMPR�������DSD not compressed�DSD ������iiiiʳ,,VViiff͚ffYeZVUiml4ꦭMMZfVکնl[M55jժm5jUVֶmUVӫfZժ]WU[[YVֵkjWWW56]mkW[6U٭խvms[uk][W[[Wk\ZZW[[kmlZuummvڷkj[un^kmmuuZ]zګmkmwlZ뮺^붶mo]m{mnmmkmۮ^mvն{]momwvkvvnݯ]ݶz|][׷^۫^o=kwwkknn׻nzmݮ[uu۷n^{[mnz׺w]vomnۮon]k_v۷k]۶m۶nwkm۷ko]]zvuvծϯWmvkvzݶk뺭W]]zn_V[zۭuݵ]vkm[jsݭzk[vn=]mv^]mkwZZm^nvUmnmvjۻs][]km[j]kmVf[jj:ճ[m]VZͭkV[U[Vնj65˫V[5mZZ[5VZUj͵gj6jfյVԪU\ZUk5UYUk5UVYUifʳMVU*MSUiUZZZiʪlfiUM4Y+**ef6eS-L2T˲YeK3)*TRUYR2LU,d,LRTTS2&jRL1ReX2UT)QTY)%S))*TɩRd)TJ)P%ITR%E*I)TLTUd%)%DP%*%II$$B$bQJ*QIHDŨLbRJ*)P$RD)J)P"TJIHbJTD EI$RI)DQQ"H*QB$DFH"EJ""Q*BBQ$B"$RJQHEQD""%$"$$QDD$"(J IH $HQJ$HIDB!%E"$J ($I"DDD$RI("$PQ0PR$$I"H%H"PH(J$$RJ$)HH)R!D"RFI"$TBIDB$RDDI$DRDI$(R$DTRD$aHD)$$bP"Q(HR)P$%)$DQEHI*$$IJ*%""RJ)%H(RJRI%)HTJJQQIH$TJTJJQE%HJHE%%$%ĥ$DB$%EJ*II2RTUSH*)JU%E)Jdd12UK)Te)LReJJSRLR**SIUT)RJSR*RbMS2,i,˦%M+eT*UM3,%UY3,JU3U22ʚjTU*5K*jYjYYZeZifYˬ2MM5YVjjl,MfYj,VeӵNN[j*ijs6VMZuYիNil՚]fmfYֵ֦VjmjNmj[6WZkjVu:ZնujjV֮mmjkVfYյmnΝgskmmkmmmj흫mkmmnv:Z[u]kέvvk]s^mպ۷knun]kzںmj۷[mnmk۫n֫ݻuvmmn׶sunݷuvwmmݻmnu{wo]y{^mk׶on{_mZomvu޷۶w]uzzݷ_]nmݻn]mݶ[vv^_m]z]}uu۶nkvw=mݺz^nگuֶ۷nu_Wmmmsޮ۽{[ۭuz]zw]n]o;k޵mvZknֶ۽mmuu[muvmZzֶk[;]lkmkk]mmuk][km[Z\ֵvukljf6mmW6U][V[N-WjNk5kZ5mkUf\ijj5NU֪j͵͵ZZUk5UUVYZZjkY)6eY+SVfZk,ʭTMfYUYj*ɬ+j&ɬeUUS3TR̪ƦUi3,UYULJҩSe3LJdVR)d2UFL,̥RRYe22L*,RJSU%*RJRRLR*TJTJɔ$ɕ2RťIIJJTRTeE)D*1RRRU$T$RRIQQI)HDÒJ1ERH$ŨQQH(1Q%"DIH$II$"TII$"H)$#) (PH D$TbEQI$DR$$T%(IH"R(Q)$D#H*H I)HIQ(E)!"E$("$I$$("J$ID $QD1D%((I)!DBA(I($"$DRD(!H(DRIH(DD)!BDQ"(Q HHQQB Q$!PHH)!%I" EEHIEB$)$)I$% D"$I I""J*"HRP(Q$II"BI(I)PFI%BI(%(*I)E I(J$QIF)($"RRPJH))$$$"#%EE#E)&JJQbQRRdĩ*FJ*RT*)JRbUR*JJbRELQJU%%b*FU*TK&LLbJRTL*dTɔV*SL*ZeJɚfRTʬeU,5*UXJӴ,VUTY4LLʦjjkeYZfeijf+34U֕Uij5UM5Zj-6jfe5\e櫪5sMimVUj֫ZګZֵZګmfUsV6:յ]jڵlZ5s[fճm[j[[6ֵٵmukusnsv׵:][[]]uW\[[nֺ^^W[]m]W]uںۭuw[]֯nܭ붷Wznݮڭ}nmkֺou[uw]]޵ݯkۻ[{v뭭۽o[[wmk{ۮ۵ounmvޯnZw]nڽmݾvvm{mn׷_ݫvuۮڷ뾶ۻuvkwumnnݽ{]wnn^wv]v[vwk{[kvַ[vݮuuۻ^vۻkkzz{k׻mzֽuݭW۵Z]ګuvݫ]vkmyڮummmշV[][lnmnuuնյmmvssmW[m[Z[YuիnյumjֵպmjZͳk:Yͭ6V]kN:mW5W\i֛jU]iU9VkUj[ejӫVVkUٚfml6ՙk4UVUM5UZګ36iYSjjMMMYYYUfijƩf+*ʪk2lfffU5X52feK2KJfYY2K,UKedQҦUU2JeTRU**XY1e**)JJXTU22IITS&)*UTEJ*Jɪ%)Rb%)RTT)JI*I)2JLTbRbRRR1JJ)EE)$"RJ*IIH"TbR*D)D(RQPPTIQBDRE$EQQH% *Q$DR)IQD$TJJ$I$ID T(HJP"IQ"DR*("ID$PIDDQ$H(QIDQ0D(DQ$BJHHDH"IQB"$"RD(DHDHDQ!D(HFI " $$AH$"$B$EE$DR$HEB$RI$J$JDDEQD$RE"J D%$BB%IH"R"ID"R"DDH%D*JIH‰DDR%($IE$()IQD(Q"*PDTPP%%"IIBT*PEDJIH(BbQR))HD($TTRTRRLU RTTE%)&*ITU$I%RU$)1RbdJc%Q*R2ʓT1JdƒRTLRUJT*efK%RRUY**UR)d4Y,RɩedJYYRɴTSLS3*ifjeeYfZfijif,̫MU6YYiiMUMV3S5fjkL5UjejjjUj;-UZf69Zfխ\jjڭֳ֭Z5jZ]Z6ڭukjnmkgk:Yչmj׻jVծֶuuծuպVVmuuz:[]vmVݭuնֻkmuu뵮{Z;k^^num{suuֻuwm]ͻumk[ozmvyun۽uumۭvۯ]뮯ݫ׷mkm޺m{w]uݵm޺kuk۷^u۵vvkvv۷muzݷm{n]ޮm۶moֵ^ݶڷڵnݯ]}^^nۯmv^۽umuwUmvo뭵׭nk[w]uֶպnn[mukk]wZjֶmkֽWuں[u^n]j]sնչڷ:ֶvζuu[Wk[g[9U֫յsZ깶tkWZ:խm:UZW:mjumZVfڵgsUZkfVYZVV6Uf]5jflljU3YVYMfYY-3UUYVҵMәee),ɚidUMUU,I̚ʪZYUSJLʩXUK2%4L̦T*X̕VR2idXUS%*bIS*&V)ITJ˕)R2U,b2Ld%IE)1QJU$J)IJTdE&%%JIIR*IIbRRTRTLJRIITQQI&IE%H$JIFI%H"TQ)(E(dTRQ)I($ $ĊQJH%JJED"%$$ J"$)EAQBE)I1$"F$$(DB)IDIH(I%$"DHD$B(H$PIH!DDJ%) "PE!$DDD1"$P) JBQ0% $JD% ($H)")HI(J"D(Q$DRIEDI$B DDE HC DDR$"J"J)"($)$) %D"HRIE!$D%"E)BAI$RBBID$PQIHR"1QH% )H$bb%)BL*IB(RI)J($R*I)*B(RRRJRQJR11RLQI*RQRRRRTT(I11RRJ%))QXJUS%Jb*M%*I*TTLLTTIJd%L,R2**i&JVdJY3b%bfLTT,̪URRjic+J&Yee34+YUR,ӫ*2UjjeZfYjjfi̛KUYY5fj,5UZi˳UUVӪMYjիVm3UjmfUjMjZյg5jYjjӛjUmV鵭m6֝ZjյZj֭uZU֫W:kmskj[fvmsjkW[jV6ֺ׺6ںV:kۛ]]kmkֶۻY]ܭmnoZݽZ]uٷWWuuն;۫lny{Wn]uۭmڷmvnn׷[^z׽um^zm[wz[nݷڵ^n۵۽vvޮznuom޻unvwmunu[^o[onuvݽ]uvwmݻo[zwkvn뽺vu_U]ۻvnm׺mj{mkW۽_n[wm:muλ[nݭ]z[un󾵵^mww[[mmm]umvZ[ַ۫]nֶnw[WYjֽm^u{\]]kumgU\MVfV��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty10ms.dsf���������������������������������������������������������������0000664�0000000�0000000�00000020134�14662262111�0017366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������DSD �������\ ��������������fmt 4���������������������+����@n�������������data ������iYYUS4jf55Yf9Z5k,WUZj6+׬YmUVjkfUWښնlkͶڮZ6׹mn[Vvk;׶Zͭյ[6uzm]m޶v۶ݶu۶wknnm>սvv^Wwuvk_v^{nwvuv]۶mmom^׫nm׵[wv^=ݭnεnnk۶Z׹zW۶vnks[gWڵ9W۵f[Vk͹նjj[ujmVljZ٬ZYlVmfj+gժ:Z֬լjU嚖YSjiYMʖiYZM-UMUU5MefY,T5l2i*XҔ*UJU2M42*dJL)*)J%)S*Ѩ *ɨ*Q2%&"U$B%))ITC2FR$)J"%)RDER%R!%Q$QQH(IQBQ$IbDDB$IDJAR()(E$$"$EP$$!I"!%HD EE$D"" JD( E E$DI!D %R$J(J"%bDHB)"QA"$($$*")(%I$F EE$I(Q"%(Q$ITDJRJR(*R$)ERR$#"%URJHTQR&%e(UI2Rʨ2T)JTJcL4eԘ*ṲRLST-MeYViefeeU5-ͲiV4sfV5jerTklٲZr6kYe[jmrVeֵj\VkkkնV[fsֶUٹvu]Zsnխ]Wz֮^]Vm]խ]wvmvko]Ww]붽mvZo}}m^{v۶v{Wmwun]^ݮm{m[ww]z[mmvuڵ^uݵ^uݺ֭uZomWvk۶mڶn[չumkWںֵֶֶͭ5ڶZkknVlsljr֬֬:V٪UUZ5[֬ZV֔ljY4*kiUT55MVeMiJUc%+MeRR&Ӕ&KI4JLQTJʤ)eRJJ)**R)IL1IR**T1J)Q%%)e(%%*EE)JJ"#I%R(%ED)%Q$*$IHĨ(H% I)"*($J$IDQH"%ITPH%H$DIH%)HH(H"$(I(($J")HJ("Q(BIAD)$ %)D(B$$"$)DRD )(%DI(Ib(H(II()$)EJ c$$%)EI%)EEEĤdRFJI%TRIJUTRFJRFUJ2UIT)e*2FJ2&U)SeTZRJS4UUR-Uej*2SSVVUYiieYYU̪VM-e3iմlViլUΖjU[UkZZYmf-7[lU[6笭fk͵նVm[]muխvέkk׺ںm:W]kݹz]um[۵ukWomݭvuvk]nu׭wv׶wv[[_nwn޶뵻{u[mݮ[vv]vvnnmukmu׮z֭m[mmy^m{Zmծkn;֞[]뮶[][6uumnιZm[Y[skV\lr-[٪՜U[VVj֪YlͪjղZlVVͲf5T5U5U6UfffL5fL3fL3)5eeL*T3TRcL*RRQIUJFUJ**%LI)IFHI%JJJR$(JTD*IJ%)QJQ E*BEJ$IT$)("*RQ$QP%RDDJ" "$("E E$("IB$J"""Q""E)QD$IDBH$J$E$@ERH""J(DR"E$IBJD"JDQ(QaHT("Q$HE%"QI(IT"I %%D)B*T )IRBIEJ"$(**EJIJ)Q1J%E*T1$IJ$MJƤJLɘUI*%5RT1iJUT&3U4RYJ233e5fYiZUUfK3˪YͲjf2U3[VU9mV+WZlZ:VU͵ZVlVkv\9Vjmku][jծ\ۺnmmݹk[Zmmzۮ]koyݮsnݮvۮ]׶۵u]ۭ]޺׭]۵znn۶^޺uwmzm{[ۭ]v[ouzz۶nۮ[nݵ]]_kvm۶[֭Vokk]uk׮mm]ۭv]]ۺٺsuk\uk]mnmյڪխ5Wk\\mZm-gUWj[檵fU[ZUklfVU6-̚ղfe5Si55UՔ-5UYY&YURUKդ,3e*SJ&UFT)U)URJ4F)LI%Q)%R*)&RTIJ**RIJ$HQQbDII%&JQ %Q)RDQ$(DBIH$b$$"EH"IHD""B$ )"I%$%!"D II(%("ER"$ J(($P"(HP"$RHRD$PDA1"%B$BI!I"EHQ E%JD%%$FIQTBEQQ%IJQTB(J)JJJTQJJ%**eJR%(FITT%S*2U*&e2J%TdjR̤Ff24UJVdhJMLUVYfVfUY3+3fUӬZMӪj˲VjMfVVVVm٪jlZkZkZkU;[V[mնVjl]l]gjٺ֝m]ݺZZo]ۮ]Uo][v[ۭvm^u^m]^vzn{uv[_ݮnݶvݮݮnwko^nv]Wo۵{]wn۶]mk޹]m\ݮsWo۵^mϭ]׫׶ڶmnmk۵mkvmVku:׺ն:kfZijeifiYZZjiiifYViiefeZifYiefffYiYiifiifeYYiiiiffZeVfYjffeYfiYZffeYffffiifffZeiYfiiZffZeZjifZiYfYfiYfeiVjififfYVfffefYieieZifffYZYjfZZfYifYYYffifeYYjffieZffiiZZefjYififfffZfYifefYZjfZffYfeefiieiifefVifiZieZfYffYfieffYfZffVfeefiZeZViiifeffiififfefYieiiffYZZiYZfifffZeffiZfeieifiYYUS4jf55Yf9ji6eUfU[Umڲ٬VͪUmjZꪭjkVlڪnU׺:ZڶZծmkڮzukֶ[^[k׶Z]]m۶]u֛znm޶n]vm^]wzzvݵwmzۭ޶۵[w]k]]u[ߺֻn۽m_v׫ݶnnun^uu]]^jm]u׶V^vk׶zZ׶vnkWWk[VݺVնjV\ںZ[ڪm5WU۬ڬZjͪVVmeYUU3YӪ֪efYͲeT3UZZUUS65UU-eeeeTUfLeIMUTJML4&5Ji*ULV2JS**ʔTQ)&IRJTQI*( T$e$EF%1R"(EJT (Q(I %*F*"P(JH")D1T(I"QERDTBB$"(DDD$"DRPQRHIBH$RP!E)"E$!1$H J$(HHC!$R$QDJ($A*B)$J""J"J"J$)"QH$IIDJ %$UH)T$IRDE%IJ%IR*I*RR%($B$UTL*1ʨU1)RI&LҘ*EUJRJJTʒ*YRcJeFc4%Ӥ)MT4e45YLYV*ͬTeeVYiZZYLٲijV6˲4YrV9kVjUe-ՖYffk5kVVklZVmZsmVjuj뚫vζUV֭n\ں͵nWέk۹]vvu[mm[uvmvkmnmmۮWwnvޭ;՞zmu޶n{u[^[[o붻nzmo۶뺾vnk_m^vk۵Ww]۶m{]ukݵvu]{mkn]mumm]mm[Z^km6֭խպ-:[km6fkY۪rVmYZfsVmr9kVUZͪ٪j5ZUkYͦU5fZYS˲fe5YVM5Ud5Ue*3e4Y2K2ejJLib23JL24RTJJ2QTR)$JQ%RJ)"JJQI$I%%$I)IE#H%J$EIH"%$DED%$I)P$FH"Q$*IJH$(I(%D$)DI$$E%R$")$"")BIIAID!E"E"HE"("HP!QB( E%PE$A%DQPDTJE$B QBIIT0R$EIbDQIJRU$%()IHHD)ĔRJ&#bT)*JTR%U)URɤFbTd2iR*T*jT2SIYf*5ɬT4U,2eU֘iZfVfUMe,kUլٲZV5Wf:+gUiVYkU[+kZ[j\VյZuuY[ulkεmn\]ںvm]=z꺶m]ۮv;^mm[okw]{mm[޹m׵mkwn{mZov}kvn[[{}]nWݶmou޺[׻znmnnmۮn^^mݶk}mmmۭ[ۺk[nuW۶]ukjںvvmuζښv5W[V]Zk՚j\:kVkj֪ڦYj5f6,)[U͖eU-eVcf55USLUUUeSMSYeR4iҘ&KRJUTTR%M*S)iLdT*eTI%UQ)iQIQQE%RTR2I*FJ%I%I(IIQIRDQR%F)T"(") )Q")(P"JHI$R$()"*) )"TDI$"(IQ("I"BRDDDIEI( E!Ib(%$I($E$$"IHBAJ$R$"1$(H!)HBQD(ID""Q("RC"J%$IHDE * )I(Q%IQ"IE%IFEU()))QI*IJ2P*IE%hd*)Q)F&R1T(S*eR&cJc2J*)MfҤJTJ,YJi&MM)3-e*TMUViffVY4ղlY5eV֬VVVժeܴiffemV[-kkUZZ[9ks֚u9WV]kum]mumkj[Wۮյ^kڹjWm֮WusWwzuvήݮy^WwWw]o뵽nۭݵ׶{{Wnnm[v{ݶ۶wkw]o][wwmwmmk[]vz۶k}m׭vm]vk۵m׺Zۭm۶[굵[ڮz]Vխέm[u]km]kskZWm.ZUݪ\VZfZjjj٪f6V˪ejeU̪jUK+˦iYYMUY4UMe43SZ2SL23TI3jLcTidjI*)L%SQ%U2&U1R$RIIdJF1JJQ2II*dE(R))**$P$#ER(R"$ %%R$Q(Q(HD(IBH)$%$)% E%"RD I!" )"%A$I!RB $RHP$DD"E") IBQ"P%A"JDD$%!QH"EJ$DJBH )Q$)HQ))(P%FB2QJER$))IT%%JEEQRJR2JQJJEI%JQѤRʤRT*X2%*R4LT%R&MF1**3JYR,UY4TUUeViZVeU5ҪV43k5ͪiUkUUVͲZ欚VMV+YkVVkͪk\fnVWk]k]][[;\ۺkۚ;vmkm[۵6vW[ϭۺ]۶nvzuzmw][v۵n{^}뭻{v[_ݮzuvno]mukWkmn箷vm붯V_{k]]vk{vvk}z=ڶۺZmvum׵ښkUzuk֮5WUjjZiiefjYjfeffeeffYffYifZfffYYYjiiZiZieififZeZYfffYiefffYiYjieZYZffeYiefiiifZfYYifffffYiffZiiYfYfZiiYfZYifYfZieiYeYffffeYiiffifeYieffVffZYeYZfYiiZZffZVfZjffffZfZZZViiifeYiifieZViiiZZefiZfeZZiYifiYffZViififefZYViYYiffZYfeYYfYfiZiiefZYiiiffefYiYjiffZeYjiZYj������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty1s.aac�����������������������������������������������������������������0000664�0000000�0000000�00000000223�14662262111�0017076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������h@_�Lavc60.3.100�0@h@ h@ h@ h@ h@ h@ h@ h@ h@ h@ h@ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty_alac.m4a��������������������������������������������������������������0000664�0000000�0000000�00000012404�14662262111�0017553�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ftypM4A ����M4A mp42isom������ moov���lmvhd������D�~@��������������������������������������������@��������������������������������jtrak���\tkhd�����������~@���������������������������������������������@�������������mdia��� mdhd������D�~@U�����"hdlr��������soun����������������minf���smhd�����������$dinf���dref���������� url �����stbl���Xstsd����������Halac���������������������D�����$alac��������( ����$�� ��D��� stts����������'��������@���stsc�������������������stsz�����������(��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���$���0stco��������������@������ ����`�� ;udta�� 3meta�������"hdlr��������mdirappl�����������ilst���"nam���data�������empty_alac���cpil���data�����������pgap���data�����������tmpo���data������������(too��� data�������iTunes 10.2.2.14���g----���mean����com.apple.iTunes���name����Encoding Params���(data��������vers���acbf���vbrq�������----���mean����com.apple.iTunes���name����iTunNORM���jdata������� 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000��^free��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������free����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� mdat ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ��� �� �� ���� �� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty_flac.oga��������������������������������������������������������������0000664�0000000�0000000�00000021631�14662262111�0017647�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������{^d����y3FLAC��fLaC���"������ B�~@KJ£Sy~OggS����������{^d���%s,��( ���reference libFLAC 1.2.1 20070917����OggS����������{^d���Bt�������������������OggS��{^d���p� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������{^d���"b$$���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS�@~�����{^d���n5(�������������/������������t������e5������@������������\������] ������( ������- ������ ������^ ������������i������g������ψ������X������ag������������S������&������������+������;������cN������Z������͡������)������������������z "������V"!%������W",������#+������o$>������%9������&0������%y'?8�������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/empty_vorbis.oga������������������������������������������������������������0000664�0000000�0000000�00000010350�14662262111�0020242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������ۿ;����Zfvorbis����D�����������OggS����������ۿ;���r-vorbis���Xiph.Org libVorbis I 20050304����vorbis%BCV�@��$s*FsBPBkBL2L[%s!B[(АU��@��AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d���((  ���@Qqɑɱ  Y�����HHH$Y%Y%Y扪,˲,˲,2 �H��PQ Eq Y�d��8Xh爎�����4CS<GDTU׶m۶m۶m۶m۶m[e Y�@��if0BCV���0ĀАU��@��J 9ߜYJ9Hy9s19眢Y 9ĠY 9'yК*9q`9&y9iK9HyRK9s9s99眨9Oޜ9s9s9 4d���@a)h Fb2A0 Bh:%qRJ' Y���@!RH!RH!b!r)J*2,2,:쬳; 1C+RSm5Xk9皃VZkRJ)R BCV� ��BdQH!b)r *АU�� �����O%Q%Q-25SEUueזuY}[؅]}}uaXeYeYeYeYeY 4d���� BH!RH)s9$ Y������pGqɑI$K$,O4O=QE4U]Q7mQ6e5]S6]UVmWm[uۗe}}}}}]BCV���:#)")8$I@h*�@�@��(8$I%igy陞*@h*���@������xxx舒h+ʦ캮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮 �$��t$Gr$GR$ER$GrАU� ���1$Er,4O4O==SEWtАU�� ������� ɰM%R-US-RESUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM4M Y ��S--ƚ $bjc R쥱H*g1^Q{$cA-)&TBc*RR 4d�p@,@,�������4 <4�������$M,O4�������������������������������������������������������������������@4@<@<�������<<D�������,4<Q�������������������������������������������������������������������@4@<@<�������<D<�������,<Q<������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������`!"��pH$ HM4eAӠi0M����������$MA �IӠi4"����������AӠiEi4hD����������4!E&3M"D ������������������������p��0 "��p8e�8��X��X%��`Y(������������������������������������������������������������������p��0 ��p(eDZ,8$ɲ�<D���(p��ASbqBCV�Q��ű,MEi'$I<Oiyi<4!hEQ4Mi*0MU��P��`��B�bYy'$I<OE4MSUIy(i,M<QETUUy(i<OE4Uuy'hEQ4MTMUu] i DOMSU]u牢i.MTUUu]Yi2@UUu]W뺲 PUu]Ye�뺲,����*ф @!+(��S0&!$B&%R RR)TJ*%Rj)UR) B*%R��؁�؁PhJ� �0F)sN"c9'R1眓J1sI)s9礔9sRJs9)s9眔RJsNJ)%A'9��T��`#A�R� cYyh$iy(&Iy'<OE4Uy(i*E4MUU],i0MTUu]i.l[UUue꺲 \ueٖ,ڲ���VG8) ,4d%��@B!eB !R ��p��0 �H��Zk@gZkZkZkZkRkZkZkZkZkZkZkZkZkZkZk-RJ)RJ)RJ)RJ�U8�?ذ:IX`!+p��s B)T1tTZB1$ZlsA(!b,sB))VcQ)RRJ-XJRJX1ZbI)Z1#lMjck*-c_dl-j #[,-Zk0[1>R,1\��w�D3$� � R1s9R9sBT1ƜsB!1sB!RJƜsB!RsB!J)sB!B)B!J(B!BB!RB(!R!B)%R !RBRJ)BRJ)J %R))J!RJJ)TJ J)%RJ!J)��8��A'Ua BCV�d��R)-E"KFsPZr RͩR $1T2B BuL)-BrKs���A���3��stG� DfDBpxP S@bB.�TX\]\@.!!A,pox N)*u ����� ���\�adhlptx|������|��$%@DD4s !"#$������ ���������OggS�z�����ۿ;���f}[� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/excessive_alloc.aif���������������������������������������������������������0000664�0000000�0000000�00000004172�14662262111�0020666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM�+AIFid3 ����X��H D����ID3 ���ORMCHAIFFBOMM�������FOR>cX.iCOMTTMM���2�X�7 #��X�FiRMc���ID3 ���ORMcHAI FCOMM�����������APPL��� stocG726��@aUTH���Prosonus(c) ���C �������G726 CCITT G.726MARKid3 ���� beg o ����d�end id3 �INST���<������������APPL��� stocG726��@Aoop������ ezd s loAME��� odblock�SSND����������+�WfrjԜ}{ w+CQ5.>I's lTpyright 1991, ProsonusNAME��d oodblock�SSND��������> ߃+��WfrjԜҶ}{ w+CQ5.>I's loop������ezd s looCOMM��� beg r loop��Wf@rjԜҶ}{ w+CQ5.>I's lo����� begCOmMoop������ end r .Aht�G7�G726 CCITT .726COMM�������� beg s lop������ Dnd s lop������ beg 26 CCITT G726MARK���J���� b%g sop������ Dnd�s loop������ beg r loop������ end r loop�INST���<�~����������APPL��� stocG726��@AUTH���Proso�������<726 CCITT G.726MARK���J���� beg s lop������ Dnd s loop������ beg loop����d�end r loop�INST���<�����������3 k�SSND���.IFid3 �����Q`CTTTTTTOMM���"���&�@ ������) ���C0pyright�G726 CCITT G.726MARK��J���� beg sop������ Dnd s lgop������ beg rCloop������ end r loop�INsT��<��������INST���<������������APPL��� stocG726�������APPL� oodID3 k�SSND��������> ߃+�Wfrj's lopyrdght 1991, ProonusNAME��� oodblock�SSN1��������> ߃/+�.IFid3 �����Q`CTTTTTXOMM���"���&�@ ������) ���Cjpyrigh w+CQ5.>It�G726 CCITT G.726MARK���J�� �� beg sop������ Dnd s loop������ beg r loop������ end r loop�INST���<����������APPL��� stocG726��@AUTH���Prosonus(c) ���C ������G726 CCIT] G.726MARK���ight 1 beeID3op������ Dnd s loop������ beg r loop��}{ w+CQ.>I's lTpyright 1991ProsonusNAME��� oodblock�SSNC�����> ߃+��WfrjԜҶ}{ w+CQ5.>I's light 1991, ProsonusN� oodblock�SSN1's loop������ zd s looCOMM���� beg r loop�������d r ||op������ ezd s looCOMM���� beg r loop�������d r ||�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/excessive_alloc.mp3���������������������������������������������������������0000664�0000000�0000000�00000001635�14662262111�0020627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3���� `TIT2�����BushTPE1�����RihannaTALB�����Musicof the SunTRCK�����10/13TCON������ReggaeCOMM������eng�www.torrentazos.comTDRC��� ��2005-09-05TSOP�����RihannaTCMP�����0TXXX�����MusicIP PUID�TXXX���;�:��MusicBrainz Album Id�f42cd50e-feb3-439-8e4e-8bb78bd00919TXXn�����M�����������������usicBrainz Album Type�albumTXXX���"��MusicBrainz Album Status�officialTXXX�X���*��MusicBrainz Album Artist Sortname�RihannaUFID���;��http(OMM������eng�www.torrentazos.comTDRC��� ��2005-09-05TSOP�����RihannaTCMP�����0TXXX�����MusicIP PUIDXX�����Musi���inz Non-Album�0�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/extended-header.mp3���������������������������������������������������������0000664�0000000�0000000�00000000237�14662262111�0020502�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3�@�����  :;TDOR������2013TDRC������2013TCON������Folk/Power MetalTIT2������DruidsTPE1��� ���ExcelsisTALB������Vo Chrieger U DracheTRCK������03�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/float64.wav�����������������������������������������������������������������0000664�0000000�0000000�00000205750�14662262111�0017040�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF �WAVEfmt �����D��@ ��@�fact�����PEAK������2!dU�5?���5?��data ����������������������?�����?�����?�����?�����?�����?�����u?�����u?�����?�����?����?����?����:?����:?����?����?�����3?�����3?����?����?����?����?�����?�����?����@w?����@w?�����h?�����h?�����G?�����G?����?����?����@?����@?�����s?�����s?�����?�����?�����?�����?����@?����@?����6?����6?����p?����p?�����?�����?����?����?����?����?����u?����u?����@>?����@>?����?����?����?����?����@?����@?����?����?����@?����@?����)?����)?����@_?����@_?����@?����@?����?����?����(?����(?����� ?����� ?�����?�����?����x?����x?����� ?����� ?�����?�����?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����@?�����@?�����t?�����t?���������������\�����\�����J�����J�����ſ�����ſ�����K˿�����K˿����Rп����Rп����ҿ����ҿ�����yտ�����yտ����׿����׿����Iڿ����Iڿ�����ܿ�����ܿ�����޿�����޿�����[�����[����M����M����.����.������������@����@����@a����@a�������������s�����s������������.����.�����k�����k��������������@����@������������z����z����E����E������������@����@�����"�����"��������������������?����?����@w����@w����@����@�������������d߿�����d߿����Iݿ����Iݿ����ۿ����ۿ�����ؿ�����ؿ�����Rֿ�����Rֿ�����ӿ�����ӿ����8ѿ����8ѿ�����"Ϳ�����"Ϳ�����ǿ�����ǿ�����0¿�����0¿�����2�����2�����ԫ�����ԫ���������������?�����?�����?�����?�����N?�����N?�����5?�����5?�����?�����?����?����?����?����?����0?����0?�����?�����?�����?�����?����J?����J?�����s?�����s?����@>?����@>?����2?����2?�����?�����?�����?�����?�����?�����?����@O?����@O?�����?�����?����e?����e?�����?�����?����@&?����@&?����@e?����@e?�����?�����?����?����?����?����?����@?����@?�����M?�����M?����@?����@?����@?����@?����0?����0?����?����?�����?�����?����@U?����@U?�����?�����?�����?�����?����@?����@?�����?�����?�����?�����?�����S?�����S?�����?�����?����?����?����?����?�����?�����?�����?�����?�����T?�����T?�����?�����?�����x?�����x?�����d?�����d?�����?�����?�����蝿�����蝿�����̲�����̲����� ����� �����Ŀ�����Ŀ�����ʿ�����ʿ�����qϿ�����qϿ�����Yҿ�����Yҿ����Կ����Կ����`׿����`׿�����ٿ�����ٿ����� ܿ����� ܿ����5޿����5޿����!����!��������������������������������@����@����<����<��������������@X����@X����������������������@_����@_������������������������������������S����S���� ���� ����@����@�����?�����?������������@����@����j����j�����������������������������߿�����߿����ݿ����ݿ�����ۿ�����ۿ����Gٿ����Gٿ�����ֿ�����ֿ�����cԿ�����cԿ�����ѿ�����ѿ�����Yο�����Yο�����ȿ�����ȿ�����sÿ�����sÿ���������������|�����|���������������?�����?�����?�����?�����ļ?�����ļ?�����?�����?�����r?�����r?�����?�����?���� ?���� ?�����?�����?����?����?�����~?�����~?�����?�����?�����?�����?����?����?����?����?����?����?�����?�����?����{?����{?����@*?����@*?����?����?����@J?����@J?����?����?����?����?�����Y?�����Y?����?����?����?����?�����?�����?����?����?����@Z?����@Z?����?����?����?����?�����M?�����M?�����?�����?�����.?�����.?����?����?����?����?�����?�����?�����?�����?����@ ?����@ ?����?����?�����?�����?�����?�����?�����(?�����(?����?����?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����ı?�����ı?�����ș?�����ș?���������������:�����:�����~�����~�����Sÿ�����Sÿ�����ȿ�����ȿ�����:ο�����:ο����ѿ����ѿ����TԿ����TԿ����ֿ����ֿ�����:ٿ�����:ٿ�����ۿ�����ۿ�����ݿ�����ݿ����߿����߿����@����@��������������������@f����@f����@����@����@����@����@<����@<������������ ���� ����R����R������������@����@��������������@����@����`����`������������@����@�����[�����[����@����@����@����@������������������������������@����@����@'����@'�����B޿�����B޿�����ܿ�����ܿ�����ٿ�����ٿ����n׿����n׿�����Կ�����Կ�����hҿ�����hҿ�����Ͽ�����Ͽ�����/ʿ�����/ʿ�����Ŀ�����Ŀ�����J�����J����� ����� ���������������?�����?�����?�����?�����6?�����6?�����?�����?�����4?�����4?�����?�����?����u?����u?���� ?���� ?�����?�����?�����?�����?�����F?�����F?����{?����{?����?����?����?����?����?����?����@?����@?�����Q?�����Q?����@?����@?����?����?����-?����-?�����?�����?����@?����@?����K?����K?����@~?����@~?����?����?����?����?����?����?����f?����f?�����(?�����(?����@?����@?����h?����h?�����?�����?����R?����R?�����?�����?����?����?�����?�����?�����8?�����8?�����D?�����D?�����?�����?����W?����W?����?����?�����?�����?�����??�����??����?����?�����?�����?�����?�����?�����U?�����U?�����?�����?�����T?�����T?����� ?����� ?���������������P�����P���������������¿�����¿�����ǿ�����ǿ�����Ϳ�����Ϳ����)ѿ����)ѿ����ӿ����ӿ����Cֿ����Cֿ�����ؿ�����ؿ�����ۿ�����ۿ����<ݿ����<ݿ�����X߿�����X߿������������������������r����r����@;����@;��������������@����@����@����@��������������������@D����@D����y����y����������������������������@l����@l����@0����@0��������������u����u������������d����d����@����@��������������3����3����R����R����`����`�����޿�����޿����ܿ����ܿ����Vڿ����Vڿ����׿����׿����տ����տ����ҿ����ҿ�����bп�����bп�����k˿�����k˿�����ſ�����ſ�����j�����j�������������������������p?�����p?�����?�����?�����?�����?�����n?�����n?�����?�����?�����f?�����f?�����?�����?�����v?�����v?����?����?�����k?�����k?����?����?����?����?����?����?�����?�����?�����}?�����}?����Z?����Z?����%?����%?����@?����@?�����?�����?����@?����@?����@?����@?����?����?����<?����<?����t?����t?�����?�����?����?����?����?����?����q?����q?����@8?����@8?����?����?����?����?�����?�����?����v?����v?����@?����@?����@?����@?�����L?�����L?����@m?����@m?�����}?�����}?����?����?����?����?����?����?�����A?�����A?�����?�����?����I?����I?�����?�����?�����?�����?�����?�����?����� ?����� ?�����?�����?�����4?�����4?�����P?�����P?�����,�����,�����b�����b���������������Vƿ�����Vƿ�����˿�����˿����п����п����+ӿ����+ӿ�����տ�����տ�����%ؿ�����%ؿ�����ڿ�����ڿ�����ܿ�����ܿ�����޿�����޿����q����q����b����b����@B����@B����@����@����@����@����o����o��������������}����}����@����@�����5�����5����o����o������������������������������v����v����?����?������������@����@����@����@��������������@����@����@.����@.�����d�����d�����������������������4߿�����4߿�����ݿ�����ݿ����ڿ����ڿ����ؿ����ؿ����ֿ����ֿ�����ӿ�����ӿ����п����п�����̿�����̿�����5ǿ�����5ǿ���������������,�����,�����ĩ�����ĩ�����x�����x�����?�����?�����?�����?�����)?�����)?�����?�����?�����,?�����,?����C?����C?����?����?����j?����j?�����?�����?�����<?�����<?�����~?�����~?����?����?����@U?����@U?����@H?����@H?����)?����)?�����?�����?����@?����@?����]?����]?����?����?����@p?����@p?����?����?�����-?�����-?�����j?�����j?����?����?����@?����@?����@?����@?����{?����{?����@G?����@G?����?����?����?����?�����%?�����%?����@?����@?����?����?�����D?�����D?�����|?�����|?����?����?�����?�����?�����p?�����p?�����V?�����V?����?����?�����?�����?�����`?�����`?�����?�����?�����H?�����H?�����A?�����A?�����?�����?�����P?�����P?�����r?�����r?�����X?�����X?�����?�����?���������������ҳ�����ҳ���������������ſ�����ſ�����ʿ�����ʿ�����Ͽ�����Ͽ����ҿ����ҿ�����"տ�����"տ�����׿�����׿����ٿ����ٿ�����>ܿ�����>ܿ����f޿����f޿����8����8����-����-������������������������������K����K�������������c�����c������������$����$����@d����@d����@����@������������@����@��������������@N����@N����������������������3����3����@����@����� ����� ����Y����Y������������@����@�������������߿�����߿����ݿ����ݿ����`ۿ����`ۿ����ٿ����ٿ�����ֿ�����ֿ�����(Կ�����(Կ�����ѿ�����ѿ�����Ϳ�����Ϳ�����tȿ�����tȿ�����¿�����¿���������������访�����访���������������?�����?�����?�����?�����Ƚ?�����Ƚ?�����t?�����t?�����?�����?�����R?�����R?�����J?�����J?����?����?����R?����R?����?����?�����?�����?����)?����)?����?����?����?����?�����?�����?����?����?����@?����@?�����9?�����9?����?����?����U?����U?����?����?�����?�����?�����^?�����^?����?����?����?����?����?����?����@?����@?�����U?�����U?����?����?����?����?�����B?�����B?����?����?�����?�����?����n?����n?�����?�����?����?����?����@?����@?����?����?�����?�����?�����?�����?�����U?�����U?�����?�����?�����r?�����r?�����?�����?�����y?�����y?�����?�����?�����?�����?������?������?�����?�����?�����?�����?���������������@�����@���������������ÿ�����ÿ�����Rɿ�����Rɿ�����ο�����ο����ѿ����ѿ����Կ����Կ���� ׿���� ׿����pٿ����pٿ�����ۿ�����ۿ����ݿ����ݿ�����߿�����߿����@����@��������������������@w����@w����&����&������������G����G����@����@��������������W����W��������������������@����@����@����@����[����[�����������������������P�����P����@����@����1����1������������@����@��������������������������������޿����޿�����ۿ�����ۿ����ٿ����ٿ�����6׿�����6׿����Կ����Կ�����,ҿ�����,ҿ�����Ͽ�����Ͽ�����ɿ�����ɿ�����4Ŀ�����4Ŀ�����F�����F���������������К�����К�����?�����?�����?�����?�����<?�����<?�����2?�����2?�����?�����?�����?�����?����?����?����E?����E?����?����?�����,?�����,?����z?����z?����?����?����?����?����?����?����?����?�����?�����?�����b?�����b?����?����?����?����?����@9?����@9?����?����?���� ?���� ?�����Q?�����Q?����?����?����?����?����@?����@?����?����?����a?����a?����@!?����@!?����@?����@?����]?����]?����?����?����@D?����@D?����?����?����@?����@?�����?�����?����"?����"?�����-?�����-?�����N?�����N?�����$?�����$?����?����?�����}?�����}?����?����?�����w?�����w?�����?�����?�����O?�����O?�����?�����?�����?�����?�����N?�����N?�����?�����?�����Њ�����Њ�����`�����`���������������¿�����¿�����ȿ�����ȿ�����Ϳ�����Ϳ����fѿ����fѿ����ӿ����ӿ�����}ֿ�����}ֿ����ؿ����ؿ�����9ۿ�����9ۿ�����oݿ�����oݿ����߿����߿����@����@��������������������L����L�������������������������+�����+������������@�����@������J�����J����@}����@}������������������������@����@����g����g����)����)����@����@�����k�����k����@����@����V����V��������������������������������=����=����I����I����޿����޿����dܿ����dܿ�����!ڿ�����!ڿ�����׿�����׿����Mտ����Mտ����ҿ����ҿ����$п����$п�����ʿ�����ʿ�����uſ�����uſ�����п�����п�������������������������?�����?�����̪?�����̪?�����?�����?�����?�����?�����u?�����u?�����?�����?�����?�����?����?����?�����5?�����5?����?����?�����?�����?�����0?�����0?�����L?�����L?����@?����@?����?����?����m?����m?�����7?�����7?�����?�����?�����?�����?����@?����@?����@?����@?����?����?����B?����B?����x?����x?�����?�����?����?����?����@?����@?����@m?����@m?����1?����1?�����?�����?����@x?����@x?����?����?����@h?����@h?����@?����@?����?����?����8?����8?�����X?�����X?����@f?����@f?�����?�����?����?����?�����d?�����d?���� ?���� ?�����?�����?���� ?���� ?����q?����q?�����?�����?�����?�����?�����?�����?�����޵?�����޵?�����$?�����$?�����h�����h�����8�����8�����h�����h�����M�����M�����ƿ�����ƿ�����G̿�����G̿����п����п�����gӿ�����gӿ�����տ�����տ�����]ؿ�����]ؿ����ڿ����ڿ�����ܿ�����ܿ����߿����߿����@����@����w����w����U����U�����!�����!����@����@����}����}����@ ����@ ��������������������@;����@;����s����s������������������������������r����r����9����9������������@����@����@ ����@ �����z�����z����@����@������������P����P����r����r������������߿����߿�����ܿ�����ܿ�����ڿ�����ڿ�����Oؿ�����Oؿ����տ����տ�����Xӿ�����Xӿ����п����п�����'̿�����'̿�����ƿ�����ƿ�����-�����-�����&�����&���������������`�����`�����?�����?����� ?����� ?�����?�����?�����6?�����6?�����?�����?�����?�����?����?����?����?����?�����?�����?����q?����q?�����?�����?�����?�����?�����l?�����l?����@]?����@]?����@=?����@=?����� ?����� ?����@?����@?�����l?�����l?�����?�����?����z?����z?����@?����@?����3?����3?����n?����n?����?����?����?����?����?����?����w?����w?����@A?����@A?����?����?����?����?����@?����@?����?����?�����?�����?����2?����2?�����i?�����i?����?����?����?����?�����@?�����@?����#?����#?����?����?����?����?����&?����&?����?����?����� ?����� ?�����?�����?�����U?�����U?�����?�����?�����n?�����n?�����H?�����H?�����|?�����|?���������������ش�����ش����� ����� �����ſ�����ſ����� ˿����� ˿�����4п�����4п����ҿ����ҿ�����\տ�����\տ�����׿�����׿����.ڿ����.ڿ�����qܿ�����qܿ����޿����޿����O����O����B����B����$����$������������@����@�����Z�����Z������������m����m������������@+����@+����h����h������������������������������|����|����H����H�����������������������(�����(��������������������@H����@H����������������������������{߿����{߿����bݿ����bݿ����+ۿ����+ۿ����ؿ����ؿ����nֿ����nֿ�����ӿ�����ӿ�����Wѿ�����Wѿ�����`Ϳ�����`Ϳ�����ǿ�����ǿ�����q¿�����q¿���������������ܬ�����ܬ���������������?�����?�����?�����?�����̾?�����̾?�����?�����?�����o?�����o?�����?�����?����?����?�����?�����?�����?�����?�����?�����?�����1?�����1?����Z?����Z?����2?����2?�����(?�����(?����� ?����� ?����?����?����?����?����G?����G?����?����?����@`?����@`?����?����?�����#?�����#?�����c?�����c?����?����?����?����?����?����?�����?�����?����O?����O?�����?�����?�����?�����?����6?����6?����?����?����?����?����]?����]?����?����?����@?����@?����@?����@?�����?�����?�����?�����?����m?����m?����?����?�����?�����?�����7?�����7?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����l?�����l?�����?�����?�����؛�����؛�����H�����H���������������TĿ�����TĿ�����ɿ�����ɿ�����3Ͽ�����3Ͽ�����;ҿ�����;ҿ�����Կ�����Կ�����D׿�����D׿�����ٿ�����ٿ�����ۿ�����ۿ�����޿�����޿������������� ����� ����������������������������������5����5������������R����R������������@����@����\����\������������������������������������������V����V����@����@��������������D����D����@����@����"����"�����s�����s����������������������������߿����߿�����ݿ�����ݿ�����ۿ�����ۿ�����cٿ�����cٿ����ֿ����ֿ����Կ����Կ�����ѿ�����ѿ�����ο�����ο�����2ɿ�����2ɿ�����ÿ�����ÿ�����B�����B���������������������������?�����?������?������?�����B?�����B?�����?�����?�����2?�����2?�����?�����?�����?�����?����?����?����?����?�����c?�����c?�����?�����?�����?�����?����?����?����?����?����?����?����?����?�����s?�����s?����"?����"?����@?����@?����D?����D?�����?�����?����@?����@?����V?����V?�����?�����?�����?�����?����?����?�����?�����?����\?����\?����@?����@?����?����?����R?����R?����?����?����5?����5?�����?�����?�����?�����?�����?�����?����� ?����� ?����?����?�����?�����?�����?�����?�����?�����?�����D?�����D?�����?�����?�����;?�����;?�����3?�����3?�����?�����?�����T?�����T?�����?�����?�����H?�����H?�����؛?�����؛?���������������l�����l���������������ÿ�����ÿ�����ȿ�����ȿ�����Ϳ�����Ϳ����ѿ����ѿ�����7Կ�����7Կ�����ֿ�����ֿ����ٿ����ٿ����mۿ����mۿ�����ݿ�����ݿ�����߿�����߿����@����@����@����@������������]����]��������������������6����6������������������������O����O���������������������������������������c�����c�����#�����#������������@`����@`������������G����G��������������������� ����� �����(�����(����2����2����Z޿����Z޿�����1ܿ�����1ܿ�����ٿ�����ٿ�����׿�����׿�����տ�����տ����ҿ����ҿ�����Ͽ�����Ͽ�����oʿ�����oʿ�����Ŀ�����Ŀ�����̾�����̾�������������������������?�����?�����ܬ?�����ܬ?�����?�����?�����q?�����q?�����?�����?�����`?�����`?�����W?�����W?�����?�����?����n?����n?����?����?����+?����+?����b?����b?����{?����{?����?����?����?����?����?����?����@H?����@H?����?����?����?����?�����(?�����(?�����?�����?����?����?����H?����H?����|?����|?����?����?�����?�����?����?����?����h?����h?����@+?����@+?����?����?����m?����m?����?����?�����Z?�����Z?����@?����@?����?����?����$?����$?����B?����B?����O?����O?����?����?�����q?�����q?����.?����.?�����?�����?�����\?�����\?����?����?�����4?�����4?����� ?����� ?�����?�����?����� ?����� ?�����ش?�����ش?�����?�����?�����|�����|�����H�����H�����n�����n���������������Uǿ�����Uǿ�����̿�����̿����� ѿ����� ѿ����ӿ����ӿ����&ֿ����&ֿ����ؿ����ؿ����ڿ����ڿ����#ݿ����#ݿ�����@߿�����@߿���������������������i�����i����2����2����������������������@����@��������������������@A����@A����w����w����������������������������n����n����3����3����@����@����z����z���������������l�����l����@����@����� ����� ����@=����@=����@]����@]�����l�����l�����޿�����޿�����ܿ�����ܿ����qڿ����qڿ�����ؿ�����ؿ����տ����տ����ӿ����ӿ�����п�����п�����˿�����˿�����6ƿ�����6ƿ��������������� ����� ���������������`?�����`?�����?�����?�����&?�����&?�����-?�����-?�����?�����?�����'?�����'?����?����?�����X?�����X?����?����?�����O?�����O?�����?�����?�����?�����?����?����?����?����?����r?����r?����P?����P?����?����?����@?����@?�����z?�����z?����@ ?����@ ?����@?����@?����?����?����9?����9?����r?����r?�����?�����?����?����?����?����?����s?����s?����@;?����@;?����?����?����?����?����@ ?����@ ?����}?����}?����@?����@?�����!?�����!?����U?����U?����w?����w?����@?����@?����?����?�����?�����?����?����?�����]?�����]?�����?�����?�����g?�����g?����?����?�����G?�����G?�����?�����?�����M?�����M?�����h?�����h?�����8?�����8?�����h?�����h?�����$�����$�����޵�����޵���������������ƿ�����ƿ�����˿�����˿����qп����qп���� ӿ���� ӿ�����տ�����տ���� ؿ���� ؿ�����dڿ�����dڿ����ܿ����ܿ�����޿�����޿����@f����@f�����X�����X����8����8������������@����@����@h����@h������������@x����@x��������������1����1����@m����@m����@����@����������������������x����x����B����B������������@����@����@����@�������������������������7�����7����m����m������������@����@�����L߿�����L߿�����0ݿ�����0ݿ�����ڿ�����ڿ����ؿ����ؿ�����5ֿ�����5ֿ����ӿ����ӿ�����ѿ�����ѿ�����̿�����̿�����uǿ�����uǿ�������������������������̪�����̪���������������?�����?�����?�����?�����п?�����п?�����u?�����u?�����?�����?����$?����$?����?����?����M?����M?�����?�����?�����!?�����!?����d?����d?����?����?����I?����I?����=?����=?����?����?�����?�����?�����?�����?����V?����V?����@?����@?�����k?�����k?����@?����@?����)?����)?����g?����g?����@?����@?�����?�����?�����?�����?����@}?����@}?�����J?�����J?����@�?����@�?����?����?�����+?�����+?�����?�����?�����?�����?����L?����L?����?����?����?����?����@?����@?����?����?�����o?�����o?�����9?�����9?����?����?�����}?�����}?����?����?����f?����f?�����?�����?�����?�����?�����?�����?�����?�����?�����`?�����`?�����Њ?�����Њ?���������������N�����N���������������Ŀ�����Ŀ�����Oʿ�����Oʿ�����Ͽ�����Ͽ�����wҿ�����wҿ����տ����տ�����}׿�����}׿����ٿ����ٿ�����$ܿ�����$ܿ�����N޿�����N޿�����-�����-����"����"��������������@����@������������@D����@D������������]����]����@����@����@!����@!����a����a������������@����@���������������������Q�����Q���� ���� ������������@9����@9���������������������b�����b������������������������������߿����߿����ݿ����ݿ����zۿ����zۿ�����,ٿ�����,ٿ����ֿ����ֿ����EԿ����EԿ����ѿ����ѿ�����ο�����ο�����ȿ�����ȿ�����2ÿ�����2ÿ�����<�����<�������������������������К?�����К?�����?�����?�����F?�����F?�����4?�����4?�����?�����?�����?�����?�����,?�����,?����?����?�����6?�����6?����?����?�����?�����?����?����?�����?�����?����?����?�����?�����?����@?����@?����?����?����1?����1?����@?����@?�����P?�����P?�����?�����?����?����?����[?����[?����@?����@?����@?����@?����?����?����?����?����W?����W?�����?�����?����@?����@?����G?����G?����?����?����&?����&?����@w?����@w?����?����?����?����?����@?����@?�����?�����?����?����?�����?�����?����p?����p?���� ?���� ?����?����?����?����?�����?�����?�����R?�����R?�����?�����?�����?�����?�����@?�����@?�����?�����?�������������������������������������ÿ�����ÿ�����ɿ�����ɿ�����yο�����yο�����ѿ�����ѿ�����rԿ�����rԿ�����ֿ�����ֿ�����Uٿ�����Uٿ�����ۿ�����ۿ�����ݿ�����ݿ����߿����߿����@����@����������������������n����n�����������������������B�����B���������������������U�����U����@����@�����������������������������^�����^����������������������U����U�������������9�����9����@����@��������������������������������������)޿����)޿�����ۿ�����ۿ����ٿ����ٿ����R׿����R׿����Կ����Կ�����Jҿ�����Jҿ�����RϿ�����RϿ�����ɿ�����ɿ�����tĿ�����tĿ�����Ƚ�����Ƚ�������������������������?�����?�����?�����?�����?�����?�����?�����?�����t?�����t?�����?�����?�����?�����?�����(?�����(?�����?�����?����?����?����`?����`?����?����?�����?�����?����?����?����@?����@?����?����?����Y?����Y?����� ?����� ?����@?����@?����3?����3?����?����?�����?�����?����@N?����@N?�����?�����?����@?����@?����?����?����@?����@?����@d?����@d?����$?����$?����?����?�����c?�����c?����?����?����K?����K?����?����?����?����?�����?�����?����-?����-?����8?����8?����f?����f?�����>?�����>?����?����?�����?�����?�����"?�����"?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����ҳ?�����ҳ?�����?�����?���������������X�����X�����r�����r�����P¿�����P¿�����ǿ�����ǿ�����AͿ�����AͿ�����Hѿ�����Hѿ�����ӿ�����ӿ�����`ֿ�����`ֿ�����ؿ�����ؿ����ۿ����ۿ�����Vݿ�����Vݿ�����p߿�����p߿�����������������������|�����|�����D�����D������������@����@�����%�����%��������������������@G����@G����{����{����@����@����@����@�������������j�����j�����-�����-������������@p����@p������������]����]����@����@��������������)����)����@H����@H����@U����@U����޿����޿�����~ܿ�����~ܿ�����<ڿ�����<ڿ�����׿�����׿����jտ����jտ����ҿ����ҿ����Cп����Cп�����,˿�����,˿�����ſ�����ſ�����)�����)�������������������������x?�����x?�����ĩ?�����ĩ?�����,?�����,?�����?�����?�����5?�����5?�����?�����?����?����?�����?�����?����?����?����?����?����?����?�����?�����?�����4?�����4?�����?�����?����?����?�����d?�����d?����@.?����@.?����@?����@?�����?�����?����@?����@?����@?����@?����?����?����??����??����v?����v?�����?�����?����?����?����?����?����o?����o?�����5?�����5?����@?����@?����}?����}?�����?�����?����o?����o?����@?����@?����@?����@?����@B?����@B?����b?����b?����q?����q?�����?�����?�����?�����?�����?�����?�����%?�����%?�����?�����?����+?����+?����?����?�����?�����?�����V?�����V?�����?�����?�����b?�����b?�����,?�����,?�����P�����P�����4�����4�����䶿�����䶿����� ����� �����ƿ�����ƿ�����̿�����̿�����п�����п����Iӿ����Iӿ�����տ�����տ�����Aؿ�����Aؿ����ڿ����ڿ����ܿ����ܿ����޿����޿�����}�����}����@m����@m�����L�����L����@����@����@����@����v����v������������������������������@8����@8����q����q������������������������������t����t����<����<������������@����@����@����@��������������@����@����%����%����Z����Z�����}�����}��������������߿����߿����ܿ����ܿ����ڿ����ڿ�����kؿ�����kؿ����տ����տ�����vӿ�����vӿ�����п�����п�����f̿�����f̿�����ƿ�����ƿ�����n�����n�������������������������p�����p�����?�����?�����?�����?�����j?�����j?�����?�����?�����k?�����k?�����b?�����b?����?����?����?����?����?����?����V?����V?����?����?�����?�����?����`?����`?����R?����R?����3?����3?�����?�����?����@?����@?����d?����d?����?����?����u?����u?�����?�����?����@0?����@0?����@l?����@l?����?����?����?����?����?����?����y?����y?����@D?����@D?����?����?����?����?����@?����@?����@?����@?�����?�����?����@;?����@;?����r?����r?�����?�����?�����?�����?�����X?�����X?����<?����<?�����?�����?�����?�����?����C?����C?����?����?����)?����)?�����?�����?�����?�����?�����?�����?�����?�����?�����P?�����P?�����?�����?����� ����� �����T�����T���������������Uſ�����Uſ�����ʿ�����ʿ�����п�����п����ҿ����ҿ�����?տ�����?տ�����׿�����׿����ڿ����ڿ����Wܿ����Wܿ�����޿�����޿�����D�����D�����8�����8��������������������������������R����R��������������h����h����@����@�����(�����(����f����f����������������������������@~����@~����K����K����@����@��������������-����-������������@����@�����Q�����Q����@����@��������������������߿����߿����{ݿ����{ݿ�����Fۿ�����Fۿ�����ؿ�����ؿ�����ֿ�����ֿ���� Կ���� Կ����uѿ����uѿ�����Ϳ�����Ϳ�����4ȿ�����4ȿ�����¿�����¿�����6�����6�����䭿�����䭿���������������?�����?����� ?����� ?�����J?�����J?�����?�����?�����/?�����/?�����?�����?�����h?�����h?�����?�����?����n?����n?�����?�����?�����?�����?�����B?�����B?����@'?����@'?����@?����@?�����?�����?����?����?����?����?����@?����@?����@?����@?�����[?�����[?����@?����@?����?����?����`?����`?����@?����@?�����?�����?����@?����@?����?����?����R?����R?���� ?���� ?����?����?����@<?����@<?����@?����@?����@?����@?����@f?����@f?����?����?����?����?����@?����@?����?����?�����?�����?�����?�����?�����:?�����:?����?����?����T?����T?����?����?�����:?�����:?�����?�����?�����S?�����S?�����~?�����~?�����:?�����:?�����?�����?�����ș�����ș�����ı�����ı���������������Ŀ�����Ŀ�����ɿ�����ɿ�����ο�����ο����ҿ����ҿ����Կ����Կ�����(׿�����(׿�����ٿ�����ٿ�����ۿ�����ۿ����޿����޿����@ ����@ �����������������������������������������.�����.���������������M�����M��������������������@Z����@Z���������������������������������������Y�����Y��������������������@J����@J������������@*����@*����{����{���������������������������������������ݿ�����ݿ�����ۿ�����ۿ�����~ٿ�����~ٿ����׿����׿�����Կ�����Կ���� ҿ���� ҿ�����ο�����ο�����rɿ�����rɿ�����ÿ�����ÿ�����ļ�����ļ�������������������������?�����?�����|?�����|?�����?�����?�����s?�����s?�����?�����?�����Y?�����Y?�����?�����?�����c?�����c?�����?�����?����G?����G?�����?�����?����?����?�����?�����?����?����?����?����?����?����?����j?����j?����@?����@?����?����?�����??�����??����@?����@?���� ?���� ?����S?����S?����?����?����?����?����?����?����?����?����@_?����@_?����?����?�����?�����?����@X?����@X?�����?�����?����<?����<?����@?����@?�����?�����?�����?�����?����?����?����!?����!?����5?����5?����� ?����� ?�����?�����?����`?����`?����?����?�����Y?�����Y?�����q?�����q?�����?�����?�����?�����?����� ?����� ?�����̲?�����̲?�����?�����?���������������d�����d�����x�����x�����¿�����¿�����Tȿ�����Tȿ�����Ϳ�����Ϳ�����ѿ�����ѿ����Կ����Կ����ֿ����ֿ�����ٿ�����ٿ�����Sۿ�����Sۿ�����ݿ�����ݿ�����߿�����߿����@����@������������������������@U����@U����������������������0����0����@����@����@����@�����M�����M����@����@������������������������������@e����@e����@&����@&��������������e����e��������������@O����@O����������������������������������2����2����@>����@>�����s޿�����s޿����Jܿ����Jܿ�����ڿ�����ڿ�����׿�����׿����0տ����0տ����ҿ����ҿ����п����п�����ʿ�����ʿ�����5ſ�����5ſ�����N�����N�������������������������?�����?�����ԫ?�����ԫ?�����2?�����2?�����0?�����0?�����?�����?�����"?�����"?����8?����8?�����?�����?�����R?�����R?�����?�����?����?����?����I?����I?�����d?�����d?����?����?����@?����@?����@w?����@w?����??����??����?����?����?����?�����"?�����"?����@?����@?����?����?����E?����E?����z?����z?����?����?����@?����@?�����?�����?�����k?�����k?����.?����.?����?����?�����s?�����s?����?����?����@a?����@a?����@?����@?����?����?����.?����.?����M?����M?�����[?�����[?�����?�����?�����?�����?����I?����I?����?����?�����y?�����y?����?����?����R?����R?�����K?�����K?�����?�����?�����J?�����J?�����\?�����\?�����?�����?�����t�����t�����@�����@�����귿�����귿���������������ǿ�����ǿ�����̿�����̿����п����п�����ӿ�����ӿ����� ֿ����� ֿ����xؿ����xؿ�����ڿ�����ڿ����� ݿ����� ݿ����(߿����(߿������������@����@����@_����@_����)����)����@����@������������@����@��������������������@>����@>����u����u������������������������������p����p����6����6����@����@�������������������������s�����s����@����@�������������G�����G�����h�����h����@w����@w�����޿�����޿����ܿ����ܿ����ڿ����ڿ�����3ؿ�����3ؿ����տ����տ����:ӿ����:ӿ����п����п�����˿�����˿�����uƿ�����uƿ���������������������������������������������������?�����?�����?�����?�����?�����?�����u?�����u?�����?�����?����?����?����:?����:?����?����?�����3?�����3?����?����?����?����?�����?�����?����@w?����@w?�����h?�����h?�����G?�����G?����?����?����@?����@?�����s?�����s?�����?�����?�����?�����?����@?����@?����6?����6?����p?����p?�����?�����?����?����?����?����?����u?����u?����@>?����@>?����?����?����?����?����@?����@?����?����?����@?����@?����)?����)?����@_?����@_?����@?����@?����?����?����(?����(?����� ?����� ?�����?�����?����x?����x?����� ?����� ?�����?�����?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����@?�����@?�����t?�����t?���������������\�����\�����J�����J�����ſ�����ſ�����K˿�����K˿����Rп����Rп����ҿ����ҿ�����yտ�����yտ����׿����׿����Iڿ����Iڿ�����ܿ�����ܿ�����޿�����޿�����[�����[����M����M����.����.������������@����@����@a����@a�������������s�����s������������.����.�����k�����k��������������@����@������������z����z����E����E������������@����@�����"�����"��������������������?����?����@w����@w����@����@�������������d߿�����d߿����Iݿ����Iݿ����ۿ����ۿ�����ؿ�����ؿ�����Rֿ�����Rֿ�����ӿ�����ӿ����8ѿ����8ѿ�����"Ϳ�����"Ϳ�����ǿ�����ǿ�����0¿�����0¿�����2�����2�����ԫ�����ԫ���������������?�����?�����?�����?�����N?�����N?�����5?�����5?�����?�����?����?����?����?����?����0?����0?�����?�����?�����?�����?����J?����J?�����s?�����s?����@>?����@>?����2?����2?�����?�����?�����?�����?�����?�����?����@O?����@O?�����?�����?����e?����e?�����?�����?����@&?����@&?����@e?����@e?�����?�����?����?����?����?����?����@?����@?�����M?�����M?����@?����@?����@?����@?����0?����0?����?����?�����?�����?����@U?����@U?�����?�����?�����?�����?����@?����@?�����?�����?�����?�����?�����S?�����S?�����?�����?����?����?����?����?�����?�����?�����?�����?�����T?�����T?�����?�����?�����x?�����x?�����d?�����d?�����?�����?�����蝿�����蝿�����̲�����̲����� ����� �����Ŀ�����Ŀ�����ʿ�����ʿ�����qϿ�����qϿ�����Yҿ�����Yҿ����Կ����Կ����`׿����`׿�����ٿ�����ٿ����� ܿ����� ܿ����5޿����5޿����!����!��������������������������������@����@����<����<��������������@X����@X����������������������@_����@_������������������������������������S����S���� ���� ����@����@�����?�����?������������@����@����j����j�����������������������������߿�����߿����ݿ����ݿ�����ۿ�����ۿ����Gٿ����Gٿ�����ֿ�����ֿ�����cԿ�����cԿ�����ѿ�����ѿ�����Yο�����Yο�����ȿ�����ȿ�����sÿ�����sÿ���������������|�����|���������������?�����?�����?�����?�����ļ?�����ļ?�����?�����?�����r?�����r?�����?�����?���� ?���� ?�����?�����?����?����?�����~?�����~?�����?�����?�����?�����?����?����?����?����?����?����?�����?�����?����{?����{?����@*?����@*?����?����?����@J?����@J?����?����?����?����?�����Y?�����Y?����?����?����?����?�����?�����?����?����?����@Z?����@Z?����?����?����?����?�����M?�����M?�����?�����?�����.?�����.?����?����?����?����?�����?�����?�����?�����?����@ ?����@ ?����?����?�����?�����?�����?�����?�����(?�����(?����?����?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����ı?�����ı?�����ș?�����ș?���������������:�����:�����~�����~�����Sÿ�����Sÿ�����ȿ�����ȿ�����:ο�����:ο����ѿ����ѿ����TԿ����TԿ����ֿ����ֿ�����:ٿ�����:ٿ�����ۿ�����ۿ�����ݿ�����ݿ����߿����߿����@����@��������������������@f����@f����@����@����@����@����@<����@<������������ ���� ����R����R������������@����@��������������@����@����`����`������������@����@�����[�����[����@����@����@����@������������������������������@����@����@'����@'�����B޿�����B޿�����ܿ�����ܿ�����ٿ�����ٿ����n׿����n׿�����Կ�����Կ�����hҿ�����hҿ�����Ͽ�����Ͽ�����/ʿ�����/ʿ�����Ŀ�����Ŀ�����J�����J����� ����� ���������������?�����?�����?�����?�����6?�����6?�����?�����?�����4?�����4?�����?�����?����u?����u?���� ?���� ?�����?�����?�����?�����?�����F?�����F?����{?����{?����?����?����?����?����?����?����@?����@?�����Q?�����Q?����@?����@?����?����?����-?����-?�����?�����?����@?����@?����K?����K?����@~?����@~?����?����?����?����?����?����?����f?����f?�����(?�����(?����@?����@?����h?����h?�����?�����?����R?����R?�����?�����?����?����?�����?�����?�����8?�����8?�����D?�����D?�����?�����?����W?����W?����?����?�����?�����?�����??�����??����?����?�����?�����?�����?�����?�����U?�����U?�����?�����?�����T?�����T?����� ?����� ?���������������P�����P���������������¿�����¿�����ǿ�����ǿ�����Ϳ�����Ϳ����)ѿ����)ѿ����ӿ����ӿ����Cֿ����Cֿ�����ؿ�����ؿ�����ۿ�����ۿ����<ݿ����<ݿ�����X߿�����X߿������������������������r����r����@;����@;��������������@����@����@����@��������������������@D����@D����y����y����������������������������@l����@l����@0����@0��������������u����u������������d����d����@����@��������������3����3����R����R����`����`�����޿�����޿����ܿ����ܿ����Vڿ����Vڿ����׿����׿����տ����տ����ҿ����ҿ�����bп�����bп�����k˿�����k˿�����ſ�����ſ�����j�����j�������������������������p?�����p?�����?�����?�����?�����?�����n?�����n?�����?�����?�����f?�����f?�����?�����?�����v?�����v?����?����?�����k?�����k?����?����?����?����?����?����?�����?�����?�����}?�����}?����Z?����Z?����%?����%?����@?����@?�����?�����?����@?����@?����@?����@?����?����?����<?����<?����t?����t?�����?�����?����?����?����?����?����q?����q?����@8?����@8?����?����?����?����?�����?�����?����v?����v?����@?����@?����@?����@?�����L?�����L?����@m?����@m?�����}?�����}?����?����?����?����?����?����?�����A?�����A?�����?�����?����I?����I?�����?�����?�����?�����?�����?�����?����� ?����� ?�����?�����?�����4?�����4?�����P?�����P?�����,�����,�����b�����b���������������Vƿ�����Vƿ�����˿�����˿����п����п����+ӿ����+ӿ�����տ�����տ�����%ؿ�����%ؿ�����ڿ�����ڿ�����ܿ�����ܿ�����޿�����޿����q����q����b����b����@B����@B����@����@����@����@����o����o��������������}����}����@����@�����5�����5����o����o������������������������������v����v����?����?������������@����@����@����@��������������@����@����@.����@.�����d�����d�����������������������4߿�����4߿�����ݿ�����ݿ����ڿ����ڿ����ؿ����ؿ����ֿ����ֿ�����ӿ�����ӿ����п����п�����̿�����̿�����5ǿ�����5ǿ���������������,�����,�����ĩ�����ĩ�����x�����x�����?�����?�����?�����?�����)?�����)?�����?�����?�����,?�����,?����C?����C?����?����?����j?����j?�����?�����?�����<?�����<?�����~?�����~?����?����?����@U?����@U?����@H?����@H?����)?����)?�����?�����?����@?����@?����]?����]?����?����?����@p?����@p?����?����?�����-?�����-?�����j?�����j?����?����?����@?����@?����@?����@?����{?����{?����@G?����@G?����?����?����?����?�����%?�����%?����@?����@?����?����?�����D?�����D?�����|?�����|?����?����?�����?�����?�����p?�����p?�����V?�����V?����?����?�����?�����?�����`?�����`?�����?�����?�����H?�����H?�����A?�����A?�����?�����?�����P?�����P?�����r?�����r?�����X?�����X?�����?�����?���������������ҳ�����ҳ���������������ſ�����ſ�����ʿ�����ʿ�����Ͽ�����Ͽ����ҿ����ҿ�����"տ�����"տ�����׿�����׿����ٿ����ٿ�����>ܿ�����>ܿ����f޿����f޿����8����8����-����-������������������������������K����K�������������c�����c������������$����$����@d����@d����@����@������������@����@��������������@N����@N����������������������3����3����@����@����� ����� ����Y����Y������������@����@�������������߿�����߿����ݿ����ݿ����`ۿ����`ۿ����ٿ����ٿ�����ֿ�����ֿ�����(Կ�����(Կ�����ѿ�����ѿ�����Ϳ�����Ϳ�����tȿ�����tȿ�����¿�����¿���������������访�����访���������������?�����?�����?�����?�����Ƚ?�����Ƚ?�����t?�����t?�����?�����?�����R?�����R?�����J?�����J?����?����?����R?����R?����?����?�����?�����?����)?����)?����?����?����?����?�����?�����?����?����?����@?����@?�����9?�����9?����?����?����U?����U?����?����?�����?�����?�����^?�����^?����?����?����?����?����?����?����@?����@?�����U?�����U?����?����?����?����?�����B?�����B?����?����?�����?�����?����n?����n?�����?�����?����?����?����@?����@?����?����?�����?�����?�����?�����?�����U?�����U?�����?�����?�����r?�����r?�����?�����?�����y?�����y?�����?�����?�����?�����?������?������?�����?�����?�����?�����?���������������@�����@���������������ÿ�����ÿ�����Rɿ�����Rɿ�����ο�����ο����ѿ����ѿ����Կ����Կ���� ׿���� ׿����pٿ����pٿ�����ۿ�����ۿ����ݿ����ݿ�����߿�����߿����@����@��������������������@w����@w����&����&������������G����G����@����@��������������W����W��������������������@����@����@����@����[����[�����������������������P�����P����@����@����1����1������������@����@��������������������������������޿����޿�����ۿ�����ۿ����ٿ����ٿ�����6׿�����6׿����Կ����Կ�����,ҿ�����,ҿ�����Ͽ�����Ͽ�����ɿ�����ɿ�����4Ŀ�����4Ŀ�����F�����F���������������К�����К�����?�����?�����?�����?�����<?�����<?�����2?�����2?�����?�����?�����?�����?����?����?����E?����E?����?����?�����,?�����,?����z?����z?����?����?����?����?����?����?����?����?�����?�����?�����b?�����b?����?����?����?����?����@9?����@9?����?����?���� ?���� ?�����Q?�����Q?����?����?����?����?����@?����@?����?����?����a?����a?����@!?����@!?����@?����@?����]?����]?����?����?����@D?����@D?����?����?����@?����@?�����?�����?����"?����"?�����-?�����-?�����N?�����N?�����$?�����$?����?����?�����}?�����}?����?����?�����w?�����w?�����?�����?�����O?�����O?�����?�����?�����?�����?�����N?�����N?�����?�����?�����Њ�����Њ�����`�����`���������������¿�����¿�����ȿ�����ȿ�����Ϳ�����Ϳ����fѿ����fѿ����ӿ����ӿ�����}ֿ�����}ֿ����ؿ����ؿ�����9ۿ�����9ۿ�����oݿ�����oݿ����߿����߿����@����@��������������������L����L�������������������������+�����+������������@�����@������J�����J����@}����@}������������������������@����@����g����g����)����)����@����@�����k�����k����@����@����V����V��������������������������������=����=����I����I����޿����޿����dܿ����dܿ�����!ڿ�����!ڿ�����׿�����׿����Mտ����Mտ����ҿ����ҿ����$п����$п�����ʿ�����ʿ�����uſ�����uſ�����п�����п�������������������������?�����?�����̪?�����̪?�����?�����?�����?�����?�����u?�����u?�����?�����?�����?�����?����?����?�����5?�����5?����?����?�����?�����?�����0?�����0?�����L?�����L?����@?����@?����?����?����m?����m?�����7?�����7?�����?�����?�����?�����?����@?����@?����@?����@?����?����?����B?����B?����x?����x?�����?�����?����?����?����@?����@?����@m?����@m?����1?����1?�����?�����?����@x?����@x?����?����?����@h?����@h?����@?����@?����?����?����8?����8?�����X?�����X?����@f?����@f?�����?�����?����?����?�����d?�����d?���� ?���� ?�����?�����?���� ?���� ?����q?����q?�����?�����?�����?�����?�����?�����?�����޵?�����޵?�����$?�����$?�����h�����h�����8�����8�����h�����h�����M�����M�����ƿ�����ƿ�����G̿�����G̿����п����п�����gӿ�����gӿ�����տ�����տ�����]ؿ�����]ؿ����ڿ����ڿ�����ܿ�����ܿ����߿����߿����@����@����w����w����U����U�����!�����!����@����@����}����}����@ ����@ ��������������������@;����@;����s����s������������������������������r����r����9����9������������@����@����@ ����@ �����z�����z����@����@������������P����P����r����r������������߿����߿�����ܿ�����ܿ�����ڿ�����ڿ�����Oؿ�����Oؿ����տ����տ�����Xӿ�����Xӿ����п����п�����'̿�����'̿�����ƿ�����ƿ�����-�����-�����&�����&���������������`�����`�����?�����?����� ?����� ?�����?�����?�����6?�����6?�����?�����?�����?�����?����?����?����?����?�����?�����?����q?����q?�����?�����?�����?�����?�����l?�����l?����@]?����@]?����@=?����@=?����� ?����� ?����@?����@?�����l?�����l?�����?�����?����z?����z?����@?����@?����3?����3?����n?����n?����?����?����?����?����?����?����w?����w?����@A?����@A?����?����?����?����?����@?����@?����?����?�����?�����?����2?����2?�����i?�����i?����?����?����?����?�����@?�����@?����#?����#?����?����?����?����?����&?����&?����?����?����� ?����� ?�����?�����?�����U?�����U?�����?�����?�����n?�����n?�����H?�����H?�����|?�����|?���������������ش�����ش����� ����� �����ſ�����ſ����� ˿����� ˿�����4п�����4п����ҿ����ҿ�����\տ�����\տ�����׿�����׿����.ڿ����.ڿ�����qܿ�����qܿ����޿����޿����O����O����B����B����$����$������������@����@�����Z�����Z������������m����m������������@+����@+����h����h������������������������������|����|����H����H�����������������������(�����(��������������������@H����@H����������������������������{߿����{߿����bݿ����bݿ����+ۿ����+ۿ����ؿ����ؿ����nֿ����nֿ�����ӿ�����ӿ�����Wѿ�����Wѿ�����`Ϳ�����`Ϳ�����ǿ�����ǿ�����q¿�����q¿���������������ܬ�����ܬ���������������?�����?�����?�����?�����̾?�����̾?�����?�����?�����o?�����o?�����?�����?����?����?�����?�����?�����?�����?�����?�����?�����1?�����1?����Z?����Z?����2?����2?�����(?�����(?����� ?����� ?����?����?����?����?����G?����G?����?����?����@`?����@`?����?����?�����#?�����#?�����c?�����c?����?����?����?����?����?����?�����?�����?����O?����O?�����?�����?�����?�����?����6?����6?����?����?����?����?����]?����]?����?����?����@?����@?����@?����@?�����?�����?�����?�����?����m?����m?����?����?�����?�����?�����7?�����7?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����l?�����l?�����?�����?�����؛�����؛�����H�����H���������������TĿ�����TĿ�����ɿ�����ɿ�����3Ͽ�����3Ͽ�����;ҿ�����;ҿ�����Կ�����Կ�����D׿�����D׿�����ٿ�����ٿ�����ۿ�����ۿ�����޿�����޿������������� ����� ����������������������������������5����5������������R����R������������@����@����\����\������������������������������������������V����V����@����@��������������D����D����@����@����"����"�����s�����s����������������������������߿����߿�����ݿ�����ݿ�����ۿ�����ۿ�����cٿ�����cٿ����ֿ����ֿ����Կ����Կ�����ѿ�����ѿ�����ο�����ο�����2ɿ�����2ɿ�����ÿ�����ÿ�����B�����B���������������������������?�����?������?������?�����B?�����B?�����?�����?�����2?�����2?�����?�����?�����?�����?����?����?����?����?�����c?�����c?�����?�����?�����?�����?����?����?����?����?����?����?����?����?�����s?�����s?����"?����"?����@?����@?����D?����D?�����?�����?����@?����@?����V?����V?�����?�����?�����?�����?����?����?�����?�����?����\?����\?����@?����@?����?����?����R?����R?����?����?����5?����5?�����?�����?�����?�����?�����?�����?����� ?����� ?����?����?�����?�����?�����?�����?�����?�����?�����D?�����D?�����?�����?�����;?�����;?�����3?�����3?�����?�����?�����T?�����T?�����?�����?�����H?�����H?�����؛?�����؛?���������������l�����l���������������ÿ�����ÿ�����ȿ�����ȿ�����Ϳ�����Ϳ����ѿ����ѿ�����7Կ�����7Կ�����ֿ�����ֿ����ٿ����ٿ����mۿ����mۿ�����ݿ�����ݿ�����߿�����߿����@����@����@����@������������]����]��������������������6����6������������������������O����O���������������������������������������c�����c�����#�����#������������@`����@`������������G����G��������������������� ����� �����(�����(����2����2����Z޿����Z޿�����1ܿ�����1ܿ�����ٿ�����ٿ�����׿�����׿�����տ�����տ����ҿ����ҿ�����Ͽ�����Ͽ�����oʿ�����oʿ�����Ŀ�����Ŀ�����̾�����̾�������������������������?�����?�����ܬ?�����ܬ?�����?�����?�����q?�����q?�����?�����?�����`?�����`?�����W?�����W?�����?�����?����n?����n?����?����?����+?����+?����b?����b?����{?����{?����?����?����?����?����?����?����@H?����@H?����?����?����?����?�����(?�����(?�����?�����?����?����?����H?����H?����|?����|?����?����?�����?�����?����?����?����h?����h?����@+?����@+?����?����?����m?����m?����?����?�����Z?�����Z?����@?����@?����?����?����$?����$?����B?����B?����O?����O?����?����?�����q?�����q?����.?����.?�����?�����?�����\?�����\?����?����?�����4?�����4?����� ?����� ?�����?�����?����� ?����� ?�����ش?�����ش?�����?�����?�����|�����|�����H�����H�����n�����n���������������Uǿ�����Uǿ�����̿�����̿����� ѿ����� ѿ����ӿ����ӿ����&ֿ����&ֿ����ؿ����ؿ����ڿ����ڿ����#ݿ����#ݿ�����@߿�����@߿���������������������i�����i����2����2����������������������@����@��������������������@A����@A����w����w����������������������������n����n����3����3����@����@����z����z���������������l�����l����@����@����� ����� ����@=����@=����@]����@]�����l�����l�����޿�����޿�����ܿ�����ܿ����qڿ����qڿ�����ؿ�����ؿ����տ����տ����ӿ����ӿ�����п�����п�����˿�����˿�����6ƿ�����6ƿ��������������� ����� ���������������`?�����`?�����?�����?�����&?�����&?�����-?�����-?�����?�����?�����'?�����'?����?����?�����X?�����X?����?����?�����O?�����O?�����?�����?�����?�����?����?����?����?����?����r?����r?����P?����P?����?����?����@?����@?�����z?�����z?����@ ?����@ ?����@?����@?����?����?����9?����9?����r?����r?�����?�����?����?����?����?����?����s?����s?����@;?����@;?����?����?����?����?����@ ?����@ ?����}?����}?����@?����@?�����!?�����!?����U?����U?����w?����w?����@?����@?����?����?�����?�����?����?����?�����]?�����]?�����?�����?�����g?�����g?����?����?�����G?�����G?�����?�����?�����M?�����M?�����h?�����h?�����8?�����8?�����h?�����h?�����$�����$�����޵�����޵���������������ƿ�����ƿ�����˿�����˿����qп����qп���� ӿ���� ӿ�����տ�����տ���� ؿ���� ؿ�����dڿ�����dڿ����ܿ����ܿ�����޿�����޿����@f����@f�����X�����X����8����8������������@����@����@h����@h������������@x����@x��������������1����1����@m����@m����@����@����������������������x����x����B����B������������@����@����@����@�������������������������7�����7����m����m������������@����@�����L߿�����L߿�����0ݿ�����0ݿ�����ڿ�����ڿ����ؿ����ؿ�����5ֿ�����5ֿ����ӿ����ӿ�����ѿ�����ѿ�����̿�����̿�����uǿ�����uǿ�������������������������̪�����̪���������������?�����?�����?�����?�����п?�����п?�����u?�����u?�����?�����?����$?����$?����?����?����M?����M?�����?�����?�����!?�����!?����d?����d?����?����?����I?����I?����=?����=?����?����?�����?�����?�����?�����?����V?����V?����@?����@?�����k?�����k?����@?����@?����)?����)?����g?����g?����@?����@?�����?�����?�����?�����?����@}?����@}?�����J?�����J?����@�?����@�?����?����?�����+?�����+?�����?�����?�����?�����?����L?����L?����?����?����?����?����@?����@?����?����?�����o?�����o?�����9?�����9?����?����?�����}?�����}?����?����?����f?����f?�����?�����?�����?�����?�����?�����?�����?�����?�����`?�����`?�����Њ?�����Њ?���������������N�����N���������������Ŀ�����Ŀ�����Oʿ�����Oʿ�����Ͽ�����Ͽ�����wҿ�����wҿ����տ����տ�����}׿�����}׿����ٿ����ٿ�����$ܿ�����$ܿ�����N޿�����N޿�����-�����-����"����"��������������@����@������������@D����@D������������]����]����@����@����@!����@!����a����a������������@����@���������������������Q�����Q���� ���� ������������@9����@9���������������������b�����b������������������������������߿����߿����ݿ����ݿ����zۿ����zۿ�����,ٿ�����,ٿ����ֿ����ֿ����EԿ����EԿ����ѿ����ѿ�����ο�����ο�����ȿ�����ȿ�����2ÿ�����2ÿ�����<�����<�������������������������К?�����К?�����?�����?�����F?�����F?�����4?�����4?�����?�����?�����?�����?�����,?�����,?����?����?�����6?�����6?����?����?�����?�����?����?����?�����?�����?����?����?�����?�����?����@?����@?����?����?����1?����1?����@?����@?�����P?�����P?�����?�����?����?����?����[?����[?����@?����@?����@?����@?����?����?����?����?����W?����W?�����?�����?����@?����@?����G?����G?����?����?����&?����&?����@w?����@w?����?����?����?����?����@?����@?�����?�����?����?����?�����?�����?����p?����p?���� ?���� ?����?����?����?����?�����?�����?�����R?�����R?�����?�����?�����?�����?�����@?�����@?�����?�����?�������������������������������������ÿ�����ÿ�����ɿ�����ɿ�����yο�����yο�����ѿ�����ѿ�����rԿ�����rԿ�����ֿ�����ֿ�����Uٿ�����Uٿ�����ۿ�����ۿ�����ݿ�����ݿ����߿����߿����@����@����������������������n����n�����������������������B�����B���������������������U�����U����@����@�����������������������������^�����^����������������������U����U�������������9�����9����@����@��������������������������������������)޿����)޿�����ۿ�����ۿ����ٿ����ٿ����R׿����R׿����Կ����Կ�����Jҿ�����Jҿ�����RϿ�����RϿ�����ɿ�����ɿ�����tĿ�����tĿ�����Ƚ�����Ƚ�������������������������?�����?�����?�����?�����?�����?�����?�����?�����t?�����t?�����?�����?�����?�����?�����(?�����(?�����?�����?����?����?����`?����`?����?����?�����?�����?����?����?����@?����@?����?����?����Y?����Y?����� ?����� ?����@?����@?����3?����3?����?����?�����?�����?����@N?����@N?�����?�����?����@?����@?����?����?����@?����@?����@d?����@d?����$?����$?����?����?�����c?�����c?����?����?����K?����K?����?����?����?����?�����?�����?����-?����-?����8?����8?����f?����f?�����>?�����>?����?����?�����?�����?�����"?�����"?����?����?�����?�����?�����?�����?�����?�����?�����?�����?�����ҳ?�����ҳ?�����?�����?���������������X�����X�����r�����r�����P¿�����P¿�����ǿ�����ǿ�����AͿ�����AͿ�����Hѿ�����Hѿ�����ӿ�����ӿ�����`ֿ�����`ֿ�����ؿ�����ؿ����ۿ����ۿ�����Vݿ�����Vݿ�����p߿�����p߿�����������������������|�����|�����D�����D������������@����@�����%�����%��������������������@G����@G����{����{����@����@����@����@�������������j�����j�����-�����-������������@p����@p������������]����]����@����@��������������)����)����@H����@H����@U����@U����޿����޿�����~ܿ�����~ܿ�����<ڿ�����<ڿ�����׿�����׿����jտ����jտ����ҿ����ҿ����Cп����Cп�����,˿�����,˿�����ſ�����ſ�����)�����)�������������������������x?�����x?�����ĩ?�����ĩ?�����,?�����,?�����?�����?�����5?�����5?�����?�����?����?����?�����?�����?����?����?����?����?����?����?�����?�����?�����4?�����4?�����?�����?����?����?�����d?�����d?����@.?����@.?����@?����@?�����?�����?����@?����@?����@?����@?����?����?����??����??����v?����v?�����?�����?����?����?����?����?����o?����o?�����5?�����5?����@?����@?����}?����}?�����?�����?����o?����o?����@?����@?����@?����@?����@B?����@B?����b?����b?����q?����q?�����?�����?�����?�����?�����?�����?�����%?�����%?�����?�����?����+?����+?����?����?�����?�����?�����V?�����V?�����?�����?�����b?�����b?�����,?�����,?�����P�����P�����4�����4�����䶿�����䶿����� ����� �����ƿ�����ƿ�����̿�����̿�����п�����п����Iӿ����Iӿ�����տ�����տ�����Aؿ�����Aؿ����ڿ����ڿ����ܿ����ܿ����޿����޿�����}�����}����@m����@m�����L�����L����@����@����@����@����v����v������������������������������@8����@8����q����q������������������������������t����t����<����<������������@����@����@����@��������������@����@����%����%����Z����Z�����}�����}��������������߿����߿����ܿ����ܿ����ڿ����ڿ�����kؿ�����kؿ����տ����տ�����vӿ�����vӿ�����п�����п�����f̿�����f̿�����ƿ�����ƿ�����n�����n�������������������������p�����p�����?�����?�����?�����?�����j?�����j?�����?�����?�����k?�����k?�����b?�����b?����?����?����?����?����?����?����V?����V?����?����?�����?�����?����`?����`?����R?����R?����3?����3?�����?�����?����@?����@?����d?����d?����?����?����u?����u?�����?�����?����@0?����@0?����@l?����@l?����?����?����?����?����?����?����y?����y?����@D?����@D?����?����?����?����?����@?����@?����@?����@?�����?�����?����@;?����@;?����r?����r?�����?�����?�����?�����?�����X?�����X?����<?����<?�����?�����?�����?�����?����C?����C?����?����?����)?����)?�����?�����?�����?�����?�����?�����?�����?�����?�����P?�����P?�����?�����?����� ����� �����T�����T���������������Uſ�����Uſ�����ʿ�����ʿ�����п�����п����ҿ����ҿ�����?տ�����?տ�����׿�����׿����ڿ����ڿ����Wܿ����Wܿ�����޿�����޿�����D�����D�����8�����8��������������������������������R����R��������������h����h����@����@�����(�����(����f����f����������������������������@~����@~����K����K����@����@��������������-����-������������@����@�����Q�����Q����@����@��������������������߿����߿����{ݿ����{ݿ�����Fۿ�����Fۿ�����ؿ�����ؿ�����ֿ�����ֿ���� Կ���� Կ����uѿ����uѿ�����Ϳ�����Ϳ�����4ȿ�����4ȿ�����¿�����¿�����6�����6�����䭿�����䭿���������������?�����?����� ?����� ?�����J?�����J?�����?�����?�����/?�����/?�����?�����?�����h?�����h?�����?�����?����n?����n?�����?�����?�����?�����?�����B?�����B?����@'?����@'?����@?����@?�����?�����?����?����?����?����?����@?����@?����@?����@?�����[?�����[?����@?����@?����?����?����`?����`?����@?����@?�����?�����?����@?����@?����?����?����R?����R?���� ?���� ?����?����?����@<?����@<?����@?����@?����@?����@?����@f?����@f?����?����?����?����?����@?����@?����?����?�����?�����?�����?�����?�����:?�����:?����?����?����T?����T?����?����?�����:?�����:?�����?�����?�����S?�����S?�����~?�����~?�����:?�����:?�����?�����?�����ș�����ș�����ı�����ı���������������Ŀ�����Ŀ�����ɿ�����ɿ�����ο�����ο����ҿ����ҿ����Կ����Կ�����(׿�����(׿�����ٿ�����ٿ�����ۿ�����ۿ����޿����޿����@ ����@ �����������������������������������������.�����.���������������M�����M��������������������@Z����@Z���������������������������������������Y�����Y��������������������@J����@J������������@*����@*����{����{���������������������������������������ݿ�����ݿ�����ۿ�����ۿ�����~ٿ�����~ٿ����׿����׿�����Կ�����Կ���� ҿ���� ҿ�����ο�����ο�����rɿ�����rɿ�����ÿ�����ÿ�����ļ�����ļ�������������������������?�����?�����|?�����|?�����?�����?�����s?�����s?�����?�����?�����Y?�����Y?�����?�����?�����c?�����c?�����?�����?����G?����G?�����?�����?����?����?�����?�����?����?����?����?����?����?����?����j?����j?����@?����@?����?����?�����??�����??����@?����@?���� ?���� ?����S?����S?����?����?����?����?����?����?����?����?����@_?����@_?����?����?�����?�����?����@X?����@X?�����?�����?����<?����<?����@?����@?�����?�����?�����?�����?����?����?����!?����!?����5?����5?����� ?����� ?�����?�����?����`?����`?����?����?�����Y?�����Y?�����q?�����q?�����?�����?�����?�����?����� ?����� ?�����̲?�����̲?�����?�����?���������������d�����d�����x�����x�����¿�����¿�����Tȿ�����Tȿ�����Ϳ�����Ϳ�����ѿ�����ѿ����Կ����Կ����ֿ����ֿ�����ٿ�����ٿ�����Sۿ�����Sۿ�����ݿ�����ݿ�����߿�����߿����@����@������������������������@U����@U����������������������0����0����@����@������������������������taglib-2.0.2/tests/data/four_channels.wv������������������������������������������������������������0000664�0000000�0000000�00000150420�14662262111�0020237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpk����G�����.��M $Q!"RIFFt�WAVEfmt (����D�� b�����������������8qdata8�BWWGVH�C!������������� e`�� �_|:ˀ"h�cDF4Ё0A1;p09'; !)&ЗE?U3P; Mrj~WS_c\ +>3>L*>j*C,%䱂ahw`BL|gO55)9t'I<g!bzh?4|h;8tLɱ#?|xa?Hn"-w,UDy>O`Jgx9$}Q(%`eAl1h)L5bp?p9Y0&( x9xR#+:AhϏPU+adllAĀLyagx*(v6F]}ۦg<"2OGz0Cb5Pew$VX? *$I<` jR3J?pv+-y-ݼSۿozp(@ c0 k`&3#98@B_cOkmJkzVs/_,J$\'@brB#! xݸd0c�慓Zޫ9[bzYmm{߿[}ABA:( U4)ss4 �wHRs~ 6I3¹}}~|>w:mZHG^ Ҕ#1cb<IH;춰%cZnT{~oz�MheH"҅ ESĒ0Og7>;?+>܉fvG.[^OW%ta<ewB[I<}_rd7z:x`*8'8dWZKvn]>d# "SsI1THHwdnh]gƃg 9n~:S|? h\Oe}=LK`ST* B qZ%9>'dZڱ62#({}}]2?lJB!$!`x5|a>d fW^RkXQ7[v5.vǿ?5kܧFv2$p4n' %!Rކ# 璜֒4Fm{5EBbb1"RDGK2\#',810<xjmҼv:s<]L G|yL?ꍁ\R7A)m@0r-nRW%~ֶ#׿~_7. 'X$)$ ewP S<Ǟ)+¢򑿱c{ϸ=`#PY ",[?BHf~Wᘻ8Y3-=cg#`Ugλ;Z'KR%IgH2'rb⚝ JDAxTϭ7mR$,9YCQAVB3|oȎs}c (#ϯ&J >;dXb?U^R k%vz $1źk_>@a�Ȱ_؈ƞ8&jGefO 󌿨~~NwC+*JWo_ gl%3g,KBE\Jȟ5n+~-Q) x1Y+1cɑ=J}=~bF8%͏?xʌ-9 \4"[4Y *Z;ʎk֣8YBLl.5gm߆n E)cQ0lpsS"jND,͡oCv}zz~SN{D2А+|ZS#-�XGа+C{WQcMUsf5rU3|o>;Zr Q!NK|?vz<^1V3x=<? 7%faXPDmSV<Ab Z *tZeg~䴞rOpxP.a=Ws.wq~p}l?cm뜃m0"aX#Ay`HC,f, -;^VGW"R!C0Jy+vȃ uSRd6{NgJ|n_O"/C>}t_҈LRA 7*kdߔ% CNnc!#"*M1yJ^,~>{~5'� iڄ_ 11"PŵckS6RqAx8f9YZ۱,ɠ"|a4-x؟Se|?z3/5?|�v>9GN}R$eGdSb:5CoG=éhDrbLN>)Xn<Є-9v9 GUTAR1h Uy0MI|ZQUI<379 O,<Sx:x|~\M3U9_9l>?|(VBNY -n(Ȉ(*V$ĝ<i`XkiYdtx0_B qqo>~ o*uFySQ*ZeF<Q(")6F= 9]pg$ٶmSQ I)= .{*ŋv|G5doJ>>q8smS<G拯]On)A�)%Ȍlmkh Eڬ\<E CҲd<mHCϬ[$ϟ4✚{V RXMA:hrRx#űSbY^o$MXզ(SV"5}=9?zЩ"c,1Z9,H;mgV2tӔJSfX*8ZˎH)/W|<:rtXG; ޠ6~lࣜUulFg0ZhHP<v)H"ŕYg ޏ j߮h??!b͵Kd\ܹDcC,-jS%)jy3wD KDW/Z:N6Dh3+jw\}uod'Ȇ{&Me%ē 1}DCTٻ7K)iBQ0$Qqٔ*VT :pgx4?`mem$@ԝXY~y%r=CeeT$9}}W?I4(: 2Iis:Ch2:*4Ek5VM,(DLRkk"|rrFv<(Pσ3,GvT: VNwvpk4�������������.��M$2=l���������������?pwvpk4�������������.��M$2=l���������������?pwvpk4�������������.��M$2=l���������������?pwvpkf��������.��.��M $qBWWGVH�C_��* �� � gDL?ֹy8[n x2 | IW&�N(rjJO"�8cR֢ *!Tbv?GzϳuL|tbR@䤤Z{3Y Aיh?7'sFQY5=Nd7�i{=e˓s`Z֒JI(@PʋEOtc+Ճ98?0ƆdDuhRQZ=a7Yo 3_k_hM{|m\v-?XSH bZbHaJ$@I=gm[=ԍrt  WP^Г|mnc d^erjZwQS\9ww|\Ǒ E`hvhc'(-׶d>(bd:SdTJ .mbZ5&A( ^1cm8RD=RO'y=?z>mHf_٬WD#,Ey{:6F;IHS!.W̯_&-:l9uR$YRLK\zX3\sJCa%Ƽ8!?~sیD? :lŒ¦eѥpєXTKR歪B5#vO\-鉫rI5=9Ub F%G\S(U%Vz"G{IɟIۿi?|i\y~֤J3H aP"M%"#Wn;==5`IF{HP0L"'*XB1ׄu'PHqPɨܕ}C|'sūG%%"Ti$߃ިO-jk-K9YB) 2]P C6$cHw|'sM@2�5Ɩ7pA,8z CfƱ77wۺmSc :(b9IFFVi>0p f<2-1#jԼO2aeK.pNh^:otb$jl`:ms??%BX= ( h3o%}x|sQWV4, H)�3R3qi-ħ3-Fy~JU|_څ\**`Dά(%?+T b\?_d94 QK\wPs;V(kb=6:a& /Ãgcg]<ۇ^p&_҇g94o3e T%˺V\ey'V~O.䒖<洘_:tEE2%,%רg~aXxI֔]BYstF>냗aH?L(BO@)rx5D,aDÄJr Y&il2;Mt,h?$sM7! </~^Tͳtu(RIh iE?.yKTQsMISYhz;g{)VK#$ލ5ea^﷋6uw#m擜X& ~CA ɧyhTv >:H Ť�SE @AA04=sh s*$ a$ OD#@@I&:(�яN\w3,roPS$',&xv\t:BrH5Tr|YmofocoIu|H%89' $r>J:1' MQ9:DS `hp R -r2"vV*NGTeX6U7F+2,߫q뷝}٥mnk/3o[k ZUh:A &yy&At�1)LP0ǁ0䟡�a.f:&bZ^Ȧի:ݼc Cc=`  Ο? և?ԓ  >'J,>0hTbnf0¸gi8J.r�1�2c ��8PNp)ѲmW۵ڶmeYN#-ɤ$($IHӓ.'N�h 'S& 7Ek`0y|!6%gi$<%MsCHɈ$I4}uE8L!a WK0`QS;؝BքzO:"F։dgC ňԢ"vL B#{ OM= $LǑ<IQ{4CS>i|vA3XC8b h3ϋ$({bPYM 8xT9XMĹXIN,܇fCyUћ !~(!Yʼ?~nH<,?UJXΌ)m;SHta-&ǥ(8F0; Uh�.)DOϚ:~JQ;86b!w;\:�x~.=ӂ_ 8E|L)7GGGMB?~#=ܸ P[2zp/_ 5v56Jǔ.!:^?9yq#[R=2]{_ }f^Û?c~W9c7218/ne V{W[8f=KM;$1y-?6i@?�ڝuu WPxؕ_D6GH[ZP7JW 1Ǘ8e Bve~}7_{%~$>M1"2FF>Bgp+&wp/]zk>-A߿`uO*?rhN^>AXG9BGjnzf\0xCn傍*-*!ċ(S }1s= i\wKh;J]Ic/Xs>=:Ea\cj^S*>dDR1R>PGQ|0CȝОO$_^5ׯ^fߚ&QGG))Fd,3Y^ٲK>;򈃘T,K�yh"PO�iЄɜ(3sr񔔧TF`Jh(0ZyZE^c ֟VȂ.�B7>"&cU՟mxyXnŽ \VR98ͧ)Kq=V ww ֎wvpk4���������.��.��M$2=l���������������?pwvpk4���������.��.��M$2=l���������������?pwvpk4���������.��.��M$2=l���������������?pwvpk* ��������]��.��M $`BWWGVH�Cs� o �� s�~x5گkv{"P1dnf�0áBN#8|gzώSjIm*&Ѯ~l0Dm)^K�Ι^IzAZ6m.;*s~`h`IXe7&llzf#<T8iQi$<N99< Dh$rI�W\wECK`;d ԑ9 Sue3"ii!WV|$czӬ@:�<�8~́LQ 5 EA62E`Ls`P`BSc$69Fu0~R�@߻V/jܻ+`N]hN ܘQE|MK쁟AaEXi?ckz|=qa!4&bR0KR$ PS2;N!Ї9HaHD?<7.8A^|,' ʃ,*%t8>/l)~Ei%q\ YCؓB;?W'C rid#RXN<3uBϵ1> a$;WaT?T2^1U uTt Q:7p?HP1CpAI^gQꮙ /^*A\vlNXI&)#5'<'\Ѕs5vtҐ;1 [kzRϊ7.cQ[ Ops&7^kI|:GtɂV%B4W.i~8Q cu%r 0TXd? Crbe(RlwG]Xц_ɞ%aSO9z>?E4dCdf?$K\xDxAqD ɱ$s wP~P)zz!4S0f$?X`0@yj`BaIS;G34#>Y*xD8%z3 $7F;0ƚ>0pQl%&Nu 5&呈iIYP%N9=RP3DϮ]D%tFRg^$Jوs_,}RXx-g8N6sy)hd?W_@D(zT!&,1<hNbb#J'uK]-5E_{=q̧gL=wieqMH}kgVI~a,t 2-V )ȄeaQ9_P=!`>{9d$NPĒFt?+EpSj/SOe1P?w:r.qm2n oTEvSC*]%Q_|X~7R>T侁vP”y #B!=\rOkruQM2Oe}7Wv1"ul͸i9/,wJ'#Rﰆ}8^ذ?wF-8Ed ><'iڄ2f\ ;͢ΧZTh8H #4Ca/j{j)z7V.OsG; Qr0&5CsZ2e2 ?OpOLt@^3)VEG<)" <0aL:ǣuNjGp o6YB1Gɐ>sg`4?BYyˏx'o=_}X3*UBEsSҌ,Xh!aI^I/ Te+s]_Cj` I=I|.ˏ9`Ao>t?pYr5!gtJL>pXJ;c8hɢ^Z/D#0fKsGC< Q 5q<Ls;KOPtO-7OBÚ;C { |x73to8Y\heR,bD?/;Yh xXgNCP;%r0OYVs CpPB}\Ùb`\~<[B5!;hF$2C]4J,r(DAr€k'5A?|y3K ,h`'蠑'bF;$&X?R|;$fe1Eh(="\6%1w@R4Rs5s/3 >FUփќz5=Q&`OTluf w,!ȩo(l5UyAФ.4� e&q B#~xӁ;&$^K(xr $vj8Ze#ȅ6*/9$,d+С! ٓԯ)WOQ:3>RLQJׄ;3@3D(py<!ň=<'h**Xs 3 NB.!6skx>a 6> GGE1AH%2yң( }@>ODHC FO$rN:?ʩ ya!!6FV<fg9pO%FcNx0| ]~h\B1\TC2C&qÐS9~T|0Ceb>x5l8CgaSsH!. LB1 d@5 89Wa^P`ġQ9 e0/:Ǧ@*8p0PPtf0cgij,`tXx0*^eك!p8Z/*YRQI,8!?�<1;]WX yv O#[8=(*6Qt0'ˉ׌CSjaA!b;2ыP@P jM~QHLBA̘!ǃ}fb6, Kl|TnQJ3~=p>`_eZRk~ 1q 1<8L?y/QI晆.ik95KgVسIU'X>'35^N w*\ cƏELE;+h!*;Ihf#8s!8>Ԅ<8֐=43s XH)u^+M8xs7xO<yt vwvpk4���������]��.��M$2=l���������������?pwvpk4���������]��.��M$2=l���������������?pwvpk4���������]��.��M$2=l���������������?pwvpk����������.��M $^BWWGVH�C��)�� *�"n�7&| b |f1l 9Kt"J.E<?J(�G>1wܡ�{p c*|AמtއƏ]84~XEFr<V%C'gE~-P456ʍ#bxyV`Q49BB@P0;f&GA0z,a> Ib\˫A+0B=T8�+3N,좔K#aEQ>/<1/V'Aq3gDfqKVYkCX?H)r[(C ~(,>v3.xe!C,Qp9!!H; 5 'xy@ hQPyF/`0!wẅ= crshdp3f8 YXgGϏ Sk7S,Pe` YO\h2g8C'?(D8Pi=D)k!߱L?!V/aLsFZR,93q1߼"`^%$<PC8Pž]XBc M"Ĵ'aSшD% xd=O\XUQ3j&8>C`ŚMA4LECC,&G'D|0gr  baft"Ȉp-Lj H!g<Tv F?P!q 6 "t(>{0hqyv.Kt#/;R {A8{ɤ{̱ y. A@G#qPԨ!LHdF>OӋ*Z�8~(<00Ŝ Ր#CtVBC8e;㕅1y!SZ!ؙĥϤPp ajBO,faO<0ځgOvbB(<AɤÙĔP=By j0ԇ9x(O.exV <FhQcP˃X,Cv=0ᜪ{ f;{g& wvpk4�����������.��M$2=l���������������?pwvpkV ����������.��M$wBWWGVH�����������_mVZZ}w/w+[>;nwmwvֵkW.9riuZV+E."Oxy^y;a1Ɲaܙ3Ν;wn56d2hbXlYLC&[@I T�p b88@@ @�pOd (FHh�4$D?fZ3et#DS!#PksY ZcJ<42 $f<#y.6*4F>!τ%y/cߋ|{yr։ނ]sS&t2z۪h?B# K .\sgz-Z@?i7eq*i=\,U-£ xzeϣ=/l= O<ц5HV>qԼ p a8:6 .FoFG(a#78qhEc룊~.5?!x'FJ#IZ#- &B UٓS-6*np7P7y6pp2^'?8>i4ӿj]nʇL8G&"(ʹ5Ǘ=ĩ`z lRn `pMT@g Ɵ;=͑CܴFX`{́!1B@A!wDhhU)xxCQM)cZ�|OS'"Kc偙>@# xsJe &<~LR։H,%3Q%w&@{G8g-HQFDx'N2= h?z0 l!l!rYCwt̙aAY³\gs)~pc)?53?cIJ:aް_G<[waPUy!F;2C+z^-=*Hs?APM@|3s<#hhҌŃ~2BAsKVRaØ=N1 oL3 k2T$xQ?Syj(5LA  3 /,:ci5V hmz\zV(92CJk7A BΩMaI1'Lۜ3V?5gFш#�1Opv~$syP�Gj4LH<9LPd((*p6C=B(#leF*(EEG&epruǦGtN9G􇁚k AʼnƯ�c.&�Q2Ix `|"2;aF>'y*a6`/VZcQVlt9A; -  $ 8;D�3; �rߙQ<dB_C!x 0?`u/4Jq7 =XG9zCבe\O=qGpF.bTH KgxciEtǐ5w -/7H'PnW'qzC r a0+A;x "#$�Oc̱BxNj#A?YFpA|ZXS%Nr~o//_Y?cmQP|dS|wtѻkVy̬ ub2H:"vsu1ZC} $FЉ }fj| #r|l!l.XiŰ:\DМ ?�" CBꂾ AWWLzIب :g XC38s1"Eś6j56_/Uw-mxepő_{%Ҳdi0qF*JƘ:f92@c`<>I?\-6lVzګΎmkkW\5qO:$x"A14P>�L0C” %'>H�Ȍ0@K=="4\T6-R2LJ*[ "h<9q1(("t^1 O;ѣ%H83cq5w Wv93VI .}p ҈\[.^{_nyO#qo}*ϽIfCK} PǨ!fPww[9 |j SוCzQ)DZ- MIH䄠9H Xھ\鴸Pƛ`=?Jq`\((2};mLq]oQ¥LV(Gȭх0<almے.ԅfz|緣wb\|=qwvpk4�����������.��M$2=l���������������?pwvpkH�����������.��M $2=lBWWGVH�C|!������������� ��?pwvpk4�����������.��M$2=l���������������?pwvpkd����������.��M$ 6BWWGVH�C~@��  ���=ssB@(1BIBqչ^%xq0$ QyҢ\:GEdo&R?J?AP>o&kf'seĊ+`])cӮ@P"ZRXԄ(YY9.y5m̿o8@N�*GΉ~a@fk=E1!.>j,nfلFZ6<H K&G ù۸xb|ٟ?ܷH}64hl_B )Z~fMε(H,811(@{ _?~1p^Ka#�#MC29$-Lhs{Ǽ)oK$e0 ͧ;cş0s4B 3 ܠ![W5rŒ"[XgqBbi{-,IÏ1?~먾#USL(By{Džv93�AJb2xgH"w%K '쑿;G@f NzzWhwQjIֳCL%HAk-mUZ )Q^6^*?{T|~Cz|cI�v'(&FGd4#CKks ]�B"!u ]LjoqN $*rQ%/ȄIKI2*\2GEShts<?<?FsEb[0{Z✊Hn).MCF͚)U<Di6QЩ2~} E>aAd!4cETAQ4%Ybr"[^"BE`HD7G1Xz@KǸP@%mF@iIرeԠM)I0pT*PO 9 Ztl XiWM}{2%YErQ)|+'99 (. KOi.#dY_vZ{/[V$<;\>qӤiBP&x|،qT"Q'_%zѭ'5[+zgs02Bp}fzuTd!?Gx6)Ǫ՝z{Z}FV>FQa,yrؙ q~?{:XA~XtbC,):|?,U[{zvͷͬa= soJTӣ l(EjDXd/#Oc# }~?v'(юUC(:OMКV>hUғ"Q[_?3s*VXj?)hUQ{߀ErM1#yd<#ْ&u+}c?Ň\KD:e<_?afL)~ o7x頋BųD`}ܒ}A{l8>يXB" |yO3k9ucsxb+hЊ (wQd_$.lF@7W#No0B:˝|J#Sl(^SI؍ օIE2/ =<eT\ KW;G,~ $SEHrDLȊSCϱCawQ_% V/h?VgCE\R8ś$O} ͏ܟ^n⟊>[vQ2<N&{? :6  F< dHͦzyxֺޮ>F?5xG_|]rV ΰw$Xcn` 1o~_}|̖�/8؊�Nꈁ X<  >,PMS XYGɩ'RˌSO80CB]rqФd+~<5n(j'eiЗҩGc?-BP<^}Wa`+SqM ڟݵП$0h"@0;R?�Z!,]S(q5_Sieԇw> d^{8щ=^ ґ`)5k# ,*iV%O� 'H4((rG5 8f`'o ��O%l VYm4#(Ng 3~VWk~)[~֔<ڀ[^[I'VaVp>I&?'9   *vƲ^N*l)4FZboR-hȯd/?bX!(á93I{zaQ3bdplIukͿvwku/55&c[&0|�c H�pIۦ8MɌ}m֖TrEbւԩRZ@x3OLz^dT߬3q2y #;ZzmMZ]ۺvt j(UIֶ]y(( ( @|sgVI{MOR2,$P9trΟ?ЁZKًIvzi{uG1IA %+@dmlӚ Qr:hRMٻ޶ 3hٞY= R;~$9k9l6IV@Ydhʺ|]kpI$Tҋۄi BM%4ɡp{XU$å H4x%~{x;#S7|J]OL& ,b (e_/  )0hidʯRfFV{V X;ITZn)ɉI,!{\GRG{s{{?mѨ?OGRoTyaKԗj᥾]%C)""Y4 +q̋BﱰzCp`lI֌Lu. 3yD$gTVX3q=S?s(aT@m" EBL5] u"OsE !D" ¶Jl $zȞ#"iIDԍ$s$'⼐ii}=?_<s?ܖC|`aí :�C dh-Ezn$JX;PgDƆ툠AMwԞsu)#WwDS$=PȜB4RYa7dtg9b2ZEs4lCvMWTQςsN\:Eڤ\ A.6|r͇kCΎ;} x5UɊS6(" E,(MʅSw=`= NXȌDزEW)nkS|U Ch"4-fʏa_ 9M+ wvpk4�����������.��M$2=l���������������?pwvpkH���������`��.��M $2=lBWWGVH�C|!������������� ��?pwvpk4���������`��.��M$2=l���������������?pwvpkp ��������`��.��M$%0yBWWGVH�C{\.'�i: q ���'Z TMxإaF˓F$RD=sA:_sfϳ>y"`:ɐgr 6r-[WȜ0okfɋ ?Iȫ\$84Ik$JVxLP+"[DXt)UQ?ED2 8Fb|pȏӵ/t�+"-rJ\rhJEQw(2aΐ#*7L1]eԃؙ =(̥Zdy$)E9FLȂY,H(츇Ko? 0y?ӪWðOGP` ΈAa)B !̞>*. 5)IzQkFbaR:pHcӆShXM&Nj95]|]?IA ^U+Q_𵄂imKL K&cjV/MTX&$#|2$*0mlZlx,%I?Cߜc:>|`$;[Z[!Wz;H2^7NoRZ(l[Oۂ {ž4<X:Ep/<Z1͇ţǫ� h܎^ "Ee~zݯVH%Ubu_k"\ /Bk XkJZӴ;zڜfܓ|tl'v*opg5R2.)-g{oNr;s %dIΥpÊ:R [K[W<~e?㜣' ?ge#XbY9xr-]4OwַFɎj?^*#bO`Ȕ2UJC~<Û'tS7F^ՔLePò/UN{wr# ~ڙrDɱ5](DecQ.y ЁlFz,>-}=Rtylcn 2RM[{;v[ik(EBǺ]Hx(#c~q&2~Bp1m=a; W\&t7\^w65CciM b *Q,?႕ׁш\E׳)v.uMdIO^2K9xi{^{w-Addg!.atqSvrݩƇ"q P ?Q̝0G )ӓ(-owz~w&yN=WQؓOJXQeJK2HL8 }Gنك,?0~A'pݣ=ZSzʯ4;^) F!;c}蝓yBBObO兪9]yLkraѼeu[]ڰ)G!$o NZ?paX1W~K4,IVe/]|m \L;zs,$!ɰҊEhO?X@ c>Ipg5>Xpc.O OJӏ_ZǢ܍Wo޳q'~,A!Eb�|(ep% "/fn({PxŞ oĥ%ûthS.Ȃs-A4יw bb}x1nI"2yoB(XDijET>G{/Rc3Ҟ,/NiÃ(35<DBgjX*`,0=/YVM̱ep~$/0)eUMw:>J૟T > 8fa079_>y<'`|;20Xo=MpM+:tJxYK2C/[!ye䞧u빰FOx硇cS)U~'O1iga;t~JLy JʟCC k*$L!vl qԸ"cYT~AQ"t#St ьg< f-aŸ_\,|0#E�Z^N*eR.G=i? >T"萏;!X~E?x\+Xq5W? FkZ#rl,x^SD~M',lMՐEIa~< ۧHS,Kuzyf'44Q>~^lw<Y}u~C't\pJ:٘}8/ krBKI Zgs5IxmB~�O7T(ޙ)ť cs=¬Dd1(ONמx$vxu;7\1b_Ew?FvђX ?; � >i:Ep?Ŀ$Z'?.,r*!;'܇FpYaeL!3|t0;b;c.F?˛C?X( vгs]$ 0PTgUK7-X^]+rdk鐸2?#GQ$E|P6Sb6P*zuRr%E< uOZ#P}r<<3#p0TDgQ"wύQ,+ >*q(؏jpU<ޟpNFz,ug|`J?k(^Ր%Qy2lybvEPgpUИB!蜝SOp4(DkR" 4tr̋(@4Ù@fqaT0JOڳ4 7XOF$aIџǯiP.XE?5?� 䫙@(HO<s\ y"?F \PD RRG0DaP=D=8K3^ϲPqCjJDxq d)bI\錨~&& "<l&Y~^8'_:<hl> q;MC .FA|eTC'0_ǡo!"KJQHRf&d9 3A 8?3@̀?0$N (Gs,N9rR<*)'X8S4p**/( Xճ?_.GL}9͹c\OiԊH6|4pH7I\3~EllydR\tQ`9W4G3^c^xfh*~e搛,N\Dwvpk4���������`��.��M$2=l���������������?pwvpkH���������@�.��M $2=lBWWGVH�C|!������������� ��?pwvpk��������@�.��M$ɒBWWGVH��������,��q�7eu~o/wo|su7]sՍ.;wԡC^4ZVԊ($- <XY^s !0ʌ:uW̸l6VXd- # J @�BB%r x:x:-�"ceSD,%"<E ZA~46QPԸC&GhB"{~&eS1X#4VF)chsg  R2TJg,,,bD?EJnY$;#$DKa,wΔ—G,)$\) | CS$;(:pbd9-O; I:& CwdMާ!,Qà 0VTځ;'"x(bWBȃ=nf/aG\aďϒ3b?I("d"XT1guJgTXC"X̳R4sxXGxI5ǿ#sD-iZcCzɋ,>5C1_Y4v? &{؃gh:oǥ /Rw P#*,;פ9Raib84ʙ A<:[ryw3X cOrOCż?t(Dc}?9yA8 ,~%/9!OT&y1էq=^S0rL?C,!4{dOE,P3F'ё06.>X+1p3;t#M*׳xaC-eU s4 l"폥d-cbqv$~dK1I?~.h&"7Z!5 qp簦yHX,GSz\k ʃHTx`e;;b$xD=O<@G ;XD>~,,=4uJ+X|0d@e D-<)8.k@>`=Vs8f4B B(6KSD@~^4\W<9)tjw,;ȱBO` LJ4~, tA4#?B?rPɐ\6|CGZ?/21ޡTG?xj;#{UNn<*̡@ΈR(wvpk ��������@�.��M$!hBWWGVH�C~�R?��i�*TXpOǃS4HG)~�D&Ϝ�9p@ )xęy؆9 B, bvgCZI!c_/R-ylxZi5YspyVQ!,ꐢ@RrT.?8/YlbqopypxKB�ubf(v!G=A3pu3]pIyp%149t䟗6gKC5)=7/za܇. ߑ~%-4KÞُd_.'_=ӝxM|ˋbQ N,U.! Bd|#T?$*`2AO;G/Lv Tc~ CcȐ;j $aDEàs(YD9\84*F(uvʈSH}|?)e>QFO=ϷRXU~a^pN|Yez0~b 1/:&d%Iv.$V^#^Ynr<PÁBqv2S3Lq{nS,Z-~a<լcbKh;>?P!~ 5WSwYc1"9|F< )E�ʣ>G s$#M)C櫟-Gq`MH )̀#:졆JцFៅ(^3ͧPnAb1~P؝)aMw@~,O)bg!!05 -J�\ǂ|c#_(i>>:藺>1"vJ}\xr$개g\' &PXX); I?2rGBl,<B QB>Wp,ϫ\3]۰Xf;:<Px@уvAO?)!N'thBXya{ -D{E ѐ =Ԑc?1navs h9ܧ/N'P s ɿ h0;\Jd\tu ˭•*yߌdQKzri^cGj]QiaU_0^td{XІ%α,xKi6a%r6 q {LGq(M`Xz:ؠdG/, ^kĜl(tE0ˠyVH$X{D[9X~h2p12#:>=mZMd#XN>;=<ډ?|a +wNI ?/3g \'F޸59ӢX3\ c92}ȁKY~PX6o ;УX<6;x!:BO>&iP* 3S^⋣SảAD(yNy`?GBܠ:E^tTʃ7?\GS-S8oBeş}O)+$zZBNygc͒ )CwY|)R&4&:e9|%_^\sYOdS5F tCH֣,pO 's)3Q/dǠsryyT=\Je!?sS,gyִ  Mq3ݩ c 瞪"l^ZS>cvM&)5fϯs;TDorrB )">KC @xFe U~^wIx %jI)cIGZ/AW`>˅ox1u"YY?aT:&SW0rI,/NQz2=+XLX3u`런, uq Q|j|1 `Sp4V45+s\A2t4,x.K1 rBoxfXi<L=M^0pk̿p>Z!1.4 Hx1x 1TX@V/ ra<!Ʌ?~ (2 |4fei__?yZ0@"4�wvpk4���������@�.��M$2=l���������������?pwvpkH��������� H�.��M $2=lBWWGVH�C|!������������� ��?pwvpk�������� H�.��M$[BWWGVH�C!�666,����J@="~qw:}6t*d>/D"Hg'T_lk҇zDCY/S%BtvXkE*�]XS3l]0AP٣c: 7S?ٞIz275!.Ωo`: O?1Ú˃r>(>Q'/P{%@iB3ϿX%c\j ZY[8Aj9K>eobC5TZ.qoz`$JbfVO?L6dƣ~SΩB{^@s@)irOet0VƯxKłfWJ*G;?bh4z D'h=vZRxܟ ?ڡwFJ ¹(_?<Nfx.O2^a٧\Y5Kl84GD &\ .pbAg*XNDcBQ5D CE!~f2 U>6='gBi9`C!Y%8:X=ZG>!^e;KbW34gN] x9hd!J]pkpK\ãZ,!~%-cͭ bjr$μ;3O?~9ɆFd><sj':4'v(SKD~&CkM=S1EDK~Tc}K!ṭX+:,HQ҃QM1:@] zx>hzEe4C'@ _Eyg)c qRB%PdcA <Z:(DtOj|D`B!=> r̳"xLdT?~'%Ś+O3?)W@3j<Λ߉c=bi};h0fW#.(s051^@DŽT &�8(U& p[؉;, zFDx(qJ̞a!QQ`=( MsW%vN,}';c"*J?r +s�a ,#Ob!=h@`gj!;`S M;d y;ԃl Yz&L+=�c 33 93MN45M_T]Y'q0<A' `PӠGF3X,g6dbD=bp 8|Bg" S=-#t#;ёӌJ:"AU1 9p Rw p4X C cB#NK<g82r}dI%I .ܸ@3F[ɵw D>XSL` D)'עh0bZ*kf2x %K#݁mY|3z랒R j},c҂clh2.q El&qq,U/1aU4#@P�\aGjZ [R]LU<UXpiN N|45#.E#WZ._nO5<np#^??&$rYѹ"˜@? ُ4K "kȓDb"^⒳o`b:gS"qY1Hac}J<!="1p ~hᣔ$@S{CړzEATI4#:eP<?]ƫw;k8YbI{z?&™ff (0cԄA0!NWJzT[~=b4p<Na(: A 39|^.eVRZ.Δ?΅2ILh8>ӣ*mG�A~йGr4Mܫ~ouofuB�(D(C:$x�$^7/,WK1Vr/ӸΡ}I m䱿<ɚI_k.�UBFʩSrg~ޫ[oX qL*QяӇ1|>(Hp+w?_wl\>e<Qq?t'ב7?A14ijEKت\>󆣿~k|hsP3X9­fk{뫅fdD'IiQϘ4I rhz$ N#WΖ?6}> :ءe9pWd͏;;0Ij(%<X&ld78* ߚ?n*L#qƨJ1ГQEd$OA.kSH" >M p 訃?n3S])L=A{>Յ^/l?Oy}dܟhqAӗaޕ-xa�"Aٜ5 VZIpr2RE4Z2R9`3v}~_ǵ%X- R!ʃPD s UL/px? ,Xx/!i7wCUx(~=OC=O|6dQ\)52 % +h^g�OS@e mo^jLBD*RHb} ,d9=!PiXFUH| B@ ̔өHU{vtߝOɿ?\y\>iȽcm$P`eBBuwQzsɠ_Y7JKŮpL+}o{, >RʐDѩs|뵴Qj)U""`0�2(dT^on~/{oĘ8q#xO~p4N @˴ k[i۾ujJ!#!+*U?96Q%I,O,tTR8@slem^׵6Ő>Å$|d~sv›{q?>LR> o GY4YpŘ_5am`@GԔJ mPޠOF r3yb>qϜCA炏h~%۠Y*׉PMvֳָ{7k\W�3ܴ nݹe3["hhq rRK1#TÜ!1=4X R!Iog9XM\C1ƖSXk\ Oa=;|qkhOJ_=Wg=~`GHE㦽wvpkD��������� H�.��M$2=lBWWGVH�Cv ���������������?pwvpk4��������� H�.��M$2=l���������������?pwvpkH����������w�.��M $2=lBWWGVH�C|!������������� ��?pwvpk.���������w�.��M$'"BWWGVH�C{d1�*�tn G ���N}R& TJZ+1(N ǨWR/bN~yQYsC2^֦.Z\*.Dx7e:zx|Gc{~~d Fi~6Jx#�$P knZʒYcaɥeY(E(jԢ!lL@�BA\1ɲ=|?r{<6q$>qjGv(Y/9zkʔmuGS</~q>s$pCAi )J<X46V,mN)* JUzR#)JL򈜅J,%ܛt r}7*~I/9ǡ4$uaux9ȩs0`/@OllȬF@5%3(= iAQĥuk1K͉9Id,r-|SigEbmTJq=<1'^¦x6%s(<{ϣx=]D|yHJF0@ eƻڮ8%HBI7Zk<: %#G2SJEd=ؚ=Hoh1;_xȔnGP{{ʉTá( X׳A?'wO.f2*K< ECڪ:-5"*KFQ$U5OQTiOä$Id`xU;~O&Tf% +\0*W&Ycxb\X,jf~gv|ۯtΩ?5=7>o0<C(v"PG� RkEIp#.zmP$EZwE �̄g.;> Y*CUhAz\LTy += ӛh}?A:"ӌTJ!n81S`#bt*H'#-7'(5eS bzy[,uAzO_?OW S*")H.x&aIYr3=ٳ'W|{9rXsH^RNZՒSژ(.˨ZZ&Iƚ,!sqL)Y$co9E'(X]0;diC\D +kP9aV仞{]|Tw@Y%U1$%4ͨuHM.9(Rͪ^TI"9`PBZm-Вq;93":sY~r$Z@\YB7gJ~,3LŁ{\<V_{OOۓ�TSCHxmP8#ZwED[ 9$IQi,W"$"DD,yվSsaF7>̞Q("9!(N# 3%u&?||'H(Ʈ [[HJ ўQۑwM$WThiJ$;Վ ΐ%2s+~`w„XYiTK""|=hVk~OZn_v3?v?\7z() +BA)h^‚hG(w$( GK`电H“j{  9;r]&h X~b#Ψ7i?#2/>&rJ?|rƻ c'NSŒyW!eT@!઄)>z{G%Q�)O5Px<:R :f{|( ׃3?cAo,Jn*,1ғ:W};ܙc*.Q[=?~׈/x&U/"1$q]V3Ccn(,S@dEyJKBA1 b,*O|v+d~SÈ$r*(&x~z멩?+q~j:|rl/C~,}!b=!Gksar#kPIRczNm, 7{<R= Hf$cɶZJ$A+"Fz[ bo9d ZR ӄʉ$Qrg[v6 ksp).4_&h.u;E!($KK%N#k>_~c}fS*朞tbd-O c[Rk7#1�ɲ$ ٴo碹-\^D#M`q#"?=ˆ+pyǯ =A*Fd�MQ ?߾R"NP_S1y9f). 5\4 fH GѼ6չA4߿|sRUuO>xN%Rl>(>ʦ=]Jʵ^k3b8J{!|;T6qY6JyY\"p'<o1N 0Og+u|Rz޴6iXz8dNq2!˲V4;Lⴈ<?9g{9 GY -^0a-S;%fl{k6 JJ>,R咝c*]J?OIzSp= @A >{\=?'nxIA$Q/ƅRSQE"$Ϩa~D 9 >JIS,E/=^S\kӓq^~OWgg;ȩ)gj3`CcR& `?%2$ ф\rdH RR#J",0EK2Iɔn,ZiH!SGŚwH$98ҁB#~p (A79hMM#2HKQsE|bP>OCZ2DCCU2y\}/5ǟZ>h|Q(iG0P(HA"aH4(BR Ą4,L *FC2N!2i= OELPV=E\dD^xeD"w s(6%L! ,N?(%' ~`P}2E,V>ڌ3pcCuVʥ3F,}ģ&rT9 ~ ͆Y,B0t(a|XČYk=,v(0y"H=:E?D09l$de0sRi*]6= wlD6Q +ž4(ONcwƉ5Xs&**4W46V/^J 0#:B(wD"-4ףXXKǚP5 K2ЛTRD gAdǨ.H=";%DKáwvpkD����������w�.��M$2=lBWWGVH�Cv ���������������?pwvpk4����������w�.��M$2=l���������������?pwvpkH����������.��M $2=lBWWGVH�C|!������������� ��?pwvpk&���������.��M$fBWWGVH�C�~6g���J@>qG g#6'%:/vJJKzjGф1ԎQO1~Zmr,s? 9$X0 RLTB}^zL0e~\@SJ?EIt~C7E$ΥS>l=b!jxl;/QOa \Yj5=NX C?R! #S&;4AKlje?5#c72zjQH?^KEoNfz3_pA7t_~.a&@8woXds?"p,ہU )xl "ED_4G!}O=G;0QP&4k?__W{{_ -N^?Ok+w>vzFA|4/q}2<’CN! )vdY@}>\[cϳ'44B$VI=.1L1!+:dt`=_�p?l!K¸z&g~e�l$%0XcrxpK.#?$H&D9{c�ZKܬGot2V&qW"U CS1XI#wj Y1,N)^\~=jDUTq#hg pUZ^qO#.wR_~bXdR Cla rZ{B"JdTJ΂)/oCa^^�lO5IG<~A7 1H6d#܇&piyW(^-d@v"}Cv-/鞷tvMP^fGB#G%*ϯ_Got+Q#sfG% 0?A  C@8 ӍhCGENRiigw)cȁF@8#IJIj 91x89>AA$$P(KGTE� qs|,L ɓI SM~062C?9ܡ>y>G $t-`"h j@ըv'^ okfB&u�9Pz2[Vvw=vnuZ/RI>^8~qb,gu>v識DX%!Q`C*r>Pьp#'4�O,ꁧP7x�y}RHvPҏE@3$uź'WY㡼oCrlf47*@)_!xGKAꘂ"n2!p0Q* ?ÃCș3R?'EQA',Mx^&@!ED<1BPb4#OmTșC}q6V85'h8Va༙aSGYGs3 b&6 p<rۂ[b sOS8ùu ˒hU/MoX@ρ:i>9V=hI5RL?KxD)HPp8l.]hG<Ot}' `JB/V\|!xpi{2 0B}yd!A/1.𜖩Fpgvb -@ڀxڅ'F){' cct!})U#)&.P d#t>q1z<҆hǎPet_ a/XX-[, P1) W%.-: Cv7o`{ $K\ZxQ2#,UYz#!7P!o7�hYlϞ628m!o!2 Be٧AKs+=AiB@?Ha`R"t(a .wRIZ??KNg^|jCTh+59lOKucjOIZL{?O탛j(:p"' XMjP E 6j$1`ui Y]?bsU+% ANoAF̞TR(4;湚9\XiXns^>v~O3M Lga *t 4:k/i^kx=3(̝aCH`9e+�.4݈*YB_S8C#j> 5< BeyAh.!ckHU:, yH>cOD "*l-)IREŔVڹK. h^( P`�胟̠ x~MHυ'&\' BJ}ul΁dasr.+WW8.ZXKR1!P(-J)Й[YkE:#]ҐE=!1܃ ӎM#� |́L̆0„   t(ulc*@3;bQ`I`?eLjgkBF(@L rfyE`~xxbf1`� `TQSt174C6�'!@O &5s/R:NLc>zIfT{ʼ0(8u 91hw\&Sgq gRX #%]YqN?]ٲ( �@@c�%`*9<|Pc ȆZ7P!QzQ>Gԃωȃh2Ғg.a>~LЌ ݑ ;PS uj `t1`tB5Pe)~3 6g1{ CpLAhZ2Qv̇c? $Pd"ŏ(d[¥9o,>Pt3 OzG8&Gzμ`#H43ٰ&#I3`D68`D`cxB 䞧{<G1xḀD"L?DCN.4s vdI3F4b�@giᇑS 3 c3ָ.ǂLc OX!M7$`YjI,Dl Ϟ hO:1C<`L$2t 1F$�f>9.Z?gLCœ gt̎; aƑe4cE~ GAh s͈ dw,#>xǜjJ=s,XOJUؑ߹r3HDA$.y`%n`[ G}< y4=`xNƃZ2͢<p3sau  wvpkD����������.��M$2=lBWWGVH�Cv ���������������?pwvpk4����������.��M$2=l���������������?pwvpkH����������.��M $2=lBWWGVH�C|!������������� ��?pwvpk\����������.��M$oBWWGVH�C���I�vR3����~ߐHN,'wvpkD����������.��M$2=lBWWGVH�Cv ���������������?pwvpk���������.��M$sBWWGVH�C|�R���!�&͜SM\ *JkFhP1z+V%ш͡E^x&*=z{;VX[1_X;ZԚpG�a98dh,e= \>0}Zw)pd1QbL0Ǚ ufAxDbτ>(Tǂ_> B#H j ١'q <` HO}U@O D &{jGV^ b˛+\? vA|C6̍BC|= ;' R1z+T<XvIȩGB>/a3? *Нb3';4 ߃`B[O2䙐)�YClA;T4;9.`%BqJP=bg@ ;BQx/ 3g{4#VPE9�1l>6cObؤS0TAL�8zP=yFL( &BO<q~a8@BCTqabBsևn<v3o}{?1C;p� Ez sK8v|ti,>}܇xofY"d?]3YQǡ$w.(5F(0ܾe`Į1뷼^Rju ѝ 1zǙʽu%¡/u^?<[K>] ص8ĽCplAW1Pg8s!(+>$ QDK] QCC:(>k [iBY̸4!k?;oFa﹐�^-fK7==e͛$mOV_0erS/|ߏ;?C\8K�>^rOכ Ws.tC4q^796bǜ[G' BM\qhhVl%`̤s:]%kyY'cJu,Z~$%LS!; @unY=X<ĿBp_i66Hz9HuLMso'G,?},=Zl38ex#HV~eH]xdKn/ơ CT~a,ĎKcVQ#3ܕ8osxl$oϯf3L| B/ ֋BNr6hx$ O e:Kޙ{ /^d+~SaZ>ɳ(X8#a?c$SZ?�mjs$bsngIgF5>{*SYH9a"ۀɃO㌦\B gȭmc M#Opi�(J7Z?~@h6YJ6U9XgzG쬝/ޔTۑW9/ ݂a:W&d^4X-;ЫTWOay8j >=UH6Xj5hu`VaG)(z6̵ §xEq)Hw䎰qI|#nRZ7:=sbq#̟vRƊZNp"K 1|7'9^Ha{Gj)s8;Z$2BB- ~?;baKi?yVaQ Mk"%%ix\MmoE+mc&R;axvW~aE~FGbY&;ĶGlltdW7&';cSJo^ʗDJ+򑞹KoOT^.!a5~Z답g/GQBF@Bؼ:4z {wd1u!棃-K<QfcoQkd-5̻ȜӗKd%�T!nWm&ތc_~fS;>n!?uƫ![Qdgh2^b]h8qOheǣ500lYbzp_?'[G|27$:e>\u)iOC;Rw@)rvoZ? PF5G~^$ݐ mܠ!Ų͡[.#?f/fdƐ9@9B{Qˈ$<_ˏlrOl9�Z`ߋ/MrH-i9Xo=\Ve5hn`?i mN՗^,J1`n2S`F7OrZ}�8_^8[$Mg f8}Exŕ)Uoy .Vw;ٙY8%Ԋsfqhyɐy_~CV9ɵ#DmQGuz Zc"٠!KtϰpdlIO!UGf4-pxw#r2Եg(W^>+MwSkSU_=hKDYsb=,r̈o/>{><{XVzDhAI,rpUt 9ԗo床*ٕ{YY iDḪ|$S[A4~$oM_[}?Lz`#~x3By�A!EmC;>ΖfcbHtPhQ5iMeWކYSlH2O#>${כiOA}(� pH>b,Ve#G,G@p:srlڶ#Ʌ0UacB"ůP_1LΏUH�90IdAļ{.,G2ɬZPd ^>Z֎6d6K6^ѤpoP8?ն|rN7>N8ϗ)4E^#Z(ǁw8*\NBQoj>\@& Mud }+g$}]' ?,P DR chS$^,4 ѫ!mĬؘ(v+4ёɵF]\zƨ@693^s3PWGsÙȅ/h+l.JǛ"`f(+t/<= GZbF x=1*Rޠde6Ɵ1av1IJPKk(ak`Ηq)uiy"|E\glC >Chww/qssBLb>P&1q�icgٹk3VeH \Ӝcr~±aKj5mO& ,~ 2O^,; Giڊ\@ӹz/=e`-.xt ^'F}.>(dVy|� 4Fqc� YXbwvpkH����������.��M $2=lBWWGVH�C|!������������� ��?pwvpkD����������.��M$2=lBWWGVH�Cs���������������?pwvpkD����������.��M$2=lBWWGVH�Cv ���������������?pwvpk$���������.��M$톓BWWGVH�C~q%�Yc  ���s9YQ3QjZC#㽴k3!N)h,z'V~χŒ9, 1"&hG\lyq8rDA<α{a1;5<_SY�UB1mH2=%}.^x|_),U)MW#⋶lNp5(|̂X\YhI"fi3;&^dq[_Box/s]xuRjiWHڈQ|Q'yqw8~0F͐*//A1&)@Vbk],Zƹw+ɏ}`M ՙNtF`cR#L�Ch^ &~u!M,Uf/ύ^ԢqLU{vş?'Y~Gp o6Pw$-A:F= U`c2Nyju~S{1 ؋`LhfLm-?QFW3�GT=枒j؛Ӏ!! pUHk%9.2^5KRqE'sF3ˏ9/(vͦ ?H^X y$16vpu2[]- 9 *M,c$502K_9v':?q02@F:W+ĮQ� -v(83/њ1Bښ?<_C)kPQy*EbYZC/0kp ]n ϙn]4RXok{0<)P#:12v�88ip縠&cOՅ>46,/ {T6>+>W ex&TE5@2$.G240</ŧˣiCh茪5d*j$|n>Oҫqr/9=NӘU#~} B,j,%M|ra=o({>2 kH�~-`/a�|$9Ƒ9'vg$m?; zAs �a)J1#m-ism;#1Ypez(,~071Ħ _DAqUFuszu?3M J0[ +=<OAFr!M(>$UY2GԋPYX'G֓@q!+qOX2UBc'/ 5^g(?3ؼBj0 b}#XQfXJ!f,K aREh3ĄT 24viTvՑs߯>`;">j2z4攤n:Eَ<#P\ў1(jrO {N(%Gn05!Ղ&AV"sLZMcs}$ ͇?mN>m<໻8}; + UH)PT}Rq$l< .We%skcr[B#y 9ɍ$YZZ4<g} /sKx sг瞏K9o'yJF='JR:Owd֊a-cRmN8 5C^~ \9-Mp*o}0?ˠG^U0iIZ==iU&~wq<➓q-tEcΗLL.l E -$Ra8ة#_YINJZֆEY�1θE!y@Q'.K2 MiW"?{>#_AgDPW٭IX ,eJQjS6�P(BitD ŪNQɹ|XeBh[伄JtR*M#$Jg||Wɣ:M-~nUz`)a`y[l]!9\6*r9 *EҒx4 ԋgk|D؜@I:QX4,Y5؎d39w>>q]Wtg\_I:{+$wAZ*I8Dȋ)ˆ*P&9򖱽i)sPóaoo<cv3^. r`Đ6(D<eK-h:2soܓbS.wqdvty^9M(dd0+Z65cH19hzC ["*X&9 vb|iNa>*#ϴ 2 d~"PFB5QQL.9~ޟ/yNG]M0[S5K@-"@<YiS,"G`,sfeɱ&I Y#IBI@*L<ŏM<; 9w/<oUÝ YsVKIilWV{gz ?篏=8{T#~Rp�iPwe}7h #DR<MG\eS$ ASjDH Lqj98 Qp~Wy~jݯ8q5a.""#s. g'"/,~jþ|>}st�ZSv$_xޥayRzF-Z#D$)ƊTNB$GBI\ "^ 9'Q xK5{'s:gݟǹ? >Sw~(.dibDq8$lNij˒R ~ߟW9L'd-#ொCtZDW1Qc^c'-NBrd84b9L?Q5C8E0jyQ'IRUdGQr>ϝ7!΁@1;6bsP.Ti]q r*.]f.s>{TqpEQKhjv|#d\W4�R�c7x,XX$X/JM>,4'RY,)tDE(,qm~sy s�[FR�bΑKr-RFITn Egz.chŜw#a>;Mu^Ѫ胧.x2?EڪY&%EXDD; sˤ#QI 8$c 5HV99߿sTgv081Ƶ;'bʡ!9LؔX DR �j015}o&./TO}'{!I)B ) ]y`�JY|&RxgSv%fG�~]8 IM\ּ "?wvpkH���������2�.��M $2=lBWWGVH�C|!������������� ��?pwvpkD���������2�.��M$2=lBWWGVH�Cs���������������?pwvpkD���������2�.��M$2=lBWWGVH�Cv ���������������?pwvpk ��������2�.��M$*FBWWGVH�Cl1#�)*���R#% [L s?8~N^0^NK.F6GBw<zH$&cZGbJ02TH�}GHGCFiG^>0]qb9]{Ly :*RQ4徣 W?QWp`""yq*~ˎ|V&8 >6E򄦟D2N?iv#O0S2u>?]y'OO. 0"5\z eb#p5l P.RZ)Y=#0b><=b0"h=쯧͌?gYXA$g…vKB)0;҅H@SxgGX{lq=ԏqsMEHaObҤ"wvBIiSXDc}^_sVI y(Wa< #*M-l[R z ӄԆ)ONRjvwBcG'ܪ]|7S0 (2FM%Y"#AK)kG~R|xP<!Ɩ)>p#&;ǓG?ϟ .~= jd=Ҙ:VIa4񊖩E.:ѐR9١a2 ~raMB%_&5^ Q]a a@NBuq&\q$-%m.JT,Oz_{RaTxv,dJ@OQˊ(rϾ>ۜ8s{xx38Kf_wV /HiY,T QBXEg }V C= 3g{{MH8 ܕ3DSU+ s&CYx`P_Cٲ&bjW^9)sx"%ʲ &VǢf *4*TYCS~?x?7/|Sk蟔9Jb âi\ gQsfC[ʳQ80<5V!W}V}p(yPCU)OZ2t#N okJ{uQ(šXRX_(')a81"CShYx,,0;7hU_XZՑOr$ Edqf4Y!N>&sx5sxrYqfD/oŒjЏ2unI\XJאQdÉ?x;Xґ 7{=.w66YQd "5>4l~c}135,joVF琲v慬0S`*D${x8dy:>1*'"aTZg׾Ӿޒ(tTS d|{X0  f846<(q~jį6{qR?,"=8'/#B7X'1Z_v k{Ajh?>_q2xܠO=@ R87x5R\Wvwjv ;DNtTcu0?$Á:1O �!#>Lk R mB[*bu.PrG6:'!"L5IIK4}Uq̪0;~ߍ?FhR cΰxBT]O&,ExԮq`w|s |z,pĩK e`5 ~_CəO7 ~ uXV|BJ' Q=on*j$LHEj1:x0O0W!kd^x<i-Ė\rA۠1x2RA t3΂̲ɥPdlIsퟓun9 l5A?ߝ/u U0 "kB1S *c]34f7;UbWHN-XNY󱤉XV9Y("59sdR8M $_О}Z$sJ :y /RyMJ ~ϙ:U؇\?~}zN nP?\`CP1@6230#&<G &=:4`9j0W}J)DEɤ$JwbOmG3HI_Ђr1%E>FEsQ'QV *K? r>Cij7hs�sFyk~Բ�yW>LM}iɯ1<GwI3fe:<NluH/[bwPCeѝ"{oeN"M�cGQj`cNr}<z c VAY~>+M}9RǜG~`@x;0D(wAơbhe4KZ]n as.ݥD맻7wN5`<eL.AƲz DRϋE! \q�<!6A� !OR{6\ysed(Cw<c)6'n8d͒6s~qYtn-H�\"î5 *;$:NH!W;)$ &P?9ck)! ~w xğC0!"Y>0cw3*r򛶙0_8]k,&ߌYZQ$@g{A#qhUG \cB~G6�" C,>L2N\a3w7?hP{=Njɲ`>lN߇]Л,J٩JGotf:`D"oMJ1&ыt?LQhT&F??BπP<5{XHhy.t.F2ԚCɌp3 391<zC(8`#ф/1ޙ6~ L_s'Q졕 Y P'LS +`ϔ(?17?ԏ1"ey zP=cqzB{, }p3rIYxQz-+C{*ELO|˨OWX;< lnњݙ{XǴD|׬BC&s<# )P56:nHLV?_.)hYݭrQ+ec ;-d4 -<zg.qYw!2.eڃ(7?&g5̠ NQv{Ĝ^*5 ,P)!q3)(FFkrnSʙT&x^R$)%wvpkH���������`a�.��M $2=lBWWGVH�C|!������������� ��?pwvpkD���������`a�.��M$2=lBWWGVH�Cs���������������?pwvpkD���������`a�.��M$2=lBWWGVH�Cv ���������������?pwvpkZ ��������`a�.��M$KBWWGVH�Cz ��j��� �^{[R`f"gig!;/ Ec# xaPp·OCt�7!SAQZظ/kͧ8%́Є?V ~<r܋eLN\o,CL^M Bq)^@?@YC@%{d;5^Bo 1{D!5pρbE5JYZ8+B|0;)ـ1 E1+EaIFGW횩FZ@i)߭T8R`KEn2YY2lΥ@s}Ԝ``$i>a(!gT(CĞߡA"n]DZBWlFFzO٨W=Qi {q UufKQ&~q(b 4=ٱ'!h xփ&a"L-X-U~OsSC)pи#ځG5^7G {~NCskLC&dNvՉs=TBfZ2f\?k�3ٲ8gbf$0*tBOP )YκDj3rvl;EPPeh T&}t( 9Ѭ)@vޓXZW+\yl�oyъ7IA2Ɂ0HFЩh/S;٬ͽ]X+}+/-NO|5܁w2b=miE+X2RJ },Q*cIXdFȸ̄DCCsbgر>(S#rU^}n{r3zlsà jF,phGy"%Я9�D( s0=D#edp=_x2-Yni֙R"osѩb#71(sC%<,=cCEl'dbAgbf PCLOod c)v*L6cxHUDb%)?28_*&iu1dpHԯfo RB ;X:;sX=+(C#?F}6\^ģef @?=/0ؑcEX�oӡ9LĐl YA"'Ć_פ<X/"`Y>AjC1<3HB"^@PGɹe}<;5:<VHDӃ!Y>̂g C… ze|:xS k*YXy#gGTϣ b@$\~_V\ތqkư-+Z.?Fz "x\ #l(4C!sҫb._G6菻"G{_ZS{LH YP@N<%7o<cQ]ARD)jLd`c`ؚL-G~*TRw,j<~O]HKc32< ~ap� gRb;?CR~"e0ꂰF{-s kA�jW·:HWyAiwh4`@B0'&p1`ynN nwzU '0n0?q  2></xw_81T g#D$sR˜T-ޫ 4.p .Ls1Ir %I~V1=Ij%;~PC,xًD?_T? 5NYDBe[Ep5s=<'<Nq%�xaiv7uM|EE8kv&-爼X1;*&!! B'm�(G' (DX(ϒ`1!7!9w^Wي:Yrj16cG;~ 4S=dب9E5$%|8"#ȁDɦ!)瀐05>dl\m(rM'Gb1 3&?O4("wkXεZծs؎JǏ[?Bq�3(DCJ7C-<kA=Kw>[ jxp!O@3Jx@pjh21{N^k;{3)a-agDQ9D4:4.S+S~ S.Lt GBhHAǥ'!I!h4͡Lrj5?fVQC*AY+mDpkC4fN I͘#r45d㺡r($gӐl<Q`q, LdX :9CacM[jsKfz~@h&AC0h�?0@t((<;1?G'J+Tï9د1qa ~&Gك-`M{ EͩN8O0\gy0@E}y3' �3�:h�vexh�: (Q $��# )�:eAcꚘRGOSi,f=ZŪ%`e! Rchg2 *0'2 Aotwtp`fbtN(tOhvш lu ЊL15S/S,AF F7UG6pQ�9t&F8b<MPS?4Kcj]DAmfnLHʠL6@6h)@2@k3=L̀2N$=πHS?&"CL440QL^94�<,Cna��5c`CwvpkH���������@���M $uBWWGVH�C|!������������� ��wwvpkD���������@���M$uBWWGVH�Cs���������������wwvpkD���������@���M$uBWWGVH�Cv ���������������wwvpk���������@���M$nBWWGVH�Co�����������&��6C/ġ?R& ? yF 290Y��h��0j������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/garbage.mp3�����������������������������������������������������������������0000664�0000000�0000000�00000017776�14662262111�0017064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������e"xHږF0Cg5[O89�}492 oblej"W  w[8�K[`c5wUdkm9c_߾y=$3a)lk`Cv k89rW>\15-丄/eoic *:g6_*N(m)ݗXZ†|¡'P3TB%l Ot'wAH/bT.bg9"f3 #hG@6j9fX|5Qn6ʽs.m N(n9�$B\zp?@Hx\o"#ϝ1`TdBNq?Km }R1ay9(:ܩڲ>,V^FUGn8Vga5=cOk5tz-y2'ْYTXNQt)Z˰&1u֛[j:LvEy?l-&g1,=`'AWʉ¹u3M8jR$Vs}F{3~ˬ.(~|QB"Av_?ȐLŝN`I0SҔr 譹7hV1{hұ eu/qĻnT]bL6%KÏ̓7zD~_ouT˟wtܧc3[aN: m.U=\Ů^~ M(}bǻN {L{%kJ~spW̮TMm+k9 �̶ސ y]h*V,?k41^t6D%%ܸ[0{n A8Y:\UoeBڎq?y4@{h+Vxxb 8RT+pʣ0t]UH~ rДYeq$B~ψ9yM,o,nAUBEoՠ*ۘO1GYE!FHeN+ی$�mŁP_ao d/ԶČ(Y5p;6%P q$3bۧ�-[  \JYlC ={pG3#ueߛ7̄nnU�_{)K`!J B dL=,'wKCC?);2qX 5;lBM+Bc1yzy;b'sv7p�}Z0HdJxɳ~}|p.I29>ˆrP$=\3FFaH1z,u$Q@QYd T$I;xe0jdZfbk$2^ č!<-6\YbLm0]!#*3|i{x+WKN!}IX ^A&T5)8AQ}]LހǞ X:-$Vƫ: 7 ;@%Iv؄p>e'hE&k{8p˫]`,ޒ(NϒI.)}yJl(F6TzDW&__yeN$=C84ëIZ ֠7%t\Z+Tڴ4 #P)hQX[Tɿ0zKQa;y*ŹC=*_|.`c8m=zIt##D#lH3ϸݕ6xRyJ\ kŢ(ˢ6 <NJg4t凊k!c斴'gYخ7e ֤#nrlZty)q<e?4p9sgzdm5ң1^ l/ QF&�gS:#ZID3����FTXXX������replaygain_track_peak�0.920032TPE1�����A�r�t�i�s�t� �A���TIT2�����T�i�t�l�e� �A���TXXX���#���replaygain_track_gain�-1.020000 dB����������������������������������������������������P�����������������Info����3�^� !$')+.0369:=@CEGJMORUWY\_acfiknqsux{~���9LAME3.99r����."��@$@B��@�^x,�������������������������������P�"!|Ha�?/etyeb#K?2#̋쬣!O&_V"4pLeԘ4@@M6r= bv?%:8o> +Ќ9՜ ިO!TlŦ!ZB[߹t%Ќ�E*[chmQX XPO;7h R�%DzAxd h] 0HPV<vLX0"2't`B'r= p@b�&4&'(w w$I#43",e{\iqN#%cs)o/:FU%F{s6 ru#۵ v~DQ*_R x3lX&tXGqڄ(PXCZB$`:Mo?#:(``YBbhԭVEХ\U+Uſi4dyرWHz1�Ε.e(Ej<3r3E5b`JfcSACD_y[e5QݹN.̨�dIAāq*R <o/g ep 9ED&L~Od1N ʐu?aQ0u EP3/hF<[TDx"H*.\?0Bu5��d,Q�ܾ=6,MR8(Gp"aqa7 qޥˎgoj[ήU?27isjekR_<Er[nۊ�(hQsR � /iAo% 8@D\4 �UeB[!Ś t l"w :*4*y-KH1xQ[fF( IJ%� LB 10 R![S wSP`*0M!U$Ye* Vڏ!n{3*6[rF$k/tRnRK,M1ȁd&0 ȭ$d:H� [ J ,;Z1'Ir6ÂkT$vL4jRN}Tڛ5BZoPM.Mb2% |yN[TK Ti>|"VPl.0$<U.DETz8$tm"_ҙQF̵rPl"ݾEQ$C'%'&Ch\'DR � m1b 4`= ME#9>_wJdOO_Sb;KR]}ciYf8 cM>@&xJS:M DmZF崼颕EA0gmT:avZbT0 gg֦ %*l|'O:G Ԡ$mB.\�R /aGft 8BqvoR]'Fg5+QgCw>hǵTEdm@zi*� 2 ZP /U7q[F"\p`sЩ=6RsEWRթ?!S*,0�H`3]5BV)" 9G("yÀu' 9jPR C1j=&  B^in(+w!kH]F$x.Ahx/l{0 lQJpIOϸK/l`0 H8:Enu>4-ӏ j>i~uRI7N5r9L5 $ 41CM:yz:a&ܚL.PnTA5(ܵ+SXZ;~CCR(� Pg5iCfpĉHv ꨴ$U Q4R$ ,L 4/cM cƭ$T[C9BxV̙1*Wm4WRtFg)  a=&h#+:(PTB[:᣺*ӍbۘTK"㯢/1{/m*ǩi6mH_H>JR4� L1NLtxj gi)` X.U  *eBF8`5Oo'uh:X:X\ZS-uz:YPeaUP ]SHTj9+qm$JzX:X,Tuh 3NVҗ6V2"P~/&ۮ>}a85D-(AٚR?� p3eA.p RlAaM 9yk?F?n+j5mBU-kM*? aX)a+JDac7}?fosd%ZH6뇩pΫhw.%v& =>&6chLز$ Ąh/V PM 6)iVPj8cL ;RM� k/j,&(b"U4hguD8HcT['P,bf5y: � EEa[Hiso|߾Ϳo'<B8-۬Q аq%1[RUEe & � <!N1.3=>f$@9r_\0=Ebh R[� lG5jIpę�"Ph`6 $~;b†@`چ v 5BR⢷`yhIQNT˞,d^z./jU 6$ `% iDr(VS?'C;XE[Y*E(j>pٔΎ 4$U@ mJ7_*%DWD.T`콓T ʋ0mpF5[Rf� h91$hGf H{Uά7؏oaO"ˉaK֐,iWˋI 9 .L5" -T<E (<`m#N#"=P7f E8^.r![nSSx0 UNYiȐ �9~ eI|f( 4(|մa_ÌG%> PRq� ܭ3 PI pFhI Qu䒁dQ;yJq[c6I�\KfF`9@$!*$|FR(IEAay :͛ ),V1n^#,|7C+0krΠ)i+`%=%�4:?X##+\%S0ޯ" J"Z1Gh8 Rz� H1mA!d(R[qq$VbקS٥!W3}#TJy|JN8Bh E6˓* ֢uKz<R ?"sz&קWښndA{:$ at*x�B<X]3I $9-XX>%JJ8APF4];yRz c/rA_fq ܫ"V%KO]C^ATTʈ*+K9vT ڬQ,ɄqjMF-2.ʧE&"�".K|Ȩ,6 R/>b/[?ZǤl,2[3|hdcO]߆shW1&LW"a'%yg3m:/v)ݐbs5cd.;ѫ,^uvmu]Tq &~<*渣MvKN n]~6oTwŜ1 J[*QW[c۷"[bi[7<`:VOg&W]y )%- )]\"ҩhM`J!:ag7Tln(bV-hcju3v3o*dIHG I##-kCc)AS$7N @&W̵y a:2Jyٴq_4C&bVp3V<<eʝu&7yk9s0. |*μ/?BE7k"rYFΩ=p7?COZ ť#ӪiѾө$2@+40l~ YA 7QPq˩^0{.4�5/=-lj*fngq n*75o1K,N&~7l'hHHͽF;A]@dtj&gXܝ(=r>aLB}+֚J\I&\:gQ)Q˔ypݠ0#!ZIol Y}b&=Q›Lch.h#REJ7�=GiPڒB<|W/ :#u?0j^살e۩jJcp)�M_ۮmI\BL(AՀa6$qn/~v2Xq*Χ]�<4G F³?{iO zV+X*9 suyP6 o֍2cJo!U]o"FOj�mʋgG<?ظ[{7*+_٭u-+296rj3e+Ά3o8v b["69&93b}B=Ka*NT̖sM25SLA}u =$@M-qXң[f3/ t_¬YX҅ޣZFgȈQ . ?Y4|.5쒙m'@,G(,J6Mzٳ!>g2ʽMX܄֮@Я{/G9#_-=.B3.�o+uF@H´\j5|+ߜJ*tr~;'&Q '~ ǟWf*8- B'oT&ˆL dۡN4jrpQJm:vnlsǹdmc@c yaъՒY-$i!:̏{L#R>I#uks(+Vd.pU):Dm36daPt�_'~pIL$'T*sT ȧBDu)V7V]0!p*vsnD.$q-鋦6ZiF 1VD?𤶍'|O,r+9=Db^s5T/\[ /M=,:y&a<H&')D e.sEP zǣ3 _K(G zڇ `%?FlVڊ:Jٶ{zIq%➳<s,hnuZK4Dz(-i^(3չᧄd]t PtyLunXt [+XP4/7T"i"ٵ"Om u8ԶK%p̑]ۡ\nY‡Dj,vy܁xU? `BJm�~M.㎚06jΣ Rz;5Pw/}B].(`��taglib-2.0.2/tests/data/gnre.m4a��������������������������������������������������������������������0000664�0000000�0000000�00000011642�14662262111�0016373�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����mp42isom��mdat��libfaac 1.24��B� 2�G!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#���mdat�� Xmoov���lmvhd����El�_���������������������������������������������@���������������������������������iods�����O��ttrak���\tkhd���El�����������������������������������������������������@�������������mdia��� mdhd����El��D�~�������!hdlr��������soun���������������minf���smhd�����������$dinf���dref���������� url �����stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���� �� b��� stts��������������������stsz����������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���(stsc�������������,��������������� stco���������� ����I����� ctts����������������������Xudta��Pmeta�������"hdlr��������mdirappl���������Q���free���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"ilst���gnre���data������������qfreehdlr��������mdirappl������������Lilst���!too���data�������FAAC 1.24���#ART���data�������Test Artist����������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/has-tags.m4a����������������������������������������������������������������0000664�0000000�0000000�00000011764�14662262111�0017154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����mp42isom��mdat��libfaac 1.24��B� 2�G!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#��#moov���lmvhd����ELII�_���������������������������������������������@���������������������������������iods�����O��ttrak���\tkhd���ELII�����������������������������������������������������@�������������mdia��� mdhd����ELII��D�~�������!hdlr��������soun���������������minf���smhd�����������$dinf���dref���������� url �����stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���� �� b��� stts��������������������stsz����������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���(stsc�������������,��������������� stco���������� ����I����� ctts���������������������� #udta�� meta�������!hdlr��������mdirappl�����������ilst���!too���data�������FAAC 1.24���#ART���data�������Test Artist���----���mean����com.apple.iTunes���name����iTunNORM���jdata������� 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000��covr���_data�������PNG  ��� IHDR���������Ԛs���IDATxc|�� Xo����IENDB`��/data��� �����JFIF��d�d���C�    "##! %*5-%'2( .?/279<<<$-BFA:F5;<9�C  9& &99999999999999999999999999999999999999999999999999���"�������������������������������������������������������������������� ��?���jfree��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/id3v22-tda.mp3��������������������������������������������������������������0000664�0000000�0000000�00000010000�14662262111�0017220�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����vTDA���0304�TRK���1�TYE���2010��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d�u�=�@��"`�1 @�?˼N(|rrDO xKإ2Mw3ŕQv. sR܄$dyA�� 0�#����4�`��� ADYa ^O-c7 EbJ!SwukrA%5:=ʹjw0l%RPn@MD] uؕd oA`�� 0������=� ���[[6mes| !)V+ -.Xڤ4ϹR[" !mdj���0�����<� ���=|TVYrK>͎pu1D{-eRa7tfTb)JT*vdkB�@���0����=�`���PTdZOz SGm=F*=)2gK̘Y +ci7bpynd dA@�� `0������0�a����U^!62z)}H:]X7NqUlλcjk)h{ech辅*kTd(jA@�� 0������8 ��{T:TVuw0QlKJ48'ދ}D9<!cW=vEFbJd/z��� 0�����9� ���\E7i*of81CQIxD1,Q,<_/m u,V)JȆݕZkU d6SB�@�� 0������7 ��o*ikP)U1u(1wR+TQԠt뻋<޻ j29N؄[Jd>lB�`��` ������5 ��� صĨ3`:4\ȴ"bY Ӟ"0-d/uҥmguΔB(tndEyA�� 0�#����4� HG*P̿b7xxrmCMMm*̺t A"(BT.?8EdK`�@�� ������: Hm~˩2 NPrYЪ1E叼XRZa饔ƠmV߲-C8غdQz��� @0���P@�8 ���In͠*Y#V$.9sgnF̛?ԺbR\Vko*U(VIi{dXXA@�� `0�����/� ���,`rј MvX*<>YLzJ1ץ/;  ]&up+C dbz��@��@��t���:� ���:D\=XYzSsWN}x^w]!F U̐FY׭$FЁte Q;1F!-UPYoLdgt�A� �����4 ���j6VӔ*{۟8^(rm&^HXRFKA"Դð"j#:* dmv��� 0������2�`��̯ar-Xʱ+84ΕT1XqǸQs醘–ݩKe+ ]S*cRduuA��@ �"����- ���|Au�Oހom61W�Ϩp9 Xѵ9}$KS<Ӹ啇Bd|`��@�� 0������5 iud5'2haµ2$!/&p4  vZc9frdf�@�� �����;`@��M93bRqό]͹(u2"*+bխǘ?C\(tsKըV6']9[h;s=PdqA��  �����.�`��B2ELv<FT!ϩ$%smNwzj ` L$`SqcbdoA�� �#���= ���0F?xņ^aW+5B.cHyV)Bބҩ4U sYz!㏋]ja{ֳ`6pd^A`�� ` �����9�`��pKPlw4i= [qUJ1*rv_\VjE."ذ쐆+ lґn`ddA@�� @ ��@�=�`��ƺ#kQqU)8YкQI4 wUk.M R 5;Y;Rdz���� 0�����; ��� EzQuVTqW9v ^ر}N~HX|\6JyyNg0Udu���� `@������+�!����E5Nb2@m 9HFG.9WlutBihU K֭3N2 1o - #{SdfA���`0����3 ���zǦ^Tk QMI(M@@εxs 4uk=DP~53FW`Tx[<dzA��� 0�����1`���ʣ#AMoc.ʪIKn5P45+8?emלYww1q@$T)q.d\B�`�� 0������;�`��MBp*Pɫ֊iN7*d^D| aҟ(F~h[zQHͭHiy"dǏsA�`��  �����7�`��Ezv*S{^[byU k C'-[VHVQO?UBQQW̉M (7fApbŲ1w Yd͏iA���0������7`���04B4U[d7&?%sْ6|G|1/k{&]i˥)Ghl6>6dgA���  #&��<� ���(krqt))G yN Jlz C)Ňʹ3*79xdrA���@������3 ��h(Uƚ,r ET7_۰}L)!!I;Ifw=p^S _do����0������-�a����mWIn qѭ =gy+0V̪'L9 (ֵHsoZkklh0,T>d��@���#�A���9taglib-2.0.2/tests/data/ilst-is-last.m4a������������������������������������������������������������0000664�0000000�0000000�00000100000�14662262111�0017750�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ftypM4A ����M4A mp42isom������3;moov���lmvhd����1'*��D�,D���������������������������������������������@��������������������������������0trak���\tkhd���177��������,D����������������������������������������������@�������������/mdia��� mdhd����177��D�,D�U�����!hdlr��������soun���������������/ominf���smhd�����������$dinf���dref���������� url �����/3stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@��p�o���stts��������� ������(stsc���������������������������,Xstsz���������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������g��,��C��G��=��?������"����*��+��1��b��U��������[��9��������������������������������������������������������������������*����������������������C��$��������������������������������������:����y����;��������������������������������������������4����������������������������������������U��������4������������������������������������������(����%��������y���������� ������������������8����������������������������������������������O����������:�������������������������������������� ��������@����������������������������������������������P��<���������������������������������� ����������(������������������������@����������������������,���������������������� ������������������������5������������������������D���������� ������������C�� ������������������������������ ���������� ��������j��������������Q��,������������������������,��D��A��s������������������������������������ ������5��2��{���������������������O��������������/��@������.����������������������������O����������S��#��/������������������8������������������2����������Z��-�� �� ����|������������������������������������������� ����������������������������������������������!��������~������������ ��������������������������������������������������������������������|����������������������������������������������������������t��{��x��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������l��x������t������������������������������������������������������Q��������������������������������������������������������������k��X��M��u����������������������������������������������������������������������X��s�����������������������������������������������������������������������������������������������������������R������������������������������������������������������������?���������������������� ����$����s������������x����!����������������������������������������������������������������������������������������������������������=����������������������������������������������������������������������������������������������������������������������������������������������������������B��������������������������������������������������������������@����������������������������~��������������������������~��Q��?��U��������������������������Y�� ��a��q������������������������������������������������������������������������������������������������������������j������������������������������M��e��z����l����������������������������o����������������������������������������������������/������������������������������������������ ������������������������������������������������������������������~���������������������������������������������������������������������������������������������������������������������������������������������������!������������������������������^��!��Q������������t����������������������������v��|����i����������P��_��������������������������l������������������ ����������������������������������������������������������������������������q����������������������������������������������������������������������������������������������m����|��w��q����������7����������������������������������������������������������������������������}��������������������\��O������F��M��7����-��?��j��t����������x����������A��(���� ��P��]��o������������A������r������b��������x������������h����{����������������������W����s������L��������������������q��-��1��K��Y����������������������1���������������������������������� ����������I������}������������������������������~��o��y��������������������������������������������������������������������������`��M����t������������������������������������b��m��h����������������������������������������������������������������H����������������������������������^������������������������������������������������������������������w��t��������������������&����������}������������������&������������������������������������������9��������������������������������������������������������������I������������������������������������������ ����������������������������������������������������������������������������������A���������������������������� ��������E��������������������������������������������2�������������������������� ����������������������}����������������������/������������������������������ ������������������������������������z��X����f������������������Q��b����+��{������������������y������m������������������������������R��������������$��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������R�������������������������������������������������������� �����������������������������������������������������������������������������������������������������������������������'��������������������������������������������������*����������,��������������������������������������������������������H��������5������������������������������������������������������������������������������������������������������������<����������������������������,����j��q����������������������~������q��������������������������������S����������������������������������o����������������������������������R��������������������������������Y��r����������������������������������������������������@��������������������������������������������������������������������������������������A��������������������������������������������������������������P��;������������������������������������������������ ����)����������'��j������������+������������������(����������������������������������������������������������������������������^������������������������>������������������������������A������������������������������ ��,stco�����������%��,�V]�e��͇���>�e\���n��$�JA�q�#��x��.�U�z��ǃ��E�;�a��S�Ғ�X��E�j���|��*�O�vT�a�s��T�5+�Z��0��� +� ?� eW� 6� � J� }� $� Je� p� � P� � e� /� U9� {� � � O� � :� bX� *� R� R� � � E� k� � � ��*�Q �v�����5 �["����^��?z�e���-��$�J�p��^�p� �/�UG�{��7���;�`��}�Ӡ�!�Y�E�j��b��udta��meta�������!hdlr��������mdirappl�����������vilst���nam���data�������Intro���!ART���data�������Pearl Jam���Malb���Edata�������1995-03-22 Brisbane, Australia - Entertainment Centre���cmt���data����������gen���data����������day���data�������1995��� trkn���data������������������cpil���data�����������covr���data�����������6too���.data�������iTunes v6.0.5, QuickTime 7.1.2���Z----���mean����com.apple.iTunes���!name����replaygain_track_gain���data�������-0.67���Y----���mean����com.apple.iTunes���!name����replaygain_track_peak���data�������0.45���]----���mean����com.apple.iTunes���#name����replaygain_track_minmax���data�������82,164��Lfree�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/infloop.m4a�����������������������������������������������������������������0000664�0000000�0000000�00000147710�14662262111�0017114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ftypM4A ����M4A mp42isom������moov���lmvhd����/"K/"U��D����������������������������������������������@��������������������������������%trak���\tkhd���/"K/"U������������������������������������������������������@�������������mdia��� mdhd����/"K/"U��D��U�����"hdlr��������soun����������������wminf���smhd�����������$dinf���dref���������� url �����;stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���i �k����stts���������-p������(stsc������������������*��������stsz����������-p������������������������������������������������'�������������������2��P��V��Y��n����x����������������w������y����������v������������������������������������������������������������������������������������������������������ ������������������������������������������������������������������������������(�� ����������������������������������������Q����������������������������������������`��������������������������������������������������������������������������������z����������������������������������������0��������������������������������������������������������������������������������L��������������������������������������|������������������������������������������d��������������������������������������������������������������������������������p����������������&����<��������������������{��������������������5��=��+��*��Q��M��n��z����z����a�� �� ������"��b��V��������{����������������������������}��o������������������{����x �������&����)����A��+��>��7��������%����������������$������������������������������������������^��B��S��W��]����������������������m����u��������������x��m��v��l��n��y��i��q��k��V��n��{��b��~����������[��[��D��������������������������{��m��i��{��z��U��S��x��h������j��I����s��n��t����g����d��y��]��������"����$��i��@��|����Y��|��o������z����������������d��=��)��4��3��j��7��v��������������������������X��=��R��O��Q��H��`��_��]��������d����{��L��P��a����k��s��p��������������������/��(��(��:��`��������P��N��w��������������������������������v����o��b����������������������������}�������� ������������������������������ �������� ��=��7��>��H��F��6��3��4��'��0��$�������������������������������������������������������� ���������������������������������������������������������������������������������������������������������������������������� ��.������������������������������������������ ���� ���� �����������������������������������,�� ��-��#������ ���� ����������������������*��#��=��3��H����!��>������ ���������������������������������������� �������������������������������� �������������������������������������������������������������������������������������������������������������������������� ���������������������� ����������*��'��=��5��!��7��9��C��,��8��1��'������������������������������ ���������� �� ���������� ��������������� ������������� ��,������#��1��)�� ��D������������+��9��-��;��O��;��0��C��F����h��u��I��Y���������� ��,��>��2��_��<��4��V��V��^����&��7��?��@��4��N����S��Q����������������������������������L��������������� ������ ��������� ��4����C��S��8���� ����������!��+��=��c��Y��2��$��>��>��N��c����m������������ ����0��`��D��<��?��U����������L��q��m��K��b��������'������G��K��\����F��g��k��u��g��f��q��q��[��P������N��T��G��V��e����W��|��H��C��~��l��G��2��|��F��b����������������B��Z��j����s����N��Z��x������_��u��x����������������C��E��D��c��J��I��H��q��h��h����������K������/��@��5��@��;��`����:��M����~��M��e��{��E��l��}��w������$��4��U��Q��������l��\��o������5��5��D������p��������2��P��|��������������}������������|������A��G��`��c��s��X��q����`��:��s��w������a����p��w��M����X��`����l��v��������������h����������������j��������������������������{��������������)����(��F��8��e��t������������t����������}��������`��������������������������������������$��k��������������������������������������D����-��@��i��G��m��S��s������^����U��r��{������������(����K��B����������������������������������v����������������������������|������������5��S��z����������������������k����������������������������������������{��������������{������8��j��`��w������������������������������o������������������������~��e��������������_��a��|������������������������������������n����������������������������������}����������1��S��x������������������������������������������������������ ������������������|����������������������������������������$�� ��Y��[��[��4��/��2����3����#��.�������+��6��.��)����������$��;��Y��n��r��s������z��������������������_��������������������r������������������ ��h��p����������������������������������������4��1��'��R��2��\��K��|����w��u������������������8������s�����������������������������������������������������������������������������������������������������v��r����������x�������� ������������������������������������O����u���������������������������������������������������������������������������������������'���������� ����������������������������������������������������������� ������7������������������������������ ������������ ��-���������������������� �� ������������������������������������������������������������������������������������������������\��������������������l��������������}����0��������~������������]��{����t������y��~����(��H��p��������}��j��f��{��P��a��{��r��s��q������d����]��f��X��r��y��r��r��e��s��m��_��l��n��/��������5��\������������z��}��K��x��t����������4��b��:����(��&������������������������������ ��@��#��H��#��S��o���������������G��h������a��������������������������'��1��-��Z��e��\��������������A������������������������D��K��l��r������������������ ����������!��L��J����]��z��W��V��b��s��t��m��)������v����&������"����7��S��Q��y��y����������������������k��&��s��B��c����g�������������������������������������� ��K��_��e��������������������9����u����&��F��q��u��~������e��n��>������n��N��U��z������:������!����-��U��m��l��������������������������,����,��_��y��j������������������������&��=����%�������� ����d��Q��v������������������������/��.��G��%��1��_��o��_��]��~��c��J��c��������������S������ ����,��5��\��r��u������������r����������x���������� �� ��������8��6��W��{��H��`����U��b�������������� ����������������7������/����+��,��4��f�����������������������6��������������������9����6��c��h������������������������������"��7��������������������������������%��!������M��I������ ��,��k����������������������%����#��1����J��p��o����������������������������$��������*��V��m��������������������������������������}������������������������������������������ ��<��[������������������%����%��9����������p������������������������ ������������������8��+��N��K������������������ �������������� ��4��@��]��x������������������������������{������������ ��Q��I����������������������������&��4��9��`��h��h��]��s��g��������������_��y����x��������'��[��K������~������������������������l�� ��������������������#��I��U��]����h������������������ ��\��c��g����������p������������������������=��Q��k��|������u��t��M��k��Q��A���������� ����������=��P��\��s������������������������R��Q��n��~����������������������������������#����������6��e��y��}������������������������H����9��@��I��W��X��d��{��P��i��c����y����l������������������)��>��U��w��t������������������������m��)����`��m����������������������������G��i������������=��B��m��g��y��������������������v��������U��R����~������f������D��>��g��I��@��i��w��{�������� ����>��A�����r������|������v��������������D��S��F��P��p���������������������������������������� ��b��v������������������������ ��J����������0��M��f��b��`��h��{��h������]����������t��������,��<��D��V��S��l����������������������������������������������,��H��K����s��{��������%����������0��^��l��������������������������~����_��_��t��������������f��w������������������+������+��@��G��m��U��x��t��U��g��^��>����T��T�������� ��A��h��o��r����������������y��g��}�������������������� ��3��I��x����������������������r��������K��)���������������������������������F��$����j�������������������������� �������� ����j������������������������������'��������������Z��S��c������������������������C�� �� ��H����b��e����������������������������������@�)��=��M��i��x����������������������������(��K��x������������������������������������q����e�� ����9��;��s������������������������*��H��D����������������������N�������������������Z��b��&��a��j����������������������;��:��7��S��*��!��]��v����������~���=��9��T��_�������������� ������ ����8��V��l��������������.���������� ����������x������������������������������������������H��Z��d���������������� ���� �� ����������������������������������#����*��#��"��)��H����j������#��@��Y������������������&����(�� ����7����C��^��[��������������������������8������������>��l������������������"��A���������������������������������������4��������$��������������H��_��e��������������(������E������-����8����������$���� ���)��+��.��G��-��D��9��&��=��%��8��@������I��c����v�������������������� ������ ������������4��W��~����������������������������:����������"��%��U��S����m��������������������7��������0��&��A��l��]��s��e��s����~����������s������#����,��@��F��Y��^����w��l����������������������T�������������� ��!�� ����1��9��j��~������������&��������;����C������������������������������*��^����|�������������������������������������������� ������ ������ ������3��;��P�� ����H�� ��.������� ������������� ����/��>��+��A����B��*��a�� �������� ��M��~��c��k����h��N��p����u��d������t����=����P��~������z��l��q��x��g��j��k��o��������l��w����������3��a��q����e����������������������������A��������t������}������y��������������������������J��[����t��}����z��|����������������������i��������������������>��+��H��]������j��a��������.��2��^��[������������������������������V��U����������������v��a��/��S���������������� ������6��K��W��s��u��������8������������������@�� ��N������������������������7��s��f����m���������<��\����H��t��U��r����`��m��x����������������)������������������ ������.��C��w��;��{��e����������������B��X��y��s������M��g������������������%����������������u��������,������z����z��������'��7��Q��������������m��\������������}��������1��X��j����������������G������������������y��-����*��i��\��~��~��������p������������������0����@��t����������������e����������������g��[������$��X��t��v����}������Z��������������������N��@��S��w������z��r������I��g��������������������������F��R��j��\������p��{���������������������� ����w������������_�� ����3��!������������$����%��?��|��c��w��`��p����|��\��������������������I��t����������T������O��-��h����z������������3������ ��2��V��n��{��������R����x����z����������������"��]��������z����_��������E��~��J��w������m����8��C��.����I��X��r������w������p��u����}������������������������������L��9��S��h��r��������v����������R��V����{������n��X��������������������������������������m��:��J��y��������������m����#��;��z��m������n����u��;��n������������������!��G��P��V��x��y����������;��@��p��d������������k���������� ��A��b��b��c��p��u��j��������������������}��������?��V��������&��������p��������u��m������"����"��E��������������������������������5��������������|������&������g���������������������)��H��e��j��������|������������������?���� ��U����������������m��������"��v��g������������W��I��v��x������~����}��}��z������������������f��?��r����~����s������X����^��Q��j��u��������x����������^��I����{��z������M����������������(��_�������������������� ����!��<��9��p��c��X������ ��������!��P��_��e��������i��m��������������t����?������������?��4��M��;��E��P��@����g��������M����T�� ��a��~�������������� ������ ����������������_������ ��������������K��n��������������������o������������������������������������o��C�������������������.��[��������������#�������C����0�������������������������� ����5��7����T������������������������������������������g������������������������������H����>����W��������������������������3������ ��J��5��g��o����I���������������� ����$��%������:������*��6������'��D��&��;��:��7��S��*��!��]��v����������~��{������T��f����������������������������!������+������������������1��2����_��������.���� ������9��)��`��q������������������������ �� ����9����#����t����������������p������������������<����F��������������������������������?��,����a��2��3��%��*��:��D��$��H��S��G��`��~����������������Y������;��l��������������z������&��%������������������ ����,����'��,��M����'����������,����.��0�������������������� ��������������!������������������������������������9��+��G������������������������^��������������������������������������������������w��{��r��}��K��d������8��������������������������������y����������������������������z��}����k��t��Y��m��o��t������|����[����[��������S��[��o��X��1��W��b��J��L��K��O��\��X��r��d��x��r��i��s��W��P��5��n��U��P����@��5��9��#����������9��S��Z��������s����������������������������������e������b��b����s��K��X��G��k��q����o��Z��{����p������������������������������~��������������������������������j��N��c��3��I��^������]��G��j����?��\��|��l��T��j��x��P����z����������o��������������������������������\��v��m��������������������_��v������������j��[��Z��R��D��0��G��D��5��Y������E��h��A��@��)��3��&��:��C��1��v����/��_��E��2��~��;���� �� ��<��X��~��@��6��W��}��������q��q����������}������������������h����o��w����n��q��q��y����}����y����g����{��p����q��z��|��R������{��i����t��������o����p��y����������������u��������p������~����������t����^��h����s��v��o��^��[��������w��w����s��u����n������������j����������������w��c������]��|��r����z������q��v��}��u��o��b��{��^��u��}��j����t��}������{����������������������#������[������{��5��n��������������������������������������������~����������{������������������x������������������������������������������������������������������������b��~������\����9��������?��\����������}����������o����q����������������������������������������������j����J����������������������������������������������������������������������������I��Q��U��F��B��Z��?��>��H��M��T��Q��D��X��L��Y��i��M������A��s��:��K��h��E��O��_������y��~������������������m��\��������������#���� ��Q��L��^��s����x��h��u����#����:��R��w����v��_��V��7��!��U��I��Q��[����$��C��!��1��������.��>��>��B��@��M��+��@��n��Z��p��@��W��d��I��^��.��{��������"����(��,������(��$��<��B����Q��-����:���� ����9��t������������������<��"��-����F������� ����������� ��������������x��l��u��������������z��������������������������"����"��)����'������������ ����������%��\����������O��!������������I��{�������������������� ��#�� ��-��$��H��0��M����3��K��F��'��C��A��-��#�� ��_��������������'��/��%����t������ ������ ������M�� ��9��(����B������B��d����g��]��R��z��q��d��e���������������������� ���������^�������������� ������������������*����������������2��:����������M����n��c����f��`���������=��o��c����v����t��]��X����d��������������������T��r��[����������������v������������������������/��C��������������������������������������������������������������������������R������������������������������������������������ ������������������������������J��t�� ��\��S��e������������������������������������������������������v��Q��f��[��z������s������%��Z��Q��u��u��z��������{��\��s������������������v��������������������6��������������������U�� ��;��f��~������������������������������������������������������z����������������������������������������������������������n�� ��-��"��D��;��S��]��|����a����������������������R����:��N�������������������������������������������������������A����������������1��-��S��H��f��v����o��s����v��`��y������������������{��[��~��������������+��y���������������������� ��<��D��a����������������������������������p����������������������������������������'��������������������������������������/��o������������������`�������������������� �� ����E��K��q��������������������������������b��������������������x��Z��t����f��������������W��V��s����������������@��������������������J��"��>��x������������=��q������������������,��������g��g����������������������������$��k�������� �������� ����,��������������������4��m�����������������������������(������������j�� ��0��<��w��p��p����������������������������<�� ��@��e�����������������������������W���������������������� ��:��=������������������������������������� ��������$��'���������� ������0�� �� ��)��A����&��6��E����1��I��]��B��R����v������:��Q��C��w��R��������������������F������������������������������������������������-������n�������� ������������������ ������������%������������������������������������ ����������0��X����������������������������������������������������G��D��a��{������������������&�������� �������� ���� ��#��8��3������ ������#��-���� �� ����"��,��:��Z��(����5��"��(��@��K��u��V����8��3��B��$��Y������������������������j������������������������������������������������L��b��������������������������������������r��)��-��>��m��~��������������������������������5��I��Y��������������������������������������������"��$��������W��������� ��/������;��*����,��>��*��D��/��I����?��;��7��1�� ��N��J��@��E��<��L��6��N��F��F��5��:��9��7��G��(��4��-��?��7��F��/��D��6��)��9��#��,�� ��3������������(����&������%����������������,������������������������������������������������������������������������������������������������������������������ ������������ ���������������������������� ��!����1������������������������������������ ���������������� ��N��������������������������������������������������������������������������������8����������������������������d����*��������1��c����h��C��o����$��!��'��9��-��C��A��P����w��(��N��n��{��x���������������������������������������������������������������������� ��0��7����m��g����������������������������A������A��i��w������c����:�������b��w��z��������n������ ��;��l����j����}����������������������������������������������������������������������@��f��{��������������������������������f������3��4������[��[��F������?��_��y��{����r����h��������/��[��R��F��i��b��q��5����v��u������������{����~������������������������\����(��L��P������j��Q����������������������������������������^������*��`����_��w��[��1������I��+��?��m��f����f����������]��{��p����X��i��{��{��������������������E��n������������������������������~����+����$�� ��K��t��M��|��v��^������e������w������������:��)��:��}��s����������$�� ��%��I��1������������r��{��C��������=��f��]����g��o��a����������������������������������������������������������������������F��^��Q��L��o����y��������������������\������%��1��u����U����������3��@��Q������g����]��M����%��$��i����m����p��n����������|����������������������������������k��������������)����!��_��c������������������������������������J����N��W��~��d����r����������A��j��������j��������M������i��X��S����q��J����d����������������������������������������������������������������2������������������������������������{������E��h��k��r����,����T��B��G����������v����b��9������V��t��j����t��[������������������������������������������������������������ ��g������N��g��������z��������������������������F����+��X��k����q��c��b��t������6��E��U��n����W��t��X��������-��D��^��G��j��T������r�����������������������������������������������������7���� ��w��]��������~��������������������������m��J����f������������w��;��R��}����������������������7������������������������������������������������������������������������������������M��K����������n������������������������K��"��f����������������#��-��I��}��������������������'��y����������������������������������������������������������������������������A������T��|����������������������������������������������������������������������g��X��:������#����'��y������y��s��b������������������������������������������������������������������������������������������������������������������x��������������������a������V����L����3��������������������������o��������������������������������p��������z��l����u����J��Z����j��������t����������������������������������������������������������������������W��F��v��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������t������������������������������������������D��u������������������������l������������������*��'��u����������������������������������i��J��_��z��t��E��p��i����d��&��`��Z��G��f��C��M��H��]��K��#����J��8��z��e��a��d��^��<��P��B��I��+��S��k��\��j��y��������L��Z��G��Z��d��x��J��N��T������*��|������ ��]����b����*��H��U��R��a��b��p��n��v��u����n��S��j��M��u��[��N����7��7��^��:��q��?��'��"��@��0��Y��+��G��?����D��#��Z����'��(�� ����+����(������������ ������ ��F������������ ���� ������)���������� ��������>��+��C������������g��h��}��r��F��T��x��W��U��=��K����5����D��"����-����b��Y��D������i��z��T��d��������b����t����'��I��6��^��^��X��N��V����b��H��K��^��O��w��N��T��h������H������,��Q��v��~������i��v������������������x����-�� ��0��i��������������������s��\����|����w��r��=��Z�� ��j��������-��D��`��c��}��k����4��T��[��d��t����s������3��h��m��j��y��������z��h��h����������_��]��z��k��_��Q��^��R��Y��P��7��)����!��"�� ������7��6��`��D��g������|��X����F��g��8��9��]��-���������� ����������>��w������H��^��{����v��o��I��(��3���� ����9��R����������S��{��x��|��������������n��t��j��A��%������#��a��G��*��8��x����������v��>������)������0��+����O��h����N��p��p��������\��G��2��B��M��,��&��@��g��J��i����w����Q��|����~��p��r������������������k��m����q����k��(��Y��]��_��������Y��w��D��&��"����!������O��i������9��`��^����d��r��c��Z����^��Y��X��t��f����z��W��u��z��w��V��5��@��������������������������������������������������������������������������������������������������������������������������������������������v��s������������������]��t�������������������������������������������������������������������������������������������������������y��s�������������������������������������������������� ��m��w��{���������������� ����������������;����������������������y����������Q��S��W��S����V��������*��������������������|��������Y����6��j����������t����������b��������}��`������O����4����v������������������������������|��=��Z��r����������������������������������������%��2��,��_������������������������f���� ��A������������������������������e����z��������8��0��C��^��n����l��b����f����}������s��������p��������s��~��_����{����������������������r��������{������<��{��p��o������������������������z����������T��z������������}��{������������������{����4��z������N��M������������������p��s����������������������������}��{����������|����j��V��h��V��U��M��e��`��V����Z����g��w��m��r��T��t��v��[������b��t��x��~��w��s��j��g��K��I��@��F��U��A��H��K��N��c��l��T��l��p��c��i��l��o��l��b��h������l��r��k������������x��f��a��|����y����v��`����j��j��z��Z����Z��l��]��u������������������X��y����w��w������������������{��n������������p����}������o��W��i��{����U��\��N��&��N��K��5��>��&��M��L��[��:��K����5��+�� ������ �� ������������������������������������������������ ����������������^��h����w��u����n��Q��<��V��h��\��i��Y��h��|����������}��v��u��_��k��t��c��Z��q��h��j��v��y����~������������������������������������������������v��~��z������z����a������������������L��H��c��}��������������������������|����������������������������x������������������H������������������2�� ��������0��1��,������'��6����E��>��R��o������������l��_��Q��z��n��������������[��{��i��������������������������������w����\��t��r��e��\��z������}��t��W��J����������������[��F��c��t����������������1��������������������������������������������������������������`��]��`����������������������������������R��I��`��������������������������������.������[��|���������������������������������������������������������������������������+��3��F��z������ ����(��D��=��*����?��,����/��������G����u��x������������ �������������� ��������������������������.��.��M��L����&����������1��������������I��G��G��W��u��t��r��;����!������8��&��>��Y����n��������%��<��]����"����������������������������������������������������������������X����������������)�������������� ����*��/��1��`��)��]��e��������������������������'��F��a��}����B��������������������&����������2��)��*��T��������������������������������������������\������������������ ������������������������}����������������������{��������������1������t��x����������#������������������������8������?��E��-����:��C��K������������������������[��q��������x����y��T��Z��j��`��[��_��r����P��a��z��U��1��6��P��s����x������p��~��L��u��<��>��D��[��]��u��d��������/����������������������~������������^��@��U��v��|������������p��x��r��a��n��Z��1��Z����d��-��������������������������+�������������� �������������������������������� ��'�������� ������������������ �� ����������%���� ������&��������������4��5�������� ��%������������ ��)������5��]��A��h��k��l����\��K��c��l��u����s��r��j��}�������������������������������������� ����4��������������9��7��<�� �� ��G��z��;��Y��o��|��t����v���� ��%��.��@��N��3��M��Q��3���� �� ����B��S��8��B��f������%��/��6��5��P��T��j��z������f��T��v��n��e��v��{��o��m��V����������J��T��K��p��f��c����=��L��e��^��h��c��Z������������5��T��w��~��������'��"��E��K��c��J��Z��e������#��/��>��9��0��4��8��L��i����/��2��g��c��w��������������������%��0��N��Z������D��G��b��r��W��^��d����j����.��+��B��I��\��x������z����W��H��I��"��1��X��X��������~��"��M��c��z��������������������������������&��)�� ��D��R��w������p��`��J��A��f��������S��g��S������G��a������������������V��f��[�������������������������������������������������������� ��/��\������������������������������������e��������%��%��#����*����������������������$����_��v����������������������������������V��������0��e��n��|��������������������������������Y����������������������������������������v����������������������������������������;��j��������������������������������������������������������������������������������U��N��x����������������������������������w��������������(��8������������������������W������������������������������w������������z��������������������������������������j������b�������������������������������������Z��������!�������� ���������������������� ��V��������#��"��-��&��7��5��<��)��;��F��:��E��F��;��J��N��N��F��_��!��[��^��C��Z��s��n��T��A��E����m��C��i��a������:����4��\����������������f��^��;��w�������������������������������������������������(�� ��B��<�������������� ������������������ ���������L��9��:��h����j������������������������������0��9��P�������� ������������ ������5����0��-��X����l�������������� ���������������������������� ������/��(����5��:��+���������������������������������� ���� ��������������������������$�������������-����&���������������� ������"��0��S��w�� �� ������������������������������w��\��y��y������h������p��q����f����������������[��y����������������������������r��z��b����������*��]��g������������g����i����n���������� ������������������������������~������e������������$��+��.��7����(����*��:��������������������5��2��3��*��+��%����/����*��������������������������,�������������������������������������������$���������� ������ ����������������������'��.������������ ��'������������������ �� ���� ��+��������'����#���� ��5������y������������(��3��.��3��)����(��"����1����#����D��L��o��8��\������&��$��)��$�������������������������������� ����F��B��>��B��(����%����%��&������������~����y����E��Q��;��=��/��7��/��D��;��B��A������z��Q������������\����6��"����'����)��"����;������y����z����������.���� ������ ��$����"��%��A������[��J��m��{����������������������������1���� ����1��n��o��|������ �������������� ��������������D��U��p��e�������� ��������������������������9��4��0��F��(���� �����������������������������W��H��\��e��X��J��H��^��q��X��L��R��T��d��a��]��U��G��[��^��F��V��l��J��b��Z��b��Z��^��T��l��P��d��M��^��g��_��q��T��[��\��U��X��O���p�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������stco������*�����Ӷ��=�u�?��+�hX��F��\z��Ҫ� }�A!�u�� �m�Q>���m�1�e!����2�jZ��%��[�f�ҫ� d� L[� � � � D� ~� � r� 9x� kz� � � �� )U� W2� � c� ?� W� G� x8� � � �@�x@��G�d�U��^��5�j�N�E��I�!���e�Kr����2;�m8��v�I�]�_��@�R��|�/�#�Q�{%�\��g�HN�{����K�H����K&��|�%�;�v�w��&�d7�d�Ҋ��9t�ny��-��\�4�k�S�Y�d�<�r�V�9� �@�s0�:�� � =� r� 8� (�!1�!R�!c�!�!�";9�"v�"�"�#�#[�#�#�$ �$F�$|�$�$-�%;t�%w�%�%�&�&J�&z]�&�&)�'�'?�'q�'[�'�(�(H�(z�(j�(3�)U�)K�)}H�)�)A�*N�*P)�*�*�*�+)"�+]�+E�+NJ�+�,1n�,c�,m�,<�,�-3�-g\�- �-Ο�.:�.<�.o�.[�.�/�/B�/u�/�/�0�0Qu�0>�0g�1z�1B�1�1�1�25E�2qd�2�2 �3�3W�3L�3�4 �4G�4�4�4�5%�5X�5:�5�5W�6'�6[�6�6a�6A�7'/�7ZD�7 �7[�7�8-2�8b�8�85�9�98�9o�9c�9�:�:J�:}�:�:�;7�;{�;|�;�<5>�<rl�<�<o�=1�=n�=_�=�>�>Rc�>�>–�>C�?7�?nn�?1�?޲�@�@M�@x�@�@m�A-f�Ag?�A�A9�B�BK�Ba�Bg�B�C3[�Ci�C?�C߯�Dt�DTX�D�Dʚ�D�E&�E^5�E�Ei�F�FK�F�F^�F�G�GTA�G�G=�HI�HH)�H�HΎ�I X�IH�I�I�I[�J7�Jq�J�J�K (�KB�Kw�K�Kc�L�LKp�L|E�L�L�M�MK �M�Mf�M�N�NRu�NI�Nx�N+�O"�OX�O�O�Or�P2�Pd�P�PӒ�Q g�Q9�Qp%�Q�Q݈�R�RM�R7�R�R�S*�Se>�S��S�T�TM1�Tl�T=�T�U*�Ue�U�US�V�VN�VJ�V9�WS�W:�Wp�WX�W�W�X1�X^_�X�X�X~�Y�YN �Y�Yl�Y�ZZ�ZG}�Zws�Z�Z�[ w�[A�[r�[#�[�\9�\Q�\�\?�]Y�];U�]sC�][�]�^�^P �^�^G�^�_$^�_ZM�_�_ƫ�_�`/�`bm�`�`dz�`�a1�ab�a�a�a�b8�bU6�b�bý�c��c90�cp�c�cK�dO�dT0�d�d�e�L�e?I�e|?�eh�eh�f9~�fu �fd�f<�g.�gi�g1�g{�h�hV'�h�h+�h+�i�iF�iq*�i�i �i�j#�jSK�j�j�j�k^�kK�k�k5�k�l"�l\[�l�lҋ�m �mC$�m~�mZ�m�n,�ni�nF�n�o�oT�oS�o�od�p&�pa�p�p9�q�qP�q~�q�r�r: �rn�r�rb�s#�s_�s�s�t�tT�t�tG�u�uM�u�uǜ�v�v>w�vt�v�v5�v�v1�v�v-��hudta��Cmeta�������!hdlr��������mdirappl�����������ilst��� trkn���data����������������� disk���data�����������������tmpo���data������������gnre����data������������9alb���1data�������Complete Singles Collection Vol.1���'ART���data�������POCKET BISCUITS���S----���mean����com.apple.iTunes���name����Encoding Params���data�������vers���{----���mean����com.apple.iTunes���name����iTunes_CDDB_IDs���<data�������16+ED451F16A28236AB4852B2C837B57231+15272277���----���mean����com.apple.iTunes���name����iTunNORM���jdata������� 0000036B 000003B7 0000356D 00003215 000361AB 000361AB 00007FFE 00007BA1 000361D9 000361D9���----���mean����com.apple.iTunes���name����iTunSMPB���data������� 00000000 00000840 00000370 0000000000B5B450 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000���nam���data�������Udo���8too���0data�������iTunes 9.1.1.12, QuickTime 7.6.6���----���mean����com.apple.iTunes���5name����UFIDhttp://www.cddb.com/id3/taginfo1.html���Fdata�������3CD3N77Q212557223V982EEA5EC41A3CF8AF48C52AFD66BC57D2P2���day���data�������2004���chpl������������NUdo�� free�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������umdat��������������������������������������������������������taglib-2.0.2/tests/data/infloop.mpc�����������������������������������������������������������������0000664�0000000�0000000�00000000662�14662262111�0017204�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MPCKSpcs�������Genre�Ballad����Trask��Arkistpcs�������Genre�Ballad����Trask��ArtGenre�Ballad����Trask��Arkistpcs�������Genre�Ballad����Trask��Artist�just for test�������Genre�Ballad)�������Title�mpcs�������Gen1�������Year�2000APETAGEXЀist�just for test�������Genre�Ballad)�������Title�mpc�������Gen1�������Year�2000APETAGEXЀ����������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/infloop.wav�����������������������������������������������������������������0000664�0000000�0000000�00000033700�14662262111�0017221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF7��WAVEfmt �����+��+����data7��|xtx|{pkiiknqnk{ùppdVNC>@@Hl޶plp|pVLNKIIHPvഀfafptfPLQQTXT\ܰihlv{kTPVX\aalܱklq|qYTXY\acnܰiip|pYPTTX^`lٮ{adnxpXPPPS[^i۬{`akx|pXNPPSY^lޱ~cdp{s\QSTX^an~aalyqYNPPS[`lᴀ`ap|s[LKNPV\k|\^l{t[KIKNS\k{[[k{y`PLNNSYfޱ{YVdtx`NKKKNS`ܱyVSaq~xaPNPNQTaܳ|XTaq~xaPNPPPV`޸\Xfx|fQNPPSXa~ܶ[Tdt|fQLLNPV`|ܶYQatnXNNLPT`~㾆\Scxq^SPPPT`|ĎcVdy{d[YVTXa|ɑdVatvaXTQPS\tɔhVatt`SQPPQYpȔfT`st^QPPPQ\qȖhV`v{cTPPNQ\s̛kV`vhTPLINXpОlS\qlYPLHISkОlPXnkXPKFHPf֦sTYnn[QNIIPdةvYYln[QPLKSfجy\[pp[QPLLShۮ~`^ppYPLIKQfۮ^\ps\PLHHQdٮ{XSh~q[LD@@I^ٮ{TNayt^LD@<CX|ܳ~TL^tycSKC@F[|澈^P`v~iYPKDHYyĐdT`t|iYPLFIXvƓhT`s|hYSLHKXsēfT\qydVPLHLYvəp[cxiYPLKP\x˛p[cxlXPKHN[v˛nX`xpYLFCHVs̛kT[qpXIA@CSpОnSXpt^ND@CPlОlPQhs^ND@@NfӤqQPc{t^ND@@KcةvSPayt`QICCPdܰyQPlxaVPLKSfܴfcti[SPPXkÐl^`fysP1$.@PhӈP89FQVN<1(,AhëK#1PpyaH38Q|n`pӦi;139<>Vб|~pX;! 1Nx؆PH\qviYIDP`|б~D+>fpK8Hnt`a٬i9)),+,Kֱshpy^1.X|H;H^da\PFIXvӹ<(P\03[idӡa90,(9n^Qi~a41SӋ[CDPV`c[V[n~~ɌI)1TȤ`�F|tyޑ9 .<D^{y<.NspS,K~t;#.Id~~dLC\xdcvhKP8 0lxDDVui07DW~[Nd[eǼbJJdgfmkfM<2!IY)ao\i׻szvx^9TwŢ~~lK2?c|O4;KPXN;TW4jӶʀ{mgmeּ|b\ur]cnfhf?5# /T.Vȹ{moNLtΝnfvrxjRSbniXO7@: Dྭ{kX7KsĴpR+'E^e_TL5:4OwКjUEWt|kYuܸ~`MGG<2�,W]u_{ʾ|cP\|~iB+�'72M|˾nmqnt{~aKFNRJJH7,;=5GZS8@dqҠ~seWM@35a踉mZMC9�"<B_zljʭjV`zuurpxu_HAH</3<=:Jbxڨf[_mƈf]^PD8%"5RX]qp[F;Igѿȩqy~xhVKSZQ2 ':APhĴح|pW. @cqp]H;41.3:BKWs輕׾wsL%/1!�1GM]yɺՙiPB5!"/DZ^PJGC95ATh׺uwzp\>�5C?/+:Uo˶ѩq:! �� (:ayv{ţuREKb~|P)-414BZrĽҽ{fI44@Yq],� !6EVdsƴٽw_?�#=]jaQPXZ\cwѿɸl>$ ���3[{u{вna\`gfTA2(! ��)YͽwwO,+G[ZKCDD?5?Z̰i<(+- 5buek̟ymjmspnfN+ IbcR=6CcϾʙsc[K0! ,:JZbS>2=Xoy|Ůsp{lG41+ ;Up~wzɱkL:8<LbrlJ+%-622>PkкŽqmpgD���Cm|cVX[_g|ѿ{kK3.(�� $CgǩmXOPT_puhO@93! %Jr¨zhmy{lWHAACO]_WS_lbF16PyܲtslSR^aWRV[]bnulWPa~~yyȴxd^_hvofe^XTXaem|sdbnɬWG1.A^~jUUVV\lx{zk_doyqgUHN\noglrqlozw{veSP\s{ife`SLO\qoa`h}zrn]IAI_xƱ}veWT\j~vbSWfv|dYTMEM`xսufaa\Yco~b]dihfjlox|s`V\j|ɥnbWLJWnhW[ahoy{ihoqljqzknne^ajtmepvwx{~~uu|wjmpyrpolo|z`nt]f}tmg`Wcv|Şzvt]EBUjxdZcebaptjhg^[kpfehffqgPVrz~twvlZT[dpgJEKTcv\NOVepS?6?MgnVFG]u~tdYZdotjg`[]hywimqjhkrw_Veph]esisp]MPeyyfF<Mcs{_GIYmvpz~ocg~io}yhZ]yrisl`TCRtzq]POYh}^GAM_wsb\`l~}v|xlhpx~ynho}vs}~sozwtx}jihqymimoosvnfqticeklfborz}{ljwuf[UL=BVexʻo^YL0/":\{rZI?-&*)*9Yh{{_JFHLB66;;B[r}ķ{nnmg_T:((8[vy{¬ws^SV5,?A;BXft|eOCI7))(R}|sghxԹiPLE5%"!7h~mRRf{ŰoM=5*GtwhltqTK>-925c}vq}tksyȨhIJI44' PysxpwǮdC47!!]|~||̸pJ9&.')erta]e`zƮ|ci^C0$($1=,<e}zpǺx^B9"  Ov}gN6 0b}{hgmqǻum[N>94$$">j|xn`erɯwfZQI?*6bzvzoV="+B\zypbM5(@[wuqs}̲kXKA2$3H_q|gO@0#)=LRTXf| kR:(#*6FNSYdw{kaVH<:=@FGGKLU`gq}Ŀuk`P?+(<LZXNKG@awn[F0)3JjydL;222-)%+>YvǮujaT@3($+2<DDFHNdxzm^O9.'&*+2AQ`inyķ_>!%/8<CPc{ǯ{m_TB,)7BI[qt]I;$(325=Ngy~}{eI1)7DVnĺxj[ND>8>JMMNNNNR`n};ubM/��%E]eltz}rY;����&NdL;#.CYtտsjhhg[J=+"+28BQdlozq\E,!0=<=HYltxô|raJ/��'BcwϼgTE/  (:Xrò�@uunC' $2=DDM_v˺~smnx|kT1 /KXiû|jXOE9<FHMOI<26CQcxǾ{gM0" 2Ndv|xwqZ7 �����?]|ƾ|cVH4# ���3WukbcbSA- !/8HNKQe{Ȱ}xuj[PB616<:1&!$;VerոxrqkeddZE3&#/>LTWUWjѿyuvm]OB8/(#&4L`muvzƶtaQF9/-,,(##(5MjƼüfF, "$+;WwƷxZ=& (-5@N[emyĿý}sdYPNWfuynZB/(*8HVdn{µqcR>.',=OTI9)$+>ZsǸmQ6 ���"C[d_XV]k͹ytjW@% ����� &KrŸnO90) �� ,CbÿǷzhN2%" +9L^q¹xof`\[ZP>(!-8AKUdoٽuk`TD50-.0,)*Bb}ŰzaL8)!#.>NTQLINc|æ|{~{kR;( $6FUahq~ŷȭu\K<$ ����&CeǽwV>.% 4KkȹsS:'���7Llʾ~S0!$!&9UmҺfE&  3Ka|˿Ĺw^A& *.+,*,7DTbpǿϼy\9"" .Jasƾƽ}{mU5 ')$7OapûqT4 �� ;Vnƶ}spfU=& &%*?O]o¼cB(���&?VjyȴygQ@643/% (3D_|»r[NGB8&  %3=DO^n}Ʒ|roofU: *@Xm{ż|uhXG6+# '6EOPS]lĸbF4*&  #7FLKEBKYl{IJk\WZ^SA*%05+#7Nh}ƻlZTSPI@934;CB9& '6GcǾ¼}vcN><?@6$� #)1=Ngε~yrqswyt^>������0Mdtʼs[D4*& .;AEK\sʹy`I;- $@T_adnyо{mX?3.(()&"-@Pap~|ɳveZQSV]eYC,*<JQ[mȿumnsqcRG;* $<O]qɻiS?;@, 4Vku}sh_ULA=>+$('.>XmƳwhkdWKDDELN7%9L^}˿{b\YWTOF;33' ��>k{pYI@9, �%=WhpwgjbOA617EKA1 -Oe}ŴŲePU^eiig[E/ CeĿ~}}zcSB:62+$,9R]oz_WZTJCDGHNE,"C^pʼ|wxrZQ^b_ZQB775$7e½ybYYTQOE<51',23CVntijha[Z\^^P9&,I^~˸znomnnlmibYKHG6)(9Tv}mh`XPDAAEQVK98Pa~;|mbgq|{k]KC?3$ 4_zyske^[TMQRTO8)'*@buɿ~xnmeUQWa`[ZRA59>;,$!#<aŶ|lSPRSMDD923..)1FUevµƸ}qfWSP?4$ A[guoptlb]^dkspgcN:<BFYpǵuke]`onji[JHJJ?94,1-#%&*G\{Ŷ~}td`abdie[PLKIH7! ).Clzmmkirsja`fnooeUL>0-,$-NmĹwgkkeZOFDLQOTL7# #(6GN]i}plc]aW>'),*F^hz}zti^cs~~xn^XX^wxhpqqty~vvjjrl[IIR[pɵz|ztjaZXgpZRYVSRQI==70;D;048:Ojuÿ~rotz~jmuqof^TNLD<4$ 1LU`t}w~vt{|{|z}{l_ZQDHOJO`k}}{vi`[\huxkeggfikd_^[YYVL?CGMevwµ}xpjjnhincTJIMMPK@;:;4250-AYj}y|ysi^\\YSONJHHG@AP]m~}|voqwyzzthejkjb[VR]cdc[VNLPQRV`i|{z|}xwz|wyyjbgmnjd]SU[Y\\VUUXZUR[er~|xsvvxwzsfmkf`WPN[hd_\XYZ]ZQT]gz}z|yxxyzywvrlhfeebccfh^ZVPQ_iy~~vrnjptqlgdfjnmklpmonjbXQT\`eisz~}ulhntvvurjeehklopqtoie]VSTZ\]YXds}wsnmjgcfjmrsrpqrssmdWUY`a`XNHDHOUT^ov|vsruuvz~umhcc_\XWXZ_^[XZ[[_a_`ir|}xyzywuqnruvwurjee`^``bhnppkd^^_]ajr~~vniggjlpuw�omnornlifa`_]^]ZYY[[blw{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{bit||wkc\\_bhgc`cjnnkkpy}|{vlkkouyxmdejnmjjlqqmLkmox|rjhkpwxqmnt|xokjfhiiceimmkfehnpqlf]^cgedabdcb_cgjpy~}yvy~}|pd_aelrrqklqrrrru|xrmoqpprv~}oa_`dd`^^adc``bckpsnf`ZZ_bhgd]_ada^cl{{{{z{|}|roqwwrqoqqoomnryzmc`dn|~~~}}tg_^agjkgb\[ZXXW\bkqph\RQV\_``achjnv|}}}~~zohhkquwslfehkjfir}wsu|}z}ywurqppu{}reYWZ`^\XTUWXZ\]dlpph_WU]fpz~{z{}~~|wrpmjjowzqgcksuwqnnptz}yuy~~{smkhjjhjmrpi]RMPW_bc`\YZ\]`dks~xqqz~uqt{~}xnifhjlpqu{~|rjcfmv{wx||w|}qhinxyrlliihhqx~ztjgintwsnffiptw}|spu|{|~}~|xqg_cky|tmlkjhjq{|vuy}yrmjlqyxrwvnmrx~xurqqsvy||unow}xtwxyzyqnot}}uz~xwz}{yw{}vkebemqqniedbdgmsw||{rkkp|yqoonmlrxurqwzytlknqw}uv|{vtw{|}~{xwwx{yxz~yropuywpopuzzyvstsutqo{~xwz|xwy||y{zwvuuvyzy}{upptzyusux}{y{}yw{{vuw{}{xvvvvyzy{{wsrvz|wttw|~zx{{y{{wvx{~}zxwxy{~zz|{wvw|zwvy}|{{}|{}{wwy{{|~~zxxx{}~{{|}}}{xw{|wvw{~{{|}}|~zxx{|}}}{|~|||~|z{}|}}}|}~xttxz~|{z|||{|~yxwxxxy}|y{{{zyz|}||~~||~}xxx|~~||}|~yxxyxxy|xwx{zxxy||zy{||{~}yy|~}||~|yxyyyz|xwy{zxxxz~|{yy|~|~zz}~yxz|yy|~}|yy{{{|ywy||zxxz}~~|zz}|yy}yxz~zxx|}||}{z{{{{}|y{|||yy{~|z{}}}}|zz}}zz}}yxy}|zz}}}}}{z{||~~|zxx|~{{}~||}}|{}~zyz{ywwx}~zxy}}|}|zxy|~~}{xwwz~|z{}|}}}|}}zzzzyyz}~{zz}}|{yxxz}}zxvwz}{yz}}|}~}|{}}xxyzzxy|}{zy{~}|yxvx}}}zxxxy}}zz}~}|}{xy{{z{~|{z{~}|{xvx{}{{zxxy|}yy{}~}|{y{}|xxyyyz|}}|zy�~{ywvvz}{{zyyy{~|yyz}~}{yyz~{{}}{{{}}~}{z{}{{ywwy|}{{|{yyz}{yy{}||{yy{||}|{{{|}{{}|{zyyy||{|{yyy|}{y{~}|}{yy{}|}}{{{{~{y{}||{zy{~{|}}{yz{~{yy}}}}|yxm}{z|}}{yy|{yy{~}~~{yy{{{}}}{z{|~zy{~~|zyy|}{|~}{yxz~}{yy|~~}{yxz~~}}~~|{zz}~{{|~|zyyyz~~|}~}zxxy}~|zy||yxy}~~}|yy{}||}|{zyz|~|}~|yxxz}|zy||ywy|~zxxz}|}~}}|zz|~~|zxyz}}zz|~{xwwy~|yuuy~}{z|~}~~|zy{~~|zyyy|[~~{z}~{yxyz}|xuuy}~{zz~~~{z{~|{zyy{}{|~~}{{yy|}ywwy|{yy|~}~~~|{z{~~}||~}|||{{||yxxy{~|yyz}~~~~~}||~}||~~~~~}||~}{zzz{}|zy{}~}~~}}~~~~}|}~~~~}}}{{{{{~~{z{}~~}~~~}~~~}~}}~~}}~}}}|{|}}{{}~~~~~}}}}~~~~}}~~~}}~~~~}|{{}~}||}}~~}}~}|}~~~~~}}}}}}~~~}}~~~~~}}}}}}~~}}~~~~~~~~}}|||~~~~}~~~~~~}}}~~~~~~}}~~~}}|}~~~~~~~~~}|||~~~~|}~LISTt���INFOICMT�����ICOPQ Sound�INAM����ISBJ����IENG���Ulp~|ɳveZQSV]eYC,*<JQ[-pan�ICRD���05/24/99 ����������������������������������������������������������������taglib-2.0.2/tests/data/infloop.wv������������������������������������������������������������������0000664�0000000�0000000�00000004636�14662262111�0017066�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpk�h����M�h����@ ���wvpk�����M� 9�h����11928D,�������������Track�84�������Album�ayu-mi-x III Acoustic Orchestra Vsmmrtal-anime.net�������Year�2001�������Genoe�(Pop �������Title�ever f���PETAGEX����������]�������rtist�浜ʎあゆみ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D �������������Tx III ID3 stic OrchestraVsmmortal-anime.net������Year�2001�������2enre�(Pop �������Title�ever freeAPETAGchestra Vsmmrtal-anime.net�������Year�2001��rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr�����Genoe�(Pop �������Title�ever f���PETAGEX����������]�������rtist�浜崎あゆみ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D,�������������Tx III Acoustic Orchestra Vsmmortal-anime.net������Year�2001�������2enre�(Pop �������Title�ever freeAPETAGEX����������]�anime.ne����������]�anime.net�������Year�2001vpk�h����M�h����@ 崎あゆみ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D,�������������Tx III Acous-anime.net�������Year�2001�������Genoe�(Pop �������Title�ever f���PETAGEX����������]�������rtist�浜崎あゆみ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D,�������������Tx III Acoustic Orchestra Vsmmortal-anime.net������Year�2001�������2enre�(Pop �������Title�ever freeAPETAGEX����������]�anime.ne����������]�anime.net�������Year�2001�������Genre�EPopM�h����@ ���wvpk��� �������Title�ever freeAPETAGEX������������Genre�EPopEX����������]�anime.ne����������]�anime.net�������Year�2001vpk�h����M�h����@ 崎あゆ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D,�������������Tx III Acous-anime.net�������Year�2001�������Genoe�(Pop �������Title�ever f���PETAGEX����������]������rtist�浜崎あゆみ$�������Comment�#osi-anime @ irc.ixmortal-ustic Orchestra Version [AVCD-11928D,�������������Tx III Acoustic Orchestra Vsmmortal-anime.net������Year�2001�������2enre�(Pop �������Title�ever freeAPETAGEX����������]�anime.ne����������]�anime.net�������Year�2001�������G\nre�EPopM�h����@ ���wvpk��� �������Title�ever freeAPETAGEX������������Genre�EPop �������Title�ever freeAPETAGEX���������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/invalid-chunk.wav�����������������������������������������������������������0000664�0000000�0000000�00000000050�14662262111�0020277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF8��WAVEid3 ���at`��ID3��RIF@�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/invalid-frames1.mp3���������������������������������������������������������0000664�0000000�0000000�00000017744�14662262111�0020451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������k1c9+V99G &goD-uB3~ ]`J4-$rN|oxRvBa;RuGZIf^BGF 7#M#ē\ķǫ}ڌJ-i,Y>ǜ2ʇN%ܥJBTp#:5O9'i'Y 1^ @m$ےҡVeYX^MeD8T�Fi#DžKzmN} '%_cKIgkPȨM֔  ŲQ-p QtQc jlfIfFCzae<mkL)QM2uak<Nr6"jVռS,M}bPTs}LDUVCkJ(F'Hm'(E%ZZB]6<OI4vE3``�zLi&r DJFJXx?MC3 1dbt&[(-FzEN J�""gI %ܒ21DΐӪ%]H{Aܔ_Mh9mM^\.^g]'a`6DŽRLreѹO34ԹCkAY4H{U~+n~䒋WЛXi/&c_wGr`y4`ZU(ƓD"{N) 6ے\VF 62XZd7'<{lQOHoוP!X d#j>D6AU/dgY?ݔe~I%tr bKBJ8.f\A2>caeȳZl�(a[k/JZF;}ab\AYmM=-U!-r#£s;s %dIѐEF8bժle2M5aߺ*̝*G2dl& ~LkM2:v~,XPpGeJV~M6;L<s~:<`3z%ma#c(=̖!NbkڿCTge=VzO,oK  Fh{*;$Uig! DuRϊzj{ ʆGi4КAꧏr .щba*.LJa!NՂ@\ſGd! -IM*Jཫ}Y+sfLIm5$gȘ &H;6g̤B"[F{`QCc]Om{\o .[jQ(r%fKhob+([h괈BC0Ql�eJYk=L gL%_$0#0u#14"uOuMʬ(xu )5?[9^yI[mUDŽzӃT*G#LmI2HB*nj2-jp$ڵg2k W</ A A$nKJAKXF<�9 n\qĉ;'#DrZg^?ECVvQe P7,B�^drX0_j* ^hRA(&GW#br2P-<+ 8s\` |e71P,PorDi9T]' d<;?|J=;%PD<jn0T SfNw}M̚Z_^WhGAi$r⨈]~_"nqGB<Aj Tb@J$Q 2M-* Rߩ"子ʈO~ܶ$�4;~[ S (Gn]l��b8IfZ ja: mL(W!ꂁZM{!f7,NPJ( 273U%OQ\ "h{dӂ{CVW" ?bK6B~HO<bJL1_6.y*AY{#>,4nKibBcn#Ҥt0fpAẈ"HxQCOvn/汑ݬm7g$u*t*) @drR˶<`Yh i4z^2҈b?%2+W\#2anZz1[lsW6s+ QLek(ޔgx)<фz1qX4T>䏬?123L&b}RJa:n*0]$G|^e…xPUVAqKy9Qg뼰3D F\(oM7Thrщ%a8<P"5ӲS$Xq_QtBlgYS2UeMa=uiM%-m"m(y@}Qqz}]$q-ҔP0 &w/ay:P,ʓ6KIhRDZ:eD]U 𙬑vqJ(2 ҂&ے铤)Yr7;c:k7Q9[q`$�z ;ph�+=t%HR~u+G%̜9Ү�y&ҥ$ĉ}J"Ŕdz92V<uXl-$o@ 4TߋB<M<*,FBQ_!R=Ywri\wuLKfk$A!HW7M{YQj>v9X[�i$UJGnQ"= K#甉cCwEW]i [+ƺ-O>Q(&nKqRa1S\, 'P'g_1q "@($"DNO֤UݵϢilXS/IfI j`L=m%%ԍJnvd]q1//(фEhw*r5%#l2kQ0DW֑UF9?>;y#X*Am~myG<uwQmے,`p.K| eP*x%netw1]*!D,/e�ni:J2-AiE=Rn�r CͣFYvf㫛 eWb>!FhY˷̰)Vz\9ox:fIGRP(WDLQ} H3|D>E�h3WB1U5V|3QLI!˵@JLݛ/z 60#B'Lۀ3 H%X#U;eg3ڤzcbR~#65?mL HﵐH}A⛎K8jls-8֭(O5u7#C$MFYX[éw HT*F(@2ƒ0HJl�%c[k/3vP%K=:kL0͙A +[?D7c ,.,G< HIkZiI qAATH!_@0-pVV%&L~)ʜ-/^SMa 7ƳNTh? L,DN{3r7Fv}/Q9(>kgu~YAy$Bj&1U8uU<MU29t0’OF1F?ovJSqm!I;lN8{~̦qXIRQ}В2˯<'ua/I!1VRSɔg"ҒM4ׄ֒("J~L &m-."ԕ~ZzM-*2QhX8ڇ<ĆClg6v=EH%0yB5m![Բ(I%VAjb|Ԃ[Tԃ򝓄@ @0g }N6QѠ_Dn;#$+!C-2O5l�hIdTěma,gL#` 2Fهr 08a #"$y궅-ky2n xfH)Z59MjTQ s{PJ~Q(M`q$t] C9ЉZѵA DTx:ru23]<J^]&A&"@N0Ka z<ҍ6.HV2)M @`Y5ƠL"X;ۀxId%\°&̐/u!P9K2 4U!̗yUռBK.JumIúYDZܒY&3Vk IVUy~IwB.9[d8&*kQ$U$Ysp1cԣ3 *RM+ :O[*Dn IMLn^/t]wo ɡXpFXprwO$e$(xul�hS KtM+="LgL0͑oh 2 n~qH|=A 95dpRNFXEصDz3 &ywTZ Di$rRȽh'(Eس[W*45J \M](Jt~ռuT #dS`nX�DrXNCZ& AQ돼]q8y)9G?Q->4 58`A /L$w` S�(fY4OW,3\Q8ZH ` K'bhYZjmm'5<u}> i8䡡olp9J ,7,"yAp<�ea"P|>,_^Y;~%De$ےفj1(jz#@/{VLFs=HF`4܊%{t xFT\Ϣ;KIkWQxrDD)jrIlfY 3K%j1DiiL='YFh2ыhQW A] DBƛ^Y*ʠ[=~PI6ܔ<` c,e#T7l_'X"886Ψ y Qtq%u9"Wd9ҩWrP&H5R)BLr-ZdqbQМeOb- ȪVt<=Nd,ejɿ9AO=D"XcH|?$է.X"|R{的/* ˇgxoe$rV4D ϶FQ(;R&M4LEmaJƹHdɐXTW?hޏ@vT_ hE&ӓ@(&�sHo+D;7?+:N94`'.D).I^:&�"aLA7-zoJ: TFM-{R~L`:n۞|l�f 2LHZa&e˩+)$f2%!%K#q;uޔI4ܩVZO'DUwdXhHE(K\ɬfoetj;+-b۰a&d@irTXWR-36Y,g+EvnD& ;}M%= f,3Ͷno+NCIVi^O14ꨲmWŐÈ:a[%q<ѿRSkWe>gF][<P6kn o/T�RM_GrVќy(\hD 7 RxY۾ۍI?09s7nrS:SG?,TdyK(ƤI-$@ZWRA$zT]2Z,fNc(+6@~DB\@"F5ƽd{8dlHqUE"P(@+^cAX~m e@nf;~yB/p#&Q|'\؆Λ)d'%lh/JdVJa^%iL%mircԵڼ&䖴dU ,쪐0#Z5l�@y� SgCr(3NE?HbA}^SPUrx�:- 1rU0@-d9&ʜNKJRyCuSsR,7%-nK[yDҦEk$W|nGe唗Aiw[_Q(G|-ֶofΊS8WKSBpu =yz3י2u>k"A4nKWԦ=tBi5˽RQuBH*-{8p0,[O~vr!>#xXak<gyr$ܢEj IP]; JYeH^ t+6ɫepFR;)>'Kֹ ˨:$ɗdI&u]~j?֯6xsI~7Z'w6[cWlYZ8J`FK]?\5mLa,Mt$!c$kh=t͸廦-%OզN" yk $N)!VON" ޷‸Ur:ƴGOH H0;fAM MܖAԂB)FH3D= 9y5 F|,}TjTc,ȓY�$/E@M:]4$ ,5@ g$'nk<i&V}슟o T <J+oTķ)edgi6Q/$r[ .<nIY#cO?fh+~oSPoۋ"- H|D`YNrT]+I$t,ɤ >#�)4d^!Pbzu[cQXAY=AڅqwŊT=WKL͎3ZfI]q*S"OFÆ;ORXVµ9Z8l�f1SD+ja,ţkL+#m*R:FGh@%?r_4sٸPEҹSP􃾩XnB,b/@5<x\Npٝ=>AUўEm$ܒFz"EQaHqZIUU.tgNKqkO dv>gy)&\H$9rȔEB^w FYjJJ]H?_RJIPS]]RDb^_8Nn,V(s|duM"Jfq<|X,e*Ǹg�W@̾&mS7yq_uz1' CUO$ϩu PZ,HLT284Ւò LlM M!\ )"-Y3 o/ӓJu^(HIJ+nߊ4fIBSlJ,FrsfUi�)lhOI?#=e+k 1(,)*6ܯXo5qsJ@>>2)bHN]{"R3-?Ro2Ym+w´?nv *ےJz͢\*:\V&)wFԬhY[` f+M"IA@ (d Ȩ{/a9q+ȑ 'jq$LB& @itTDe[s8 *X?tpb4 u𦛗Rw'�[)$4a#g,&p'Z3ȏl`>cJ+ʻJ H`imIܒ/,k\Ԟ;;եϡ ᄥ„5-3xܐ/_ޒkv&\('h^odk5VP$f=!G6:NMCѠayWnU+uPUjk%t y_rOn^m7$I����������������������������taglib-2.0.2/tests/data/invalid-frames2.mp3���������������������������������������������������������0000664�0000000�0000000�00000017332�14662262111�0020443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TL @P,-h`fN' v0v2Nc( 2�R!mC ,o|@C xN$``k9 d/�Aݽs� F�aB,0êf1y Y4U;,&s &\Dͳ`hP@P'!@Lx'a07sn( Р |U �2$@p2� (pe2|q`0o \xz gi}@l@Е�D�o[(`c�S*pTt ̉h 0  go`@�  � @!>�@ٙ p5c@* 3u ' i xY`T 3@`-9$;?GI^82n (Ŗx8JCN!1}=;js}1V'( g+RRH&G1LJeAufs_?qCz h`EDpV�`|K'y0(O@@|X ? S8f+`N[HP?_M8rrl½~uo V)^iIrG;rP '_a7v� mY)E iS x猢h@L¤JLRD,LB4Tb_D4*V3S`2!0\"RGG , 1(8V �  "-0@ =.IMjL]@L5"XZ)^'ZZԪb׀N%@ot)gqļ+=Kx)NGmMIbJGaK%1L?*y57�}Aߣ評HGCM{�ixi{H\:Y� P; �/3=$A3T%1n03(1 "*3w$S 3H c+-[hLţ!L 2Ӌ#X [9z#M(@AbA΍s.8�!Lz3V,F!A1cHqx_ 2 ˣ!]P[`qe ~V5�`ɘ TĤȻ蠌إڇ)}xnW $16#"LgyGv_K??h�'H4eD!2*n4 !b¹q! `Xv^iR#Hz}yXH+-v=VM,~5!nͭ1^'Kۜs<t#� *A&b <p`5Fbݝ"f F-e-bA9�h:A.XO2"ĹAghvױJq/硠XM>#Y6ו>Ӟ"+"/bOXB H� AO7?B$280@BlB,X`24Ηq�fys).<nd#'ĎfeIT'(GJ`kcL΅_wez uh, wHWUPjF#|FO[>[(r5{Ykz�M ΅:l 0̢*9@,HL& LWzcf׺P:DI_Y0 ``/oCj aH6Mrpf*`QQ`nt{W'>T�Hs@Ϫ{V$4΀'ET Pj @/;\.vUD+wxu!r%SVP <$<HūW7Bj<~[Q.x{�-bzY"<D<ZF>7(d(Ă {K>C47җajvw8 <W&аq[4<ziԆRt/u[Lc}tC"t(Ҟ Y�ƫ3ld`srL:2`3oѳ{ ՍM! "2, uVDk҇e'@Pu!ʕWbU,v3jk9Ztz?: EϛApYHmJ !ͼi;ET'n��\+# 4|"3yL02M1x]Ef!�BӥaFJ,3-kp!܂MHJ <IR҄ͣ,vCa"Yckv?\Sl��bٿ9{bu5EHe@l�*pjn\+d}^h@eъ;B]}Ȕۈ ^:=WʾOmg{dX]ƓooPP-<'h2wPm~竺qO0o@�4QDԐ pSh6"]N<$7yl)F$qh (*(e Z!4LQh^ݓvto$:2؇:!Aظf wg8BuMfkGJ7XE&3C x3&?oPD̟,D"&P^5.r+6! iU<b$W4hVilVJ>!E7y(5[G7M-!;n�)l lIN"(d0$^ƀ2 HCXa[ 3PJpHSohO;JMVY&) ўJ}ypm( rp5?^G1yي坾%~ 2sq{؍4<wl1p:Jbʩa@鈋xy H:Mb3f[ Ppܥxe4b0EUGe6+=^:9P:K'|3YEJJGմ5Xlw/�c�� Eߔ'!kS%� K`j"/j)-B7.[ι!B 6zAT &9TJHά?+TI5Eqeۿ}n| A�� def(|jƐ*g`H4.a&JT(�!US4 �2thQ݊T 'B˦챣zĀ{>FP]BpzCp13mw'.~gs���18D@…L24jb`F"id�d&, Ak舌6m Hdi iUQ;u3enzf(s9L>D}F_FЬ~ߙơ @rO bdA Iқij SlF-9Jm&) z<M<P i8ǍL`d0`` jU�$ .ΧT8�V+0!&6BZmƋGK/n_�0ox挵D&(^ GɧlS#F .NHV\pL`G'Rgf� !PXQ99p,;Z%PiSM0ȦY<g!xƞ ^}fEРuPb ĠDRDKgS)FV9G|v��oBLߔʠ!#: 61�T،+/}f&y1+i,"1xftSލlIy0imlj5:"=D8( fBձF.GlEs>$;ڂ;�N "&o3�h9(  ØA aTA'b񮘋7'Bjxg5n+& zG e0}ZaJ f61QZö. >c>ƽD_$4!6?o=�lY, $HL™" ?Mlvb Xy-�!慂 sS:�M_NRx+` colAHM() 8TY1zMHl5C]8M.3>34n^N(\C_΍z 8/6�JH8i�hsed,e6r0�/lBjN%*G\lUDaI+P<!D;v RE C2OCo`߻g}jTׯB#**�� EL,$\ D<TbnÆRaA&tJtGep56x4MWP򱿐Ö\)C C\X9Dђ\H쀄T̓w;e}ړ~n]z(��s16z" 7!W4@3P#Aʌ%DH>caG7VDV˛Ie2u9,|-7v\@ƳEP×yF&oyş}]!a�8Og@?8@``|5i< uֹR@A*w�&Pਲ਼=VWRcܲ;"8dAPH wk<ۼ9yGjgxޟt>*\ c<苍P !\�&PRlColAHM ')Mx(faDF%1*0`l<2't4P<۱9ؗgR?Î Ù 3gY&ߘx]$_Ce~-^*'>��iH.0$ds1 n0lI `0L �b2D8RPN &lLeI Bg+-v8($ JԨ#踰& N8^~iHРwЏ=qr Ȁ��b)f�S�i(dH@�H$b!Q2oi8$8 h$%4PD&(誩H`<_,lΰJx. 4%4&qHhz7#iw,19m7',jA&utbSɤUeXc2A�ꁅ !aĆ ^-A.ȓ͂&"#P܂-PIV%qi,T5<0Q#riE5L(hg7/t&%ǟ6/" v*buFtQ0+ >PP:sKnAAFndH((x8S!6 � Ck#H6G<m)Z0ya1&9a`dElQ\ͣpKX �iPؘ(\2p\jy40Xn+H ~q=?DRʤJ�� CWH08NXZ4$HpgQ$`Ao6j 6:x vE�0tYDy yGr.l[[$"qW`$~C+6 8Ɍ/j,&g=%ZY9n̼ҡf{ސ�n�ȇ1bDqF>fE`36B 4vc`ay!dJ� MUցS`h2NhJkɜǒ|Iz XKZ57Sq_VeVzPAd0a' J"Ô'Y0`E2 HDj2v"!f0:.,A șj쭸Q7c.5;ѫXԢҗ@s<T*>:bڒBQ_*H*]P }K@ryk2x0@Q@9GCo?ћJhz3o l ?Dnhu ɼ5n 7LaTˍ,([K^Cz/G(%y"3)dI UY|$/[)ǀ"AH2N~ZI9;_/گxN.Y��`qWdƦEN B Pu4x2*'! pC9:R$44H=W\nzCv}^՘h<8)d'xe2'l9?؏:T1r T�N75Deb#l-(*T)0^gȶS,ɄKU50%nB-5;Rn-E>8А 7a#la%0GlK>>4$\ 5FXSjw5H,3d'"D LL8S=N4-vDh16K#�~E"V6,1C E WaǠ< &; L'}gƲ&q=Em7IWh>PB Pwiy&d.t1BZ/<< l�ARy*So#^h<NeoBkg P4H"$iɃ�tq8NV6€.T%TJVT8ơO&Gڦ=Pp*Iw+n툭VYn*J(oHrp?YC.%n��ædLTG͊3LD>0 l/1`laґ#!m,˼a� S.®� 4Dj-u!R #a":S秘"Lzy:fIЧ7ѰMW͠M`�XcSQ%/9ufi1FcK)�L#)0xl*%0\aS'cS4be$0Lp^,1x5("qPu|؜i+^!|,JDҘ4J%&H?Al]r;lU#rma6F#3`9P (6ڋ7/z+3:Tv0&J*aD|Fti@U~!hJj[j:tK-[T?acKkC aQ-Z}W|%5*@< At `N ݄ LV Ds(z'Bm Q(MxpTɅ�8ľ#B])TḲ9yXJ^Q-uN1ܲo߹xeXL @h9IHĦsu uV SvXQ*a)� 1.1h6:�3sDG3Óv2N0A?jJ�LX  2vJB!? gD ,lQir_6 d; + (O1U\nGW-ɳxuanP���(F!;u823<S|8ds0s*t00RѢbTh)Hﴩ @簡4@)m)ۄ-n. pDBoi"8j8핖zwF)[nP;acO/thTnj`d@QE b/a8XPAI${S%qAj.f țgMzX9dhzóGŇEe ޯgV ~c[/Ko;.`�3`D̺<* T0e1xf  ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/invalid-frames3.mp3���������������������������������������������������������0000664�0000000�0000000�00000020000�14662262111�0020426�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������u|Fa ah߶t%Ns<MB);T �e$ 9B@P,4q0Dw$I$HF21^8�Lrw}B)FR+Nr=F(]E"~coSpCnp>qe�@~}yB!c?@���K�� p�� ���cT����? @���(��a8�%M�G 5#P�#��(G� lf3��(@��hB8PK f cdRV�7#0!w'*Hʀ{K"vU,�t̡vgZ ؋V)ʠU? jv$4^P@��%1.Xc6 l+i|#-*U@89d!MDmlvC ya;:JP {BBxOCP sC1ĖeW��@�P1F ps#'l NjK[ĝC"ZVΣbm`(+5'5{~ۆzψl@1nŰ2.B>&G! p2쿋xcȧdy,E*y<㯯lyb ~"@��-k2c[8A1i*F@L�%:_/ Ršj˺!A~HןCI-#%@PQ,,# DSƦG}gzʮG,HY$�u]dd\Cֱ-"HReNg*kNT<?O4((-ddP0?H\D¢ Odg+wV%1m7d�q@4K )v8^"p@ 4OFk | Prrĵ{ DB@9x[C Kǂ̅݊<VBZP7$`K ^;֪Ԕ?wJDthHm̀ `s]ac 0`fPPhoAn<yذ+84ǐj$Yxq=5!t,'R~fY}) ;WX@�� =k8k\Yy) 3"uu c$F L.:q&ؠ%0 X{9CX]kbLD!JL̥ 0>\u']>]y_r:&PJSYj8ػi3j#k4잌Mu7qi1KYTĖ\m݇BCxR`=r7$"Hp+0@QO�" ^N)|Y MȎ̏ 2k 6u.`" c^g܀ƀ6veݙBLrA ܸ#Αp)Z`~-҆Բ \]^QGr�Mu7q鈃#(>PGr3%r;n;e iK0Icߐ@| smK _J`  dhǥI�Q|@B6x8p*'RX 5B`2`BI؉[&vZJJLTD$�+TE'.&0L9 0 Ir /R)8YbK&d]!YAc Vy i~ʦZPv 45L}muZ+)v<:#hXB,xKwVR)0d 0A�&Ua+ܰA@% ]FBr�[1Y]LE�2E Ȓ4gn  hh)ȵkNfs j9~B� [7iy@*[@MaiU7U)kqk9k3r%iǿl@�� CJhy\sz]QGo+I#(wAMc]PF&Y=tt鈔GBqKF><j 6^gh'.jSL:,.�   %Ö_tv؛]0 0@Q$4QѠ(( ِ$`lRX@jG!K!jxnWn!e-3^0]s~+4)3PwKie V7\t?56p^esɨgCNy@q1! nCFPc%fvJaf~rbF0p DH , 5L(I-bn郄 a8t0o2bf4oZ1)"mQRrQ]F*<i<rWEbıoWՑ'#5cy([V[r"2nc{I{4XR)$RHN i6N2``2408QHA@|B#&3P؀ i4y�@4$Нp(ḛJM(&Fl\GqkD:L2ՄR\p| Sլ,b nCb%jfNdώ |Md\Y|$921$+>~6rb_'t"I$@(BbD Tcy�8QHA0$xH#.4؀ih4yʠK@f*9` YTtQ"3k̅#~qrQ  xhätaglib-2.0.2/tests/data/itunes10.mp3����������������������������������������������������������������0000664�0000000�0000000�00000030030�14662262111�0017116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����Q7TT2�� �iTunes10MP3�TP1���Artist�TP2���Album Artist�TCM�� �Composer�TAL���Album�TT1�� �Grouping�TRK���1/10�TPA���1/2�TYE���2011�TBP���180�TCO�� �Heavy Metal�COM���eng�Comments�TCP���1�ULT�� �eng�Lyrics�PIC� �PNG��PNG  ��� IHDR���0���0���W��IDAThYII}Wf;l@ !.ܑ9s?8܆ˌ`2Àl]]K=UՇ)+3+#"ߋ}ݸq_K'p ߉*3SޠV5+c۟\d7 HU ΊF fh" [; MiF6?5Ϳ* mޑXػ^0{?ϨMăx V5W/wSo"w6XZ^uPkHiå\h>cmù0! mKl|_av`:[x17ǞJ1{WE�oԛsچ~fh~0TU5\Ts! !mDcAہ[!4mKI<\LH3/'ˇ \]GV\8*3 |IY.B�MAO(Q9n [SeX*Nq33)_sQHh$rfxۗf H4}yZÓ3akK{,3ܡT&�^DuuJd:3UL$f#D9ttdDMx!ﲑf1\[6ڝ}S\燓v\LM@J'S} tv$,J0c@ӭַ ll|g/N= SP9`9 4"# <RvtDWK?8)MG{7ϰu[̍l^Vذ% Fiޛڔˣok4FH ͤ`l155PHb>Uᓿa6l GrIdRFy䩺M_8Ѝ>ܿz ~Q_Pc<:b#tB|iJQ6MC(^+zg!*]ӽ~[i>}3iB:12ˤJD654#U KQU+*mF7~1gciɤɧl| aF? ap0bm3sl�}SL3eZ <NVJV?@FY)Ƴx5s)HpLeFe1XЌ gj>O^*E@6h&cFͩHćG=t42x.[*�}AAma:4+qEJiCI娧yޓpvz2(J@ 84̈́_Ӱ5*Hܨ8Gdo;&`!~`HQROLtT*.4LnT*GS/92>TI& DoC%R!-4\2$dj*DoZIQS(4JEGI`i):S+8K&j+\8(ްM һjJpbR>"8HjOBgUH%vmJ'}`a7'W ɳ߼'.  3$&LVL.h$%[# WEh ]a@f¶R\DwD}EO)y;e`0E̦Qϩ>gI I'<6"=yQ[W.OID8 a:,LřFsI^ *G?}{E{{ B>[guzQeUȲ,tk9z&LN,T4 TY:<$FD )�%N?c4~QiI`.r ? -e72iB+.S4Ӆq Pr#E(Cm Sˊ', pAem/x|\c~,Y+0qi#>l\4TS<cOoO]" *!ܾm_z@Yj],>ͅةR"ީ\<ڦ#m\}JHLF{Ο[Ǔ J T*sm#//3J/بp5skLS}\xhF `9Z ,5}@V }K-Ńg@yZ kkk|/=dQ_O4-cll=:'Rk opZhYq3}#j<Ǔ =<K^Qq}u?-x�\2cO0����IENDB`RVA�� 6666����COM���engiTunPGAP�1��TT3�� �Description�TST�� �Sort Name�TSA�� �Sort Album�TSP�� �Sort Artist�TS2���Sort Album Artist�TSC���Sort Composer��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d��������������������������������Info��������W����9LAME3.96r����-��4$M����W۳(Z��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������d���i����� ������� ��4��LAME3.96.1UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.96.1UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd��i����� ������� ��4��UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.96.1UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/lame_cbr.mp3����������������������������������������������������������������0000664�0000000�0000000�00000010000�14662262111�0017205�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����FTXXX���#���replaygain_track_gain�-1.020000 dBTXXX������replaygain_track_peak�0.920032����������������������������������������������������������������������������������������������������������������P�����������������Info����3�^� !$')+.0369:=@CEGJMORUWY\_acfiknqsux{~���9LAME3.99r����."��@$@B��@�^x,�������������������������������P�"!|Ha�?/etyeb#K?2#̋쬣!O&_V"4pLeԘ4@@M6r= bv?%:8o> +Ќ9՜ ިO!TlŦ!ZB[߹t%Ќ�E*[chmQX XPO;7h R�%DzAxd h] 0HPV<vLX0"2't`B'r= p@b�&4&'(w w$I#43",e{\iqN#%cs)o/:FU%F{s6 ru#۵ v~DQ*_R x3lX&tXGqڄ(PXCZB$`:Mo?#:(``YBbhԭVEХ\U+Uſi4dyرWHz1�Ε.e(Ej<3r3E5b`JfcSACD_y[e5QݹN.̨�dIAāq*R <o/g ep 9ED&L~Od1N ʐu?aQ0u EP3/hF<[TDx"H*.\?0Bu5��d,Q�ܾ=6,MR8(Gp"aqa7 qޥˎgoj[ήU?27isjekR_<Er[nۊ�(hQsR � /iAo% 8@D\4 �UeB[!Ś t l"w :*4*y-KH1xQ[fF( IJ%� LB 10 R![S wSP`*0M!U$Ye* Vڏ!n{3*6[rF$k/tRnRK,M1ȁd&0 ȭ$d:H� [ J ,;Z1'Ir6ÂkT$vL4jRN}Tڛ5BZoPM.Mb2% |yN[TK Ti>|"VPl.0$<U.DETz8$tm"_ҙQF̵rPl"ݾEQ$C'%'&Ch\'DR � m1b 4`= ME#9>_wJdOO_Sb;KR]}ciYf8 cM>@&xJS:M DmZF崼颕EA0gmT:avZbT0 gg֦ %*l|'O:G Ԡ$mB.\�R /aGft 8BqvoR]'Fg5+QgCw>hǵTEdm@zi*� 2 ZP /U7q[F"\p`sЩ=6RsEWRթ?!S*,0�H`3]5BV)" 9G("yÀu' 9jPR C1j=&  B^in(+w!kH]F$x.Ahx/l{0 lQJpIOϸK/l`0 H8:Enu>4-ӏ j>i~uRI7N5r9L5 $ 41CM:yz:a&ܚL.PnTA5(ܵ+SXZ;~CCR(� Pg5iCfpĉHv ꨴ$U Q4R$ ,L 4/cM cƭ$T[C9BxV̙1*Wm4WRtFg)  a=&h#+:(PTB[:᣺*ӍbۘTK"㯢/1{/m*ǩi6mH_H>JR4� L1NLtxj gi)` X.U  *eBF8`5Oo'uh:X:X\ZS-uz:YPeaUP ]SHTj9+qm$JzX:X,Tuh 3NVҗ6V2"P~/&ۮ>}a85D-(AٚR?� p3eA.p RlAaM 9yk?F?n+j5mBU-kM*? aX)a+JDac7}?fosd%ZH6뇩pΫhw.%v& =>&6chLز$ Ąh/V PM 6)iVPj8cL ;RM� k/j,&(b"U4hguD8HcT['P,bf5y: � EEa[Hiso|߾Ϳo'<B8-۬Q аq%1[RUEe & � <!N1.3=>f$@9r_\0=Ebh R[� lG5jIpę�"Ph`6 $~;b†@`چ v 5BR⢷`yhIQNT˞,d^z./jU 6$ `% iDr(VS?'C;XE[Y*E(j>pٔΎ 4$U@ mJ7_*%DWD.T`콓T ʋ0mpF5[Rf� h91$hGf H{Uά7؏oaO"ˉaK֐,iWˋI 9 .L5" -T<E (<`m#N#"=P7f E8^.r![nSSx0 UNYiȐ �9~ eI|f( 4(|մa_ÌG%> PRq� ܭ3 PI pFhI Qu䒁dQ;yJq[c6I�\KfF`9@$!*$|FR(IEAay :͛ ),V1n^#,|7C+0krΠ)i+`%=%�4:?X##+\%S0ޯ" J"Z1Gh8 Rz� H1mA!d(R[qq$VbקS٥!W3}#TJy|JN8Bh E6˓* ֢uKz<R ?"sz&קWښndA{:$ at*x�B<X]3I $9-XX>%JJ8APF4];yRz c/rA_fq ܫ"V%KO]C^ATTʈ*+K9vT ڬQ,ɄqjMF-2.ʧE&"�".K|Ȩ,6 R/>b/[?ZǤtaglib-2.0.2/tests/data/lame_vbr.mp3����������������������������������������������������������������0000664�0000000�0000000�00000010000�14662262111�0017230�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����JTXXX���#���replaygain_track_gain�-1.020000 dBTXXX������replaygain_track_peak�0.920032���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Xing����3�,�  #&)+-0369;=@CFHJMPRUWZ\_bdfilnqsuxz}���2LAME3.99r����.'��5 $@A��,u?����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� $@�� E($�fq Y={F1d.6  @{vzI@>O{HK�B~>�˃8'|>1[mI Hܠ&-fܾK/i:aI\&)T,’ݽJ ]s$I$KGβ+AӔX`TQk?sZ޳T{iU+>|gK_eڶ,@�:&J~tb=/Ba %$E֦*O&0(72<ai-Q,CrVj.*袮>װY!_%Ts9cYA[B P?1^wM'#=X Xz U4QzJh*< E)�y[e5QݹU}H㈐#+AL.TD&L~7AK|>OI l4}BY%ukr[v*Dj0(D,/ Tvhlu-tZ{fGP ܇'Fu%Qf.xFX !JU3Py(@Ad5M0Tb͸H"eY 0NuHPf*ϝm)UjG" 0IdVeydlg'#6""ّ%Hi\*4C8Dz(Q%.<X"P� X[10A=&pX6!PT dc@kڍ!x`%ⴔ{V,r|F]'s(I=``n  YsgƁZ|4)Sl8*(N]S<L4:,*mMrb PmX?[n#M ^. wYc<3$zOٝz:E{EP�s9czt xmeXjY'Lp,tm"_QF̓ . Kk,DRNO8 )jNfBxpIgPMp]^MR\#_XVy6Uc�MrzN&Iqdm$B+KhZnkƛ8\,0e3Jp`8P 3Ya0$ȳjV eQPgM6Y{j<,HbxO"*rmJ4'~$BÒrA 4^j)b -p&T R!2L  6 d ᮔ<˅ )X/]" Nf H`!-i۹g(ҝcpGTm %X*@@ A1<lt pjiI}RsEU~ kIRBEdkG W BDa)>Q ۝O9oޭ]{5\dIEygwzۖ+ݮ'=4S|+n/u'YdD*ŜYQW92f(*|>P� E3r ft */_ lrD,8q$JdL[GP\vaMj}}Gm$ ʅ+^L3b[d]^j3.{8'M .I%7*Lr #nZZG}wbԲF.Qn0*T♑@h [Xa9BF P X=1ÁM ft `�\ XkrH0 ,rJޭ3,c5<^taglib-2.0.2/tests/data/longloop.ape����������������������������������������������������������������0000664�0000000�0000000�00000000270�14662262111�0017350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC ��4������������Tc����2�³�b�� � �I��u0� AN9SHrj1g MonkeyEs AudioC.fpe MompressionAPETAGEX��"�����@��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/lossless.wma����������������������������������������������������������������0000664�0000000�0000000�00000301305�14662262111�0017410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������0&uf��blw���������ܫG� Seh�������R|tLʨu?ł������Πx�������PL����S���� ���������^4��^4���_.� Se-������ӫ� Se���]&EG_eR�������ů[wHgDLr���������� �����I�s�V�B�R�������4������D�e�v�i�c�e�C�o�n�f�o�r�m�a�n�c�e�T�e�m�p�l�a�t�e���N�1������.�����W�M�/�W�M�A�D�R�C�P�e�a�k�R�e�f�e�r�e�n�c�e���Z�����(�����W�M�/�W�M�A�D�R�C�P�e�a�k�T�a�r�g�e�t���Z�����4�����W�M�/�W�M�A�D�R�C�A�v�e�r�a�g�e�R�e�f�e�r�e�n�c�e���lZ�����.�����W�M�/�W�M�A�D�R�C�A�v�e�r�a�g�e�T�a�r�g�e�t���lZ��t E˖ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˥r2CiR[ZX�����������������������<�$������(�"������?4��������}1���������)54�I"������������@�^PH��������W�M�F�S�D�K�V�e�r�s�i�o�n����� �1�2�.�0�.�9�6�0�0�.�1�7�4�1�5����W�M�F�S�D�K�N�e�e�d�e�d������0�.�0�.�0�.�0�0�0�0��� �I�s�V�B�R��������(�A�S�F�L�e�a�k�y�B�u�c�k�e�t�P�a�i�r�s����r���]��[��0u��F��ȯ��*����G�����������0W����� �����# �����@B�����\����� �����@KL����������@Rц1�H�������ARц1�H����!�W�i�n�d�o�w�s� �M�e�d�i�a� �A�u�d�i�o� �9�.�2� �L�o�s�s�l�e�s�s���5�V�B�R� �Q�u�a�l�i�t�y� �1�0�0�,� �4�4� �k�H�z�,� �2� �c�h�a�n�n�e�l� �1�6� �b�i�t� �1�-�p�a�s�s� �V�B�R����cܷ� Sez�������@iM[�_\D+Pÿa�� ��������$����������c�D��2�?4����������������?4?4��u{F�`ɢ ����������6&uf��bln�����R|tLʨu?���������]����-����?4�� ����`o��9@��Mbz:u5*S*UB,%4)CIM]`M!(|ˤȰsEC�b9)}y1+;Z.![CZXvժ/5$Q0(120GY(3ڂhp(Z)̝)Μ)fhv"Lh)Ʋ,6tae\㬵tٛ}wop�n4fhfi98bP218VF&9(*^N!N*z֚[}mUbO"do$/ CG('tJmfs1\;9gBݛx'x%fX[ĩ'n5"0DpX5 Kf$'ʢҿF̚ Kju6&֚Gg,*x p�d&  |^?WqjK5 >/xx5//JL" . 2,,,0:DVjG'Kff&m[Vٳkfm_Өs!hQHX(�xa(!XxpFJ&f}[ĥ G$LAD#BPPA�Ġ*R(..@4FAD aDV|5_XQK!�AҌC+@$ɳ<+ պ|3ҼuxG`首8OwkG}xNdz=A)I* |#}龙esGA*ImMa/xO\+<lÅr-�) u<8X>Q}�jl<#�*>@[ rlW?C�\SNGD[ї'2ݸӝ|<QE 0%||ԼK<[usgОD8�SjxO*)C!B晵h4(lyz/@@�0HZ(Kh¼+ʽ+}+}'r;Al9G¼vǖ<1/ygJTQHYz='<+|Ҽ|]2EGrQ|ʽ>]�M"N O)v)IH~Q.G �N(6{_WWxGGg.GceRijK�{zu-`�daFW圷nt.4.2N0ճlREel9�s.`^)^%^iޭӬ9TOGp|#v^=<#dpTPI�^KzGzWGҹNѓQ�v]H ]1=3<5rZT1( " %8V.xz]Zb Tj{>һcxg.2.i:Qx,)04tHҴZywxyy> cvtfM Ɯ80d2uS|#k፵syxu�"JTK^=Mp ml0+8#(Fх[%>>]/yb'�X=�FE-4U3%K�xz|_u7qrqaī[=<_b)x11qk+ky?>~FL8*լ595pӆ CSx7g)".-Zmf;x滮qn3!L\45fk8oq]LJh8Rt'GԄӧ�>~?�Ar@+RM8pp:t<O_=OOOSFq1qqqugz3=<�&[[[W1湮cqf3À� Ԉ>On-:th�.8xzΟ|>|>x�'� 4?@/kǘ9qxYE=Gv[3SJ` ).O?y\Utu bpx>v\`'�C�ugu88�J'ᦦ&83gxht:[[Y;W1vw[bjS0h�.^ay^cr+Ejxί=_G� �F�^uz޳z~4 W[qz_S~�q5:O|?~�]-]NhC=?^^Wk6W)A84px=�;1s\k8^ �@:0? ȯ59qt�@+kkwcf%M8h&:x_<gzίӧNi��hӣpxg[z3WY4�~/x[@�+MLLM]^/[:3}_ѥiN :}OGG?}>XD"4ptx<=S|>xzx0<q8o19\㐹[88pT88:xx}g3qz3VVMMJp8NS<>~�!1SFП|O>G 4΁N&3+c�E8'ӧYs[5s\12LYuo[qs]W+k D8'��?�?@F4Dٳ9u;c湭8f..&E\5j19sqg5xpaV\]f\5ffу *akus[b1qq(R+VYq9u7YjFSM8]^3Wxηz3YĦ"DI8z8xzgx7Y1eeIJk1w/gz:ztQ :u187n3cqmeed�銘k+`^B'{'=,��ʰ𶶶؎;[ qʕptx>}@??G>Gp��eJsq淎q\L8pQNLL^.83uo8/Q�+GOSq\]eJ*ԀMN'|Nxh�tjquu:_~ѥ1154zz'}ڳ!eYt:/<Oq:NLLLJg x^_?G>?@GF�iYGS}_|NWx SS<Nx'tp:\L]goz޳z^&4`)Ӊ<O}>@�"S>=?O�Ѡppt'z/u}^N$+8q1us\u<uyW1Y44“>At8p࢝MMfxqfY1q*bTz/_z^&��:_f.J Zqn38j1154h”S.טy++u0D4�Wbgq:^Wʔ�]<>/|~YR PGP-7kxYu~� � s]cn3[jbiӥ'<v~ǑWk+u584f5g[|_´ OG@�~VVTÇF:}>~GxG�p³O\1wc`pkky^^O}Oz880VSV^ugYuemXV V7]8gjӣ&qηz9c5*SS�t~~<�h�<NS|gW|gS 8p:OOx'8th,Usx83Yӧ+MM<1)ӃʰVc8k5tÂpix75ux31550bNʵy y!v#\jTDCSNxOz=N�jµqf3[8޹|oYiӠhJ[5s[kqjT:qx޹w\q9cq%\X[W+טcs+1aJ()}>"%M:N��kw\oqq8tpOG@#sVbQQtY[x7ugYì:7ιk1ef,)jo5srɣk%!l�:3Ŧh<t^7ousY[ũq!Yѡ8z':3ʵ`jUOS[uy1y =থH�>'=_:SjS Js\ǚqyW1X�p!X\�~|> 8(jT5s]usbJ� ?Wkוy b2 )@N'?{]ʰ0pçOSz>~|"�4t.W<__�A~mf%Mppt\us]oqu8xxx5�ҵ)8}_z~'z(Ѩpp=>~?@.f3 14p}@KwI4i|/qy1nR4'O}ڴYWoY^:/GJuqfs1n2IM4ԜO:3\k`jĺ`N5$4yr}>'Wի�f%X88xg8;c�Æ 3WxgWY[ S�umv ^ϧq1`.-VOx7\ŭ &$5g ~'UŐbx\ǘOFgb\/WcalSxj+:gn;cx4B%d._}uxaĜ8ttu=OzoWW[UU 08qkk򼿀�u(q;ksf2-0a4΢q؍89\cR0V\qs滭8g!87s5\LL8h8 µqu7:޳uz=N )+SOOG>��s S�O>^׵/17=a.kmfSN.Ay>@?|�GF4ѣx|^W8))É_<OWt1M�+y^cwc滌aãB%x8]�^וy]ǘba(<r]kqs1k1q(8t=>|zJ(� 'טyW1%JO>?y]VM4P0hzS4ц�tptt�Ygũ0S80U>�>|O&0 ӉW\y�~'æ!Yp/u5xN& tu<_S|OY}_ç Z:g/|_Н Pphj5qo^==<�h�t\L]^/W=_S=>�Q4h?Ky^cwkqu(tp<S'S}>|�a,qg[u3'FF%:nw]Ǖr@0M5'p:/Yugd3du:g5l-3V:8ήc1aH4iX Y[]5滮湮ku7F&&&o[sĦMtpp]~|@zx�4 'OW|OuzM:S A ]^3[19c[\� Jʳ|>}>/SÉLDnb-ef6b=Ǖ<k@R .!~>GiӉRO~?|==<0E1N&&...7n\bxʺ�)qxޮxzz<,�"Q)<gSxxN51*!v# \��ѣNYx3oknYhH+Jy�>GON&%Z8++++kk9]ǘwuȉLAJSSOWz:0h1q18N'x|OS|>�eXEzH/<OSx�ѩ\qyky^c+`,@MJkumw]qws թR*1xoq^àZh >|?O~.VUti4ikqf0pr1;5fpP8S'}O?�^YYS�:�C@ ^c5pҘpÇ}|GZ(-4OO>4� \\M^3u3<_Sttt<�#7[^pAb'O@B<+z3#p~{r+c8ND8R~'}O>]ᢂS@zy]=+kjB%8pL8� r5nW84hx'Sq:ή&&%0QY/x'NӀh@~}?G>F88wǘy^{^{J Nk:sV)ĩ4WWqW>΀4:"k1sqr2YV3jVW1湮cn9k1qq(E8pjkxηz3x3M8gJNz~OSSS 3FJ}^ԫ�0BFLf2+KÂR qr'>GjT1aի:xu5x7[v]<LLY4T:x+/'<<M\IB5j51W|?Sx^WYRVԃR;א|}O]fX,Iĭ'G/g~/ H0ʒ@&3 mlTZ x^/k1`,8N.3Wu潯+p�ԫY ar|G/a| ^O=FBV-\]gqo[5:4 ãBf383_t�S"Tç|?>lhp >~ N[ }~Æ΃8uo5s9]AӇ :\fqw]qr+M�a3[[\<+pQL:^S=_S|>|��S8�D?O>^v#qpp )]ǑW+9Ub`�Ѡhx'Ӈ 3@S1sk+h8i>?:x8 8(iīUnqnWL) XY qr]+rM4`”~'|Gd))<O~? 1@tZ^>،Ӥ q 8|O=} a:TpO@^s\8ίS"`C8G=> ȏ+\3===&'@pix=y1滭Àpp%L:xxz}_|O|� 'w51ttt?O>G DL4P4p�~ay HDCу/x'FL3YuoYq;VA�bb+k/!{^O}p UYY]5滮w171 LLLM]]^7o[z\L8p+ n9ks]nYSMpQS :gg:& h\+Gטvl,iWR"լkff5u8tazfYq\f &U. ?��G[[RN<N=N=_u:x4hgGAeel.Vq<u滮UĩM4ӉWNx_x:N&`Ҹ.3k5ouxM:t (ë.98kR04iӫu3[qs]9dJjBrav^cy19YVjbi=OG}ȉE)N~vf.M�:~>kk1tNѥ(|vu9pq80q8z8|>WӃ@V/��-r+w1w[WR "GGz>?�y]ȎBч/>?=>4`р`pz^Wz^3jjbaN..3[uy1y]z\0ԩ \k ^50éu|_|gxSthJTiqxz/~/=OO+N C]^7x/x}>@բGS;]u9g&&�ksq3:z8�Ab4p�[[oW^@YR@ ɹ~ ]1xN@,äpSA]^'OGK.cu'J⎩<;:޳<OSҘ�4plGkks]uf/N�ÇO>�^בef2@_7[q7oYq8N:58phx>}~�/+ՈQSOOO>~jBp|ПO~z>G`J[Wy >�ZgJD\LL]goYg[Ӄ�LDS<^<OW<>�8@O>�@ʴ*hzz}OO>'tphѣ'<g5*('OOzS}Ox|8�&J0p<>'z}?>O�zOzx(1N/ef2<Nxsv}�tmXjU:on9[\+:jXt+!vz~'p�MӇY ?=éDD�tz|==OYz3WeI k!lu+'OÉq&k:9w}>& TRVzγ5ykMI IRp5qv �=M8ʳ�Hys=Oznkqjʛ0Հku6\p–Ԁ`O}OSլREJG|SxNXY *é><^upa :<N'Wu9+Ձ rNJ/+�}8(MJrz<'|gYc1jR 8 A}OSM4SMLLJ3[qou^&8(Ѡ*c5g<oYu^/SѣFE��?G?A|.;0hMN/zίYz^SF8i�txN&jB`D&38޷:3N&@q5xN'WzS<:J)=?~vz jpaѧ~'x}_=>8?>SthуelGkǵ/k =N87mn9VAÂE8nr#ǐ mtiE1B '/'y Y @8z�tOS'ī4SW!vg/k^@� pJjBEw+v+ml+JΔ1u7kn:&WNb8u8O<OW}>O4:5/񞯩z=�Dр>~�ʔ:pӧ}=OOOti +8:^Y5rY�)V SSWW[:s[c++j0bTtpp:/Sz3k5qujի4D02�[�z| :jk8޷5sk8]]\8h!Jk"2#;Vn&SSSSYqoqsp y?Kוr[g�ɩk+51滯1r#  0ju޹cus[UV�q11kqw+Ǖcq~�! N<:N/qo[q]eJ*SE<>��y\<�.^>_}>=��J^/Y|g:'OO�.@^׵q0z}_z|OON X��.ט WkV5*Sqx'W<Ou:84@�&.q7uoYuM^/N 0iz'|>G�)Ӄ|?O~O=@4h4t'z/u}^N%3@Smw"9\+`ALD-@~~zAÆ"q*59suqQN��)SWuz^4�/D} rks[&)ѣr\yc]F 8 u8_uM^&@@>z�b�"a~y ;t84L]^.}_S>Ox jSA: h:[[qn8γx'GB&S@\^'|O> 9p 4xSO@�y vcqL:xt1N'S=O|`DDtq<goYk1` 0V}==^/Yn[!Z2#^W}��hÉ ~/:/RDg|^8<<:0`jիY[[\Ǖy1yr@YRVZj5sqg8^'O��\^3z/x|�� 0 1oYx'> *`GtAv#kug!a]L\N.8gj58M`µ1t:>'|,i)W\8γu<^)S��!r+[W# Bqgjz|]��"QӧS|N:_ãjc!er\Kȏa{k.sV� ={]1sp N'go5YV f7\1wqlѠD��<.A>O<Oa�Zz~F& +.jUqn9WusqHaI=>xj񘸵b,RՃc]|>'d.-lc}'xda+}?|<^3W 2bIт g[ctjA Hqq+~/-[4՚s'~|^Y[pqgwqvV ռ|_[<oYgYVX�&M8^./z<NE*TVqk;1ʸ`)�uz^/z7k1jU t:N<=�)E8tppt?~ Epp}?Oz:tpiXRq\f5oY5ux^' R :88<OS=S|�[R8mn91nq]^3çB;^cv#k,b` i=�}~k9[Ģ �>>?O~O@`Ҹ)uxNx'x|>G�ҰPphb5onf%Jj^pO|OYxMNJjUs[\urWqmmemXRqqug[x'<�Fié=>z>�z:J" :88��A`.BҐ"F\N<^ �iÃ@?�������]-��-����?4�� ��\զzNS|OS}Oӂ15548WS0Tb\A?��`o��9@+FlcPv.ڼ(T,E*(=d7YZѶ;n7<LuK9AI񃅛ᶚє6RI#"P r Q =H>zʼjj` rH·hv)q'DeP �t"63OmiXOel\Sɜe<6κ9&b[̧0DA8Zb* H?1II=(BZqp.VRUjU=O`"!؆""i;R} dz \ܸW ++~?:Ni-.Rw�U==J `\0cd: bcLf֛ dl56Y3_*BdH 5 �l.B1Hgno ox?oq186k&]_,TYD2$Ƃ�pl"8048DP`xJ %gچnlvu`)�,H `lTDD |L<xҧy~{vz<,JP! tarL<�� "@I - F )@0hyD;@ -epO$36[J ,0ܠ̠ Bѣc7^@2ViP&fZ#AEG>G~^).Mehp ОE4z.@[Ι3^d*ѐؕE6C��^em\1e:- lpGej´UҼ'={ū$ dXʸ_I^~~=fՃlG-b^U^轗|{rܳkZFc't.N8olm|'r|XC=#\˛s:soc;Oh^ Ga3$NrQ|^~C>]#" l\әqNrNvMmK6z (4'Ek e3kz_k rfZyqޚ\<O p%"%<xxx<\ƛVcc`rZσv =ri;+:D 2:-\/zgyompoMfض)rZGK{???/@{C{}{/g|Eh0KE8N3<3\ e5p]*'Hlv<a|?//c<p:d2[,p_,tL r mzc*m68� Ұޜ\\ia3MR4V`6;F9n[Żz/E=# FG,&Y>xGWc9cv41 (QJ{W Ӝ˫xx�=*Dd2p\#m|zoN4.n;<h4b[.^��A?!<CQkEiԸP/1/s?c_C. ER)4]\鼸ӌӝyo,圪Ճ)8VƚZYY[VhT4TtVL[k eo,Ehp8X JbOS=_>'|aZr ?| :jk8g[g[x3W aXl #qy]YV<3Ygu5\N&0)/>/Gb88Gz@Gr#jbaF&}'O<NN+0<?S>|O}>Bu|NGt�N4���/@(?cwun5gF<<<^'z^f.$&ʳFp:/<^u88 :|N/Yz[u]\]H�aY��>?O}'5*[[]� ^g@tP)өq^osbԩ8='{�/b< �LJ^/Y8g|_h�4pO?�/r0`=>}>:(RW}o^jTũR>O=>Gx4�iÇSx|O�Zeeoq7[o[Ӏh:8:|A>| ~x��pq:3W|_W:xxt<O>>�U4g<O(`o5^&�AI|K #ڵq4t.c<"qvcWS�& ]ǘGusÇNpK >�=�p@tbTW1?}>Oçq4q9v+0 Led.>><gOWSN%Z.B|=:4RVkw]1s\x]MM:(bbjbj�Bgsqsk*T��]_ /!dH S#.^n'W}OSGOG4ptOO~rzZa8u4`u:|OW<_W:N)рpt�</g#vbBiӇSGSzx:x)�8xLN/λx湭*�tJ::z>>'= (F&Wg[k8\f&.C%"b8f3[89jjbj`��. :xzOz=GziJW:Oz}FG|G?bQF �op8u8xgqoYjc0Ԧh<>G<>@<~���ӆ[8gW953UŃE441uw'--1!M\Z}?O}N'3@çNk滮k91V@q!Y=='x/83SW+(V=?}Ox|_WSM4ÇMN3uz:ޯ8(Ѡ�Ymv\w^ט<+n%JDGS}?S|@&MzS~z=ѥ`RJgYu3x/<�N�Y[\5;ou54LSG��!wWkw^91`|~ {+iiL8<ks^q9bjjiҔ`eZwkx޳8^WAJ|O>'|> jS@�MM> ~�^B1k5p&(8tpS}}~�]L:N p}~+q(Cx8=淎qo[}_SJià�>Gb9w]utҵ'�)SogYqzΧF0߀`>}O<G@@)Rx'ѣ�SSSO<_S`M\\Zy\ǘW< -�çNL^3[k1s]sŀ�hb%JwckȮ+!d)N .&'zW>'t&GGQuq1ux޳x3:<=<<V覌L]Mf.qι:o\uSX *Ηk~ ^בkmn-HRM8pp:t:_'OSFVtptVVDrq滮5rn%JiLL:u8^3x_q4 ąqqfW[ogWӧ@т:q*1n9qs\5eZ)0h55o[kxnV��~~F&..5ssjN%H8V}OS<MN&`5*V]zt`pj55xgYgYĩWpQ~/!y kk1`&jT8޷83/ A8ttx=G}/khp#7\7:3ttp��CFSx}|@^BmZA]]NOGk+1taL�)O~ʘp`)];q^3@pu8|>~�]qq4 4\cw[qgqzthHXYYY[k9n8&&0S:γz38޷E4J\y]q=u湌ũR4px=?~ =Ǖ6ef%R"h+WWWz'S=qpL�eZs:t}OA8n6+o|g=)N  vw[[11q4t`8?~�f3k:Y<_GOA\Jo*ppzlWqw\5n\uf888uz]^8޳q!рq4γ87xnDX â)R5w+;1ڰ� ѧugWzη8fJjaz>'>3`ë5]b;^'FR8qqy]AiMX(\f2^WGH4`<:tu8oe\f-Iʳz|OWzx]LHV:�>Gz}OOOb%8jk8޷8qx]N'F:pBjTk"6~+׺]qeXMLH8g^'i�+V\MN/W8}OStqqfYo\|sjbjhhGO|?>!54ӇN'O<O<OVtp}OO~Пx�&84}OO|>z:<G�^W�^בB9!4N 9|'x<Op �if%555x=N|_Ox=�15!I>'<>'5f:n;+k>}=8i`өkqs\1wYY ))<>'>?>�p`+8@~Ox:8tӇOSS<goYzL]]Jhã@>G?Gx<h& :n7cv`.Wk'!58f3[k;ctpj@8pSWY\qwk9pa :n9\ �~|>'S&%l.|>'u3Ŝ :n61y {^xxaH jԬy G!}k>�ӠhxtNWâD(Yv?>t`A&&.kxouf3W;թ *W1s\uobQF\J11qqx[n1u(L)F=N'Wuγq\f-JĬVW1wc<WkvVVUĩ aN|^/WxNSâ�)W8湭uo8&8Ja}G>�Y��Jb:u8O<OYzu:tiѣFeef9sq78ޮ4P15555uux3usqsk*Ԧ:88�G>'@4 �ppp'S|gW3Yĩ8px_S=>:th,Usx8nWS C@54x<O?�^@1sN88 *±Yk湎qs5upӇCNu5usq޹xf3WbVf-\gx3x_OO@In6+5q140pqMxz<Oz=>G´F�~ו]7 &&&/Yz^|gSzN°x��}ay^crĈpaL4;滮cun.N&x3|^'M4M<O>|��YQ>} ��éR uZ;Dy]kG<ÇJ`�t|>ÇQEjqu^:޳zMML RiZ3W:޳|gJt`�Vbx3x_tp��CFf8gttp()SW|O|?Zp�)N&&<_Y/x|N8S4hx}G?Zpi:[[n9u3ӣO�h<ON4ᦤ&tq8x_}O<_WhZqf3[:Ybxu4ӣJQN�?�#v<12D�0p�_W+ooѥ0`57[jz'QMN'Sz~~pNphz�G;cz:tpp%Ju583}_Sx8[V�G4tv#s\1tHWN'O=_SO>�|�ti tO<OxNS "ELLd-O>>}}N-K.-XBy^^O�_  iZv{qjTÇF|>YV4� իc5gz7zM:4hDÇS}>Sz|>(ãOx|>>t )Npf-LN'~�kqth tv#ufSS"N:fY8.0\J/ίWxNN%L\n2#;[\]qeXR YNSSW838*T �@��?G�[a0x_W~'~�x1 1pi=?S|Z)AMM<>_+]1.aN?Oz|�)MN'xOW=OW4p�Vz/>',� ?Gv+qjTF:<<N5n'zg:gWRttz=>O~>j1n3gzx<: QJ@~^וycqq0'F1:x}_S~>[V4~}/~�]ef. L8x'>#FӇ�"h[ufS:|OYM8`z�^cyk9\Vc*S(:Ar^kvq9k1piF 80^z߀k8^&jAF]M\oqn9*bUtp<_Wu|g38q4zxz<N @)0aѧ>GOz:U4 zNx(`�";[\x8N �I>^W<cmef-J(@F>'}>8q0Quz7z/SãNLuz޳x3[5n-n,5f-YYOOGtbUM4ԝоγ8f³EjVBv}G?>8p`8pD4b1s[ouZ0Ӈ�+Vqη8γ|N :tjgsqU :4UY !v^ױ,`'S:޳x3Ŧ HG>|0SW 8f7\fYĦ"QHWYYr6WVN >O}@v1AL\LM=gz}\JpЉYn9ksqηӫF:=Ot810ӉDDk+5sW &M\f31s]+k*ԫL&�88x8N/g\8qf Ĭ>~'uu!Xpb1n8\5ȉLCN@f.3xg[qxN&0h*bqxO}Oz>@AFu^xzz88884bieXr#{^D{r�th(R6+)+WSS?~K+10 7]8g[u::8 G�"`x>w\5uN8 YW>}G;:='A ^c9xS @G&G<:�u'FJP:W==^BsWWM[\ǜwusjt��h8z|>uzxih80p=OW|OS=_GO[[kw\c5u4aJ)�-1sq7\8p4? r#10u�4�/v~5s\ J°Vb;\yW1en.$C8LSOOO|?A�xN �x ;5snYӤѥk1oug[:/ѥQhO�DÀ� Enq3z/SiZJ>?�y ]O"(<+@|�+滫ӧ�'ÃGG qrbãB&Oz>>Af.&JBo|gYӣFLLL:N:'Oh1:88=>G>=?O�Ҕ)�񺺽^/G<ѥ)kxqgu:84RpW:/Su5xBTz|_Sx=<M:u1!X0aËEskȯu; &&..-f7\9k涮.-JhxHV7zgYޯWu:: +t SA4`en7\5g[ѣ@#�54t>'Ow 8�ZfYx3<g:'çF>΀S 8�<>|tx8���R\Zf357zMN�p -sqoYq:8x884у:^8u\\X4 1 u58fqs\q\Ԭd�z^jV@i1jk|?SND4gqgYuxSN515qqn1\qk1q)�V3[xη:gq:|== Q@�t~~}@-L:p?@�.L SpqzOW}_:|+R�pE^}OS}N ëz~O<�x�()OO@>cwuhPR+V<>'K{+mn8:3g83<>uIpGG/k5pEppG/av#w\]^.N&s]1[5xί<& @#<ǘW<k9[LLA]L:|}A�qn-�xz�>B-�8p81)ŭ+_ ~ӧ51*c;^W�|ӣM0KF:7o1j�L) ��:h8pjxιfx733u^k*Ԁ8k'GOOSxugWbbB 4թVVk^BW`G`�40hZq޹<o=OGAHF+^3:On(qηus3[N%0GS/35x11jpS.7wWbk.BY�<QM^3uo[ηja VU��y?KkYRteY\f.[xγ:'SAZp`'?@ jaR<'yW1ykou44gqg<8thqx_>'}@/1.)]]]^x~'FhF�gպf/Sz< 1(' qFVc5oY|^x=,4p>~Gv+k1`&*T�qx޷u^:80  bjjjp:|O}?�:8SJsqy湮1]L8t`Dpiz}?�^O)ӡf5x355:4 ��)SWOWOKiq:/>/z•(SF=<ttp84:s[qnut��hçzγzίYqxLLL0RZ]!y��˛˫ι1n%ZjEp�y^@b>Wg#' DS'SzW<<<<:p0"Sp=OS>L Zηzu:x4p�M�Ë=Ҹ�@8>_1y^csYEG <N>@Z"ttUgx޷q3piӂ511jc1n79cuf-Z('G?|>'4ix0Ԭ1wWoI:::::qu5q)i޷5omeed`t ==OW83[ Y*Msc|8jLV[|_q^3ŭARH|O?O<<<N0�jc <:uux7Yqwc aө 2өW^c^>OOӧ$qaŝzg1dF@q J dӉ5λǜy.GF 3ū[GGO<O'jSM4DL:u8Nx3x/t + qfqogWSNupT5޹s[ūR084iӧWnuw7 4UūY k<k<ǘ1SO|>>zF +�+W[878g<M=<��+ WY'}O *i|N}Oz:4 aI,OS<=:xR*UksYnuL]L80 15558z>~ �Q=O�^Wq6F8(NS?@[ iCWS>@_ �]8S q|>'> k*aAO�?Kqn7ph� p'S|�+14 iMM[[nug@ ad-1n;㕸湍1q@~�/؏+k+1(F?_> �0`:x8xz=_8NWN4Ӊ 8x8xN'Y3Wqx.DAN4tSzz:z4 &&&'q8N:'� 4h.A� aZaҘ)>~1rVb.'x/OG"4 E\Mg[<^u:_W:8 T�SYu'xxZ(Rx~>��x�,mn7kx[1tFbjjj875fjQL) �~> �phū[[\+{^< q11jՀ� {^Dy rN1!:_z8x:84: Ѩx:|.qs8 ]M:>'�v\b`JR(<"uηJ0t!.cqw\1;5f/������]Z��-����?4����( [F 0px^3ιo1DLf2+y^WBGb9=,��:tq4zg|oYu]f"\XV[cs\sf.&& 0�MF'~~}>@&N:xtq<<OWx|>/xM-g x�p�'ʩ5֥ܵaXUB: >=gg6Hъ1n+jtH2` ܽ)ΣJ+.뤼0Wwj!XCSi) 0E Q`/E=hB6J8}1_m9Sع=~>w""CXp$ PHet8rQ`-eֿX1XX9~.eԚrI4aK^b?%9lrm[k6z:XvGekYtix\ 39H, Z4m/Q͆2nlo.n2"iJ +? !0< Ba~Q3St{u\wtֻ'k÷vz?4'qsn[! 0 |4 hD, FRt4),alY2@a Ԭ(r,{0E�"Apt4$ $L%GA8s |go=n9+HI@`i 0iP9 P 11SGט̶Ym9(A(hBd : D!Pd("*(ǀ΃[D4K,@[(IB(V3]6sC$Vm5*Zg'mZk.-ͼ@~G¹Nkc(&pTrƚxqV%`�|'`,)}|GxWj\*©r@`P:-'LiÛwO2?輟ge4LK^|/<2`X"'Jv|+3uyunܻ#lQH@<3ی8Ës.)�t[;98W�. =}|> "Օy^X|5 ;gLmX,�h8Jg,᜷o5oxc6h2,GC>}y��MB,ǡ�/Oo4ownp-ecadY'{W</WS¶QQJ vZ,Iʲzs9uxsV.͹b2t̵N-S]ݹ6ڦitLGgiʸVӵr,vlxcyGG: O'IJnSunaaոnڥE"Nӄ:V\/;c ~zG'8-BŽIS5nm0ۜ9ˋjصi83pKcckg:gdr: U+)5sqq<8zkLi' `pX<)/l r-p mz^UTrrr]1,T[e]֜yg axE V<3FO'C)@qNSwo8fشIU(4:z�yyunr�cJp+'<(?�jH4سme3g{cOxYQdtHU*ʶ<|�;/ # @r�un zOM+p? /q9q^˜8G kzYx'+04�/+sq^8N0z x>&�@RQx'{zίEF:G [[8/�]+Pf.x}�I8zWwkn3ӧK @ \gS|_S~ jTA8tttx�^O9=,Ţ:N �Vk7qux<:�CGSz</b<eZhA]]N'S}G~V0`ԩuz/<_thҔD8ef3q/z<::8 ap!r#ךqn234�?G{ ^vL&pi=O~'zâ0phӧW\1k^c�0Q�ӉV\}G~/:/Wp`SSBGԀa&LLer?@O<M<MLZN q*TȎDv]1^W؎VՀ+}>G 0:pj5s1r-phRtOx/8.Za55)qz^|^À`;cvǕw]cq*BjS8oWW<^SFUnuo8&8Ja}G>��R1<N'xOxxNthх`+YYYcx578޳WN(o:޷8TML:tpp>|x@80xNί<gS 8pu}^<OWtуX::Y qsqfWS V!x<'�/"<1f%:p`, f9sn~�!WW8pD4SYsη[1)iX: 1qn8/[q|M=<�#'r^k7Vjbi`&4x'z}>><=i<�@y/qy]]cqa0jbbbb53u:il+/+טǚs[E8pӃOOOS:/x/dçuzγ87x25*<O>'<>iÉL4W[[ v/ay ��88u*CSYYVy]pੂ+V[Vk涮.ijgYq^ӂ4:t<=>��Jt4�~�^y jӇ8xx:=_'�~N �޷zgtzz�Cp4ptx�yvyW1Jp`sYiY +17z<>O@L8 .av1qsթtѣ<g:/W:MM4SLѧN&Yk?\!}&0ӣWYx91滮؍aJ0<^Wo{^ y]r&j%Jjbbbqg|g:x<84` Darkv#<Dr(@\Jt8x}gzzS�Ašx:=>_+vcqL:xt)~'~g:'<]^un2 .%lgYqef2ɂjVDr��}O:: 7;Y[xbթ8Zʺg\zޯu_�0hu:/x/WĦppĩV\ǘ/1y/ .\�A&/k57\8n7TN 4kmn7+x޷WY )�\yn@`xx<=_WWpQGSz|_Sx==Mth0ūk;[\]ar8*UY[ ]<ǐ؎B!5jbb87:|gSӠaH@M8@roN��,kkqqsk 0PpӧW^/zηg(G'o[qfZj@ S&SL4d�]]gu_u=Oz}i4`|@/kwĈ(4G?>NJnx'Sz3uN/WR)0x|>'z�ѠhV.-f3[k8γΧW@L8|>G~�<(80-^cy]ǘ;[\1jSAJttz=^/k]d+(�7\78tzz�'�4hǘq9k58Kkw^kkupph'B\f:gx|OO<i|�� ט;ujh:4pq:zx'xSitc1y1p`ĀpiVc\^װ>&0Ӣ%]\n7]1vuv\ LJ[/qy^Wy]vaX8V؎+wks 14у8p:|Oz>R'An]xnqfY8t80h�W1ykqnWWN <:Rx>z=>=N'&XJ9؎^װ�=(ӆ"B;^Wy^Ww[ RҔ8gz3iӃi*U9cs^csa4µ1158z>>\\M4qj5o�:޷/Fu4:/=O<>LD?~t )NpZg>/ YS8x}�/1s]u7YѠt1 4bjq8x3z/|O@0Tb³r~^B/"=jB!50bqgY:N<=�t8x<<=OSz}>W=<4h�p<G?tx8xhit`'WxxNS4F:on9kmdF@ ppk:3[qs+v\J&UGz|oW1q!X�h=>'x^&N.% 8:==<>'xhp<>G}O<==N  ,hĩun9V@8u*Dԝ.N<gYf�Y) u5uw^cOR Hqkaj1wzzu5i"$�p <NYqoʰ�4ԫ4`1u9Wq9 `.Aq u<_zo[Y� @4i87[5w\ȎV<A ,<O?G@рqgYo\83YSp`:G=G>Ұ`)��'y]WqsWRh: ã}?�F>'?@?\4�J`ҕ14z}}WSH@-nu:N�>^׵y^וvEģN��n97Yq1xpph4OSz^:gYELi}O}OSz|=NNO ==O/zN.8@N..7\5wǕy+-�4Ԛh:uun75wWkѦ 0:t8qy^@�u4@qj$M8�sǕ<@|MH1 84bc1>}zM\eYR&8>z}OS|^.Z& :^/kzn\k1@4īSux^3uf7 uu5u湮k9�iF/3z3Wũ0jTjq8γnY+1j 1^cȏq{+[Rh ]HV|�}?G~z4T NN/s:o*+ qz^389bm\eX�L�_g1*ԘY8x0ipz:/SY8f%LJ %L]sYqn75r#q`@ԩV3]טk~9Vt�Y�.ay ȏay19]+1b0N=>O|O'0K&el/1wcw\5ʺpч 8u8^WzzNpS >=?>/Æ&"cx3Yuskxf&80iH qux^3x/x:<==�\q7u'ttFs[87Χ848]=_S>]JtiAMM<>^/kw\ū�# `Zu8N|OGmn%:4 :@r\+9[ũ8hSFN&3z)}OO8ti�iV+}}zEJR�<NoW BZqf3[:Ybx:th”SG�?G>/<+bӀLAe\\fYxSz}�Nji}>ʕ0hp) ]s5n� ã@sc7q]^F�?x g�A4}?�/+J"1ti�@�/a{^y]++q*iÂ@>O|>G00ӧz95s\+6xp Guokn+`0ԩ8:γ5n @R"SSOW=_Wzz::88իY kvkk;rtp�tpӇSSqfs[cqaZ "B[ �A{��:4 0.w=86\5+1ȋR�V4x]^3:s5f-XVSML]]mf9w1eX�Ff5srDn6 L\n2#;[\]qeXR YN838*TF�?vc滌Ӊ +V5xxWx'OO Ӄ�t}{]sJTl,k5f-&�W>'"QAL4��.וk9[YÀp=OO>}RN[ w5gS OIрtzv]k9f& =f&3/Wzth")\M^|O?GE�SO8ί�z 144yk淭jxz8 GIM:ȍ8ηt< `'OO@;sq7N }>sYS0h&:zz=??G/+sħÅ0 =ǵ*`ӠN/[:gtp4h�MM<=>O>108 #]19z'::N]5gY|_S|`Cb&Gqrq;:7FSGOO>~>UÅ0`'S mX`JM=OW|�4�~� jhx^σ�^וyÀi1@&)ã,japh>qf  8=淎uo[}_SJhà�>Gb9w]ut°�R\o5ox7Y5p8:SNz>>�LC<^<OYu<NN4iZj*9gu^pp su9q3RL:th@?GK|�:V��}�>A�iHF�~_} }?G�~"a�ʰ9q3qtJD4D><1kuq0iAeY61v1sen-1  8pj18^[:ίӇuz'z<OOhx8Rx[[[W1;+6VCN@�x]_ #\@ NM<~=_=>>p��,Ov]Ǹ<+7Y ӇNux:^&SN:p` YYu9g[`ѣF]N.95ʕ)4ppt@}~~@(F Jr]|}? @<41s\q<ו9]QRh5`u8f[5s]q g@L%Z<|?>=<=<<:(N&N3g\:޳x3SS84`z<.O}�~�>x�ѣ8LM^3qn5skeX�N 8+171yWqsY V�Y~|k+aHL) afy]f8γu5484iLZjbbqxx3W<=V�*jq:Oz}~[ 4R&3SWY<g|Ot0+Y[1x^/SN:8Ҙgqz|_'•148=|>� *DL)j&  5w5oӇ@Љ��zy1؎1en.%:0G'':tF:xxN'gz^( N&1yv[\u4q15o87k5[qq1*B `Avb9<Ǖ9YYVJ"N]:^3xίWөN& Ұ|>=u555]gγYquuuq(`��~> �ph)cqryk{]ѣF&J`�B|~|�Kh/gWu^Ģ HYY<Oz}>SzzL8jSS 8fW^<^ç Nӧx|>S=Gh1א�_  �4Q�rI>~C*c+++99qWW\LNz>�>Y"85 :xz<>'z~AOG FW+k;[]ך\uf LQVkqqo[x/O4&�/+]p �V.[gz/V!0ptz}?�>t4`8�,\qsun..4i]4ix� /ayrÃ'Btt|Otph84 `>�>}G>z@�hOOS/W:f1&)Gxx/ã�= с*UOSSqn3\湍u k&f%IY5=}x<<M5*J&-emv ?|@~OjN!r~l�4pÇ98[bH0k++v1漮ו9 0u;5s1aJiѣ� Ǖ؏qGw\Ԉp )M#<Ǖ6f3)+WWWxOW~'x�,Å0�@#1qsg:z|>B �n9uγt�  v;񚘚tI:�#=g/+y\!f7YS@n[gYz=N� 4EnksY3F&Wz38fYũX(4abcqs\5sf2"@R3z=}Otzxx80a q1k+k^B=aD)R25滮s1n3 ) +!ms+v]c[[R:88 Tzu<_u<>JQ@&'x|> Quuz_>�b`EY=N~]cqq((�E}ȎkupN<�~~/ B(ӇOS~/:z<4R+;^cyr*SM8pji:xYx/OS@0piZµq湍5n5ÆupTxqxγu0Â1+]1ne <:7[k9W"9]Aр MMM]]gkx河Y µ&ɣFx'x&Fj@88xtx7kxueY� >OSN.,,M0~<N/W h5`u8f[5w]+z0T\dz3uokBa"$N%X?>z:z<8jT:=OS=>|N&4S'SS<g3u:S 4�@G>|�:)=NO|>�C�J :]N3Wx/thѣJbԈf3\sugӇN0a&..[u987q\LL4-HVVr]uwbԩ�tix|>O}?Gz=�jW.f.&0! =y5s]x3 у8x'>^Wk*]8 aJtN~]9c5x�`�L]^z~'}?�eJ0�8x|>S}}G>�<0pp&'|O>=D)LTY[kxq3|g�8x88>^׵.+f&8 iL\::>���P:N y^cwuqtpi\88:<�>^א+n7 p>Of.&JD\LM]]^3|giѣG%LL8N/Yu<O'S�4+ T:^S|O>@+EJh|/{^cmsW|>z~S|>ӂP0=O<>'(8580z__Y:^'SN4M@ëīw8]qru+ jj+r{^Ϡ>өR�88u8/c898ok1թ,EĈ3Yo\|oYp`ZjUcskq淎qZ10LDSf5o]x8/`Jq11151uuoz3bq10a�+ kx޳z/Yp88x4tL:=O|�~ 6(|k^cs=a.k]+k5t@r ><4fO|NuzoWRR8MN'W'zGh�q97xηiF 8]=_S>�%:phz>~?Ѡ`4<OW}Nu:ią`ç&.k1wcrY8 T-~~zA LN/Yo[gWSR(�aZ38ηx/<�O�+Y5nYgWSãG4+SWu<_Wz=>xZpš8us1w:ަOG х0`�x淎kx[qM<<<aXWx_<Ox pqG>#Gk2 :0h�t'~zz:ztࢤF.,-c{^_}4S"-Zn7q95s\+qpiίYuo7V(Sn;W5滮sV)�рp3:3xgRtaө:γoYu]f.%4QF8:<=Oz}>F0hthx<^uzWSS /�S:o[uef7H8k+qy]{^y�  +�40xgYugqn.&&%�4SR!y^OxFiujOBu:^n8sV@tM3L,SSY^װ~z'SRYLԫR�'uz]eJ4aą`}?|NWR[Y[ <@�h�S}������]��-����?4��?��8lOS'Wu8La VkwǚceZi\8q58/z^/<^'tQ!V-#vqy\7DDԬ+|q4bBzg[x9N YpU9v9, pRf2+kw0ą"3xnpiӧ&u7qnuoԧ )Z.�8��@ =o9|7eG x½\V4 S) `d 6VVbC$b3Mf3|vE1L^eW@PG8>f#HqHG*mw(r63?/D[Et7@7s*2ouFNye"<� HJ)}lSU!q/v a*cF<c>c2bf`5ԴMF125x?E}#:ДK[w탱>?BimS6.gnmu2|Q.(ZAphBaXt@%GIS9f_X+gM7Gpqq4JyM)Q"4t%1/@FlF'IEZyr,Oyw<G޿zQq=er>OC"`@   P, gⵁdF**0FØ aL x[%7GЃqTlBи + e>z.;.V *86, T$LH P0  DNYa r9T() 8S$LqGA!p9a.0I$FD)0$tHK<J$R)ySF8@C UYK66)YH*TD3,3o._sTʴķR* 0nؗUҸc-ݼXXsNg[\Z"׶vN^#pƚÈ-&-9Dk3s/0^}[qN yF\e1Z<xknr%yOKŦꕈThf]G|x_lr6eHVN& 2ʥ;gN-ܽG|#1[q0J5T#+2>v-iΝ.UDZ1Ūj4 =ˍ:RvS,Lb,@r|1uOPEJL鬹4c][0' [ļW;ViU@Zrb\@/I5\9ӌ82)1e�Zn<?|yGv}#ltTtT;M ´18kyokcy^´ lrbXo.t\i8ێZglm{NQ66x;FSzcmp Ua6TrrA1LK28.NNr-e1YF Ɗ(t+kXY鞚X¶(4ԵN)۪unݺûqn-fV80~�^`^e]өqm4N6<%xOyO?(=y+E8 O)ŵn0S<̼ ԼT q� a#^@2`,p)^;koˍXڭtVX �| =Լ@4ܱDe4U i\3<}3=/:W*@Y *;M ᜷..rNtNt[8c J)4M&zcLm宙厘鎓򌎇#RmT1.2.rNrNt.2Y0E"NQ(RiX^W8�X@QGE#@DҔ<OO?G>eXFWq<_S~/<=@rky]y+V5*Sqx|Nx'tph:\L]gox3x^&4`)Ӊ<O}?�)Ӄ|?O~O=@4h:|O=_:Sq4ӉLчZsǘ1{]]pQR!IzN3z\w+al4 Jb2[{`@O>GOO )RZqs\5޹5351)8޷:3xh DVZmmmsur+91q"4}?}~vbQxR<~�]qخf&!&:}?y [V"`ѨpqqS~'Jpa50?>?]+ +�Ctn\f3[qgWxz88N =�-W+ysbS`84u<_<_W:WpbjTj<=_}g^ĩLE8ʱ<k�~�8�4q5u11u9575wYbՅ*@�GzN aY&.25<ט/+^cVAMKed�G:xt84UOx3YjU�!YSY1wǕv"<M4ԃ@t}g<_k8 �c*OAz}Oz/x]LHV:8x:x87Y1؍0bA:g7ar:` J�?Szx^S \[ér+'{'F0DJʵs";+lFq8z=?G?zp��2XVuougS �tpip=GZ!t~� p�<��?{\㕕aJ0OGG�^WȉE�@N8ίWSB-80�N :<@�][ ԫN |GO} z<�h:pż |N/Yzu]\\X4 q ӧWcw]uwt )SS+;]G'th8" լck1red�b9y1ykn3: S??G�@ħph�773��4h/1yW1Jp`\;8f'N Yj8<_==0&::v�^W<ט;cu14Q88<<_Sz'S|gSitk1skv?A= q �ptttxxNWSĦJN qskq<!md,0`w[57[jz' S?� jT0MK]c9kq3SN& @-u9c5o^/t^Wq;VqiRbc*xxγ޹jՃRVĬ?B}?O'O%X$~Sz'z&JL3h<:u87uwV�`V@kk?G|OOĩV0:tjqg\z3[n1qjԀ0aqzί83q\\ZZtө^/W|g5*ᦥ3�éx75s-XR8p=}?G:��S ^/W'tp`< C >x: jiq</g��x| 3 Jun2�1'SW8n[q`E3GN8sטwW+~�!U>~38LMLX[\K_'?G~GhÂ1jk+x[q7\nY W-f3kusku^.&&M8 /S :Vu8NxxYN �px>?GYVNz^Way^kr#qqjDL818(q88/'|_S�Vkqsk񘚜]:thNWfuqu�G}?��h:pbqx8淎kx2[ūX4cu9xY °�q11*1s5sqV�p~O::4p袥YE:f9c]טy]V�8p��}/`�vz !X�;=k87qLM:(N8pj:75oSLDXY Ȏkw^9c1jD8>O|}))WW񞧉x~/ *D8S� t涳\ogWFG bjq:z~}?tJ(�tZ3|=�S�Q]8tt�^csբštpx|z>?�vn-�8z��Fy{{w³ASWg{^_>z=q)!J[;1wqsk11(:x8�իN�'Y83YuN'J`%iYW[u3<OO|?@,ħ \MN'O~ [YF< tç>=+;bti8�)Oz>��;VSNx_S]8/YNn9gt"h4�WSO~?@"9bQ�HR+5susk8^&_:pR.3Ys\kq7[StYYY;\xZJiq10jqzg3Χ84$Eq]g[3z<:xt (ë3w5w[R04iӫuf[x滭us jլ. ؏q9YVjji=O~}�}@ȉE)NO@�q湌bjѠ�n[gz==pEĈcqsq7[uu14ӇLCsY5uf7Yĩ./S=OGABpSSz}?}�}/qef&x�D>gv#;kfSNh‘^x>>G8=G>y\u\]M:84ts5s\nphҰ Џvr) !L16wxγ:^FSpq:OW}_:|V4p�V.3[o[zgWxx88�' 8/Wtt<�t\n7s1ux:tѥ(O�]1W.W"6ģ�0p+]ye\]MM8V<Ygq^/h)@•15xx}>~'J(�t.&.x/|>:p,qo[:O:pa:z�>?��}GY !Jb.^&_/|OGO@haX]~=ѣS7qs\5s\1R1As\W+ycmeLJiMMN.[qxηx^FVquι^3:_S��_8\N3SçZmscǵGrt L8gxs\u7S 84h[ks[z_]f4SxrGk;1n3WM}_S}_OOF  O}Op4iӆhçcmr+v][�V+ y!v#aX BjթqoYu7ΧS@pms]598.<@��Y b775n3`çNYz^oW8/Q�+ <N/Y:޷5ʵjU�)N8xz=>'>' a j8޳<g>'px �уGG}k^cr�ef7s:&Sѧ@<8ugWY9ob�jqr1;q ,MM\\f75s5lVȋL&(v {^Wqrʵ)x8\ ]qs]\gSF8 az�{+w\ŬZgW=OS=?G~ȉN+M84� /+sbS�#�-Vxx3iY +1ggzO8R \+w17Yр�Fpttx~��+k1`8 թWYu3q:z<<tBs\cz[]^'@CAJ^/|OWO�.&-Yu^W<>"`BL:z|r\xηҔ4t&N?]r\MLPѧ}Ǖ<k6V0G"jqx_zz}>)q5zί|_Wxtqk"9Dv#c#rԚir>~OzOaĦ:�,9\Wk<،VbզG~CAhq33z|0`&(�_{]qYZ:pç|NxtiVp=^:9u*5`SWmn9]1w)ԭ.Dvy>LEgj\}OzxMM\\I XqMLedG>թ1 `Āpk;^>qufȗV&,<M\es}_WNRa2'>щYM8skqscqmmd)4T)V땰�ӉRS8q?OGz|Oxo[u[1 ԫV'87kuحq< 0 +++ay^א>hp8q58gqoYa0`@@?zfDi]_v4DJ<.ϣ{^~Gv+`ER.&z/uz/[p4OGG~=,�8Jnqszίu::8 Ѥ Ak83OS�N8xqx^>#jFS �s19q7[uxN' & Gu;koӃFÀtL^.|> 0iLZiÉpu=O|>OxdD�<OS}O}\4•qk5^3Yu^3éѧIpx='z~GpBié/WxzO 0x}?>~< *AJ0px=-1\5^3SZ(R=?S�^Bڱ +wcn3[jji1�\Ju583}Oz=Dh>��/'wk湭jt+}O}? ΍"$�gk1jLYq+gOSq湮c�LӢp uurWB}�=<4ԆN\~~ÉHV��I kmf-\0q1548N<g'8M:0iG|O'SӢ&1*U{^WFpTyw\1ef3N%LeHVku53YӦF&.[8[:o[qSS3s]5snWS pjk5ιz[MJpE\L]]MN/|Ox4`<+<-y;^cvqN,:VqfzN' � F'=gWuoYN%0�tԩV _+>y :tӉxg[f.Zq)@�� +#gSS~'x<� SN:N'x}>t0&j+qk]+ȏ1edMHxjc+k /+}� zz:8t%4ԢHWcq57[U0`ÆLM]\]]f/xgq5(Æ"@Vf9W1;1y\*ԩ�)N'W}?O�+R@WSO~kw\ŬӀ0h�J^W|OGmf%:4:@� Gu;n[N�<�~ Wyv[pBiN/ίYx^N8a5 �tttx|>|^W�*b@�^YnVVNaӧYc1w]1Ȯv[р��pppzgqx^3"�ǧ<g8\meeed8!3M\nDv]+^W.`ѦV�\G��^@.@�4q:WuxgWW51q1(N 8:z:>/=O> Nx<>O?x:z4i`4 N&&7o+uwǕ @"Uk"9Dv#^k\1jԢ)YugYuzM=:4hj*լ1w5q6YV0Jgug:qxL8thѥ*bbjjq_z'tpp )Y[[\Ǖcq ��:4Ӈ3scqaZ M5*U1vו<[�Ґ K1jbzgt<<=�t"hjx_W~� jԣ44. sg( Ҹttx^ו13S8SG@^ctOʔ BJ5g}OOf%ҘtUuzO>} b3�JN �j3=_z|PiYG�.Osqη<OL*`�W59k8gY<ED-f.|g<>GAxW :'S}?Q�B�c9qγu|OM bix<?v+q04MN'S|Oz~A=�B&z kjw]5sq0P�h:z/N Jc5湮c滍, k>>> @ .k59 �<4R2\u;uwuf-4`•*j_iq:|_S>O)p:WN?Ay]qt&\Eyc9uxth�}>Oz~tq8x_xzjib%X8GGϋu}^MԀuDr<1l"+1skw9ʓJ080\^7x9n$Eħ@�{ .qmJhb�|=>}A�?@=-q0:@-u9jgWz^piml.WkGq9ũ85:x:/<_x<N :q!ZGx7xηq*°QSV#b<Ǖy^B.Ox�+Vґ.x7g[Ӄ @Mxx:<>}?@� P�9x7xN� N��}{??�?G0DJkqwב@>q!H'l/^Wyv^EvYViR�cw]1]]:0#0Ӊ|gS}_Sz84 +tSF&.88skuʵ*TgKcay;^cZaJ!4çzY}gxN)YYYYY\1w1w[Yquq)=?G}� b$CGS>'z>>@x04p𲶶?n[`8�\nuoz' ( ѣz ;c8uu80�NN=O}y1f&hE~v5n7W �N<}k^csYFEӀn2sqgYq8tpphOWN35:湬1q`�`cq滮;2RH c+1f[3<)E)&_+mn7taH>'>^OJ4p|k]ef.�88:[cuozޯ8z|�t�"j<>qn7SIЅ4iJ=Ǹ;q4(4b8O}>|�f&H:<G>@]� D]M]gx<_W�b8tit<>}G| QN&:8::=>O|?G E_4ptG��?|��h�}/kWy3 @>OO 0:0qu9cusuȎDr�84L):O<g8.Za55)q8NWxSh0D\Jd-^אױ@]$MJ|z>OOtĦh�tos\5;驋/+>>}OpaXGGS}_qgWW @Rq|o/W|Okw]unU!Z`z<>O=>SN4bbU1^W{�/`piN!\X[WqWq[\j@Lzx8zg<^8\]Zqf1*Vcky] Gl-4TŃWR�O~>D&*լȎc7Y5]MLt83Yu9bxʺp ;?K=kYbAx4��.BǚDn6LJpӇSF<=gO|OzOO( u5|oYqsucqbT:s[k9W"9] ,(*8:<>'x<^S!LL]N.g[qxupa@ ūk83Yzޯ�DӀimmmmn7\8uu8N<pjN/Yqox]\\X�aYY\B/k {],�hѧO5oqf.7L&(OG>OGF0h0ip|N/Wz^/jjaYjjqux޷Y:sbc1`5*v /`/a{ ^k`� µ*SN&'SW<_SpE4ix|G?Zpi:tn3nugѧF� :58^S8/q\M\Jj�BBiӃGGOS>'Sx<=8�O=?>'}^)N&3kw^W!v<+0!0{^@^G>L4qf3q9n[u)ÀhZqʸ7z/['S�M�'z~'<q0�FG^cr5p|=A�Dzγ3=OSѥ �=�#<qw194iDSSYqoWW8޳uu0!IpgqosY[ &4:gsus^c`� @�:pb+5v]Ǖ{]�@É Հ}>Լq^׵{^W0baīV��.@^g/k^> 4 |ougYq^3Ŧ HYY='>xxxpqx38sl,pBRjjbc`�A>=OX0ë:]>/u8*Ճ)4bjbc;^D{_1F&-Ŭgqf3°`St<O|gWk1 .5!d1~:NWS 0V ?�>ӇR>=>=_z\BA������]��-����?4��l��HH�qxn9/kԅe5jAN9usYY`*Q��>=_S^WVIk �@>x<=p@YYtR7\1svǘrA MIx^8+qN`q*VavA?'<F )"c19k]UP�u8^zz~'��hr}/`/k^B#jSL<<||}/'\N �Y8gu|_��4iz>וyW1?�XP� 56^ AWN*$rvJ/Y6~lm[ܦ7͡l;Y|o" (>35+2bAzZ@Y?6WC 5J/Z ekkfv\uOMtC:2#o L%84qJ1G1%A;)|错Z$e&e%!O4!`BZOH;Qgd7Y?6h}C 3C3v:+3L;{Yu #Ð.aPdh3!*'j̿v`[tQJmUY)a+EJSaHE,8p|>'e]_<6ywA꽗/sM8{\EMCLS($1J(GD"idwBGVVNY?hMbX\'Bx8% @qXt* @ I <?CnKz_;ftPȀ   � &2?E&2<4 20ZulD) �' 6$a "<^µ:@D b8� H�PPU%s b(;tUB4!H4�t� ̼:1( j<H~]5vlڠ4t@jz/>|+L8f%UJ;?) 鎙ZZo,2  k'i)'2�>>>K~ - ƊiY:qoN6mMi2MeȪSIZO ^^Q콟�X0)Zj\OWOE|@9�TVUX]+¼+ʽ+<#x>ÑW%`'ў = uNmƜZ\8,O!<� unmƜi垘]gt*hӂr 't04ZV2)pAy'~p ڶ:g9c{gJ;N%c�PKi8ˌӍ5=GC`Y<QZ^ᝳ{ommYg-m^iQ,2�=�||}|� bZ]A<ĽKսKĻs.%Q(a8<(<J(HG6�'GxCGC>G쾇C|= 4M)ʶ/_W\�Npa?A�~�>>ȵ� i<e Hh?e/#<�� {>t>#}?'.�&EiG(GzG{O|>6<�#<#yG2Tr "U,@�#G{GzOt|:L S>~ K GEhp6DmU3Ni }y/rZ,'IRmN6ntnvn7-GaAaadr KGh=4%4Ç:4<=_|>'x:th450ӧz>|G8  >~>zhѠp<=N'Y:g8g11*bT5qjVvǚ19)R6<+lFVU(V&Yxz|:h:V\us1wuoWW �]N/z'z<��0� k*kg<O=8:S/+83p: +S>>g9V&&((>^kqn3R q58^u<OWz Fηzγu:_W::z�рħWuy\+amd4RtOGOSgYkuʰjVCtլx|=NWfY[`|O<N&DLLM]N73ugWx<:tS!YY k9]ksn6)LDVefkqnuLMaEL515uquoqoqSԫ r"1wck+1)Tũq+1o8MN&4jbjxnWJjbTą"s[q9qq10 L\ff7[u;zojS(q1u558'xO<&xHV8gγx^&�n7qsusjji4hSM]of[3YRJ`)80k+xu滏1;\1-f3w]1ح5 *Up}? gу*bq8NW=_S<h'z:<aiWW/<OSx�щ j+/g>=)RZqw]1_w1w\Vb•*@V....kx3:&AҴ4é<>>�^BeZp'FF@? ^c2YF8 !skq98[jjq84()}?O>r&t�G@{5wufN :SҸq1xzO�V(Stxz}?DJh µqq3z<^�x8V-]g8_x'GO0pWu38z|Np(Ѡtf3[q\8ަà!�]]^/Y=_�W `[k+8޷_Ntj 0pmf7kz޳<_8P4 .v1mnӠ!рh򽀽";\V110pieZgux/<OSx-Jjhr}Ȏט]s[Ӊ <Ozq4N :zSzӀx4pp<>xx|MM:i(c;rkl�iÉq98n\n3 '@==>/<g/[TV&&&MNW:'OtiĈpt�hN +|_?FbOQ588|{ \qq8x:|OG4aĩYVtG|?|>OӣN40ŭ^W+|g=FLTV#vǚ]uʵ*S "SfYޯYq8MtQӃONWzίS5 NN7s[U:\o\qyykĦa(V=O|gf34hуS8787)ithS?|?т u55n9cqwclQѠ`Éusy1�hĝ-#^?�}@GF 0jj58g[g0SR`:}O<Ou8x:(Ā詋Rky^׵��4ӆ"DX1ǘ1y[9[V`3޷5n-f3�0T�~'=>'q4ą`Æ;^cǕvvp  1=ו88(\n65;^cGl )5`<>/Yf[T JN3ǐ=?3YYq l:_3ZF .&%X\׵_g} >Ԧi\f7nkskuqq5q0))|O>>1Jtj:V&S=?S�GC 4tus[q7uxtpp�\+yy^Wr\Z0�)=~�/+#qhy]k ؏k؏+- JvW|OM8°d-w1wqsk11(<<G~� jզgenޮq8xN+GAe]oW=?�&!88�Fcuf<Ox=Àp!ѣ�Y\Drc1fM4aAN:^zγuxi 84h:8:x>O?ѣF�0px8z_/Su8N&&DO'\n71؎Wkal JL<xs[cyו)p"9ȏu<+ `�襒)q7qsuq)"9^cksRp+55:N>'>=�L'� ):/|?=4`�ë>3}>'D#N� !_n7k\o]N�)F���/kȏ+;DJ(� :8�յ9cxjj(R&'}O|Oz=*5u5xg/W *1r#="++`,@MJef6bjԩRqqqn3gxx<:8SS>y ià4vqeoF88 +w]ku7W � <OW|Nq:ią`ã&.1s1wcmraIx>hӇ )j8q[5gS&h>'/1108 �p/u|gu*ԅ'AJ*5fuγu4х(~}G_q<+bӀLAe\\fYxSz}�Nji}>ʕ0hp) ]s5n� ã@sc7q]^E:1N<=~�ρ�A4}?�/+J"1ti�/a{^y]++q*iÂ@>O|>G00iuo湮kw[<8ã:η57[uʰhjTgYu7YU )*azW<OSGGG�"k!mr{^׵v`.N�:8té83Ykuι1X!X\�{��:4 e;\8f7j@ уO:383[bՅj 4cqs9 �phSY8湮cw]W1ȍ!X0ᩋEskk^k9\VU ॑4jjjk:383q2%Jh@[��'y;^csqZq4Qjo\\^.:ZhN<=_=}sN "[83/OLH�qg:NzO\8#@,|N~"+ � 1s\ug844ӧ|??�rS&jq:xz|>��W =#;c\8f & 8: qms5xtѣ<N&8γ8ηĦ (88�}O}?t�8�уFSuugugu1q1*BhÉ1v^ky^Wkv!mdLӆ�84jjk5וy]t1 ĩ�xn1r]D)jLIz7\ו�>&ĭ?G|>'bU )5`:/Yqx7YUpT Jiŭ~ԃMI!ī�xz]f%eXDjNuo[qmw^y^W'tS M8jU^c^A_+��-N%Jkmn5nk3"ShZ\]g[x<^�&@,YqgY<^::zxA+R&/x|Oj`4xSO��\q湮cx]M^ CLUӉ'~/tq:4"@�<|S|=<:(ӂtaugugugÆGrkqv\n-Zh pptx<��K.VBA4p�?a|�yYYVFoo}_S::4hӣptt<>/x=]:Q tz^:/WxtуFGA>}Otч:0ixgoY8]LJb$)8M<]o[k5淎c+q& чs#ӆ�aą`>g5u:gF.2`�~OÆZv/?G<'NLH8pDIG=_z[uuqjT`2+uO|=NJ`C`8s[\av8r 4Auv `TհjUAz/Y89a{ORՃWW>:fu5`a-Y[\8nq1n+vk~өRjVd�MLnB�~BzVe0Yk1;+Y0HjխxγulG' VJç`<Ok1k & /+�<N,jVS gufq؎5*vA?�~@ѦAO|?WqxiL) a+5]gxγq5484iLZi4|_}Ot:4J0µ1ux^W<>'@e4R]N'}_Oz}?!SG@ G995x0()F��gs'x<i8S�LN>]qM<= µ0a>�..%848R�&S|?O�v@# tu=?KwW <n8gqzίWppz4�[[\Wk+7ħS�Bpix'xu}^ԩ ՚88x8z'S8gRʱ<k@ N3xoYqo1*Ei��?^O~\a4Ҙth�_+y rE)YtK�V%LL8N/Yu<O'SѧhNz}/`Y AL8湍x38^ �#F54q::<V覚s]uw\usU �pSYkxw\n2"µ`0hbqx7ιxf7)Tū[[�z>?othрiӃSf3s]f5pDDfYx޹F�L\t|>'z=?z=0=O~'<ON :tqzίx3baĦ~}zh�<��z8:4`N&.7\qv+`4NVt:/:^ūV4cxkqn7��q15xn3湮kحŬŅ*AF&Wn[5qLSN>?=F80 z=^<^W񚚘°iÉVW#ȏkz]0RZ1wq\ed+Ja5*S8޳g[:4�uxgzY<_:x:x4hiÉp|_}OtiX(ѣz>>:��8:8|Ou|gux*Ԧ8tiu:>'}OOON  °&&&W񞞟'aJ^/|OW~':TqO�wcW :8R�.OSOV540<+OO>�{^W16.)AMzOK+x<PF i <57ph+@&a~sx^G�ũuxAZhA0ʵ53}?}�FbѤꞓ@65o8z�a8”z\w\8opjx\滮q3Yu_Tс)&S~~߀pJt:/Oz<)E��x3z^�f3Y8>}lE `P<k1oW=OOrJ1IQq4|O{^\:3<)8Bto[uz}>@zWȎc3q^3@)YU78oW4h)F DL]gx/<>�"Q)8γuxN/SӃ 51*n6+\VDn,)I<NWη3YDАbA�}O3qmedRiī8tqx95yk/+taH[jU>OM0��?~G>Gx847[kqryVVN 8bbNp:3k<ו9rbխ"<+@pa5s[e\n.-1A@\kǸ<qyN">~}𽀻;jSBn9\u;usq0�N 8> yc+*bQAL8Z]g<=_Oѣ@DGS~O| SE П}G:4�iÇSt/=O8:8�&8|�@8q8N'=gp88x84Ґ4x/|_~=>' @,Yc5g5x^/Wj&  � �>k`+ Aµk1skw7Yu=Oz}>|Rp1LL\\gx7f MMLz|GG�>ڱՃeZf38޷qxu8ppaJp=��/+n3 )XSG�`/+\ck5qtѧ=G~o<>O 8hB/'>pphÆRZwךWqw8f..&EL5qq15uux/[oWpGXV7u7qt8N�en2qg[zgA,?1n3Nӧ>b<1]:48 !fOWOjF <NO?AvbQ@k"2+湌5npK!xOxΧq8F 0p'S|=?>O U��\ yk.W ”hr|ΧWu:_:<84 �t�RZeousf8\ щxo8f  aZoqoY:<=NiL5NG�?~FN 4jqsk8޷58x084Ejq:/}?>@&p8z}_S>Bz~OЋ8::=?>�Ѡ?=NbTbVcr+{��GGG at<quncw]q*U0&sqskʰjskǕ]+l-XV*Uc5gx޳u^/SFEx>?>�/+`,Ep:z3/x|OjGGG<O|_u^ 1`8u87Y]b<@F `Ux<><N'ū ]qի & `/}OxfYY ah4SS5s\1-:pBLL\f6+9]Ǹ.q� 1!dF+�^W{ G@ qq7\mn9cun7cu(eZn7\57qojxp uz޳ xgxγSWMLJX8�/ @O~zF (84bju4qgYgqququ)īLG/Y3x]LZaJipq<g|g<NѧFV淎9x7:N<4P15555uux3q989c5jTN�t~?^ϣҰ ��@=}?z~F0un[sũRp�.��{ v\q(4pu=_Wz=>'@0TU~tiӆl��~z<>"VVW1;c<W5qq)À=?Gy\VSS"iOG|OG| 0p[\mw]q^4d-1qou^24M::]<qn6YN'*jp}?>�?Z8) 1ou/S84Dc753Yjjp`)BtJ0h~?@�~Zttwcsԧ@0<�.^ay^cr+Ejxί=_G� �F�^uz޳z~r@8� x/Wx0p .&S|>Av+u8tSvk5x ˜4Gvu;kg!ѥ0tO~^VVàx|S|=>OL&&..7k<ו򼀻[:5%8fWq;^W<<4Ai\mr~bU^׷О>/u5ʰ`RѩטttՅ`S8nk7qaчN9u&z�^σ?}�R:Sz|?K{^Ws+5iq58Nu=>~hG(iq<_GF0J)X b:^'}>z=p~'AҰQ4�OS>OA{[f&&��]~kk1utL@hef|g?,ӣӇ=?G�-i(ӣG?=pGOGOz'éR 80b5湍+;㰹��SLU:uux7[k9\+�ө4 adk:]ו1 Yal55wc~>&LLZw+ȏk|`=tVC'|Nhөu/3ΧtM4ji8ηq^.qxtL�<ߧG|=4` :tk<oW<sxoqeK,�8z^9cq`�ᬚr`><NN3Ć  X<&ky!{ՀTf%dӫ������] ��.����?4����X[ay zzpՂl0u8^/[nckkkk&�xjk1k9v"=>�::0`Jn6Wu;9d+VJbjjjqu:_/OSAZbk1gzYu_ttp�R"S/x<>�ѣ��{?Gz\�@'>''Ӈ5k1vǘ+�>EHh'Ap3ηq�4jS +@E4S Uū:351)cx98g[qoSWMa*ԫqumn3V&&.JBtO?~amf%R”韀, (��{�QPUJTģ'q4icH5 38C),n  068ljS¬oҽd}pX)q  ޭٍhm�l *PI !�C ? 9R/蟔C]GJLMTMKIuEd5@o _%dp $%F3OT}M^ Y#3=?\<IŜ_[Ĝ3;olMIs;wE{Hqz"5$)(RAx >zeO 1O*rJZ 1cdruKffE a CCEyDUVf:>C_^|W<ȼM=.Ps�\;<|&8 `d6  cA\uBQIW6c8i I[]j2V$,J*R"0j'A @n5GrWn8w0u l8 Kq7}Z<oSf1hH 8A HxhhpQ &X/akf'[٫gY.* *|=/s_ӃD  "Xi�� 䂘 9�AC HZ�Zhj>#杏�q @1:G F\ Gh)rͳ6%?bҴ{--dYڻOz?smXNB90 ʶ�/TYoLQvIm$@p;&Ι厓=wMe'C� xc,pJ)?smazN@,reG^m.n6 Sc�4tq.%͹ˬˍUxG#l#Yo 8yq\W 8+$2[.�T)wV.t9N2Z,g9OJ~/<3c8W rxENj:gmr Mt,յi:FF��(G<zG>Q_}>C�N!@ 2-Ӽ:Xsm[6M#(趞ʺWKc/;W*;FeHepvG{??|G>m;*,"фeZN׵Z垚፯ix;+A�t|>>=�d ap\#rҼ/}1I­4TKEgeyVZzk-Μ]m\qb�<>=ev GJceHK2ܹ�}=-V^ykNtNNtN6MaV<aLז2c}{O]!� Pb;F9Oh~Se__#йfElQr^ |мK<[<aƙҴB vVp>U(/�'gCfz �MXq.9kc;Wi.ˠQ%a:A=��Լ<̻t[t*ggE`{=';OWy_yOxOhe�BdQa9Vj%4D #F `ӧYc1w]1Ȯv[тxMgx޷qeX'�WYk<bǕ�:h^q95cڳY�8)!Zr}�'{^@^@l) ?gWoWYjSSx4ptS|>O|>i4Ѡptx|>u=OW|_`@k91kw$MXYA=Ox/ST1p:/Yugլŀ L3`ëk1scv>gÂROg8;qY A T:3z'W|OSSmmn<cy+wc��ӧ&.[kqs]sŀ�hhĩV+;^WvvY !H`ѥ8f3gY8^�Q):>'}?+*M8Rt&}OG't:8zK<Ǖcsef-1  8pbqxgu^' +YWWz'== HV87\gxMt`ҵgAd,Wqr]cks[ũNpӇu:3pѧEb$k �=/`=Z11uxgW<gWpp )FR=�]qw]5o4ngY=O>. ��)OS~[YF!G>{ yq8h\8z~'~?@[W&z޷zOK!48γ=_G:<('ZN'~}.Ӡ(Yk13z'S(A}_>q00�'|{^crk1xNF8 1u^=@YO�_@iM]oYzW}O^V´ЃI]f'}!f-`L\3[8<<p;5o::<�@ : ax<G6W:W ` 8=u/= Ўg'Q4t�[Yqoz�WOHC@UptzG<un+F' 0b;[qgxpo[:ίWu<OOGGB& �k8γz>~ȫGpV"i|?yw\xtIx^Wq;n:<N: Qaxgxgu::(ѠpiZ[;9k7\Ç�&y^W#]Dmr8(� WksQE:th=_W|OS<_SNiN'5n[cud 8q*:^n Հ`Nk5湎9;p0Օ1qGxz|N<fa$,>S<Oq8LDtp8޷unvx8)SMMf湎kv\"9`pӆ.3Ys]+ksVVTDө87k8*T .WGqs*`Dz/qz^W+N 1th?G{kG+k*@]wc`L8t|N:NSNM4Pth8:<>S_z|N NF ,88�?zMHMLMf3kwcv .@:0at k89u湍A.-N/Y[1vg>R`>O/ηVh  x>/qn&"LUrk�}Gç +(F]mev?�; t `xr^W}/}:8MJ`)Oz3gquxJ 021滯1wcrV�44DHV!r~� {_ @G�ÂNl7Y\qw]1nŦԅ +YY湮n8&&pÉWSWqougu5tM1Z;n3V.&.JSFOz?G-ţ�t=__p�eZs:t}OB �f7u/=)N(Kqs+[bbbiғt#=b<+v\"3 àn7[8޳p88 4\7x^3u|N: B k"usuf4h`O>>�š4txγzO@' +S|O>�l+ +WSz=G qq0�@|?G>>z\�1110ju:/<OG�4$COzz=>|JQWOO~�^cȍ@#jpp=?G \1M< CF :z<N|O|>:@чR2>O~O}OOF8pŭ^W+|`=:4hb.-eedG+ky1;]uZ))qx޳W|oN/@0"Qѧ|Oz|z<i<�@!|�.BjbjajίW<_u|NV|?~~x��8z_|_4t1u8N'z|p`JbbizO|OVp,0'S|_Yu|^ĩWp~}� �4iq:N3ugYu~�!q5xLJjAN3Vwqr#A° x8z/Sx7ugYu\J1)F z<>O|<��R:xxz|>Sx=O>SѠ)ÇN==O>'=> @ ǵ;^cn7L!у~?�Kviu8^S|^t:4M5`<O'zLLL0h�ëqskv]���oxn9yz:t$ Ӌ ?O|=_[<gVkaLu11lWk|ELR==>|OgRH�4@4a5v ^u80RgGKz>Gp" 14qι8qWOFB9\5湮cu\f&: FN/|gxΧW^]8!Zh>}Op )өOxgWuzgWu8t<@>}}/avx<�h jBog:Χ�sxuoq]N:0hbjjjjgz8UR:4p|?A?O4 ���=.ד@JSAJ0|Gv#b-L84!]M:N>�YV"i“14p}O|>}>ҴӧG@r]ǘ W+FkqihiÇO8M8pP)Zʸ^/W<_Gt�M Bnsqo[4iуK*΂Y\v\sYYW' 8upi:_Yu|_u8thӢ1��x<>=Nӂ3&.,z>~}>'ÂÉR��~|>SpjQD�ef6s^kmeZڕ)�Ҙt:z}OOӿ�>zg Bɣ<^W3:ޯSRE�<8N^zgoc1q`(Ā'7znf6�bARS `xz|N8ηY:!K8716�(jHY<<=^9Wq.Ύ0Շ+{?~}^]\X))'q;y+&hÆwk{y TRtOY7n"6�8 L5*cq1WWk= �bT8x^3Ycr^B�=:eDYVt|^g5ml a0@>qfVh`HLZȮט#/u:MJӢ&.[qs]5滮؍801aemlG^װ~�>|>�aYGz8]h8p8_|^Yq:^M:p �|?O??>OOFF8]^.Y87Y5q*b�<M:ηx95skupLLIN-Z? |z}=<:`5.H[:|<N/uo+1&SS[k;טkx<4ic5w=ѧ JFMM]o8湎cmeeZ�bjjbjgsqsk*ԩS�t`~?^ϣ 4t�<|G|>0hÆfc57[8fJa<��/+^kd,EL4QOOS|O<�ч2>O~O}OGF8i0ᩌAy�x|0E1ScwcY\ħ�W~@򶹌 DN=OOGt4у\x95sjaӣGNuzg:/qgDjCY4q5us~�!]ky^> Y)SWYky_ ~'ĩRIN,ө1v��|JS <]]ncy^W`ө Z/ 0t:g59]Ǒ`taL)z:OOz7ued, ZpxmmwW| ԩ88fqvyz8pBfk8湮;]Ǖ0jqq83srXX4b1w]1YY��if391w1aX�>OthEJNuus\uו]Ob,,@��rAd+� N}9ks[q3Çz޳x7jQM1�+ǕGGy+meY BJjիӃ115u9x79UũD@0��~�>|>aH8Vf3Ykqsu0`�M4x|>K`(˜0GkyEwun[FIj5ok:o|NOFQF Qӧz�DÀ�*kxgxu:N +�Ѡp/YgYugթLD|_W<_S|=N80 �x}O~8pS 8q1qscy]y�/1�E5.|:OFL:1qxg5fu]fL4!ZY[]qw;Y[Wb)W5ux3u3SÃ"OSt=�GFiWb=Ǖ<kY"@\C?|>ӧ JO~~F)qs[\oWW80";y]wTp �>z<`�� *UlFw5sbaÂu11xη8:/q^.VX !mr y+FHӧOO|?~<-(�!d18zz|}�~;UѠG}?O|zg�t<^juu*իV`a09v"++`,@ML\\f5wqlW1JY8oqg[:VRM>OWy]ǘn. ”u8|OG|GY:88=||k]ef.& Rz<>O~}/k10!S[]+q3S�8K*Tbx^Wx'J4:`|~1n3ӧ@&pu:<>':G�ӃG�}~~���/Yx==N :0)M8u8_~/SzOIA8 *cqo:^/<��?�GvaH8Vf3Yqsq0`8p8OSOx=> N"T:78gSS}O�GmXAKjxη<gS_HÇO}ky jbPpP4[;9k7\ç�&.B GyVbզ4tx.s*T4� sxuo<^'G�'@}='G'dZ/+ǵGrt @F8gkx9u7R@W~��t�4_:/Wą.bbk1w]qr6T*VJ'phjjxǚ+xzt³ jN7x7Wk�<p Y"-el^@??A<.�ѣW8s]s]cq%M8u8xguzO::4MLeH[[\K{kGarŅ &t@_SM�bU`@>OEū7\\*q+1(`ҰgW=OOOGSp�N�\1s]s[uuxta0':<N8N'&&%Jʳ</'_'�tth4ppu<O://Ra4jpx|Nx'tp:Z7Y89g5xM80hЉө<O}>�DN =G~>z4���� @ x)=N'S}O|OS4HN/|_Y<> �1iz/'JԈ0� k:3}gOjTé~'|G[Y(@)SWu|g<NОF�?�#v<1 k*@˜0�kv#wbja´`f3:/x|>O'F:8:=^.vc+qq)у@�rx=|?x}OOG810QN\x783R(S5;87\+jXY :quqqs[19kjbUĩ @k"usk8hѠ"Q?|~�ht^?�VNѠVcu3zSx<�xSG\]N/O>/5MM<�4t@�~k9l,L./Yu^3gF@Ry]+vY ʰN Q}~^BY0с~� h�]@@u51jky > JjTo7"7�M�u1 ~q5*TEM]\\\ncs]W5� SN\\\n\+;5s]qp уNn9Vb;B jAR \JtgYuɢV4:<N/Yr> ԓZ8湎ו{zR Hj@GOqs\qy\Y8-\mv~'=_իR `L,|^f[V]jH[3oc1pURx<<gr:8jTb@usq9kbYdщ Hqq:γx7adD)tY�'3ugYqZM5r~�|/gæJqk1[w]s0h)S<=OOGz=118_z=OO8GxzWrFp\4px=�+en. �@ `}kug:==�p�F^8u^t0pãOO|?~|"�4t.WzS~mf-Mppt\us\޷8xx:x8iZ8/=?~@=h88SA~^kn3tҘ8zz|AKVUӤ�4>~un7[F�~'z>}ڴґWoY^:/aJpN}O>.UÀh�-LMg[qgYugWt4h8:SN=OS>Oxz:� T<<==OG|�:5� +1oougWttt!@ 8|O~~ �AJ":th}OO>'tiVhés[csY80ѧz[87[1`1 h[";]�zz80Ph4g\W1;Vc1piëu3xx<Mt *ՅG ad�pb,<=<<<OzoW15q*TxO_O|=<�j΂Y\v\wVM4u}gS|gѣN0DH @<�}}?@OVjT^/O4N<qw\gOSZ0VK1qg>'40�SNO>}�]i@+ef7s:.çN818iqf7\u\8n 4j8^'x/zzt<=�~~>,0 )B&wckq0pJS=?�Gb6tp"q4|>Ki�v1wfN"pgx޳zgÇрtFuw^k\u\]\L:ph`ϧG>vz "%4`Ѡh�@��88�??Gzр` :tx^3z3WYMi?_a}k>g' DNS~'x::8tXRMM^=gz'x:Bί:zJ@E��YVg[qzΧFPp?+vck5(x8@ӣ?�n-0q<1v awă 32>@OSӉV*LLMo1wוy�?�ÉY*c5vGo5:xu) t`өxg8;qV`x=>G<�pp`:c5nyvLӋJ |?>'qxj@Ytŭ^�80 JY9s[`)]fYoqgjbS(,VDer+scef3 80::}|"<ט6()�+<OS}�'GG\fqu8O`x/O|?@Eh&} \pуt<>Bz=|>ѣ�4ppq:x}oxγM�`Ԭא~''Yj faV!{^ϠtB &LM\nc<k<kF aHVz}>O84ba馥Lf[1s]1w[jՀ|/x::4hJ88:'/uz������] ��C����?4����h-zM08q58N|O|<N@:�+usoqoWӃ"q:zx|�|?O~O=@4h>'x_Wu5tӉ NL]f31s1wcrY8L):[]?GO )j8qku3%H Ah>'x<_bja q*@�<^/YjbԫR�8 իW1u]f&&:0�z ;^kG"f.& )4 �c5oYNSp=TA!:: kb59qz<NҔA@:[\ks1s[񚘚tI:�}{^W#en�& 1f[:γ:zz8 4w\usk5�8��@ ܰLcW#ڔa@j<>\͊N qcEmή]gRgNxs'\h~mvr(`ffm(z#(s ё$R8HTZ\UXIOԙBS1qQ ..H P'FyOv1bZ`K|ܙ]<6.[`;^v5D`eRX/SrRR],u \k}k,vԂxKh ƁT@xL]ƑEU6e9SΟ׻wzO :3Bזr6,WZғ|@$1>"!Hh<#IAd^Selݡ4Si0VU:Gp �0b)"QH~Oay규A/'6߀~8gխi9Ajh �@P@IЀP`pat:YNaYFn qA8ۦcjXl�,@`l`d Pt,�4 $05xg/Ug �؂ h:�i6(y%8g@a@p-JC �!9U``4jK$"Eu ZQJtK3C2fM4�d8Rt <e.rޚ]�!Ue\'v'�w.-/h<FQzG_?azw.-Zi:-�JB+(:G>>)[Xg*hv'`:4d<A]@̼K<SsN2Xpy,Gl6;.Ov~=¼/rd6,San2mջͼS;w.ķ (Q[6:y?{OtUa2NK 8P/0e?#~}'hʰe0ϔ p֜i]պxwpx9L#%H hƚӜëwo4/1Z;TGHG ڶΚۍ9Ӽ:v.]x%Gi0MSRӸ'yz{�:G#)4]peųL2rV9� v/+_,rlv^UhztVzFKm9g<1^�=c tA3r5m-tlv G{MbX�5:GH|�}<ŹÝ7;>dYpGv\|W)!#Ȱ;�[6չˌӍ7Ηe=#`rz-¶|15\3<1;^W@ty,S3rnrNNr07l*]1-St[xyͻwNmŵ, 8GWj~펙4J,y= #xOzWG~\dSDʴc{W=+|'}%hy xA>G>>Cx�6@@v G<Ҽʼ_k;FQ`bٷ8uniݻzxr�\@�+8y>Syczg~�!-ѓR`�cE5aV1<`/g<^X emr^W/!y]Y8pgMf2'}?GSx' 8t8γY:޷cun2@p@![ >x/x.X%K#^W}S=<i)V<_WgYb*bbjq_<g|_:z:880hjլDvk׵��G8u589cuscqn,+VDHV/+�{�^A�h<9se;qs9땸V�:0ixg[uoqf-XVSMJ\mf9;c9c*�8tjixw\n2"µ`0hbqx7:\un3 ABbV {^@�h�h}N:N&L4Q!Zn6qwqs[ūM4ĩ7:γiѧE"*խ؎W+ǘw\5' %3N&emr~>ӧF8f7\+c6 Ԙ &�><=M0`F:tx^o[k8\]MHYzz|OS'8N'&&%XWWSs5;qn2 J�|z~}O4Q j78ηjj`k1r1{v1US�RN}?G�+R h+?�_+bYY�# `Z^S)ry7S@"?{k\ʸ!4E:^'^u^iӆR�GGGz/}Ouz`$�pq8xγu7[1w]рVtk5f7]9vV@4`4@�<=^z^3ū`�Q A>?�on-I)s[y^W�>zzxpbQ�=Nu7q7[1mH0T 8]fc;kvg “}>z|OS}NF  <NzWz<8 1V@OO}O<_xutăD�:}>u:gYk5X,5o5r\�itN[\Wk^OM5!ӉV ztaR/G9k1qq0ӆ(:ί[<gY.8tph~G?O4`L,0c1q湮w㵐Y�0VV@>�?^��~@�^os|^3Yoq7[bՅq!YOS^/]]\]\\0�4Ā\@_ hÉROtL\L\\Zn[ku9񘚚p ԧSOS|>G�X"iG@z~~+!0`>GG?>'t0 V`ѣz:'x ө5湮lVmYRz<^YjqqYFJwkW"9@��`W8ηugk1*UʳAt}OS<OSSS<8uo8nun9ŃE8`<Nsqs[dF$5+%1�}_S3:޳YYpTū[] O?tÆH^3[k8oYqγSSN4pcu9:sԦ8Zno[q3q84ph:0Ékxwqs\uŅ&:[]~>Spu4MD4bq5ogq5x(@�q^3:/S�M:4� �C=_W5uoN'�b8�<{buf.$Cj%�|G{]r+kq0D4Uju/Yt|>O aL�]MN}?9L' az\8&4pqu8z'(˜(>lx `\LN/}O}z qhR؎un9qo888x^8^8^.D)4`щxs\56#+!X�Ӣ"@Y�>~~'ѧ 15555]^38ίqxpQDjՀ' vڸp*aq:Χ3x3u4G�Ox~O=>SNjS4щR-/+K84h%HYY[[w]5n2bS ĩjcqnxYk5u5544щιf8.%8ĩąbus7c*bbaNq1uo\sx]f8(&'=>O<&x8Rx[[[\Gqse5ptx>}@??G>Gp��eJsqo5p:]g[qg\83[q^.�VW7qxʕjU�)N%8N'OW=OSL4X juz/>:<)Jbbji:/}) *|_Yx/SpbbbT8\?>_'�tth4ppu<>/x/zM0pjq58^'|O|<N@:�+uso[qoSWçF  D4ut|=O>'�Jtz>?~|th� �=>~~thD)0icqn;]+v`.Wk'!~QЀVp8޹8l-#1?G@O>AӦ�4jjn;^Wא}OS aԫ41ur <N&$+0DJad�=�pQSWzo\޹efM4�/ /y]Oʔ)F�/+Ǖwc9u0h4�v57jF @q:_y9_3Wgu*l 88el[ 9zzzM]J��Fjex^Yr53^\vt'(JP1>\q<p-gZ׻/_ kK-ЯY34evgo7{YZ˕u}9ϟ>kP����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)54�IR������������������������������������������^4�����4:��3�I8�������R|tLʨu?�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/lowercase-fields.ogg��������������������������������������������������������0000664�0000000�0000000�00000010575�14662262111�0020767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������ۿ;����Zfvorbis����D�����������OggS����������ۿ;���߂ޢvorbis���Xiph.Org libVorbis I 20050304������artist=TEST ARTISTg���metadata_block_picture=AAAABAAAAAppbWFnZS9qcGVnAAAACW5ldyBpbWFnZQAAAAUAAAAGAAAAEAAAAAcAAAAJSlBFRyBkYXRh���title=TEST TITLEvorbis%BCV�@��$s*FsBPBkBL2L[%s!B[(АU��@��AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d���((  ���@Qqɑɱ  Y�����HHH$Y%Y%Y扪,˲,˲,2 �H��PQ Eq Y�d��8Xh爎�����4CS<GDTU׶m۶m۶m۶m۶m[e Y�@��if0BCV���0ĀАU��@��J 9ߜYJ9Hy9s19眢Y 9ĠY 9'yК*9q`9&y9iK9HyRK9s9s99眨9Oޜ9s9s9 4d���@a)h Fb2A0 Bh:%qRJ' Y���@!RH!RH!b!r)J*2,2,:쬳; 1C+RSm5Xk9皃VZkRJ)R BCV� ��BdQH!b)r *АU�� �����O%Q%Q-25SEUueזuY}[؅]}}uaXeYeYeYeYeY 4d���� BH!RH)s9$ Y������pGqɑI$K$,O4O=QE4U]Q7mQ6e5]S6]UVmWm[uۗe}}}}}]BCV���:#)")8$I@h*�@�@��(8$I%igy陞*@h*���@������xxx舒h+ʦ캮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮 �$��t$Gr$GR$ER$GrАU� ���1$Er,4O4O==SEWtАU�� ������� ɰM%R-US-RESUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM4M Y ��S--ƚ $bjc R쥱H*g1^Q{$cA-)&TBc*RR 4d�p@,@,�������4 <4�������$M,O4�������������������������������������������������������������������@4@<@<�������<<D�������,4<Q�������������������������������������������������������������������@4@<@<�������<D<�������,<Q<������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������`!"��pH$ HM4eAӠi0M����������$MA �IӠi4"����������AӠiEi4hD����������4!E&3M"D ������������������������p��0 "��p8e�8��X��X%��`Y(������������������������������������������������������������������p��0 ��p(eDZ,8$ɲ�<D���(p��ASbqBCV�Q��ű,MEi'$I<Oiyi<4!hEQ4Mi*0MU��P��`��B�bYy'$I<OE4MSUIy(i,M<QETUUy(i<OE4Uuy'hEQ4MTMUu] i DOMSU]u牢i.MTUUu]Yi2@UUu]W뺲 PUu]Ye�뺲,����*ф @!+(��S0&!$B&%R RR)TJ*%Rj)UR) B*%R��؁�؁PhJ� �0F)sN"c9'R1眓J1sI)s9礔9sRJs9)s9眔RJsNJ)%A'9��T��`#A�R� cYyh$iy(&Iy'<OE4Uy(i*E4MUU],i0MTUu]i.l[UUue꺲 \ueٖ,ڲ���VG8) ,4d%��@B!eB !R ��p��0 �H��Zk@gZkZkZkZkRkZkZkZkZkZkZkZkZkZkZk-RJ)RJ)RJ)RJ�U8�?ذ:IX`!+p��s B)T1tTZB1$ZlsA(!b,sB))VcQ)RRJ-XJRJX1ZbI)Z1#lMjck*-c_dl-j #[,-Zk0[1>R,1\��w�D3$� � R1s9R9sBT1ƜsB!1sB!RJƜsB!RsB!J)sB!B)B!J(B!BB!RB(!R!B)%R !RBRJ)BRJ)J %R))J!RJJ)TJ J)%RJ!J)��8��A'Ua BCV�d��R)-E"KFsPZr RͩR $1T2B BuL)-BrKs���A���3��stG� DfDBpxP S@bB.�TX\]\@.!!A,pox N)*u ����� ���\�adhlptx|������|��$%@DD4s !"#$������ ���������OggS�z�����ۿ;���f}[� �����������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mac-390-hdr.ape�������������������������������������������������������������0000664�0000000�0000000�00000000200�14662262111�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC <��D��,������� ���nd��'�� ���RIFF*�WAVEfmt �����D�����data*�|���L~�c-�d�= � �Yk���RJ�nw������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mac-396.ape�����������������������������������������������������������������0000664�0000000�0000000�00000000150�14662262111�0016573�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC x��D��,����������z��������RIFF$ �WAVEfmt �����D�����data� �X�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mac-399-id3v2.ape�����������������������������������������������������������0000664�0000000�0000000�00000256103�14662262111�0017536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����]TALB�����A�l�b�u�m�X�X�X�X�TPE1�����A�r�t�i�s�t�X�X�X�X�TIT2�����T�i�t�l�e�X�X�X�X�PRIV���'��WM/WMCollectionGroupID�mkW@d&`PRIV�����WM/UniqueFileIdentifier�A�M�G�a�_�i�d�=�R� � �1�0�9�6�4�3�9�;�A�M�G�p�_�i�d�=�P� � � � �1�3�4�8�4�;�A�M�G�t�_�i�d�=�T� �1�1�1�2�1�0�0�7���PRIV�����WM/Provider�A�M�G���PRIV���'��WM/MediaClassPrimaryID�}`#KH*(DPRIV���"��WM/WMCollectionID�mkW@d&`PRIV�����WM/WMContentID�6)$myIPRIV���)��WM/MediaClassSecondaryID�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC ��4������q����������������sMNr9Y=l(A �� �#�������D��r����@�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ 0���$��u41؍0?Y/~' oHlah>}!Pscj~"-=+~`C1YdĂyz1Q{^b}˭WLc [WzԨM1VvVyluh2gDPF Y='CrfY]U(<hR :P9}kv~u-/ʅNEտD+iņfGpԯLMͨԟ N#) ,9@4em3gR3(~P.ۉ8ٖS?a.ϋs*M(:-VNpu*C%^Z9GY`bY9B\^"y؅3%̫Wߖ9[=@unJ#eF+<'EjWfUW KhA Di }5AU*38Pb [SSps?SVૌ}LV�0|xK%yA-�+Dsʫ^KiNldŞ2r Lx J)(Q~3kW O _?+ V"Skx$q[+.ySwh#*߮'((Y[fV>l؈i:>%&@8 "JȓT6~2y7  Gt3Q˼bPQs x8'$Ⱦ\wڏhH^z*<en5ȵ 짌qPD&ɘꀉIyeԿ6}�ŇZK )13DFS*=�E#͙CC@%iU"/eAJ3|9xM݌! t۾ mS[D5 GœoV#xL>`*+C<2UVKq긨?WcM+wa,< yz\HR^*x 0])Q'l=PDG(7PпG \6.~B)dDӕJ ʗ$ܰb13O7a@V*t=?H L.'AzC7zLd/ؙc=VRN./5 /<m$[;jkb$bEJޚgrSCo ?.Gc'r.$259t<&pˌd!S#Ks/ŚK H6^Xb?nўkҎ$7H3"db~-(^Y>D˱\I%2llCG`8&bE81m'8_(-k?1@GDS1^,#1NCL-C 2c&+'@F_l\zK=xvژŷۢ^Y&Q}xao$ eżcoy#O'I^V!֝&lL+?8YQ5J d4n# "|R:u3B8/ e\22dQ 4 Z2Auo%5g%ko1r n4 )O2(Ʉ�S G*u3hTyz+u[}m� ˎyy|JS#5-.Wb1d ~>~+cH=54=\̗h[YljxF6eyr;ziVO%ժĹHkM8g7Ex_�999:Q yfWs_[K XIDTj\ EW:k(OL_Kx&.H&<Lx;7wlnu_ !^l!3�Ŕ1fLtW#Uo Nz8bSeҀA1]r][�a-3׈'5" V_=Sçޚjo>Ңqݖ6!é =a~vk}b؏J~ = U;YdEPRͦ�Y1Ovo 缾q2KݲVH4YGؒnH#LMCEӎ! oSV)8"zM4a+t_2泪[H6X!vK 6"7\iI3!pf%+W!Jb6ս % ~{5!gw%:ߠ "JU%ѠCM@h?Lz8?Vhr{PLYtMAGA#p-BkK\R6G~*["0rjsb@5u2+dq\cq(D<Ҧ5M ?P6T}GXŃջDyze'A=W%(oˌ*1[D,ر·&5A &^ \ KTc7T߹^jR48nc~ktÐǫ-R[̴.,kגM;o!^1b$"Q*.G:8~t"l f?RAe@C%ׂ1!9To˫D(Xքt؄\$B!" >3,fz ⇇]P3w8Hp ~-~X)]]}02(d8$Yy'Y1 w۱1U&zNp}ĺ+ ,t!9 ?K0CxeCPy &$( \#q<͂kC9k3>F+ C-KJ!msN�\d/z젒D+).vԶrn̛͌]@u nTE}tva`Mi3\=Wla nn% Nj,A憺WdIľ7KN*~K<~%cf& B H3$˜OfGޤJZmh$Uum ;yiD$pG\W]7** NHOM,`51է¢}e ]y-ӽփ))2ђ!i]QTF;",O]o>մ-k4ü5[lQύO25ʇ1 Oo6*|R;Jq8kq"zCܖ$XfǘX,7JÓ?=!Y ON kwiږJN= y{v9=+c(F�^*FLH k, 5#|4[SMAGe#Lj"OxIvجW9ʕ nZ+ό&6LJj_ነhu?ʝ!٠v12Goni5/,<82ALqͽJåIFCw$+[~@/r*ylř53„! tJA>c's_ȩW6ǁ0ĊܚHdʦ�#O>kg5!'ϝN3a-KZ~U:UU^܀U[uP oɩlK=dѯ1-lQUVW8p8~b aǵͥRd>qYoyי8 TV˚:٦[qsȋ]G<!D| .T#!֛<:<GWn)3H2s&AH<WK\]kqC} TFP7a.xpu!nC8o|.j ߔUzm�C6v秭AGoDetJ ̿ br4vGy(.:Nhղz3\'c3^ǫ5GSy:XG9֐S+08 ą;F%茉)mr*s  =~v"=4]j&|uVKut<=�gR7n!y&mmVqP3;J;KJeI={_b q'-J ]‚F4,0{BrWǃYh2`k0㉳wֱ,3Ġ$/*EunShESlVs|)Aq앶ʇ!*45ag?yz)Y0'I&m&ϋQ_Vt>8.-*ZȾ穤A:45ټ62|qtV%.Tvۉw1S}f<@ud'9L d`GkB^$mI7R0p!9$ZaiÛt,2r%#C|E1콪sBUՌ|UwjOb @jFoɮjp N;PU >}c~iְb<V=ؽc@}r)ڊes4h-exgu)3aahFAAD6w]_K3^ȃf jt Uw)>g޴m0iLe Ғt|aB/i<}.|9:Ƹ]�eEg@>$NBfᄴwr C8CJ!rLw &X$v4}ZV. ˏdl4yicն禎T�L^wo4@(0qc�Rk;O\me\}ALQ8> >_3uLx9HELFM6!cݐ|HK$x%^=j=j=bŊHOQ}(.}"x #P։iWCޛ-qb)+i-@ڽI'f[B ⃝}5<-@7:.zXڸH|K{StIriP^"d3,K'Ch:);+Iݟz%}~hLFK<fĸFiBRfH>Nu�Uݿz* NhҽNC14\6KyF 6`HYu-&§S De l"&E:=./aY-ܮL08f_/V)Wz&uWXwב);Y_yI%6Q^ Nw=vL.]D/d_;GDL?@MץPSuzg YVfM ;:˔nFdz̓^q=kÚemѼtZ�V P´�qX=桸˦t߶Vm sIrsR35`G:$<)u5&iEwC#Xg (Wy:U4E>CՐaNJ@ D` ,CE[9ȩ4i<26 zJ6a0>"UKiWp4LTVZY"`{RB}Dsy /H`L\V%?Rv8/QL "LnA4ȶMKz@?YGz0m;062}Py|jŸ!{;xŒ7DLO;G1M@JpJ>gP <<e@D;Э_rۡd3)~a$Uf&{eoChԑE,~(3x'%0<'S lX=T-qnvRƞٚYkKDIY8_Eĺ1RZ77 d:-i1xe RsWvR`Nt^&x̫ncfQMlpW](CHVGb~@l{H8^7;5^R!*E ]I>  klv?IO$sIr ϗ·ʼ깃B8Uw x2n HN+>|KLLT19`Gjsa;J,.F.B3Ag@v_MoIȺ5GdPzRiTrX/`.p)LcB γ+eN1ĎZ_.Wxی}5y!rBPJpG'VѬȖ$Jmƍ~#J^rZj翝b14sF Mm}~[74ǠC|<`̥wzWBYy@3Wú)߶}tO.lB9bKZC־@>Rq#J*'t_ @ CUVISxn!)@߹t'zaRm? lQNpRyPO^(5o072˻%wlYRu}Y=Sʂ@>VB˭Eq64sbeU~ .vy"EYonc"SKx<uWi' ǁ.V/e+Crs$>Șg2 6롦Amݧ+FddY.밠 &&6q-2E m%H!ۿa\/sQ= s!,tߏ8}C2%dI0V;ѓ=z h)m@ @4b7"Jݣgn,#"mC("~` RKq>^F=K5ն]Dqs9z5I1T^"u ;~KyDnbs/i~t7}]v4Jn^&>3O?R?^ݭHǔUQw^W~]=ǂ/FZqT8oh'L Z]*nbMnWLcQGG(f\hZd <<NFG)2ͪTF8x|d(p5^h P~>W62L۱Z `I'",Q^AvO@ן_ӷ``{"˓'-65o$wJy|4{|�)̧i; r/K%WVzJSLьc&Rx7pjEzp`322-yAC %ӊIQF# Ҿ5~ޢMjǙݴ /5_D8RX'a}YkY`&҄D@MƹH ;HQB\yv@Ŗ@4aEZ(7ۗ'�uhڻ_c.aYz)Heas*-|@Q#ބ}=ior$`r#E;{dZ6; a.Ce` x~R*6k5e\(| z÷V \?׵._|`,VgM YBǧJ!/, pnCʏwlѪ_rNԇp"'h'.ħ>I.d Y1} _Tb 5bj֤HtC<G<qSv!23& Էv0 [n-}# <~J1›ld_1A⁠UO^kG <aT50"'RU֓&ё ;JhIwZr2ֽ}@u&S+S3mR v69ȧ̾~;ӁbxiB/tl+%lSԛإBva't_m}Nȴ^ET.#1[9΅/;L�4eVԽ?\4Tcaմ*l۬rlLm$yӎ PT"-[&j!j|8qL ]fJ ns^x'c[W[ĝ;"5n;OTB~ R61dIuά$jW4J12 'B} {A2=Ɖh۷%6Lv#7hCrEPC nj5QR /O41bNѵ7ǞZ'[K]-doА1 ߚ+[_\)^-M۶`Ϩ>hAEvW4npm=$G#Z/% jjR=Y<*t*=IlR߶R𜞕ӨV LjTHoeEܷ* ҟfۛ_�whH~Sv3)<?=ߛTfyߟ-xIQ!qژ̾ fգݸXS*דt0%z<!;z#=m9z;&"$otx,nyS,,/i$~~D5SR stUX`O{~܃?Ш/ -즾4tETu}&UuҧFi9S椰v8-1=zU79 dAܩV.~7k$;VkE^Ԉl=[e 7@mkٔٶx誼ޓ9;0�&v'@,Aͫw+DBPd{NrSĂۖ{ik,E~T޿ )H ی9\ 7!IeR֯y8ؠhnVHdeo6-,�$� {z{iqlR ՙ74gtOG'8*2 Z$XآQ6!<)nk̰Vp 9%'QI)WYe|lP6ǽWQ!1,z1(YyE;=27csN3b{f aabjMңnHFR2glz3I{jFBsaL{> AL?uv&[{,ug e[/@qmq#?r6>Uivc;B9_;RQ"擂̫vGԚu " x=KM^1'Y*ygƮ^.ffIqI&,^% Q[]lvv2]qYVCwj{[eVZ�?ȽT0~*;(^Ar7U. 9u48c*v*i=bɄTl&ԀOшDY[SciI;W8S+?aDaGlQ\*#bϰu-D&8ũՒjUh`\*lb6H%U{>^.Z�yƬ.;/gh1_J0?/m=,cƗ!+@t5|K'P@)4?Y#pY*iPXg^\#}t>Đm#0#G$sHAx0!\ ÇfkU �L~=1~ n n,nbO]d d~3wzB&&/\ܵadva8FRJJ ټsHJ [<w]яe՟W<blsx\\g&MLsI: ,j\懽 ᎎXS=ɇxX^T@ի髎:4s*x14 lҨh ϣV֍vV*41.*FA_SQTcr)ҏ#.3EP`;.s~a[TJ% q󤒲l%n)Ա݃+u"*Α&@ r_M2)(+oCN>B (]5˻T0LVd-u|._<~*C~Ĕ< ]!ӂ=iLfy#☆>*5?"qbl}} k!Bk5AKo̘]Pzd֦ VE<>_-Cg aȋz}m`!I�q^=$HWu[K; n}d)b_4wfWBs de $QՉCalKZz�C"ӧ6L],gy^g+-9h~G*Mc_tMړ\#\pΰ<NAǘwӑDG$R)ew0Ak6l<nHGh؜e@b: R^F9Xd]3nJGbH;)4_t`<G}WrfTZj\6݅JЀ3<"0 ^2ī ws'i ޙ>_?d('Ii{=HVz6֗/ڃN_Ǩ,pE8n32*6F(QBrȑ˱MCJq+<ie9A�F_B5#i> 4fgZa4s>s=a*(LuHJ<KK˼X(>1Ei0Oޮ0C:uK#}zA~\ekM#0R X! gb˂)Xgj=#{QKmp6hxm�F}1K1žaO\j5ؘ3XQQBD<0@ruƉ)iO[n>#.ppqWAK}g `ZaA{C#Y%p5\chbؕuW�6I;+2ykK?'jq!m Hg%R'HEa嘙P2nըxLup+=G{.vB2,m~{W+BLYZ@ 6je l_EW]!B__>0DC6y*>&7w3uHG)�eJ!ȩ9|p8}C&Oġ.m-9, cb9MtOľwONq <^[I6~V "y9{HAV[DkHxr(v&2͂/woζK/ a:EϯBX2goK1'D Ӈ̏a{kQz;5i[̳ duؼ2";":(!n& ]ɤ̟0H6dKބY%Z݈"$I?+#!ܱKj?x෻e =ƫp8 ٚ)wxpR|+O1c.&́).*4񞯲2^89 =2s` 6և&~}tp#`EsOPS!L8VIM}y$oS ؖv4B5e ŝlׯ}F bb7 H1_9jY-Qt])+'県rgwǚI|pr"Q1v;HP]@Gb׮Ftpy Ḙ́\t A W`6lZ+lfƒB<8GZ=Wi|-Fi<;K.U]ɷv]Y[D.xKg;tU'ͰdANH]6xȘU~f=>{W&eYBTۡ�`KTK)E廉!Q_<I%߹7 ƕIMU-Pp 'Sރ _E!0Z!^y4ah$kWaWR9lIXZXZtNMͿzϘ2]RB7$ه+&<i  tD0숡�U1哼Y (˺s~G$/bh=|vbfhQprXllt[}R`boY\g@i+(Sz cl)6Ɇ; Zp6_S,qC \2%ˌ?aw+\_Vaߍ(1dao2*0~&kgEds#neUZTɄZ5Y' dғ#, x1jOBG[]n܉]dF)źa?}>.sCc:Mx@{YM' [gu5zm RkPD"�˂!Tlj\\�9p!*AynoMQf˰KvM"ƒ=j�@'!.z-]] QS9͊bV<ZAj&Lí |YƉ! Mp&/Π Xhr |>HK)hIZ^Nk2?%?maMO9܉!*XEĸ�S D"V<oAɐjm|YmH3E؇ք($|Qz;Vy ^ '˜/ZReb)9A"~[H<Az?�A\Ts.&/VUV4aYn\ _T̯ B"RSV%h@O1Ů=րfK4cȸBb'i4Øy%&]Pl@윶4[ui /&X�Wx5 ɣ: Q/|H~ceiCRN394D stsZP&]`("mM37 xaiQTGD58@wds�Њ{u\b&-B5Xn�YeZI5u=fWDi}2[ڄB_X:LjjvoPhїZi YPW tގlH:?;ɒ`[Na3'qx(h#& 8+<|'_\ࣇxuq9M&Bb(Ti0o*ҵJUdh 1(;Ltsk}`ÿl F_ut <m͉9ԴF!+( af\ e MZy)=Ƹ ~hBNnZѨb`Ʒ6 L~)?1Պ:ZVg \=ȴ�f\arQ!(wTJA=❼IM_)08yn"5ZK8 *!9X�b2N"w#u%U$a3mpHzF]7DcۙZᗗAbN5-* =f"8 7{+h(JїvAf?"K7Ckӯ97xJOPKXPyckJ[o]v[<u&Ӯ}&5a+ ;M(9)ʐ9P :< 1qX*G&-`SV>^tt) #C!|OY`*G&vsPq!7w=nrE`zyL(\{/5N {߫5{-! Y`… ej3C=ƚ^ˌp- 킡ϹXv^(SpL;%$G>#Yk{E&A?.MDDZΖUGA%)Lf$$ӹs?zBcB1"Ŵ@EO8\a$jx�bIREEX?BnBSCЪ񼯀7e ͍V|fރ&_)k^~#sgȄ,$,<t#][N PB60 _IW~Bv%#L S],ٔV72M9'h o$5c0}CàYpx݂2a -ng+  */jU ݾM$22/8.u+r,O%TfP}(~YV*to.F[ s62a M*G!OGA|�9S;Mkw4f.S!?TJوmAYv{K8& e5(|+C_OVfhB5߇m<3w&osD&t+yejbto)_%4kUJ!Ǝ94%9k]U]IB4Ƀ4$QdYoۖ8{V#<CX3DLOA\hDDwbin?H moD@̷YyЭFpeϺ<alKp`@6gzO٤H7@dhxkǁ.3/zµ[+1ޟ>Tj.?TF\&QMG2EH@,JP˧l8?FFLi?K`O!cڻ$S~ t ;+-xK>7;C!컷i;f]j4YljQ'򐸃m)rW(Xw:R71ghTS9Ṳ8%)_nY2vzD6/K윈9F=H?Sg?)UWU`bPtp!>,SkMroS9 ,f9yɃ /$Q^u$~E۔�qL"-GJnl8xg1L ؿ ? )CeF5YG"B]&<MHY@^RړԙY'B9/PUGDa)pbx>Ɣzl8%,;Krpk?+j(UvR.SGT򁓌 ]+Z5lx?_)=?TuX3 -%Ip%t>{�l MDYy}|NXO@p]jgEj4K eM^}%+A]r<IaU'g>" !|<5" Q?ɕ|.yN*!--Z]rؓ$";MgQJެTZ$L`.k'gڎ)G'Fi/IϖYeOʳg6ֽɀ6 ךǠ%q6I͑qf-QF BَUHK{wCl(_)M0?`t*]/88 Qw|5yhBAY�*yHpQõ@Z :(nPDgVr=1 ,dJJ?ׁ]7k0_fnJ[8#1fXlkOBYV:M-HaxF^7Fx5/'~ urGG�sէ6#HߵYJK-b:&PkB%>܆LH){NJ2ֵET2U\R&l뗚?7@T׀?ѯ'*Q.&:;A|)ES1FJ>)r<u#MHQM5"GBǵvDbۆl 0Cl}beO(J[. (~3)F:U7䲕)x|3u 5nf+I#Swu4_v?\sL ?RsaKǣ|QHQN%uj)[p|dy,Nfر{% r(Pi[Ȳ_#TJ )oH>T:V54t:uÑJʢ%GBm_7;<Ejzt)!ɯipOruDT|}}2/-|mB;'vbQƂeUK{3m!DRг?}܉=9VzP+o1@MJ]hƋ>K)VƧIyzo&wa+$Ʈ;0/ȦHߊ\n6$ LLLrfDJdn{(kcCuݝ%:sQ֚޲JdZIb5%;!CO'Y}剚pM.gʊٕ3ELR'7EEȻҶE'Rmuu8>jjMu0NQTZ`!,<P9{^_ir*L.:Rm?/�/ jW(3T8R Y68mj˜ i q;,KAGLFDR}�CAU'Yknx->r?~2WNSj-y66t!drY)|ľ~ԾxZ!_KdEfGF|0/벥\�e\L/ؔ%{VCc>!Egs_|IUNk3Z`N=I,dИL2-z.<FBBm4o֖V)YսVPX :KQX=(RP?f^Ъ 7܈,>`H*VV̠\9ƴzً ݺ�X;;YsU<}-8<B߇E.76ÏO#?@=N3|x2#%pP}yzG2ʰ)|V5W&&2?*А4sjDYGc^3([6?R]T"?.b" vAT[qh6}q}ae{ܕꅿ:؆y^PMPQo&zI~[RVo5o:~?]Fİ>Bn]!>roY$ni Lm!q^$$>)D1q9 { ^j>U`7%m~ 2 SFwr]D^ |kopm~+JLLxxcq]w'p)+_Xl^ylw42�NBBvK(r`&@FC? #!kݭѹXݕ'kV9CM$5*o&Ө,W'Yx(" YQ/aG@(a|y0=<.޵UuUlszv IU,H^L�S}H}[T*Gx"9f+v^t 鹾Ŝξf@&uv-)mr)<a#!LHs#lɛLE2?o 7„+wQ>;w<UO&s+W[Py ZD_f!Fijǚ׋1`rQ]t[tՄpA_@XC#[٭/ڀ֓]Ʉw@}AUkmp D1@ 3<}ɍs,Jk .qKo~;{[N 2hcW{-R!Sj''Z$Db{Q?l"Օ/ۓ.Yw8]v#NQ�|1"C` 0�Çy+D&@3m<CoZ `Y&%�T^fLIv""/&4:)ƶ -@˵Lv<ˠ~8@F܆x<w{Q7k,CmtbITU`Ls Qq7q^#x_�_x_ 3W=*ix=:W;Y jXI| c|Kqa:ٹN#5z- ,+f]u�qEX#ZU8UZY%#y狼(P/gpfk a |Ѵ[)LO[۲/ƽhbi>Ν[k I)@ w^tBHX\k&OE�X:MJ߻Ђokţܢ#ۢrP7ݠL@فYRL,Y$DsgqM=pwen>gKlǓyCZ\DeUUPSMG&3aw �<Xr4@Va A?h' dm)6@ > )\IQ_Mf) 5MZ r}5_aMb3C?_w[fkTc ?fvq^\! :p)?I[,.y[BC+jEo0@!,qXekOumٍ&I�=u:r'RySfgpʿ_UCÎFP|iLKa6"F%VCرS;GPXP)'i$݅I-Inj;v0[<m-L) Xif=|H2%`ɖ>$Cm,;Tb,M;ى+9@,LsM}A"c]�FA�)nkja > LpeE_BWG~?gg'?^Bt�nps}2{;\h{I/k r$4 ,sg+ v+?q�44*KIm|G@,đ:=i^W�bxx1+bٯBޠY~c<RA&k950}6r|8zlrE~<iJO�iW')E  2SjRoc状JO ǼIyxiXe8�n7[WdMδb 3.a-{cp^d8{<vr)P%OaYj첇Wێ/<4BuF>,lL!C5K;[{v%KTZivl RH!tuv\@ ?OSM8s2aFb ntX YY3 MEbd+1!xaQc�϶$ %2;ՋMp;:{YBTȉ"+%{/x'($/#?AV.XX5fWҋ<*d%gLA'u/%Cx#iF(y#>{+/d'GZųn`HaEgbY! tw8~~£x{-xX+F=vnж0=nJcH*%ZSŔԸ&ΝQh-qJ1!lDz,!?@yYpJx T'ziT'EcO^3TsW[~0$±^$Lid(~zdžy0jU=hKigBI͒(-{K\H%1U \fs7fd멮3a0Ci&mʓ-zjn{wV$YYyI뷇8{8A3mj))k t`IJF4j@(zOnd#YYGptR�@9^ A|r ?3pξ2dH,?XIoD"qX^ӼS`&D,2T}Uv`Kmw&4 omBoƤL(w[ά2 9a c7*z+ ӵ 7 ZIdyd=4=>-ߔK;⸻oWC1Pɝ߯|Z!RᘙHl-J! r;EVx](T;?+”J Q�0IJ؄$Zngmq3aQCsR(t$ulfΩ[I=O)DӨ[-Q&KDFO\mVd/ �g,|Ij }`$3R@=j~Έ0ߎEN;6NGB.g:PTx]wh>E'TJ4"bY3~Vh.!�rJh bvXAtɃҖ!wU*wf|sHt O%bRn޻n̈?!8b鉭w,{6t>A^f�nfkR?rY:=1iv$Aק` %^BDKī ,e [9IrAbǨEԝ*^Xs= MHwH rE7j/r^`mymy9eJ/ p #@$ 'ٰ&&MLkF0}?Q\~K۰? 3KDKau뻇&(Stq0+8yM#\b*jka6̌&Ĝ|WOP$9䍂@dI˒ʴJ%gր?{}۰ i$c љߏK2։.M;"KڷOWSwR:,n[]3lOV1{-ORd`N4 ("QfS<X@H<`!b_e5 Xh~\,Q&:Q<ڈ?U='>t"ݓ}k*dXڄ_MEg"ݚ%* Qc/%HO7x򧙤[=oӫʤ AoBwM<QZt]o`o!BB㡾ȒN_角7C,5ιĖm vLBީdUQ+L,'vʎb&!iX{1RVy8[�Y)SJm>8jش",K@ͧ<ǖ_qɼ5lCw1hJW-\t.'N7/&}9c\X;@Fl&9lUo xf{oH -\+ ZVZ~A1V|j7=0U5$[Kw? ᅠj&mS '9zJ!iBn,SX1> h&!Y]GB=Av -+qX | 9-AjrYqe2?ƁJ/wT!~c[V kt�w.xK]0>!CΓ7Yz 88v f@%Wƃ�uimN̸X4Q͜*Tk2 >S߶cM?-T#ڴ7ȃƷj̔1ti]M $Eys,b6ff`}sO]TczZ:5j3vZ0„3^l5+Aݞ"Yc9LcY`4swࠡJ9փ<@ J)c9u5jg_B5}SK- njF*7Rېy;JstA c-|Zǘ*5؃<6%*]E/RDt{:E x= V d\ yJ֗(HHQJăk\h$G 3ܽM%YCc lWPi/-Jϡ`,.q }V@Y&͹|r]a_R+9ؿ+me#HG wl0w3Gar%|)'Xz!ɪUG8\*D#m㜓X"<\ŧgѾ|GAc΀uU7ǣ|7 nlĥQ+jrG ַ+hy͹SFjTk/|6dzN)}n ^^JRƾ^x *5/lUֱ⚡Y`HW(xL,Xwsv="ӿg%\ [`59?ql"N<v_@D?`Y$> B}o cݸKPfg3ULK'B;{ ٙTA<xt!: %1CrWE@7p5ҲXh F_^)Y1 &t9E�/Ump3N CϙzuKqg[r lW,\JGψtS٘fյ΢bEm�09ƥL)_mTT)9czH9gpQ4T]kE,@P퐅�gD?55_wW'0%1I#ڔִ`" r)_;/չF]d6]9S^Q{E#Q;/_/{PAڜo%Jth4ķ WramվGZdIT.URe?cHR6UF#`LnA,tCA4t~[i1:nv˄6^I+S0*At#)%stXD'@ޡQLWANYb/bƜJjKpySu t>�rFH܆z,pKԪy\>Vpd@YfO T6d3-LYi C"713Zx rXw lՌgZCpu&h$}x(*<?bT(A,+p?I`J|?CTl<rYt5Uu 8euZd h,TC2X3zZq=BtQeY#SEmXGw6 )}o6C֭ݭF#ʂE)ȿؒq9G+Kb#%d%np4#GkȒ9  Mmbf hnUET6 #G&W C:IѳgeEԥL(VVfǥ1*}0Sd2j\L%I2٬l#VSmQi|Nb 2K_5hd2wq)d>XmUaW{,nv¼‡[ s}5blônˆM9M)- R޾f_<? K/ kt2}z[y2* z3DHCA:FnŀE\6+*DɑբZ+Vk:,P-kZ~ jn;M56KfgKGC] 9txXQ䰿"ksH.,tdY AVH-zԐT@sHus{}%ռw9Uv=A+ #%Ӟe4bJ˿d &0�Wx]3?bmrp;Ią'7u?;4r"W\j=rjX}~3 }O|`aTU%B%/i_5l^r $}(f%fjWk\<lNոڋڜOv@DnK?.uVyL 4b7�zB䈦nj JJtxvԾ~kJ"I9 @yͮ1} Jgh7^foˡ>K# gEraG8#@\cx\>jkXRSG|#h>U~F $j=FiK>i`y } nju?ȩݪ$Q.hx}cgx]j4YqL+qtp̨hjZJ5fI=Vsdӻ۳uˇ~ <*w AzCIa @InȎ`pWtBXZ8,"2x-Sl%! q4Fw18h(֡؁8Y�b`Yl\Eh^$ɛ&[oFsv ҫϭdN|1U*˔<l-v9*:ǘ9l?(#%Kg$iP ܑSYNA跒Xps=-ISQ-[AQ0Ih+d 2�0+'b?}+(Sq0O#n9x5;tʺSQNoڮG!֑nV{,i5Q=;&R`YlKa#9f'&1b\ʜ-A^Ξtm 2^f z3Ҳ}$b=1Z{r-W�n9ͷ#C8;/[#naG[r%{Z-By mj S^r}֢Ֆ7ϐgh䏷7O\l2*&9M_Oן(+3{ș޲,dAP8`ٺXWkQQ'˾S2TτPSG?<`s,2Hwwď2n[A�ʑ HO{Prd+Mu/QH1>-^-;\ {88SDETM_^޵[<zYL1qoW@e I1 qJ~nwIƹ4&].֓ jWu15`wvu>iK.~ {fS[TL;iԀ'"8´ k p(Br&&gßlڊTz^<ICB<oO 5TwݟWin� t7Vj�}s1uw!LziO8Q;&<oRYսk6Wy=Y| G;e|dYga֞� 'n{f2zXr$W>3Wk8#m,CpYgjm*{ +E;wt*XY-0]ȈH!o2@7!H ,K >.h[ <#ΈO!NJZp;Tn֙b޲0.hAI+_q�5yHM`IčԢ+]W@Fmn6ôHO؀-# 'oH7ê3#Ss|cw[qߴ#s?$ @Ŝ0&hz*S3Bݓ.EepW,*"Y58`jIbp*kA-&J"W)sc&J)KlT9{q0:{y{i_ -@~[-TM[2&1_žUI4M>Y۶_S%P=y}Tyq`vC/onwKP{ qAJV޶x_TpJUfܾ+u7a'mTW3zRߜ嗽ղUpO4*u*;佪:opd(EyW"R+e#b d+#IA3_7�F[w2{d~4 ۠RiU<~ߪha㈗}ĝ7X(xQQe,x<u~gVI\'vo؀Z5U7s묏v1[kh' Cp[cUo�Qz>ɠreG#\Jo&bj��z�����&VxY4[Xx{g 7|ӄro`ڽXK1C!QCtry&W6hrwxN0ZvLd雹, ȵ8ZZ2a@&$bR8"dtK͌Z~CmF^<WmXqm"5/zuٓwG1j )&1e~{? Ji--m < }H .V8 "K1?VZYu{p]pOunR_wd%tz-;zZ,xQ+t#}#qEO`8R~;W *؞mP#=>G*%zGP֣ 5RJO^| )(4m[{ gð5- >E7fmOhM`uR}YuV;ni[AL,%{W}(AzO*Ԋ=?tMhU~ҡ@軟5lBlntt!N\\h֩uZfԖ|ַ2Fɛd}H?VtZ6m6 y'6FآQ^d>G\\^! YRsPFbOp%Y4՞7hY(B_94O:zWdm O2ij+T>v~3]_S9{@�*7z^U7&ijld+ϡpSN`T0WaV /i^(QP7:" U=3aL5`D~>ˍl5.֨X?ETE+^)w}gx1Q< YB7\,o.=;=MA^�Xi/ tfMJ﹯;֌oc?V\HF{[~t,coX;ekd8;(_Za1D7|w{s+G808 6z9b2CVR|qUዓK[(LP9Bd.*BhJPk0DҗL7Qn}E:ԀtinHfTsbHG3K6aYS ^ܽpZ隟.e=薏77^x99?귄b6fY}.nU.8O\& 6YO, ωF$X#" p~ۨ˚2^Xc\^F!sqenCTnC61կ86/*C:WS 03l9]Z5aPv/;g!fLX YfӬGst-:ͼ!ljn hQk#^ɱ*pЛ76{:)ѥ 71bZcOy�+lo˂ߤRZ9Fx` 1nGe;؃ aA?\ECEmY.\[i;Mś,顈'O`pٚeѪy>\’*c^b΅ǝ ^"؜ uk)xZ4!tΜU⌓&VeJvȺkq[lxAivNK ]I*R;w/O1An wcA!]]z�%jRfK sL=5CR^+4;N쮂0|)!Xe;: 30@(Ns;.DČPURj8 2J _RExGNR06bw@2A]y?3IND9ImZy".R|@cח"}cC|$Pkh+oC #o,&XȅLqUvŠ$U+ !z�dɎ)2|gfYXrAUCRA!)H yڥxUAGuX8o4KWѤl!ZC49]>§]Z}%p?Ra@ǵNa.3U9D f5&._w:oT?3mĻ4jȆ7Ȣ :dg | cuͮ׌})8G9˂Z&hԩ72]+a \Z߹3Pv3( {@Lm;.2&&^kɫ W4ew̓.P>E][\~Yn ojAV/zW̱7vf(:eDˆ Z&LnZ9:ڦ!:Y:+)=ӞFse/Z2`0wK>%r]ߝ'q_7s%iLjXAW<KM<TB z4Uh vm {XtlǃE*>E4)x Crm;n^Oj7[qx>r%]ufNO109roej'%%@ S*_ ,ӐRPq[pnucݝ!0*M^j{zB2TIZs[w/>?")>I6a\Cj|kn0/?]arz<2ԓfڪN|/y1ݥ8[.j:3u`{"̲R,hbF,4y-z&i"Ё1*WI*a`+\v}],-HվoAq{ڐxpa.f] y%kg Zў2'4v�.azQ)AgԖ!@PDZQH; ~JTo(b(/Uw[2.8/)) P28[�Nkl�e: *372>þa- #1:׽`uebQPAc-hqhZJvt;)ǥB=p4r)|E 1h[Ymg@jzu^#_hJSyGj[<w;QkLFw8JN?E zGVZZڅCIYC$‹1^y_#7-UӴd.g:b[<Am$f͸;.QY%Y6 _M-]>rI0m�TȯO׼3�\J8}i@ho nt\!P{KʧEH]T~3]!. VtyobUīl:8kTK1"Bm'5qăI쓷/RIgݰRLҠ? h�0>M^t;Rn|(m%]^Ɂۄn%!MA0$ !BRcGēKQ:$oSf|nW:: CR~B:\|X(N[a 2 .mpQڒO$m�|I@^nRv,{cpF7s33^)֨@dłs5/.iC(DHۮ2 3 %e+|@I^Rx8!aKgib0B۔[)|^[Njp u.&)]덼8u[æ2P՗+O^ś$%R8ƲiN)uɭBr@>bfKMv9+_!䠘I"J"GI?,UŃ*Q_uȭ ccYJ]5 $ D|CIk5qNH#YjYVŸospٶ酷v72!񆚸ֹ .g7Ɔ[U ]4�#S0J z.l2 >q>ȉ`e]o d[uyXd9DfMi^@6/tV`I]o=l-�l�N}FPSu?ǀ Kj_/f_5|'Ra c= -!̛x߽ۻ%Wjq}|<-X3]-t66ڧ=._neHx^|+,OcSrHO�f-|=cݚ ^^ 7?2ڠ[g7 C=F$3VlHsK:vP>VFJ/|?FAX�])H?Ury[rOuA!zsP8\j\$>~g*{+ J`] ^ �W9F>%d& D U-b./yt2Qt{Α+obM\-;Mrt`CAvꢣZRY|TT7O̯-(~aueI1+%rY%0 "KnסaBOCHoTs-6c4 9>U6:򕱡+ koxxF--hdsulf ]{܇q-[BK0 95'݉@f}}(7Q6NX> sLKj&J(JG1=IdvmRѭʡoFMsW wgE]"f<Y: 2G>Ua9'j_u`eǵh}p+8%EoEnKPL!Y) 7ǔZlvf70\XԘ(r'Ѡgx1T~&1daR۔ *Q'UxH¡68ו>ArE|QW"4V"︗F;wIϙ'يAֲDy?EzX,2ނ,P\GH5 9qȹ{3Aw*4x$GՂ֌ GնYd +-t2dj&Ydebc`^QҖh ;Gۑs^DXϙ͏n ek-ZE2ª wOٕ0E 󥣈PBӛsnIl<_ .zP,]SǖQ7 0"HCC,j|j''^7_"~=&ߪ.yT& >?CSlўCAYKhL8�#8 %o~fatb![vvNjrd q֡>S܆}n  CXqw7M[6(q_*mj ?ΏSn`(D>>e{WuYS1. kOB nW:Eԭ"rgu@؛HY կyvgy/@P[Y@Ӿ?ցU~1F0;7T=Yx.1}2@kr2AwmmdNAUv5`Qjqps?V4"b3O_=aw.\,%Y8 u4r2;y#dϋ{Zۄ` V\)y) KG m$;Tp G{{ReGC:T47wd ]wQwg/Ku<| g}%U5BNd�6"]BCR3~n^I!Q6]cjb~] T j-Y 0 :~pJƓ8u_KMYʡ)±: ѢނyPB8[gIKt=PrVrMڋ[㯕 eU6 [| 5ҷ]ξ̃4ݒ,Sɤ1AjmAG6GtI(P#I8E]*^=!N-3t~ V^7Zkn}rʈhUǏ;LfM5α!6wWƒ+ǁ#jn"?Dڿ D�2s&Yc=Q( u4>nykgSD{B\SNj?T B:SRzh8�fS,bN@Z`]e4֦S!3w37Sw~6ixZ1Fj@ b\g,^cwi$6 sg6s\͉jj"ZvE DZ_+1]_@~lzssEcKdJv?KrTN뷪ŜՍMl݊B`� - HR0j$Ko'E [N�ÍC'Z}BVz6{LN1 e젳4)qKbDM_L fzq!Q?q`c4N]ef=DgwKC1*'g2=GB J$I^ݯ"f1̝} ㄖɍNޯp ,>fƶ�uDNa<j�S60GJ.Fz*F%O9͉vQ])pV4y@AB>2Lh# HвI*j1dC*2PnT/93i~|&;8, _%p\wB՗`MƋ Q"nU{0Y� p kZxjF ,T| 9dzoXc[ 76+cҳòscad6 `;{)�Qz2 /fƥ(Z?M wiCp|U_q O4 ڙ(_ڍ`l7 o26-ڧwm rN:R%z3XN^fHG:KlCMIaA/GY`<#Ց .nׇ3{d!ơTwP/e[k5$]eqjޜE F!5ߡ8mI-ºD\} vlKy؁1靖 ѕ4 AaghALml)ڋխUFvJWW/yL~ ?|\Jo^B *$=t'I>*e7 Az\L[`XP@=C|fk99­3WY__GQUE~)Μ;ȫWoTLSb{hӺ!*} \TH(!s$$4J~? w-)DC0~}' <˭*U]M �hѬ6*3.N_4r_|}h%GhgBm51 zҎ*l7d՗|\|+~tw? t.H 0^DI8FG@ޣzQZ* )>OQsyOXdo9tpL Tr/sͻOmk4O5"Q2#Zy&ϯx}͜.8"gCyJEᬠ ƲUzjrKbq�qh2S%cM~.RE/ :t&v| nf~I<KoSu%Yż3Ls�*׆o J]|Qj.hfn*:v_A] gNvR0-%я8)zg ɔ`iv6y֖K䯣ʥBƯDo!OTڋ|VY-3$ V)uW-{p|۰5n7HRi?OÇQ *yGtfVtkYC(+t0_eX&n�-h�YUa;̛e +A_:cMy)> zfᐄ7G+c3E$znPA3nWSVHIbp>F<l\�r*~iߝ6lpzE1Mer< oI3a<R\o9e\d,]y``ȑd$F) ܯV.(/zC rŭ/<ֶ00`)#j_^&[VĝHGG$i}͜qHH*O=7wm>=D: $=,"@hi_aCIdQrARv=tߟ,.rȺ8ћZ b ңKSvi/濅^j^A[MuB*r-Өv[{ ojPyƂo]pgƷừd7X0( ~|ZHveM2ז(v)*.ðSpР?R ΢~==ۿ�Uf?xs 4$C,y"&J@L?E&DNEi,#H[ @49b'@,`owXj4 :*}p*;I&ɉ̂&82\saxe")B <C%SbAC )e{6p97 hҟtfy YkxU|V?p4ָ� H ݫ <zX5>4cv9˒ASU!tYFXERoK4w#5p ͖v-33w+Mo`9DSp`S"ahv~!mKg;-,bF́E*0}휿:Ti>!.#(,BnL~!acKϞvB9xUd)uGrp׳>-3ԕU tuze+>^U3o3ɶkUXd"iov3NvIT!xxqampV:a1Ғ/!<(կJ8LzJ Ymm?0aSHe#lII YBL5UnoMcc0$" (83.8 Fqt+`sr(= L {ѤmDȈh nfAD„gq-~3E:Y ;-ޕ|jL{Ow7H0'Q=m?.3coF>ۨ(~Vb /٣V^\.�bG MzK{q.J[:@,Մ1_cWCNXmb=5)y=|H~/FgOY<^NŃakKq.0�6�Bk.ʮ• Y$6iJn<>1|aE4T+vCơD!4�t>\U0INoK8DY =;r><F[~rN+Y$icl1?4˕m1ay|E.N\a!|bώ5mry(lqJryǞE$<bfApO>CHR޺%HNW2p+Hp\>׾H/`I&Ft9JQE}WۇN(Ρ#I*/Z;t(E�O[F(6ixˤYp5]6{Z76"3,,4$49?DSK=ݹ*a>@'ˉFSgP@B~}ĐU3[`\]{= \Y"oHyH75XӉ/>:MGvX7Q\kg4!3 ['+^SWs Ji2l({-KN{^<rVP?' VViJ37 cdfI悚Vlqæ92ZT`[PґYv7m[ MT@XkMGo3.TD?w~pDGIW-*Vfmw\Frcz3pnLω+b倘P费Ɏ9d9A`+P8IQC_7#qD`@FC5l�6ݒo9zP}ku o:RoLYe5`ҊHm~!+_WFSkr$t[C<m Qf8gEv,cJ];Y"W|q3fxa*j #gyZkF"fiH �UMG*$__1 _**"(Kjz# lq[j487Եr~I@m|x{b'}$:KzbUb7/ۘD2,fRZrQ+da/FJՑy)9?8iT}J;)~3liV 4J b 7ݢݱ ]؇g#Xʒ#8Ak#VR28YOlTOcӄy$K%y~E7Ԯo;KҺ6Waf~Ssl'gdP-Lqt<p+Hc'l^,TA;i:'a@.Ux"J*UP2 gؕC1oL^%"xBo uVѨYD(mc *L>6[/p=5H@%<fN]4sK|D>Zd/Ï9b?_-!ɐ<h=mgt~rLBN>oL>ʢ?l qI\}Wj3kYuMofu0{qdIĮ ;eY\�r]yCvY* Ϛ,G�7w{8PlLŇE* gTuF33(c]g|M59lS?s2IHt& a7k6'z|BTVw{Kg@Wڡ.YQݽJTwlզ.uXk٬u5-62@ tCIF_ B!/0is_!~zϴUI2DQA+pS&kޭq dFH$/?-pxl\Ut y>ju`QaIQ=w>1 �g9c%\ Sf=g2ecx=6Ljl]f0g'=8Ȇ2]�%fckb1u5-wm9�Q,&?Tzw?5\%[<Ba[M�b+�ڱ>M1Slmb=8KPL*0HGS�jOVr[y) s|ts%P&0OD.d[j[Aቚ<*I�7G-X:-r@hgǏlw8?ͰS7QDpP~"};Imޥr5j5V.JcF6GA{JtW̛�5�X| L|7ӅӽqAsnDS 'EG[OH <%H̳9?W<rd6\PL6G NwJ+lQ6x.ٔ\='<x}B<aV{n2+ (鵮͝?% 9x|+ؔeAv,23e[U~@LY*/!e 6Vp1!i B"T/ftVp@o[[MH. Ҕx+́ΩwZLQ [%|rw57?5*T9tO -Gi YP&Nba\i%U-uV=B7.XpTMz3zg�(wrJcM.FpUcf ދ/lWՊ/J!ٗ;03>gݡ0-NY'x$f, BӦW=4f(y 5 c5<7M;|'%]app|+I3z$q0e ^S4G E:PbڇGL*vۋ5ӈ (4yj6U_d6ʦo+ܣ>6?q sj|$" ځZȖGr+Q'ðx/3s&BW%hk%.'_, æ�bxJL<v|p^}Y{<ZP3^Ԝܟ6pWtLB_YI)0xF!$ĕΰAW.Ʊ!;ܽ3l۳_\ҭ.ڒG0ܡ)0Ŏ>|\{1׊oB?MK1l`.d#h9ƼBgxٔ3½ τ</<z{)nı&r yFH񻺬:;f Geۦqې}?QvM OH-#ئu3C"6ic[E|tCz hy;E\b?(8AtYjZ > >E/|߀Clal趖&>?qwGƹ%~?�TKhJz|$-�0\4W~ض�֜ ьđ?PDPĩ/K %s|9v!w|k .9B_:TYۢb^dAȒ / )fYA\Q g'OnkAUVNN^?OՍq%vd6O Qʮe|LHgSDT 7+ LMݰ!3ЇW*Ǝ+u 꿈 eIl:df(Ez<}';d?}F%5N%:aw hF6!9qTQĄǓ_E,Ckc\9NsXMpumTV,*NyAD\T $#ϹDM[O\ -ΆSj!4I'VʤZ&* lwrzEJnȄY& &th'Thn�, ÊXN6E6Z7հAWYʁ:qcVb5L&e&73)ZdYg7c`)uB ڒ ̸* TCԛ҆xЌDhB:v=R{R]"4>S?-PIؾ1KR_68A Gև{ 8|'?QORvul>mZKS}ΥHCKML~�~JWK /p.;S% _,G Y̗i8eَc�~zݥK@*G!eZ)VFVC' =zKHW xV$Cr%W4T]YƪhѪ 0|8&Իa+тMy N t.CI2JurUMrke3tSTjE!i+&]WTKR:Q /P ). 8* d"' EȔ-_\SDwՈ5Wc Gnyh8O7Vٌ}L�3G#"rԾm:q}M2iSs{1P|gMLS2۽J0,O�S4MjؤӕKqƈ\/1+AKVMZb,G*綧7` U)da lJ2;〟}z:<̋'Zu سb*P7Ni̟3qLOyl۵so[ ){zV;9%` IJrgv1N9J4Ñjޡ}V--_CT /+xl[L=4Wt6.UmlR}Vq\|.Єp&ٹb]>hҔ>ό5zn"LKoL= ('|<Gū Oᧁ1xr |A2{`RׅEL6 �>%/Hpiq9fc�K/AuC%_'om3&6˰j瑕U'�9 f[ qy4wR'\yI_A]! YxEZ l%?[l0DǖA4¸B!٬Og- <.8'QSI#>n;Շ{N%[+f3E O 4NA`TWz~;1Q>P}fsIv߳A5p g %4@ֹS!++Q\Ƅ/n<(}NdR瓇h L.},hy8d^?�fko y^A?yJ*F+ʸcRDZWk „] l3̝~Q$A#@TֽK /0/3Clp6}ld"S>ߕ^&#s\H ^Z tX3vtmv A@sMĺD=6K=_ j{'4[Y<aN.F'말HO[u.SN")"x2Gp_\r�3Xs }ŝYjZ6Ṏ~Dg-�g E|8?>4H1^J,BW#�ֆx~u!iien5 Nɕ=(l<:w@>r#Mv 9̅aV"S$7)$5rr edOz[=hА=qpu/Vlϐ@ ݧĔreIyo*'ޑ bVt'ֳ14M!>^b0#} iyCUԖ++#Odڗ\i*Ps2"- ձq}\u(䥩PRM#":[+\}iBE@{a 0o)#eAۘqIzoj ' ;*YeUtPDE%>+24NЪR<x)Z ] }rnNfpY% YH[ˁ @hY"md'6WؙMq 9l4ަd8#s0y>@68G DKןy3sOxU=1#vt(9?!mk9l.mN}ƒ"<?fSӑ"|v=^|36W-/&!O׹m#˖[+jIomo&/e -hzV娣\jҝsqQD'U>P"g w͙gI92$;RE+OCQucȻ`QCs1`dV@u*<`;"/¡4M8VĈZj>ąa$(`y^TIɮBw|C_ߣne 8ex˃yo |gpӜu r gʃp<?{ˍΒX@x'\umϦm֨ p_Tb%5m@ ۵D% D(B<B(pqejh}.P7#&>a-|jsg iy+ν12kZFӷAԙi@aӱ{QEPTT|c2Z;`0~"'~ ۰~ƕiUsDDm( Ikm3M ?e7׾?/míB<Ę&8wYcw"m<PWhrMW'D}n|#L\xK6kPT [2zlhäj^V2meVsnc~:QA(`mF2)؊Zƶd;5^!+j#ڈX_u 1MW!GiO+/hI8jzf lAWL]~cwl"~͠ꮠ93 ݑ\`#Ez[]_yn֔M՘5\g߆aqMhbCz/o/4]߬aN_Uub᤭K3ի!;9ޔq:F${Ci-g.ª)jo*%8 pDꟆ6ĈtqG98s8(k3u/|3'gcsK^PR�VBo THQj﷈Xq'|p=׌;z-kj8HSc\GS(&o/B0+vaȆ2OhS>l~VD_?ϩV}v̖eNЦIjhMk]E$xcNΒ@?Fӧtq?vuF;|g=ڑ<51/]'}TGzfU*30T'+ojfo >GӎL%ܟds1�&M~!4-dalo/'RcEGssu3뾄pKoq1a B)h>9Y"'J6+musWw4Tp&F˙fX$TS^+&HW& `l}m,h#SYy3D[*+񀖹b!r2gsݿ,$b DZ[ X3oQ5�Q"ٳѧ;VqᴹW&eHGlcPc`&+AUjsO9|TR :w%Q(k~RŻt[<,gcY{O#T dJM< +oOhF }/ Xl$8~\"RU4 c$->] (�GHYmۮӚ)@&ʋ:5� WÖ0JU+4>|� J.#t( USy,Sjc Drךxl='4bWN�XpnUILvWmFkDYv9?`V5<}a!EYH'yq>NrmtQ'^޹' lI9kA-(e墸ź߱4+o\p6Ң0D808=ie퓘 :t7ܭy~4 g4tj: +[@M=:P& $-@$rlDž Z AgxRY6gfEb pw#ѩ`#;?:@iadWRp8rRg,wݕG frꈃgVዚ~;gyHy|b7Ua$$v+o[qnΌɒl<ӕΒ!-G% g M26ԦSt;rr+R&WS݈V moBzך43loa%TE_~)�~R![�l9Jalڑ0o J @p1`@@9aHS1TH8Ny) jOx,Ό,Q&+XJ!Y<יх�.-zDlu&o_=yj ҸLx`, Ɍ[jd3GXRq.(6e dRBe̗H P&#k؀:pPC!"d XUZᢠnʤRD[_) 3x%YƹR HK A |0.YC%o4VT iA2y qlPuGJة2';"i8U1cmjȜV,oނ&Ъ$hA<oVG^,m& � m?X|S*,TH 0,/9ϴ@Oڽ~$/ح]%yz NyIbWBi-MM=s=+D4 S̖ݠ=V7g%IS%0+<RVU]T?*㋴ QeF2�YVdKepӘ4*\)C3!W{wM3N\o<;I̲ѫL96ڑ8O&ǵ&ZFZg8P K/[cR׊R!vBn݆ʷs &V>C~d>Cɳ$%Oolu6Zi' ~.v4V>x6^.N깖ߔA?ϴ72$ߦ+B=m-k./Nܰ'= |@7<3ew[Hn%fU5TC?8-h,s!"`W9+I sH\@;d\A1LJ $ɦ$Mq笹8mR 6%/qq`PwzdǸ*@mj6,[ܓW;1HZ3o*N/cLEv&ɒ AE^[ԈoY| #e뉵V(G{'Ŧe/~X w@GR:$^ hz8F9^E垣s6Xߔ6E)KB ó0`#.xC%T[u[n:}^6ORV`P ;W8[J b w>=Z Rĝ"hLH|Q"VQiSy ѼGT63<$ ǼxSV.Eޠ]>&llm.|(H%w*$~xCx3>H`fˆ mlUS& ?k˖ zS}W63_0ǭ~p~Қgw$- I q>Q@\/4^. b4&jzRŰ@UTR> ?D&I2{G<̒�% ;BFs!'մޏOMR8=ju[|WEO2�5�+|6vwj]REc8e:aL*D*Hy6S4H G pŷ(Iȴc:}4VLmO@V+>Q½ˈ/nsVw_ fڬKC;DQ8ya6˻ňx'km(s!`T}˩]~[baM*YA@y=+~fp.Igarfdhӊ&}B >�EY{ m*?9xwC2mo<,w#b ǣ0hE_P]*@:J<7?|ؕ2-[%'w_$H;zIM `J~%R!#.}NLۼy 6T7'T %�v2;eNMib]axiFڨDUXp14]Ԙlg T݋.΢^0B29uDt|]kiF.ЯbKqU$^4e,r+7k} dZkE4r!߈:jG?5S[�M~4ClA2cْh%N\ fyɇw:Ԅ r{D zmޝ ZAeEAљ2fQMpR9O>W$VB/>¼ğI*PNqBeQqN^A\` PU5Iܝ'mhXDZ Egdf|8G/NF/FD :==iFk ˬbX\,~.redMhc~C@`OpљB1 lD>q KB~_VPtC"<ɹ b/Zx !|̎O5t:Z4Eqm~+{Iؖ{"Imf Hh=;:_PlRF;Eh*o4L7"}Uz6^6 쮑Ѣ)$5U}8V݃N yB=Cul0:�1}*V6{`ΏBI9VKfHӨxr$SB%ֺ5yO]-%҆'L@9ؾb靿ç�Ra@$ vNȢs $jBқ92$׋tG *"NA�;^e0a:'2?0xP<%>mB7vkf~MC;4fe#bmQϺ${0E;w�E:և`&lfJC!!=|FmNuB_Bd;c8&bjk#j,b GQ04kգ͟- _SI�};<z=!3ɧZhC8'~/W6@Ias՚HP"R&Vi<[֜JYWIOANf'A9L y\yz~mvo �%R '8G<yf�U$Uj"k=hDFo*4%iR@nyhaSYW6ys7PW?2_|">Zi76yŷD V{2JhrXX%DI*nZ uڵs?#k,D;p>H_.2 ӍW6W'Բ7K74utV)m7;[WgeJU_g6s,۴+^?hˉO(*~0fXnT(v)W%j,Xg/ dԮCrr"V7L pk, ?!砦- L'W菃!`SEn7_'+1;[:(Y%KxAr*AS 1 [zi[.‰q V"0E (2^@u"WK?}lvFL[-C;>clþ) D. W(M[V=ZSڈ})2>;0uVN匥yόP#1`0*}1A+%{r?<L+.]YzܴZmy\e`WzĎT${ٿ$13߳7{]UVp0=L袁ԬEV AB׷J� 5/4dA5SKBa(c4] LRmx#3!ǎ�&n靖/K4�ғP`kv|޽TV=D`SJgPH_dHuST'_9J ] ɦtagX'Ej~hӀhq8lP괕n'dI |Lw|N>#_H}KjyWMҗ7= 7z;c0 ˕]Kׅj8r%~w\s ͯp>I!6U;-{Q!M֎c獖仄ZƲ/l?e!F]ek"IltKKjs݄4{C^WZmjg骛(.|Q>7ťgvjhKtr#f 0 |8v4K{3ŢPz=h`h_P27lx=rՋ3iJ2�3 ۷[pqNeQY[v|yBShl/?Mfys(GDБ_a$cX.\+.懺ߗ"OLp=RV` ]8ah=V'Vq8'E@m;Ȼ)cBJgVa_-Rf/Z~.5~g_dQ-t@& 8ueC3hoo_0(g,hzMVUR*.y[1@uvĐg Z^f]Jy]/[\ۯJ0ZCC附fM[A0E#F$٤Tz:θx-ԭ}G{3 =%j4&>I0o2+6@B%Ao"T )6X u&Z\Wȷ3dB5ʵl;p6U޽0:lV8 gqRŔkl^IA\�BJT3^N!kgȱWw8,-F wt. ;N ^ P72P| s*߆Şo . )2'2@%[C^ݴAoFYERkH*I/ۍ$(Bw`V{6$4K;9DU'%R܋j,7O#mn:h@LUS%VeO)o(m- e Ul"W ̻h$k�MI <\& )zƝ5dL6 @q=%zpڻ#Վx߫|'k1DYjȝ-%{+BKg#4H/d.> n)1i29h|ѭ#MeE+ME@oASDqYzh\7=gw{Z>HaS"xȓLKz`XK*$p߈ޣ>:/E屘PNI$V -ۼ2 K#jil$oɓz8"pL@[19YWY*gV)If@OsNjjg}c5Ma8ss6.Tw%>#]5w Z#qU0wO?(QlM`S/F'ti]3LE#Z9OW5hOap1ؐK'fEAL얳R'u(f:WYd5Sf:oĉY_n3ʓ'j"ZPd<83eh(`!KGfK=t�8Sǭr:ǫ74 +\:9R8�VN?ۗ*#~Ճ${)7 9mGbv |zn_I8 /j ZUN0P2bmez2f@a5H#eWx (d~# M!H=N[痺=|<Ъbkxso,8%Jƍ2aʕl*Xqqi νHW{{ ~ɞ`3'A1^K[3:B&?\iIDy_t~�\+z/ɧNוp\됭4p=~6\|/l>T)|Sl痨-1}a+L05"~%ǪFE7Gu}mG"C!.߉Ʈ]%8$.\@)8En\l܏VGh!vK,e;h#[S .su))~l>m(2kG6՜Xࣿʮ2|-vv3#dJ|j)OuEDrL+ʠ(UV#$)5B>|`\3.zBiwP+ԅ{zU Omlɤ_Sh7WqhWt 7B .YRo,-k! E$f$�u:va<l dQ]gIoQvE*Bo[};oC618NԹ; <hUp؀&AQDOX*4MsY1jJM wɣNC1}fGөBV:Rfo+"7H;lȊᦞ'*+ۉ^֨Gnt%:i:2I߆r̙`f/hhȠܞZh^6^ I0gh!^k]r['UvRϏ.{ t-f1zuAT/J`T%gc֙,2ͤW+2nV#&5! C\xyV &y0Q U+A7#ȄP!0<M1y:UXܿB@藜&Ut;Zɶ b͏| if5RA[ cqo`! mttwA< +ϹVHWjs6mxKmQk(XMhu*TyPi^a m<X[~@pTK${Z 6"òΔj裋 R%1|D BjZRL﹢r`P9$7+X 6; D;~ޡўoߺ|{q-iͰܫ̭i^(/y�Mn&]rv9Av:IԣNg<@; P%ҍsjWwwC#?tDFMcWʷ~.�77޸X3fAT<[@RoAvc� Q:zAI5.a ?oc!2GÆKPsb>F2׎80|B7<cwW x"-\٩iVu(6I'gsBL3[`f+F:ӿu,u:컢m)t8DT%r >w۰ K5$*\㵧"8vDA(t(Z@$u0O15?̺Jq99v(RQQyD~h Sk(eECYm!Bj8"x;'q,Y4LJh^6[c."<3n]mר*1f<OiI$DbD5~h*MyF8 e.KnqjUa]8vsn!o.Rek $62QjFe7ԝ‡܃Ь vq5d}`rTw_< $ #=^?67Ͱ}o7)i1fzW~ԣں"_g5j.WE ໓uBi(,>p} n^c]F{! G N-7.Y W9&&78_z*?E~Ȇ@jM%Ob"eUkFعΙ<xO< @5+m&X����A �����}X�s=띔JkDqithf`ZRU[1}wMQj)q%+2/:fϣnćL'~ HQCxhLIm28yTvQ#7fEڌޞH\oe`suGp%GhƆ] ܙG$;借#<DPDl`/ vv}o Hj?dg9e.cp0cM,5lJԸW09/2y/1WȧD�vJjt{DZ.oh  rW,%{jٺ8SdA˕;iv^/r`ufw|댨GɰzBO?ȟ0$"̋MX>)*ե=ElٌQP?P ?g~ȷ ȐUǶXR7~,iV%!%3UAxG!W�e]Brg*_ouí9d8Sɬ7/<fdV/X= �6F{ ղ }iZwƛZP#nY #5o`HSЗ]y͟X {vj R+q{,E([GC>BQz #1ށFs҄#GvfnqnqYb@ r@On W*`7G&�]Yǿ9[URpJZ# qR?$HUݤ t\^vŏnyu[+&LFd+ `*V-,ĝ' Ę{)9Sǐ ^BWTR$ѷ13/Š[B@<+1䷼ X*"L<f/peU+iyTc$TT &b\ ʚbr}E1$E_sɊlC`悷Jd}Yܹ<GBxB&@x`e댃Hzr k.}ɍQ;C-4 I;_BN+:k 1zW߼mkA>EuSS6?su?H% LKw *r` 5&[4$k. �MRB_PI)/; ǯ]9EmZ.j? =kt]o? ;+7aCPSx#s3L)3rf;+9V-46Ʀ*?Cm8.DZ@e9XIDh'ov^d_ ݽlٍ9#DPe?)3CzLL3Q|Nbd w*e#Dn_9 @=BQ}wDZ}NG>^]G:^Gx lz=R<TRhy\sYfI­WPRa�dvs6+`fWiA)"847N9N58gF85n +46_M.pX1{LHP,xro6)+zEjWu6\:7@>OwMoGQSO}5_ "+fHvYǓ9CUg8+'?2TF"V6MIKmBl/dy�="8k,ZB-hx go݄ƈ< ChJxShp8]rPHc+94Cr-Dczi> ^8w{Ch^O< d)3ʢ$ 2؃(]aBHk.,c0&D Yv"+ 6SZR!(|YkvO& 6e¥m'nʩRz%62i!@ F\+ܥ; Bl dk< qLUu{^aXz>ʁי wb O_Wi |Άq 8 LO F<v<րRҌKVEiGMW4+C q*'xE˔.K ٖv#CI3|wuF2"NzO&d=v�}vnbD7޷u2}{%f8 B^8a'}֌Yx0$=EU[#+ =3'uXW"*s4qDkc]\B<}=:fm}D,RBFX]�QVeip뺍7ZL}`{=̌ f1)XG|p .4aGQ5uqE/1Ed| \!M$*=ee/ H*=ޓ\Y:\va[1>9dX$@N!�L#Ӄ?+TK!iE 6ap ,g{Ғsq~@^QUgwex> :]S^<ѵ _m\t [Zzk2?eLbAwoP : (6A.)JjjTZah\yΩ1JW`W2(!md:tTD;Y]2iwff;i]31,_T|BpWBa/8OpvwlU�V#ndL3F76zksSۀ==orR]hq+[hdA^ A9v\_!F~2dw +{ ?"a7Wx[56FgUWJX5sW=v%(΄+L;/*eO*ڲFmv#lk`!xd4n�DTo2`%zü;G*ao&.߾|,hmݭ �J!XWR_8'6[IYm>qr@43-�5#'~D 4OmT*WE8UK&A6پ6OA.k W*BUL$8qKR˅(ikx9OS�|�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mac-399-tagged.ape����������������������������������������������������������0000664�0000000�0000000�00000262707�14662262111�0020051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC ��4������q����������������sMNr9Y=l(A �� �#�������D��r����@�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ 0���$��u41؍0?Y/~' oHlah>}!Pscj~"-=+~`C1YdĂyz1Q{^b}˭WLc [WzԨM1VvVyluh2gDPF Y='CrfY]U(<hR :P9}kv~u-/ʅNEտD+iņfGpԯLMͨԟ N#) ,9@4em3gR3(~P.ۉ8ٖS?a.ϋs*M(:-VNpu*C%^Z9GY`bY9B\^"y؅3%̫Wߖ9[=@unJ#eF+<'EjWfUW KhA Di }5AU*38Pb [SSps?SVૌ}LV�0|xK%yA-�+Dsʫ^KiNldŞ2r Lx J)(Q~3kW O _?+ V"Skx$q[+.ySwh#*߮'((Y[fV>l؈i:>%&@8 "JȓT6~2y7  Gt3Q˼bPQs x8'$Ⱦ\wڏhH^z*<en5ȵ 짌qPD&ɘꀉIyeԿ6}�ŇZK )13DFS*=�E#͙CC@%iU"/eAJ3|9xM݌! t۾ mS[D5 GœoV#xL>`*+C<2UVKq긨?WcM+wa,< yz\HR^*x 0])Q'l=PDG(7PпG \6.~B)dDӕJ ʗ$ܰb13O7a@V*t=?H L.'AzC7zLd/ؙc=VRN./5 /<m$[;jkb$bEJޚgrSCo ?.Gc'r.$259t<&pˌd!S#Ks/ŚK H6^Xb?nўkҎ$7H3"db~-(^Y>D˱\I%2llCG`8&bE81m'8_(-k?1@GDS1^,#1NCL-C 2c&+'@F_l\zK=xvژŷۢ^Y&Q}xao$ eżcoy#O'I^V!֝&lL+?8YQ5J d4n# "|R:u3B8/ e\22dQ 4 Z2Auo%5g%ko1r n4 )O2(Ʉ�S G*u3hTyz+u[}m� ˎyy|JS#5-.Wb1d ~>~+cH=54=\̗h[YljxF6eyr;ziVO%ժĹHkM8g7Ex_�999:Q yfWs_[K XIDTj\ EW:k(OL_Kx&.H&<Lx;7wlnu_ !^l!3�Ŕ1fLtW#Uo Nz8bSeҀA1]r][�a-3׈'5" V_=Sçޚjo>Ңqݖ6!é =a~vk}b؏J~ = U;YdEPRͦ�Y1Ovo 缾q2KݲVH4YGؒnH#LMCEӎ! oSV)8"zM4a+t_2泪[H6X!vK 6"7\iI3!pf%+W!Jb6ս % ~{5!gw%:ߠ "JU%ѠCM@h?Lz8?Vhr{PLYtMAGA#p-BkK\R6G~*["0rjsb@5u2+dq\cq(D<Ҧ5M ?P6T}GXŃջDyze'A=W%(oˌ*1[D,ر·&5A &^ \ KTc7T߹^jR48nc~ktÐǫ-R[̴.,kגM;o!^1b$"Q*.G:8~t"l f?RAe@C%ׂ1!9To˫D(Xքt؄\$B!" >3,fz ⇇]P3w8Hp ~-~X)]]}02(d8$Yy'Y1 w۱1U&zNp}ĺ+ ,t!9 ?K0CxeCPy &$( \#q<͂kC9k3>F+ C-KJ!msN�\d/z젒D+).vԶrn̛͌]@u nTE}tva`Mi3\=Wla nn% Nj,A憺WdIľ7KN*~K<~%cf& B H3$˜OfGޤJZmh$Uum ;yiD$pG\W]7** NHOM,`51է¢}e ]y-ӽփ))2ђ!i]QTF;",O]o>մ-k4ü5[lQύO25ʇ1 Oo6*|R;Jq8kq"zCܖ$XfǘX,7JÓ?=!Y ON kwiږJN= y{v9=+c(F�^*FLH k, 5#|4[SMAGe#Lj"OxIvجW9ʕ nZ+ό&6LJj_ነhu?ʝ!٠v12Goni5/,<82ALqͽJåIFCw$+[~@/r*ylř53„! tJA>c's_ȩW6ǁ0ĊܚHdʦ�#O>kg5!'ϝN3a-KZ~U:UU^܀U[uP oɩlK=dѯ1-lQUVW8p8~b aǵͥRd>qYoyי8 TV˚:٦[qsȋ]G<!D| .T#!֛<:<GWn)3H2s&AH<WK\]kqC} TFP7a.xpu!nC8o|.j ߔUzm�C6v秭AGoDetJ ̿ br4vGy(.:Nhղz3\'c3^ǫ5GSy:XG9֐S+08 ą;F%茉)mr*s  =~v"=4]j&|uVKut<=�gR7n!y&mmVqP3;J;KJeI={_b q'-J ]‚F4,0{BrWǃYh2`k0㉳wֱ,3Ġ$/*EunShESlVs|)Aq앶ʇ!*45ag?yz)Y0'I&m&ϋQ_Vt>8.-*ZȾ穤A:45ټ62|qtV%.Tvۉw1S}f<@ud'9L d`GkB^$mI7R0p!9$ZaiÛt,2r%#C|E1콪sBUՌ|UwjOb @jFoɮjp N;PU >}c~iְb<V=ؽc@}r)ڊes4h-exgu)3aahFAAD6w]_K3^ȃf jt Uw)>g޴m0iLe Ғt|aB/i<}.|9:Ƹ]�eEg@>$NBfᄴwr C8CJ!rLw &X$v4}ZV. ˏdl4yicն禎T�L^wo4@(0qc�Rk;O\me\}ALQ8> >_3uLx9HELFM6!cݐ|HK$x%^=j=j=bŊHOQ}(.}"x #P։iWCޛ-qb)+i-@ڽI'f[B ⃝}5<-@7:.zXڸH|K{StIriP^"d3,K'Ch:);+Iݟz%}~hLFK<fĸFiBRfH>Nu�Uݿz* NhҽNC14\6KyF 6`HYu-&§S De l"&E:=./aY-ܮL08f_/V)Wz&uWXwב);Y_yI%6Q^ Nw=vL.]D/d_;GDL?@MץPSuzg YVfM ;:˔nFdz̓^q=kÚemѼtZ�V P´�qX=桸˦t߶Vm sIrsR35`G:$<)u5&iEwC#Xg (Wy:U4E>CՐaNJ@ D` ,CE[9ȩ4i<26 zJ6a0>"UKiWp4LTVZY"`{RB}Dsy /H`L\V%?Rv8/QL "LnA4ȶMKz@?YGz0m;062}Py|jŸ!{;xŒ7DLO;G1M@JpJ>gP <<e@D;Э_rۡd3)~a$Uf&{eoChԑE,~(3x'%0<'S lX=T-qnvRƞٚYkKDIY8_Eĺ1RZ77 d:-i1xe RsWvR`Nt^&x̫ncfQMlpW](CHVGb~@l{H8^7;5^R!*E ]I>  klv?IO$sIr ϗ·ʼ깃B8Uw x2n HN+>|KLLT19`Gjsa;J,.F.B3Ag@v_MoIȺ5GdPzRiTrX/`.p)LcB γ+eN1ĎZ_.Wxی}5y!rBPJpG'VѬȖ$Jmƍ~#J^rZj翝b14sF Mm}~[74ǠC|<`̥wzWBYy@3Wú)߶}tO.lB9bKZC־@>Rq#J*'t_ @ CUVISxn!)@߹t'zaRm? lQNpRyPO^(5o072˻%wlYRu}Y=Sʂ@>VB˭Eq64sbeU~ .vy"EYonc"SKx<uWi' ǁ.V/e+Crs$>Șg2 6롦Amݧ+FddY.밠 &&6q-2E m%H!ۿa\/sQ= s!,tߏ8}C2%dI0V;ѓ=z h)m@ @4b7"Jݣgn,#"mC("~` RKq>^F=K5ն]Dqs9z5I1T^"u ;~KyDnbs/i~t7}]v4Jn^&>3O?R?^ݭHǔUQw^W~]=ǂ/FZqT8oh'L Z]*nbMnWLcQGG(f\hZd <<NFG)2ͪTF8x|d(p5^h P~>W62L۱Z `I'",Q^AvO@ן_ӷ``{"˓'-65o$wJy|4{|�)̧i; r/K%WVzJSLьc&Rx7pjEzp`322-yAC %ӊIQF# Ҿ5~ޢMjǙݴ /5_D8RX'a}YkY`&҄D@MƹH ;HQB\yv@Ŗ@4aEZ(7ۗ'�uhڻ_c.aYz)Heas*-|@Q#ބ}=ior$`r#E;{dZ6; a.Ce` x~R*6k5e\(| z÷V \?׵._|`,VgM YBǧJ!/, pnCʏwlѪ_rNԇp"'h'.ħ>I.d Y1} _Tb 5bj֤HtC<G<qSv!23& Էv0 [n-}# <~J1›ld_1A⁠UO^kG <aT50"'RU֓&ё ;JhIwZr2ֽ}@u&S+S3mR v69ȧ̾~;ӁbxiB/tl+%lSԛإBva't_m}Nȴ^ET.#1[9΅/;L�4eVԽ?\4Tcaմ*l۬rlLm$yӎ PT"-[&j!j|8qL ]fJ ns^x'c[W[ĝ;"5n;OTB~ R61dIuά$jW4J12 'B} {A2=Ɖh۷%6Lv#7hCrEPC nj5QR /O41bNѵ7ǞZ'[K]-doА1 ߚ+[_\)^-M۶`Ϩ>hAEvW4npm=$G#Z/% jjR=Y<*t*=IlR߶R𜞕ӨV LjTHoeEܷ* ҟfۛ_�whH~Sv3)<?=ߛTfyߟ-xIQ!qژ̾ fգݸXS*דt0%z<!;z#=m9z;&"$otx,nyS,,/i$~~D5SR stUX`O{~܃?Ш/ -즾4tETu}&UuҧFi9S椰v8-1=zU79 dAܩV.~7k$;VkE^Ԉl=[e 7@mkٔٶx誼ޓ9;0�&v'@,Aͫw+DBPd{NrSĂۖ{ik,E~T޿ )H ی9\ 7!IeR֯y8ؠhnVHdeo6-,�$� {z{iqlR ՙ74gtOG'8*2 Z$XآQ6!<)nk̰Vp 9%'QI)WYe|lP6ǽWQ!1,z1(YyE;=27csN3b{f aabjMңnHFR2glz3I{jFBsaL{> AL?uv&[{,ug e[/@qmq#?r6>Uivc;B9_;RQ"擂̫vGԚu " x=KM^1'Y*ygƮ^.ffIqI&,^% Q[]lvv2]qYVCwj{[eVZ�?ȽT0~*;(^Ar7U. 9u48c*v*i=bɄTl&ԀOшDY[SciI;W8S+?aDaGlQ\*#bϰu-D&8ũՒjUh`\*lb6H%U{>^.Z�yƬ.;/gh1_J0?/m=,cƗ!+@t5|K'P@)4?Y#pY*iPXg^\#}t>Đm#0#G$sHAx0!\ ÇfkU �L~=1~ n n,nbO]d d~3wzB&&/\ܵadva8FRJJ ټsHJ [<w]яe՟W<blsx\\g&MLsI: ,j\懽 ᎎXS=ɇxX^T@ի髎:4s*x14 lҨh ϣV֍vV*41.*FA_SQTcr)ҏ#.3EP`;.s~a[TJ% q󤒲l%n)Ա݃+u"*Α&@ r_M2)(+oCN>B (]5˻T0LVd-u|._<~*C~Ĕ< ]!ӂ=iLfy#☆>*5?"qbl}} k!Bk5AKo̘]Pzd֦ VE<>_-Cg aȋz}m`!I�q^=$HWu[K; n}d)b_4wfWBs de $QՉCalKZz�C"ӧ6L],gy^g+-9h~G*Mc_tMړ\#\pΰ<NAǘwӑDG$R)ew0Ak6l<nHGh؜e@b: R^F9Xd]3nJGbH;)4_t`<G}WrfTZj\6݅JЀ3<"0 ^2ī ws'i ޙ>_?d('Ii{=HVz6֗/ڃN_Ǩ,pE8n32*6F(QBrȑ˱MCJq+<ie9A�F_B5#i> 4fgZa4s>s=a*(LuHJ<KK˼X(>1Ei0Oޮ0C:uK#}zA~\ekM#0R X! gb˂)Xgj=#{QKmp6hxm�F}1K1žaO\j5ؘ3XQQBD<0@ruƉ)iO[n>#.ppqWAK}g `ZaA{C#Y%p5\chbؕuW�6I;+2ykK?'jq!m Hg%R'HEa嘙P2nըxLup+=G{.vB2,m~{W+BLYZ@ 6je l_EW]!B__>0DC6y*>&7w3uHG)�eJ!ȩ9|p8}C&Oġ.m-9, cb9MtOľwONq <^[I6~V "y9{HAV[DkHxr(v&2͂/woζK/ a:EϯBX2goK1'D Ӈ̏a{kQz;5i[̳ duؼ2";":(!n& ]ɤ̟0H6dKބY%Z݈"$I?+#!ܱKj?x෻e =ƫp8 ٚ)wxpR|+O1c.&́).*4񞯲2^89 =2s` 6և&~}tp#`EsOPS!L8VIM}y$oS ؖv4B5e ŝlׯ}F bb7 H1_9jY-Qt])+'県rgwǚI|pr"Q1v;HP]@Gb׮Ftpy Ḙ́\t A W`6lZ+lfƒB<8GZ=Wi|-Fi<;K.U]ɷv]Y[D.xKg;tU'ͰdANH]6xȘU~f=>{W&eYBTۡ�`KTK)E廉!Q_<I%߹7 ƕIMU-Pp 'Sރ _E!0Z!^y4ah$kWaWR9lIXZXZtNMͿzϘ2]RB7$ه+&<i  tD0숡�U1哼Y (˺s~G$/bh=|vbfhQprXllt[}R`boY\g@i+(Sz cl)6Ɇ; Zp6_S,qC \2%ˌ?aw+\_Vaߍ(1dao2*0~&kgEds#neUZTɄZ5Y' dғ#, x1jOBG[]n܉]dF)źa?}>.sCc:Mx@{YM' [gu5zm RkPD"�˂!Tlj\\�9p!*AynoMQf˰KvM"ƒ=j�@'!.z-]] QS9͊bV<ZAj&Lí |YƉ! Mp&/Π Xhr |>HK)hIZ^Nk2?%?maMO9܉!*XEĸ�S D"V<oAɐjm|YmH3E؇ք($|Qz;Vy ^ '˜/ZReb)9A"~[H<Az?�A\Ts.&/VUV4aYn\ _T̯ B"RSV%h@O1Ů=րfK4cȸBb'i4Øy%&]Pl@윶4[ui /&X�Wx5 ɣ: Q/|H~ceiCRN394D stsZP&]`("mM37 xaiQTGD58@wds�Њ{u\b&-B5Xn�YeZI5u=fWDi}2[ڄB_X:LjjvoPhїZi YPW tގlH:?;ɒ`[Na3'qx(h#& 8+<|'_\ࣇxuq9M&Bb(Ti0o*ҵJUdh 1(;Ltsk}`ÿl F_ut <m͉9ԴF!+( af\ e MZy)=Ƹ ~hBNnZѨb`Ʒ6 L~)?1Պ:ZVg \=ȴ�f\arQ!(wTJA=❼IM_)08yn"5ZK8 *!9X�b2N"w#u%U$a3mpHzF]7DcۙZᗗAbN5-* =f"8 7{+h(JїvAf?"K7Ckӯ97xJOPKXPyckJ[o]v[<u&Ӯ}&5a+ ;M(9)ʐ9P :< 1qX*G&-`SV>^tt) #C!|OY`*G&vsPq!7w=nrE`zyL(\{/5N {߫5{-! Y`… ej3C=ƚ^ˌp- 킡ϹXv^(SpL;%$G>#Yk{E&A?.MDDZΖUGA%)Lf$$ӹs?zBcB1"Ŵ@EO8\a$jx�bIREEX?BnBSCЪ񼯀7e ͍V|fރ&_)k^~#sgȄ,$,<t#][N PB60 _IW~Bv%#L S],ٔV72M9'h o$5c0}CàYpx݂2a -ng+  */jU ݾM$22/8.u+r,O%TfP}(~YV*to.F[ s62a M*G!OGA|�9S;Mkw4f.S!?TJوmAYv{K8& e5(|+C_OVfhB5߇m<3w&osD&t+yejbto)_%4kUJ!Ǝ94%9k]U]IB4Ƀ4$QdYoۖ8{V#<CX3DLOA\hDDwbin?H moD@̷YyЭFpeϺ<alKp`@6gzO٤H7@dhxkǁ.3/zµ[+1ޟ>Tj.?TF\&QMG2EH@,JP˧l8?FFLi?K`O!cڻ$S~ t ;+-xK>7;C!컷i;f]j4YljQ'򐸃m)rW(Xw:R71ghTS9Ṳ8%)_nY2vzD6/K윈9F=H?Sg?)UWU`bPtp!>,SkMroS9 ,f9yɃ /$Q^u$~E۔�qL"-GJnl8xg1L ؿ ? )CeF5YG"B]&<MHY@^RړԙY'B9/PUGDa)pbx>Ɣzl8%,;Krpk?+j(UvR.SGT򁓌 ]+Z5lx?_)=?TuX3 -%Ip%t>{�l MDYy}|NXO@p]jgEj4K eM^}%+A]r<IaU'g>" !|<5" Q?ɕ|.yN*!--Z]rؓ$";MgQJެTZ$L`.k'gڎ)G'Fi/IϖYeOʳg6ֽɀ6 ךǠ%q6I͑qf-QF BَUHK{wCl(_)M0?`t*]/88 Qw|5yhBAY�*yHpQõ@Z :(nPDgVr=1 ,dJJ?ׁ]7k0_fnJ[8#1fXlkOBYV:M-HaxF^7Fx5/'~ urGG�sէ6#HߵYJK-b:&PkB%>܆LH){NJ2ֵET2U\R&l뗚?7@T׀?ѯ'*Q.&:;A|)ES1FJ>)r<u#MHQM5"GBǵvDbۆl 0Cl}beO(J[. (~3)F:U7䲕)x|3u 5nf+I#Swu4_v?\sL ?RsaKǣ|QHQN%uj)[p|dy,Nfر{% r(Pi[Ȳ_#TJ )oH>T:V54t:uÑJʢ%GBm_7;<Ejzt)!ɯipOruDT|}}2/-|mB;'vbQƂeUK{3m!DRг?}܉=9VzP+o1@MJ]hƋ>K)VƧIyzo&wa+$Ʈ;0/ȦHߊ\n6$ LLLrfDJdn{(kcCuݝ%:sQ֚޲JdZIb5%;!CO'Y}剚pM.gʊٕ3ELR'7EEȻҶE'Rmuu8>jjMu0NQTZ`!,<P9{^_ir*L.:Rm?/�/ jW(3T8R Y68mj˜ i q;,KAGLFDR}�CAU'Yknx->r?~2WNSj-y66t!drY)|ľ~ԾxZ!_KdEfGF|0/벥\�e\L/ؔ%{VCc>!Egs_|IUNk3Z`N=I,dИL2-z.<FBBm4o֖V)YսVPX :KQX=(RP?f^Ъ 7܈,>`H*VV̠\9ƴzً ݺ�X;;YsU<}-8<B߇E.76ÏO#?@=N3|x2#%pP}yzG2ʰ)|V5W&&2?*А4sjDYGc^3([6?R]T"?.b" vAT[qh6}q}ae{ܕꅿ:؆y^PMPQo&zI~[RVo5o:~?]Fİ>Bn]!>roY$ni Lm!q^$$>)D1q9 { ^j>U`7%m~ 2 SFwr]D^ |kopm~+JLLxxcq]w'p)+_Xl^ylw42�NBBvK(r`&@FC? #!kݭѹXݕ'kV9CM$5*o&Ө,W'Yx(" YQ/aG@(a|y0=<.޵UuUlszv IU,H^L�S}H}[T*Gx"9f+v^t 鹾Ŝξf@&uv-)mr)<a#!LHs#lɛLE2?o 7„+wQ>;w<UO&s+W[Py ZD_f!Fijǚ׋1`rQ]t[tՄpA_@XC#[٭/ڀ֓]Ʉw@}AUkmp D1@ 3<}ɍs,Jk .qKo~;{[N 2hcW{-R!Sj''Z$Db{Q?l"Օ/ۓ.Yw8]v#NQ�|1"C` 0�Çy+D&@3m<CoZ `Y&%�T^fLIv""/&4:)ƶ -@˵Lv<ˠ~8@F܆x<w{Q7k,CmtbITU`Ls Qq7q^#x_�_x_ 3W=*ix=:W;Y jXI| c|Kqa:ٹN#5z- ,+f]u�qEX#ZU8UZY%#y狼(P/gpfk a |Ѵ[)LO[۲/ƽhbi>Ν[k I)@ w^tBHX\k&OE�X:MJ߻Ђokţܢ#ۢrP7ݠL@فYRL,Y$DsgqM=pwen>gKlǓyCZ\DeUUPSMG&3aw �<Xr4@Va A?h' dm)6@ > )\IQ_Mf) 5MZ r}5_aMb3C?_w[fkTc ?fvq^\! :p)?I[,.y[BC+jEo0@!,qXekOumٍ&I�=u:r'RySfgpʿ_UCÎFP|iLKa6"F%VCرS;GPXP)'i$݅I-Inj;v0[<m-L) Xif=|H2%`ɖ>$Cm,;Tb,M;ى+9@,LsM}A"c]�FA�)nkja > LpeE_BWG~?gg'?^Bt�nps}2{;\h{I/k r$4 ,sg+ v+?q�44*KIm|G@,đ:=i^W�bxx1+bٯBޠY~c<RA&k950}6r|8zlrE~<iJO�iW')E  2SjRoc状JO ǼIyxiXe8�n7[WdMδb 3.a-{cp^d8{<vr)P%OaYj첇Wێ/<4BuF>,lL!C5K;[{v%KTZivl RH!tuv\@ ?OSM8s2aFb ntX YY3 MEbd+1!xaQc�϶$ %2;ՋMp;:{YBTȉ"+%{/x'($/#?AV.XX5fWҋ<*d%gLA'u/%Cx#iF(y#>{+/d'GZųn`HaEgbY! tw8~~£x{-xX+F=vnж0=nJcH*%ZSŔԸ&ΝQh-qJ1!lDz,!?@yYpJx T'ziT'EcO^3TsW[~0$±^$Lid(~zdžy0jU=hKigBI͒(-{K\H%1U \fs7fd멮3a0Ci&mʓ-zjn{wV$YYyI뷇8{8A3mj))k t`IJF4j@(zOnd#YYGptR�@9^ A|r ?3pξ2dH,?XIoD"qX^ӼS`&D,2T}Uv`Kmw&4 omBoƤL(w[ά2 9a c7*z+ ӵ 7 ZIdyd=4=>-ߔK;⸻oWC1Pɝ߯|Z!RᘙHl-J! r;EVx](T;?+”J Q�0IJ؄$Zngmq3aQCsR(t$ulfΩ[I=O)DӨ[-Q&KDFO\mVd/ �g,|Ij }`$3R@=j~Έ0ߎEN;6NGB.g:PTx]wh>E'TJ4"bY3~Vh.!�rJh bvXAtɃҖ!wU*wf|sHt O%bRn޻n̈?!8b鉭w,{6t>A^f�nfkR?rY:=1iv$Aק` %^BDKī ,e [9IrAbǨEԝ*^Xs= MHwH rE7j/r^`mymy9eJ/ p #@$ 'ٰ&&MLkF0}?Q\~K۰? 3KDKau뻇&(Stq0+8yM#\b*jka6̌&Ĝ|WOP$9䍂@dI˒ʴJ%gր?{}۰ i$c љߏK2։.M;"KڷOWSwR:,n[]3lOV1{-ORd`N4 ("QfS<X@H<`!b_e5 Xh~\,Q&:Q<ڈ?U='>t"ݓ}k*dXڄ_MEg"ݚ%* Qc/%HO7x򧙤[=oӫʤ AoBwM<QZt]o`o!BB㡾ȒN_角7C,5ιĖm vLBީdUQ+L,'vʎb&!iX{1RVy8[�Y)SJm>8jش",K@ͧ<ǖ_qɼ5lCw1hJW-\t.'N7/&}9c\X;@Fl&9lUo xf{oH -\+ ZVZ~A1V|j7=0U5$[Kw? ᅠj&mS '9zJ!iBn,SX1> h&!Y]GB=Av -+qX | 9-AjrYqe2?ƁJ/wT!~c[V kt�w.xK]0>!CΓ7Yz 88v f@%Wƃ�uimN̸X4Q͜*Tk2 >S߶cM?-T#ڴ7ȃƷj̔1ti]M $Eys,b6ff`}sO]TczZ:5j3vZ0„3^l5+Aݞ"Yc9LcY`4swࠡJ9փ<@ J)c9u5jg_B5}SK- njF*7Rېy;JstA c-|Zǘ*5؃<6%*]E/RDt{:E x= V d\ yJ֗(HHQJăk\h$G 3ܽM%YCc lWPi/-Jϡ`,.q }V@Y&͹|r]a_R+9ؿ+me#HG wl0w3Gar%|)'Xz!ɪUG8\*D#m㜓X"<\ŧgѾ|GAc΀uU7ǣ|7 nlĥQ+jrG ַ+hy͹SFjTk/|6dzN)}n ^^JRƾ^x *5/lUֱ⚡Y`HW(xL,Xwsv="ӿg%\ [`59?ql"N<v_@D?`Y$> B}o cݸKPfg3ULK'B;{ ٙTA<xt!: %1CrWE@7p5ҲXh F_^)Y1 &t9E�/Ump3N CϙzuKqg[r lW,\JGψtS٘fյ΢bEm�09ƥL)_mTT)9czH9gpQ4T]kE,@P퐅�gD?55_wW'0%1I#ڔִ`" r)_;/չF]d6]9S^Q{E#Q;/_/{PAڜo%Jth4ķ WramվGZdIT.URe?cHR6UF#`LnA,tCA4t~[i1:nv˄6^I+S0*At#)%stXD'@ޡQLWANYb/bƜJjKpySu t>�rFH܆z,pKԪy\>Vpd@YfO T6d3-LYi C"713Zx rXw lՌgZCpu&h$}x(*<?bT(A,+p?I`J|?CTl<rYt5Uu 8euZd h,TC2X3zZq=BtQeY#SEmXGw6 )}o6C֭ݭF#ʂE)ȿؒq9G+Kb#%d%np4#GkȒ9  Mmbf hnUET6 #G&W C:IѳgeEԥL(VVfǥ1*}0Sd2j\L%I2٬l#VSmQi|Nb 2K_5hd2wq)d>XmUaW{,nv¼‡[ s}5blônˆM9M)- R޾f_<? K/ kt2}z[y2* z3DHCA:FnŀE\6+*DɑբZ+Vk:,P-kZ~ jn;M56KfgKGC] 9txXQ䰿"ksH.,tdY AVH-zԐT@sHus{}%ռw9Uv=A+ #%Ӟe4bJ˿d &0�Wx]3?bmrp;Ią'7u?;4r"W\j=rjX}~3 }O|`aTU%B%/i_5l^r $}(f%fjWk\<lNոڋڜOv@DnK?.uVyL 4b7�zB䈦nj JJtxvԾ~kJ"I9 @yͮ1} Jgh7^foˡ>K# gEraG8#@\cx\>jkXRSG|#h>U~F $j=FiK>i`y } nju?ȩݪ$Q.hx}cgx]j4YqL+qtp̨hjZJ5fI=Vsdӻ۳uˇ~ <*w AzCIa @InȎ`pWtBXZ8,"2x-Sl%! q4Fw18h(֡؁8Y�b`Yl\Eh^$ɛ&[oFsv ҫϭdN|1U*˔<l-v9*:ǘ9l?(#%Kg$iP ܑSYNA跒Xps=-ISQ-[AQ0Ih+d 2�0+'b?}+(Sq0O#n9x5;tʺSQNoڮG!֑nV{,i5Q=;&R`YlKa#9f'&1b\ʜ-A^Ξtm 2^f z3Ҳ}$b=1Z{r-W�n9ͷ#C8;/[#naG[r%{Z-By mj S^r}֢Ֆ7ϐgh䏷7O\l2*&9M_Oן(+3{ș޲,dAP8`ٺXWkQQ'˾S2TτPSG?<`s,2Hwwď2n[A�ʑ HO{Prd+Mu/QH1>-^-;\ {88SDETM_^޵[<zYL1qoW@e I1 qJ~nwIƹ4&].֓ jWu15`wvu>iK.~ {fS[TL;iԀ'"8´ k p(Br&&gßlڊTz^<ICB<oO 5TwݟWin� t7Vj�}s1uw!LziO8Q;&<oRYսk6Wy=Y| G;e|dYga֞� 'n{f2zXr$W>3Wk8#m,CpYgjm*{ +E;wt*XY-0]ȈH!o2@7!H ,K >.h[ <#ΈO!NJZp;Tn֙b޲0.hAI+_q�5yHM`IčԢ+]W@Fmn6ôHO؀-# 'oH7ê3#Ss|cw[qߴ#s?$ @Ŝ0&hz*S3Bݓ.EepW,*"Y58`jIbp*kA-&J"W)sc&J)KlT9{q0:{y{i_ -@~[-TM[2&1_žUI4M>Y۶_S%P=y}Tyq`vC/onwKP{ qAJV޶x_TpJUfܾ+u7a'mTW3zRߜ嗽ղUpO4*u*;佪:opd(EyW"R+e#b d+#IA3_7�F[w2{d~4 ۠RiU<~ߪha㈗}ĝ7X(xQQe,x<u~gVI\'vo؀Z5U7s묏v1[kh' Cp[cUo�Qz>ɠreG#\Jo&bj��z�����&VxY4[Xx{g 7|ӄro`ڽXK1C!QCtry&W6hrwxN0ZvLd雹, ȵ8ZZ2a@&$bR8"dtK͌Z~CmF^<WmXqm"5/zuٓwG1j )&1e~{? Ji--m < }H .V8 "K1?VZYu{p]pOunR_wd%tz-;zZ,xQ+t#}#qEO`8R~;W *؞mP#=>G*%zGP֣ 5RJO^| )(4m[{ gð5- >E7fmOhM`uR}YuV;ni[AL,%{W}(AzO*Ԋ=?tMhU~ҡ@軟5lBlntt!N\\h֩uZfԖ|ַ2Fɛd}H?VtZ6m6 y'6FآQ^d>G\\^! YRsPFbOp%Y4՞7hY(B_94O:zWdm O2ij+T>v~3]_S9{@�*7z^U7&ijld+ϡpSN`T0WaV /i^(QP7:" U=3aL5`D~>ˍl5.֨X?ETE+^)w}gx1Q< YB7\,o.=;=MA^�Xi/ tfMJ﹯;֌oc?V\HF{[~t,coX;ekd8;(_Za1D7|w{s+G808 6z9b2CVR|qUዓK[(LP9Bd.*BhJPk0DҗL7Qn}E:ԀtinHfTsbHG3K6aYS ^ܽpZ隟.e=薏77^x99?귄b6fY}.nU.8O\& 6YO, ωF$X#" p~ۨ˚2^Xc\^F!sqenCTnC61կ86/*C:WS 03l9]Z5aPv/;g!fLX YfӬGst-:ͼ!ljn hQk#^ɱ*pЛ76{:)ѥ 71bZcOy�+lo˂ߤRZ9Fx` 1nGe;؃ aA?\ECEmY.\[i;Mś,顈'O`pٚeѪy>\’*c^b΅ǝ ^"؜ uk)xZ4!tΜU⌓&VeJvȺkq[lxAivNK ]I*R;w/O1An wcA!]]z�%jRfK sL=5CR^+4;N쮂0|)!Xe;: 30@(Ns;.DČPURj8 2J _RExGNR06bw@2A]y?3IND9ImZy".R|@cח"}cC|$Pkh+oC #o,&XȅLqUvŠ$U+ !z�dɎ)2|gfYXrAUCRA!)H yڥxUAGuX8o4KWѤl!ZC49]>§]Z}%p?Ra@ǵNa.3U9D f5&._w:oT?3mĻ4jȆ7Ȣ :dg | cuͮ׌})8G9˂Z&hԩ72]+a \Z߹3Pv3( {@Lm;.2&&^kɫ W4ew̓.P>E][\~Yn ojAV/zW̱7vf(:eDˆ Z&LnZ9:ڦ!:Y:+)=ӞFse/Z2`0wK>%r]ߝ'q_7s%iLjXAW<KM<TB z4Uh vm {XtlǃE*>E4)x Crm;n^Oj7[qx>r%]ufNO109roej'%%@ S*_ ,ӐRPq[pnucݝ!0*M^j{zB2TIZs[w/>?")>I6a\Cj|kn0/?]arz<2ԓfڪN|/y1ݥ8[.j:3u`{"̲R,hbF,4y-z&i"Ё1*WI*a`+\v}],-HվoAq{ڐxpa.f] y%kg Zў2'4v�.azQ)AgԖ!@PDZQH; ~JTo(b(/Uw[2.8/)) P28[�Nkl�e: *372>þa- #1:׽`uebQPAc-hqhZJvt;)ǥB=p4r)|E 1h[Ymg@jzu^#_hJSyGj[<w;QkLFw8JN?E zGVZZڅCIYC$‹1^y_#7-UӴd.g:b[<Am$f͸;.QY%Y6 _M-]>rI0m�TȯO׼3�\J8}i@ho nt\!P{KʧEH]T~3]!. VtyobUīl:8kTK1"Bm'5qăI쓷/RIgݰRLҠ? h�0>M^t;Rn|(m%]^Ɂۄn%!MA0$ !BRcGēKQ:$oSf|nW:: CR~B:\|X(N[a 2 .mpQڒO$m�|I@^nRv,{cpF7s33^)֨@dłs5/.iC(DHۮ2 3 %e+|@I^Rx8!aKgib0B۔[)|^[Njp u.&)]덼8u[æ2P՗+O^ś$%R8ƲiN)uɭBr@>bfKMv9+_!䠘I"J"GI?,UŃ*Q_uȭ ccYJ]5 $ D|CIk5qNH#YjYVŸospٶ酷v72!񆚸ֹ .g7Ɔ[U ]4�#S0J z.l2 >q>ȉ`e]o d[uyXd9DfMi^@6/tV`I]o=l-�l�N}FPSu?ǀ Kj_/f_5|'Ra c= -!̛x߽ۻ%Wjq}|<-X3]-t66ڧ=._neHx^|+,OcSrHO�f-|=cݚ ^^ 7?2ڠ[g7 C=F$3VlHsK:vP>VFJ/|?FAX�])H?Ury[rOuA!zsP8\j\$>~g*{+ J`] ^ �W9F>%d& D U-b./yt2Qt{Α+obM\-;Mrt`CAvꢣZRY|TT7O̯-(~aueI1+%rY%0 "KnסaBOCHoTs-6c4 9>U6:򕱡+ koxxF--hdsulf ]{܇q-[BK0 95'݉@f}}(7Q6NX> sLKj&J(JG1=IdvmRѭʡoFMsW wgE]"f<Y: 2G>Ua9'j_u`eǵh}p+8%EoEnKPL!Y) 7ǔZlvf70\XԘ(r'Ѡgx1T~&1daR۔ *Q'UxH¡68ו>ArE|QW"4V"︗F;wIϙ'يAֲDy?EzX,2ނ,P\GH5 9qȹ{3Aw*4x$GՂ֌ GնYd +-t2dj&Ydebc`^QҖh ;Gۑs^DXϙ͏n ek-ZE2ª wOٕ0E 󥣈PBӛsnIl<_ .zP,]SǖQ7 0"HCC,j|j''^7_"~=&ߪ.yT& >?CSlўCAYKhL8�#8 %o~fatb![vvNjrd q֡>S܆}n  CXqw7M[6(q_*mj ?ΏSn`(D>>e{WuYS1. kOB nW:Eԭ"rgu@؛HY կyvgy/@P[Y@Ӿ?ցU~1F0;7T=Yx.1}2@kr2AwmmdNAUv5`Qjqps?V4"b3O_=aw.\,%Y8 u4r2;y#dϋ{Zۄ` V\)y) KG m$;Tp G{{ReGC:T47wd ]wQwg/Ku<| g}%U5BNd�6"]BCR3~n^I!Q6]cjb~] T j-Y 0 :~pJƓ8u_KMYʡ)±: ѢނyPB8[gIKt=PrVrMڋ[㯕 eU6 [| 5ҷ]ξ̃4ݒ,Sɤ1AjmAG6GtI(P#I8E]*^=!N-3t~ V^7Zkn}rʈhUǏ;LfM5α!6wWƒ+ǁ#jn"?Dڿ D�2s&Yc=Q( u4>nykgSD{B\SNj?T B:SRzh8�fS,bN@Z`]e4֦S!3w37Sw~6ixZ1Fj@ b\g,^cwi$6 sg6s\͉jj"ZvE DZ_+1]_@~lzssEcKdJv?KrTN뷪ŜՍMl݊B`� - HR0j$Ko'E [N�ÍC'Z}BVz6{LN1 e젳4)qKbDM_L fzq!Q?q`c4N]ef=DgwKC1*'g2=GB J$I^ݯ"f1̝} ㄖɍNޯp ,>fƶ�uDNa<j�S60GJ.Fz*F%O9͉vQ])pV4y@AB>2Lh# HвI*j1dC*2PnT/93i~|&;8, _%p\wB՗`MƋ Q"nU{0Y� p kZxjF ,T| 9dzoXc[ 76+cҳòscad6 `;{)�Qz2 /fƥ(Z?M wiCp|U_q O4 ڙ(_ڍ`l7 o26-ڧwm rN:R%z3XN^fHG:KlCMIaA/GY`<#Ց .nׇ3{d!ơTwP/e[k5$]eqjޜE F!5ߡ8mI-ºD\} vlKy؁1靖 ѕ4 AaghALml)ڋխUFvJWW/yL~ ?|\Jo^B *$=t'I>*e7 Az\L[`XP@=C|fk99­3WY__GQUE~)Μ;ȫWoTLSb{hӺ!*} \TH(!s$$4J~? w-)DC0~}' <˭*U]M �hѬ6*3.N_4r_|}h%GhgBm51 zҎ*l7d՗|\|+~tw? t.H 0^DI8FG@ޣzQZ* )>OQsyOXdo9tpL Tr/sͻOmk4O5"Q2#Zy&ϯx}͜.8"gCyJEᬠ ƲUzjrKbq�qh2S%cM~.RE/ :t&v| nf~I<KoSu%Yż3Ls�*׆o J]|Qj.hfn*:v_A] gNvR0-%я8)zg ɔ`iv6y֖K䯣ʥBƯDo!OTڋ|VY-3$ V)uW-{p|۰5n7HRi?OÇQ *yGtfVtkYC(+t0_eX&n�-h�YUa;̛e +A_:cMy)> zfᐄ7G+c3E$znPA3nWSVHIbp>F<l\�r*~iߝ6lpzE1Mer< oI3a<R\o9e\d,]y``ȑd$F) ܯV.(/zC rŭ/<ֶ00`)#j_^&[VĝHGG$i}͜qHH*O=7wm>=D: $=,"@hi_aCIdQrARv=tߟ,.rȺ8ћZ b ңKSvi/濅^j^A[MuB*r-Өv[{ ojPyƂo]pgƷừd7X0( ~|ZHveM2ז(v)*.ðSpР?R ΢~==ۿ�Uf?xs 4$C,y"&J@L?E&DNEi,#H[ @49b'@,`owXj4 :*}p*;I&ɉ̂&82\saxe")B <C%SbAC )e{6p97 hҟtfy YkxU|V?p4ָ� H ݫ <zX5>4cv9˒ASU!tYFXERoK4w#5p ͖v-33w+Mo`9DSp`S"ahv~!mKg;-,bF́E*0}휿:Ti>!.#(,BnL~!acKϞvB9xUd)uGrp׳>-3ԕU tuze+>^U3o3ɶkUXd"iov3NvIT!xxqampV:a1Ғ/!<(կJ8LzJ Ymm?0aSHe#lII YBL5UnoMcc0$" (83.8 Fqt+`sr(= L {ѤmDȈh nfAD„gq-~3E:Y ;-ޕ|jL{Ow7H0'Q=m?.3coF>ۨ(~Vb /٣V^\.�bG MzK{q.J[:@,Մ1_cWCNXmb=5)y=|H~/FgOY<^NŃakKq.0�6�Bk.ʮ• Y$6iJn<>1|aE4T+vCơD!4�t>\U0INoK8DY =;r><F[~rN+Y$icl1?4˕m1ay|E.N\a!|bώ5mry(lqJryǞE$<bfApO>CHR޺%HNW2p+Hp\>׾H/`I&Ft9JQE}WۇN(Ρ#I*/Z;t(E�O[F(6ixˤYp5]6{Z76"3,,4$49?DSK=ݹ*a>@'ˉFSgP@B~}ĐU3[`\]{= \Y"oHyH75XӉ/>:MGvX7Q\kg4!3 ['+^SWs Ji2l({-KN{^<rVP?' VViJ37 cdfI悚Vlqæ92ZT`[PґYv7m[ MT@XkMGo3.TD?w~pDGIW-*Vfmw\Frcz3pnLω+b倘P费Ɏ9d9A`+P8IQC_7#qD`@FC5l�6ݒo9zP}ku o:RoLYe5`ҊHm~!+_WFSkr$t[C<m Qf8gEv,cJ];Y"W|q3fxa*j #gyZkF"fiH �UMG*$__1 _**"(Kjz# lq[j487Եr~I@m|x{b'}$:KzbUb7/ۘD2,fRZrQ+da/FJՑy)9?8iT}J;)~3liV 4J b 7ݢݱ ]؇g#Xʒ#8Ak#VR28YOlTOcӄy$K%y~E7Ԯo;KҺ6Waf~Ssl'gdP-Lqt<p+Hc'l^,TA;i:'a@.Ux"J*UP2 gؕC1oL^%"xBo uVѨYD(mc *L>6[/p=5H@%<fN]4sK|D>Zd/Ï9b?_-!ɐ<h=mgt~rLBN>oL>ʢ?l qI\}Wj3kYuMofu0{qdIĮ ;eY\�r]yCvY* Ϛ,G�7w{8PlLŇE* gTuF33(c]g|M59lS?s2IHt& a7k6'z|BTVw{Kg@Wڡ.YQݽJTwlզ.uXk٬u5-62@ tCIF_ B!/0is_!~zϴUI2DQA+pS&kޭq dFH$/?-pxl\Ut y>ju`QaIQ=w>1 �g9c%\ Sf=g2ecx=6Ljl]f0g'=8Ȇ2]�%fckb1u5-wm9�Q,&?Tzw?5\%[<Ba[M�b+�ڱ>M1Slmb=8KPL*0HGS�jOVr[y) s|ts%P&0OD.d[j[Aቚ<*I�7G-X:-r@hgǏlw8?ͰS7QDpP~"};Imޥr5j5V.JcF6GA{JtW̛�5�X| L|7ӅӽqAsnDS 'EG[OH <%H̳9?W<rd6\PL6G NwJ+lQ6x.ٔ\='<x}B<aV{n2+ (鵮͝?% 9x|+ؔeAv,23e[U~@LY*/!e 6Vp1!i B"T/ftVp@o[[MH. Ҕx+́ΩwZLQ [%|rw57?5*T9tO -Gi YP&Nba\i%U-uV=B7.XpTMz3zg�(wrJcM.FpUcf ދ/lWՊ/J!ٗ;03>gݡ0-NY'x$f, BӦW=4f(y 5 c5<7M;|'%]app|+I3z$q0e ^S4G E:PbڇGL*vۋ5ӈ (4yj6U_d6ʦo+ܣ>6?q sj|$" ځZȖGr+Q'ðx/3s&BW%hk%.'_, æ�bxJL<v|p^}Y{<ZP3^Ԝܟ6pWtLB_YI)0xF!$ĕΰAW.Ʊ!;ܽ3l۳_\ҭ.ڒG0ܡ)0Ŏ>|\{1׊oB?MK1l`.d#h9ƼBgxٔ3½ τ</<z{)nı&r yFH񻺬:;f Geۦqې}?QvM OH-#ئu3C"6ic[E|tCz hy;E\b?(8AtYjZ > >E/|߀Clal趖&>?qwGƹ%~?�TKhJz|$-�0\4W~ض�֜ ьđ?PDPĩ/K %s|9v!w|k .9B_:TYۢb^dAȒ / )fYA\Q g'OnkAUVNN^?OՍq%vd6O Qʮe|LHgSDT 7+ LMݰ!3ЇW*Ǝ+u 꿈 eIl:df(Ez<}';d?}F%5N%:aw hF6!9qTQĄǓ_E,Ckc\9NsXMpumTV,*NyAD\T $#ϹDM[O\ -ΆSj!4I'VʤZ&* lwrzEJnȄY& &th'Thn�, ÊXN6E6Z7հAWYʁ:qcVb5L&e&73)ZdYg7c`)uB ڒ ̸* TCԛ҆xЌDhB:v=R{R]"4>S?-PIؾ1KR_68A Gև{ 8|'?QORvul>mZKS}ΥHCKML~�~JWK /p.;S% _,G Y̗i8eَc�~zݥK@*G!eZ)VFVC' =zKHW xV$Cr%W4T]YƪhѪ 0|8&Իa+тMy N t.CI2JurUMrke3tSTjE!i+&]WTKR:Q /P ). 8* d"' EȔ-_\SDwՈ5Wc Gnyh8O7Vٌ}L�3G#"rԾm:q}M2iSs{1P|gMLS2۽J0,O�S4MjؤӕKqƈ\/1+AKVMZb,G*綧7` U)da lJ2;〟}z:<̋'Zu سb*P7Ni̟3qLOyl۵so[ ){zV;9%` IJrgv1N9J4Ñjޡ}V--_CT /+xl[L=4Wt6.UmlR}Vq\|.Єp&ٹb]>hҔ>ό5zn"LKoL= ('|<Gū Oᧁ1xr |A2{`RׅEL6 �>%/Hpiq9fc�K/AuC%_'om3&6˰j瑕U'�9 f[ qy4wR'\yI_A]! YxEZ l%?[l0DǖA4¸B!٬Og- <.8'QSI#>n;Շ{N%[+f3E O 4NA`TWz~;1Q>P}fsIv߳A5p g %4@ֹS!++Q\Ƅ/n<(}NdR瓇h L.},hy8d^?�fko y^A?yJ*F+ʸcRDZWk „] l3̝~Q$A#@TֽK /0/3Clp6}ld"S>ߕ^&#s\H ^Z tX3vtmv A@sMĺD=6K=_ j{'4[Y<aN.F'말HO[u.SN")"x2Gp_\r�3Xs }ŝYjZ6Ṏ~Dg-�g E|8?>4H1^J,BW#�ֆx~u!iien5 Nɕ=(l<:w@>r#Mv 9̅aV"S$7)$5rr edOz[=hА=qpu/Vlϐ@ ݧĔreIyo*'ޑ bVt'ֳ14M!>^b0#} iyCUԖ++#Odڗ\i*Ps2"- ձq}\u(䥩PRM#":[+\}iBE@{a 0o)#eAۘqIzoj ' ;*YeUtPDE%>+24NЪR<x)Z ] }rnNfpY% YH[ˁ @hY"md'6WؙMq 9l4ަd8#s0y>@68G DKןy3sOxU=1#vt(9?!mk9l.mN}ƒ"<?fSӑ"|v=^|36W-/&!O׹m#˖[+jIomo&/e -hzV娣\jҝsqQD'U>P"g w͙gI92$;RE+OCQucȻ`QCs1`dV@u*<`;"/¡4M8VĈZj>ąa$(`y^TIɮBw|C_ߣne 8ex˃yo |gpӜu r gʃp<?{ˍΒX@x'\umϦm֨ p_Tb%5m@ ۵D% D(B<B(pqejh}.P7#&>a-|jsg iy+ν12kZFӷAԙi@aӱ{QEPTT|c2Z;`0~"'~ ۰~ƕiUsDDm( Ikm3M ?e7׾?/míB<Ę&8wYcw"m<PWhrMW'D}n|#L\xK6kPT [2zlhäj^V2meVsnc~:QA(`mF2)؊Zƶd;5^!+j#ڈX_u 1MW!GiO+/hI8jzf lAWL]~cwl"~͠ꮠ93 ݑ\`#Ez[]_yn֔M՘5\g߆aqMhbCz/o/4]߬aN_Uub᤭K3ի!;9ޔq:F${Ci-g.ª)jo*%8 pDꟆ6ĈtqG98s8(k3u/|3'gcsK^PR�VBo THQj﷈Xq'|p=׌;z-kj8HSc\GS(&o/B0+vaȆ2OhS>l~VD_?ϩV}v̖eNЦIjhMk]E$xcNΒ@?Fӧtq?vuF;|g=ڑ<51/]'}TGzfU*30T'+ojfo >GӎL%ܟds1�&M~!4-dalo/'RcEGssu3뾄pKoq1a B)h>9Y"'J6+musWw4Tp&F˙fX$TS^+&HW& `l}m,h#SYy3D[*+񀖹b!r2gsݿ,$b DZ[ X3oQ5�Q"ٳѧ;VqᴹW&eHGlcPc`&+AUjsO9|TR :w%Q(k~RŻt[<,gcY{O#T dJM< +oOhF }/ Xl$8~\"RU4 c$->] (�GHYmۮӚ)@&ʋ:5� WÖ0JU+4>|� J.#t( USy,Sjc Drךxl='4bWN�XpnUILvWmFkDYv9?`V5<}a!EYH'yq>NrmtQ'^޹' lI9kA-(e墸ź߱4+o\p6Ң0D808=ie퓘 :t7ܭy~4 g4tj: +[@M=:P& $-@$rlDž Z AgxRY6gfEb pw#ѩ`#;?:@iadWRp8rRg,wݕG frꈃgVዚ~;gyHy|b7Ua$$v+o[qnΌɒl<ӕΒ!-G% g M26ԦSt;rr+R&WS݈V moBzך43loa%TE_~)�~R![�l9Jalڑ0o J @p1`@@9aHS1TH8Ny) jOx,Ό,Q&+XJ!Y<יх�.-zDlu&o_=yj ҸLx`, Ɍ[jd3GXRq.(6e dRBe̗H P&#k؀:pPC!"d XUZᢠnʤRD[_) 3x%YƹR HK A |0.YC%o4VT iA2y qlPuGJة2';"i8U1cmjȜV,oނ&Ъ$hA<oVG^,m& � m?X|S*,TH 0,/9ϴ@Oڽ~$/ح]%yz NyIbWBi-MM=s=+D4 S̖ݠ=V7g%IS%0+<RVU]T?*㋴ QeF2�YVdKepӘ4*\)C3!W{wM3N\o<;I̲ѫL96ڑ8O&ǵ&ZFZg8P K/[cR׊R!vBn݆ʷs &V>C~d>Cɳ$%Oolu6Zi' ~.v4V>x6^.N깖ߔA?ϴ72$ߦ+B=m-k./Nܰ'= |@7<3ew[Hn%fU5TC?8-h,s!"`W9+I sH\@;d\A1LJ $ɦ$Mq笹8mR 6%/qq`PwzdǸ*@mj6,[ܓW;1HZ3o*N/cLEv&ɒ AE^[ԈoY| #e뉵V(G{'Ŧe/~X w@GR:$^ hz8F9^E垣s6Xߔ6E)KB ó0`#.xC%T[u[n:}^6ORV`P ;W8[J b w>=Z Rĝ"hLH|Q"VQiSy ѼGT63<$ ǼxSV.Eޠ]>&llm.|(H%w*$~xCx3>H`fˆ mlUS& ?k˖ zS}W63_0ǭ~p~Қgw$- I q>Q@\/4^. b4&jzRŰ@UTR> ?D&I2{G<̒�% ;BFs!'մޏOMR8=ju[|WEO2�5�+|6vwj]REc8e:aL*D*Hy6S4H G pŷ(Iȴc:}4VLmO@V+>Q½ˈ/nsVw_ fڬKC;DQ8ya6˻ňx'km(s!`T}˩]~[baM*YA@y=+~fp.Igarfdhӊ&}B >�EY{ m*?9xwC2mo<,w#b ǣ0hE_P]*@:J<7?|ؕ2-[%'w_$H;zIM `J~%R!#.}NLۼy 6T7'T %�v2;eNMib]axiFڨDUXp14]Ԙlg T݋.΢^0B29uDt|]kiF.ЯbKqU$^4e,r+7k} dZkE4r!߈:jG?5S[�M~4ClA2cْh%N\ fyɇw:Ԅ r{D zmޝ ZAeEAљ2fQMpR9O>W$VB/>¼ğI*PNqBeQqN^A\` PU5Iܝ'mhXDZ Egdf|8G/NF/FD :==iFk ˬbX\,~.redMhc~C@`OpљB1 lD>q KB~_VPtC"<ɹ b/Zx !|̎O5t:Z4Eqm~+{Iؖ{"Imf Hh=;:_PlRF;Eh*o4L7"}Uz6^6 쮑Ѣ)$5U}8V݃N yB=Cul0:�1}*V6{`ΏBI9VKfHӨxr$SB%ֺ5yO]-%҆'L@9ؾb靿ç�Ra@$ vNȢs $jBқ92$׋tG *"NA�;^e0a:'2?0xP<%>mB7vkf~MC;4fe#bmQϺ${0E;w�E:և`&lfJC!!=|FmNuB_Bd;c8&bjk#j,b GQ04kգ͟- _SI�};<z=!3ɧZhC8'~/W6@Ias՚HP"R&Vi<[֜JYWIOANf'A9L y\yz~mvo �%R '8G<yf�U$Uj"k=hDFo*4%iR@nyhaSYW6ys7PW?2_|">Zi76yŷD V{2JhrXX%DI*nZ uڵs?#k,D;p>H_.2 ӍW6W'Բ7K74utV)m7;[WgeJU_g6s,۴+^?hˉO(*~0fXnT(v)W%j,Xg/ dԮCrr"V7L pk, ?!砦- L'W菃!`SEn7_'+1;[:(Y%KxAr*AS 1 [zi[.‰q V"0E (2^@u"WK?}lvFL[-C;>clþ) D. W(M[V=ZSڈ})2>;0uVN匥yόP#1`0*}1A+%{r?<L+.]YzܴZmy\e`WzĎT${ٿ$13߳7{]UVp0=L袁ԬEV AB׷J� 5/4dA5SKBa(c4] LRmx#3!ǎ�&n靖/K4�ғP`kv|޽TV=D`SJgPH_dHuST'_9J ] ɦtagX'Ej~hӀhq8lP괕n'dI |Lw|N>#_H}KjyWMҗ7= 7z;c0 ˕]Kׅj8r%~w\s ͯp>I!6U;-{Q!M֎c獖仄ZƲ/l?e!F]ek"IltKKjs݄4{C^WZmjg骛(.|Q>7ťgvjhKtr#f 0 |8v4K{3ŢPz=h`h_P27lx=rՋ3iJ2�3 ۷[pqNeQY[v|yBShl/?Mfys(GDБ_a$cX.\+.懺ߗ"OLp=RV` ]8ah=V'Vq8'E@m;Ȼ)cBJgVa_-Rf/Z~.5~g_dQ-t@& 8ueC3hoo_0(g,hzMVUR*.y[1@uvĐg Z^f]Jy]/[\ۯJ0ZCC附fM[A0E#F$٤Tz:θx-ԭ}G{3 =%j4&>I0o2+6@B%Ao"T )6X u&Z\Wȷ3dB5ʵl;p6U޽0:lV8 gqRŔkl^IA\�BJT3^N!kgȱWw8,-F wt. ;N ^ P72P| s*߆Şo . )2'2@%[C^ݴAoFYERkH*I/ۍ$(Bw`V{6$4K;9DU'%R܋j,7O#mn:h@LUS%VeO)o(m- e Ul"W ̻h$k�MI <\& )zƝ5dL6 @q=%zpڻ#Վx߫|'k1DYjȝ-%{+BKg#4H/d.> n)1i29h|ѭ#MeE+ME@oASDqYzh\7=gw{Z>HaS"xȓLKz`XK*$p߈ޣ>:/E屘PNI$V -ۼ2 K#jil$oɓz8"pL@[19YWY*gV)If@OsNjjg}c5Ma8ss6.Tw%>#]5w Z#qU0wO?(QlM`S/F'ti]3LE#Z9OW5hOap1ؐK'fEAL얳R'u(f:WYd5Sf:oĉY_n3ʓ'j"ZPd<83eh(`!KGfK=t�8Sǭr:ǫ74 +\:9R8�VN?ۗ*#~Ճ${)7 9mGbv |zn_I8 /j ZUN0P2bmez2f@a5H#eWx (d~# M!H=N[痺=|<Ъbkxso,8%Jƍ2aʕl*Xqqi νHW{{ ~ɞ`3'A1^K[3:B&?\iIDy_t~�\+z/ɧNוp\됭4p=~6\|/l>T)|Sl痨-1}a+L05"~%ǪFE7Gu}mG"C!.߉Ʈ]%8$.\@)8En\l܏VGh!vK,e;h#[S .su))~l>m(2kG6՜Xࣿʮ2|-vv3#dJ|j)OuEDrL+ʠ(UV#$)5B>|`\3.zBiwP+ԅ{zU Omlɤ_Sh7WqhWt 7B .YRo,-k! E$f$�u:va<l dQ]gIoQvE*Bo[};oC618NԹ; <hUp؀&AQDOX*4MsY1jJM wɣNC1}fGөBV:Rfo+"7H;lȊᦞ'*+ۉ^֨Gnt%:i:2I߆r̙`f/hhȠܞZh^6^ I0gh!^k]r['UvRϏ.{ t-f1zuAT/J`T%gc֙,2ͤW+2nV#&5! C\xyV &y0Q U+A7#ȄP!0<M1y:UXܿB@藜&Ut;Zɶ b͏| if5RA[ cqo`! mttwA< +ϹVHWjs6mxKmQk(XMhu*TyPi^a m<X[~@pTK${Z 6"òΔj裋 R%1|D BjZRL﹢r`P9$7+X 6; D;~ޡўoߺ|{q-iͰܫ̭i^(/y�Mn&]rv9Av:IԣNg<@; P%ҍsjWwwC#?tDFMcWʷ~.�77޸X3fAT<[@RoAvc� Q:zAI5.a ?oc!2GÆKPsb>F2׎80|B7<cwW x"-\٩iVu(6I'gsBL3[`f+F:ӿu,u:컢m)t8DT%r >w۰ K5$*\㵧"8vDA(t(Z@$u0O15?̺Jq99v(RQQyD~h Sk(eECYm!Bj8"x;'q,Y4LJh^6[c."<3n]mר*1f<OiI$DbD5~h*MyF8 e.KnqjUa]8vsn!o.Rek $62QjFe7ԝ‡܃Ь vq5d}`rTw_< $ #=^?67Ͱ}o7)i1fzW~ԣں"_g5j.WE ໓uBi(,>p} n^c]F{! G N-7.Y W9&&78_z*?E~Ȇ@jM%Ob"eUkFعΙ<xO< @5+m&X����A �����}X�s=띔JkDqithf`ZRU[1}wMQj)q%+2/:fϣnćL'~ HQCxhLIm28yTvQ#7fEڌޞH\oe`suGp%GhƆ] ܙG$;借#<DPDl`/ vv}o Hj?dg9e.cp0cM,5lJԸW09/2y/1WȧD�vJjt{DZ.oh  rW,%{jٺ8SdA˕;iv^/r`ufw|댨GɰzBO?ȟ0$"̋MX>)*ե=ElٌQP?P ?g~ȷ ȐUǶXR7~,iV%!%3UAxG!W�e]Brg*_ouí9d8Sɬ7/<fdV/X= �6F{ ղ }iZwƛZP#nY #5o`HSЗ]y͟X {vj R+q{,E([GC>BQz #1ށFs҄#GvfnqnqYb@ r@On W*`7G&�]Yǿ9[URpJZ# qR?$HUݤ t\^vŏnyu[+&LFd+ `*V-,ĝ' Ę{)9Sǐ ^BWTR$ѷ13/Š[B@<+1䷼ X*"L<f/peU+iyTc$TT &b\ ʚbr}E1$E_sɊlC`悷Jd}Yܹ<GBxB&@x`e댃Hzr k.}ɍQ;C-4 I;_BN+:k 1zW߼mkA>EuSS6?su?H% LKw *r` 5&[4$k. �MRB_PI)/; ǯ]9EmZ.j? =kt]o? ;+7aCPSx#s3L)3rf;+9V-46Ʀ*?Cm8.DZ@e9XIDh'ov^d_ ݽlٍ9#DPe?)3CzLL3Q|Nbd w*e#Dn_9 @=BQ}wDZ}NG>^]G:^Gx lz=R<TRhy\sYfI­WPRa�dvs6+`fWiA)"847N9N58gF85n +46_M.pX1{LHP,xro6)+zEjWu6\:7@>OwMoGQSO}5_ "+fHvYǓ9CUg8+'?2TF"V6MIKmBl/dy�="8k,ZB-hx go݄ƈ< ChJxShp8]rPHc+94Cr-Dczi> ^8w{Ch^O< d)3ʢ$ 2؃(]aBHk.,c0&D Yv"+ 6SZR!(|YkvO& 6e¥m'nʩRz%62i!@ F\+ܥ; Bl dk< qLUu{^aXz>ʁי wb O_Wi |Άq 8 LO F<v<րRҌKVEiGMW4+C q*'xE˔.K ٖv#CI3|wuF2"NzO&d=v�}vnbD7޷u2}{%f8 B^8a'}֌Yx0$=EU[#+ =3'uXW"*s4qDkc]\B<}=:fm}D,RBFX]�QVeip뺍7ZL}`{=̌ f1)XG|p .4aGQ5uqE/1Ed| \!M$*=ee/ H*=ޓ\Y:\va[1>9dX$@N!�L#Ӄ?+TK!iE 6ap ,g{Ғsq~@^QUgwex> :]S^<ѵ _m\t [Zzk2?eLbAwoP : (6A.)JjjTZah\yΩ1JW`W2(!md:tTD;Y]2iwff;i]31,_T|BpWBa/8OpvwlU�V#ndL3F76zksSۀ==orR]hq+[hdA^ A9v\_!F~2dw +{ ?"a7Wx[56FgUWJX5sW=v%(΄+L;/*eO*ڲFmv#lk`!xd4n�DTo2`%zü;G*ao&.߾|,hmݭ �J!XWR_8'6[IYm>qr@43-�5#'~D 4OmT*WE8UK&A6پ6OA.k W*BUL$8qKR˅(ikx9OS�|����APETAGEX��K�����������������������Album�xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx�������Artist�xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx�������Title�xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAPETAGEX��K����������������TAGxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx�������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mac-399.ape�����������������������������������������������������������������0000664�0000000�0000000�00000246334�14662262111�0016616�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC ��4������q����������������sMNr9Y=l(A �� �#�������D��r����@�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������~ 0���$��u41؍0?Y/~' oHlah>}!Pscj~"-=+~`C1YdĂyz1Q{^b}˭WLc [WzԨM1VvVyluh2gDPF Y='CrfY]U(<hR :P9}kv~u-/ʅNEտD+iņfGpԯLMͨԟ N#) ,9@4em3gR3(~P.ۉ8ٖS?a.ϋs*M(:-VNpu*C%^Z9GY`bY9B\^"y؅3%̫Wߖ9[=@unJ#eF+<'EjWfUW KhA Di }5AU*38Pb [SSps?SVૌ}LV�0|xK%yA-�+Dsʫ^KiNldŞ2r Lx J)(Q~3kW O _?+ V"Skx$q[+.ySwh#*߮'((Y[fV>l؈i:>%&@8 "JȓT6~2y7  Gt3Q˼bPQs x8'$Ⱦ\wڏhH^z*<en5ȵ 짌qPD&ɘꀉIyeԿ6}�ŇZK )13DFS*=�E#͙CC@%iU"/eAJ3|9xM݌! t۾ mS[D5 GœoV#xL>`*+C<2UVKq긨?WcM+wa,< yz\HR^*x 0])Q'l=PDG(7PпG \6.~B)dDӕJ ʗ$ܰb13O7a@V*t=?H L.'AzC7zLd/ؙc=VRN./5 /<m$[;jkb$bEJޚgrSCo ?.Gc'r.$259t<&pˌd!S#Ks/ŚK H6^Xb?nўkҎ$7H3"db~-(^Y>D˱\I%2llCG`8&bE81m'8_(-k?1@GDS1^,#1NCL-C 2c&+'@F_l\zK=xvژŷۢ^Y&Q}xao$ eżcoy#O'I^V!֝&lL+?8YQ5J d4n# "|R:u3B8/ e\22dQ 4 Z2Auo%5g%ko1r n4 )O2(Ʉ�S G*u3hTyz+u[}m� ˎyy|JS#5-.Wb1d ~>~+cH=54=\̗h[YljxF6eyr;ziVO%ժĹHkM8g7Ex_�999:Q yfWs_[K XIDTj\ EW:k(OL_Kx&.H&<Lx;7wlnu_ !^l!3�Ŕ1fLtW#Uo Nz8bSeҀA1]r][�a-3׈'5" V_=Sçޚjo>Ңqݖ6!é =a~vk}b؏J~ = U;YdEPRͦ�Y1Ovo 缾q2KݲVH4YGؒnH#LMCEӎ! oSV)8"zM4a+t_2泪[H6X!vK 6"7\iI3!pf%+W!Jb6ս % ~{5!gw%:ߠ "JU%ѠCM@h?Lz8?Vhr{PLYtMAGA#p-BkK\R6G~*["0rjsb@5u2+dq\cq(D<Ҧ5M ?P6T}GXŃջDyze'A=W%(oˌ*1[D,ر·&5A &^ \ KTc7T߹^jR48nc~ktÐǫ-R[̴.,kגM;o!^1b$"Q*.G:8~t"l f?RAe@C%ׂ1!9To˫D(Xքt؄\$B!" >3,fz ⇇]P3w8Hp ~-~X)]]}02(d8$Yy'Y1 w۱1U&zNp}ĺ+ ,t!9 ?K0CxeCPy &$( \#q<͂kC9k3>F+ C-KJ!msN�\d/z젒D+).vԶrn̛͌]@u nTE}tva`Mi3\=Wla nn% Nj,A憺WdIľ7KN*~K<~%cf& B H3$˜OfGޤJZmh$Uum ;yiD$pG\W]7** NHOM,`51է¢}e ]y-ӽփ))2ђ!i]QTF;",O]o>մ-k4ü5[lQύO25ʇ1 Oo6*|R;Jq8kq"zCܖ$XfǘX,7JÓ?=!Y ON kwiږJN= y{v9=+c(F�^*FLH k, 5#|4[SMAGe#Lj"OxIvجW9ʕ nZ+ό&6LJj_ነhu?ʝ!٠v12Goni5/,<82ALqͽJåIFCw$+[~@/r*ylř53„! tJA>c's_ȩW6ǁ0ĊܚHdʦ�#O>kg5!'ϝN3a-KZ~U:UU^܀U[uP oɩlK=dѯ1-lQUVW8p8~b aǵͥRd>qYoyי8 TV˚:٦[qsȋ]G<!D| .T#!֛<:<GWn)3H2s&AH<WK\]kqC} TFP7a.xpu!nC8o|.j ߔUzm�C6v秭AGoDetJ ̿ br4vGy(.:Nhղz3\'c3^ǫ5GSy:XG9֐S+08 ą;F%茉)mr*s  =~v"=4]j&|uVKut<=�gR7n!y&mmVqP3;J;KJeI={_b q'-J ]‚F4,0{BrWǃYh2`k0㉳wֱ,3Ġ$/*EunShESlVs|)Aq앶ʇ!*45ag?yz)Y0'I&m&ϋQ_Vt>8.-*ZȾ穤A:45ټ62|qtV%.Tvۉw1S}f<@ud'9L d`GkB^$mI7R0p!9$ZaiÛt,2r%#C|E1콪sBUՌ|UwjOb @jFoɮjp N;PU >}c~iְb<V=ؽc@}r)ڊes4h-exgu)3aahFAAD6w]_K3^ȃf jt Uw)>g޴m0iLe Ғt|aB/i<}.|9:Ƹ]�eEg@>$NBfᄴwr C8CJ!rLw &X$v4}ZV. ˏdl4yicն禎T�L^wo4@(0qc�Rk;O\me\}ALQ8> >_3uLx9HELFM6!cݐ|HK$x%^=j=j=bŊHOQ}(.}"x #P։iWCޛ-qb)+i-@ڽI'f[B ⃝}5<-@7:.zXڸH|K{StIriP^"d3,K'Ch:);+Iݟz%}~hLFK<fĸFiBRfH>Nu�Uݿz* NhҽNC14\6KyF 6`HYu-&§S De l"&E:=./aY-ܮL08f_/V)Wz&uWXwב);Y_yI%6Q^ Nw=vL.]D/d_;GDL?@MץPSuzg YVfM ;:˔nFdz̓^q=kÚemѼtZ�V P´�qX=桸˦t߶Vm sIrsR35`G:$<)u5&iEwC#Xg (Wy:U4E>CՐaNJ@ D` ,CE[9ȩ4i<26 zJ6a0>"UKiWp4LTVZY"`{RB}Dsy /H`L\V%?Rv8/QL "LnA4ȶMKz@?YGz0m;062}Py|jŸ!{;xŒ7DLO;G1M@JpJ>gP <<e@D;Э_rۡd3)~a$Uf&{eoChԑE,~(3x'%0<'S lX=T-qnvRƞٚYkKDIY8_Eĺ1RZ77 d:-i1xe RsWvR`Nt^&x̫ncfQMlpW](CHVGb~@l{H8^7;5^R!*E ]I>  klv?IO$sIr ϗ·ʼ깃B8Uw x2n HN+>|KLLT19`Gjsa;J,.F.B3Ag@v_MoIȺ5GdPzRiTrX/`.p)LcB γ+eN1ĎZ_.Wxی}5y!rBPJpG'VѬȖ$Jmƍ~#J^rZj翝b14sF Mm}~[74ǠC|<`̥wzWBYy@3Wú)߶}tO.lB9bKZC־@>Rq#J*'t_ @ CUVISxn!)@߹t'zaRm? lQNpRyPO^(5o072˻%wlYRu}Y=Sʂ@>VB˭Eq64sbeU~ .vy"EYonc"SKx<uWi' ǁ.V/e+Crs$>Șg2 6롦Amݧ+FddY.밠 &&6q-2E m%H!ۿa\/sQ= s!,tߏ8}C2%dI0V;ѓ=z h)m@ @4b7"Jݣgn,#"mC("~` RKq>^F=K5ն]Dqs9z5I1T^"u ;~KyDnbs/i~t7}]v4Jn^&>3O?R?^ݭHǔUQw^W~]=ǂ/FZqT8oh'L Z]*nbMnWLcQGG(f\hZd <<NFG)2ͪTF8x|d(p5^h P~>W62L۱Z `I'",Q^AvO@ן_ӷ``{"˓'-65o$wJy|4{|�)̧i; r/K%WVzJSLьc&Rx7pjEzp`322-yAC %ӊIQF# Ҿ5~ޢMjǙݴ /5_D8RX'a}YkY`&҄D@MƹH ;HQB\yv@Ŗ@4aEZ(7ۗ'�uhڻ_c.aYz)Heas*-|@Q#ބ}=ior$`r#E;{dZ6; a.Ce` x~R*6k5e\(| z÷V \?׵._|`,VgM YBǧJ!/, pnCʏwlѪ_rNԇp"'h'.ħ>I.d Y1} _Tb 5bj֤HtC<G<qSv!23& Էv0 [n-}# <~J1›ld_1A⁠UO^kG <aT50"'RU֓&ё ;JhIwZr2ֽ}@u&S+S3mR v69ȧ̾~;ӁbxiB/tl+%lSԛإBva't_m}Nȴ^ET.#1[9΅/;L�4eVԽ?\4Tcaմ*l۬rlLm$yӎ PT"-[&j!j|8qL ]fJ ns^x'c[W[ĝ;"5n;OTB~ R61dIuά$jW4J12 'B} {A2=Ɖh۷%6Lv#7hCrEPC nj5QR /O41bNѵ7ǞZ'[K]-doА1 ߚ+[_\)^-M۶`Ϩ>hAEvW4npm=$G#Z/% jjR=Y<*t*=IlR߶R𜞕ӨV LjTHoeEܷ* ҟfۛ_�whH~Sv3)<?=ߛTfyߟ-xIQ!qژ̾ fգݸXS*דt0%z<!;z#=m9z;&"$otx,nyS,,/i$~~D5SR stUX`O{~܃?Ш/ -즾4tETu}&UuҧFi9S椰v8-1=zU79 dAܩV.~7k$;VkE^Ԉl=[e 7@mkٔٶx誼ޓ9;0�&v'@,Aͫw+DBPd{NrSĂۖ{ik,E~T޿ )H ی9\ 7!IeR֯y8ؠhnVHdeo6-,�$� {z{iqlR ՙ74gtOG'8*2 Z$XآQ6!<)nk̰Vp 9%'QI)WYe|lP6ǽWQ!1,z1(YyE;=27csN3b{f aabjMңnHFR2glz3I{jFBsaL{> AL?uv&[{,ug e[/@qmq#?r6>Uivc;B9_;RQ"擂̫vGԚu " x=KM^1'Y*ygƮ^.ffIqI&,^% Q[]lvv2]qYVCwj{[eVZ�?ȽT0~*;(^Ar7U. 9u48c*v*i=bɄTl&ԀOшDY[SciI;W8S+?aDaGlQ\*#bϰu-D&8ũՒjUh`\*lb6H%U{>^.Z�yƬ.;/gh1_J0?/m=,cƗ!+@t5|K'P@)4?Y#pY*iPXg^\#}t>Đm#0#G$sHAx0!\ ÇfkU �L~=1~ n n,nbO]d d~3wzB&&/\ܵadva8FRJJ ټsHJ [<w]яe՟W<blsx\\g&MLsI: ,j\懽 ᎎXS=ɇxX^T@ի髎:4s*x14 lҨh ϣV֍vV*41.*FA_SQTcr)ҏ#.3EP`;.s~a[TJ% q󤒲l%n)Ա݃+u"*Α&@ r_M2)(+oCN>B (]5˻T0LVd-u|._<~*C~Ĕ< ]!ӂ=iLfy#☆>*5?"qbl}} k!Bk5AKo̘]Pzd֦ VE<>_-Cg aȋz}m`!I�q^=$HWu[K; n}d)b_4wfWBs de $QՉCalKZz�C"ӧ6L],gy^g+-9h~G*Mc_tMړ\#\pΰ<NAǘwӑDG$R)ew0Ak6l<nHGh؜e@b: R^F9Xd]3nJGbH;)4_t`<G}WrfTZj\6݅JЀ3<"0 ^2ī ws'i ޙ>_?d('Ii{=HVz6֗/ڃN_Ǩ,pE8n32*6F(QBrȑ˱MCJq+<ie9A�F_B5#i> 4fgZa4s>s=a*(LuHJ<KK˼X(>1Ei0Oޮ0C:uK#}zA~\ekM#0R X! gb˂)Xgj=#{QKmp6hxm�F}1K1žaO\j5ؘ3XQQBD<0@ruƉ)iO[n>#.ppqWAK}g `ZaA{C#Y%p5\chbؕuW�6I;+2ykK?'jq!m Hg%R'HEa嘙P2nըxLup+=G{.vB2,m~{W+BLYZ@ 6je l_EW]!B__>0DC6y*>&7w3uHG)�eJ!ȩ9|p8}C&Oġ.m-9, cb9MtOľwONq <^[I6~V "y9{HAV[DkHxr(v&2͂/woζK/ a:EϯBX2goK1'D Ӈ̏a{kQz;5i[̳ duؼ2";":(!n& ]ɤ̟0H6dKބY%Z݈"$I?+#!ܱKj?x෻e =ƫp8 ٚ)wxpR|+O1c.&́).*4񞯲2^89 =2s` 6և&~}tp#`EsOPS!L8VIM}y$oS ؖv4B5e ŝlׯ}F bb7 H1_9jY-Qt])+'県rgwǚI|pr"Q1v;HP]@Gb׮Ftpy Ḙ́\t A W`6lZ+lfƒB<8GZ=Wi|-Fi<;K.U]ɷv]Y[D.xKg;tU'ͰdANH]6xȘU~f=>{W&eYBTۡ�`KTK)E廉!Q_<I%߹7 ƕIMU-Pp 'Sރ _E!0Z!^y4ah$kWaWR9lIXZXZtNMͿzϘ2]RB7$ه+&<i  tD0숡�U1哼Y (˺s~G$/bh=|vbfhQprXllt[}R`boY\g@i+(Sz cl)6Ɇ; Zp6_S,qC \2%ˌ?aw+\_Vaߍ(1dao2*0~&kgEds#neUZTɄZ5Y' dғ#, x1jOBG[]n܉]dF)źa?}>.sCc:Mx@{YM' [gu5zm RkPD"�˂!Tlj\\�9p!*AynoMQf˰KvM"ƒ=j�@'!.z-]] QS9͊bV<ZAj&Lí |YƉ! Mp&/Π Xhr |>HK)hIZ^Nk2?%?maMO9܉!*XEĸ�S D"V<oAɐjm|YmH3E؇ք($|Qz;Vy ^ '˜/ZReb)9A"~[H<Az?�A\Ts.&/VUV4aYn\ _T̯ B"RSV%h@O1Ů=րfK4cȸBb'i4Øy%&]Pl@윶4[ui /&X�Wx5 ɣ: Q/|H~ceiCRN394D stsZP&]`("mM37 xaiQTGD58@wds�Њ{u\b&-B5Xn�YeZI5u=fWDi}2[ڄB_X:LjjvoPhїZi YPW tގlH:?;ɒ`[Na3'qx(h#& 8+<|'_\ࣇxuq9M&Bb(Ti0o*ҵJUdh 1(;Ltsk}`ÿl F_ut <m͉9ԴF!+( af\ e MZy)=Ƹ ~hBNnZѨb`Ʒ6 L~)?1Պ:ZVg \=ȴ�f\arQ!(wTJA=❼IM_)08yn"5ZK8 *!9X�b2N"w#u%U$a3mpHzF]7DcۙZᗗAbN5-* =f"8 7{+h(JїvAf?"K7Ckӯ97xJOPKXPyckJ[o]v[<u&Ӯ}&5a+ ;M(9)ʐ9P :< 1qX*G&-`SV>^tt) #C!|OY`*G&vsPq!7w=nrE`zyL(\{/5N {߫5{-! Y`… ej3C=ƚ^ˌp- 킡ϹXv^(SpL;%$G>#Yk{E&A?.MDDZΖUGA%)Lf$$ӹs?zBcB1"Ŵ@EO8\a$jx�bIREEX?BnBSCЪ񼯀7e ͍V|fރ&_)k^~#sgȄ,$,<t#][N PB60 _IW~Bv%#L S],ٔV72M9'h o$5c0}CàYpx݂2a -ng+  */jU ݾM$22/8.u+r,O%TfP}(~YV*to.F[ s62a M*G!OGA|�9S;Mkw4f.S!?TJوmAYv{K8& e5(|+C_OVfhB5߇m<3w&osD&t+yejbto)_%4kUJ!Ǝ94%9k]U]IB4Ƀ4$QdYoۖ8{V#<CX3DLOA\hDDwbin?H moD@̷YyЭFpeϺ<alKp`@6gzO٤H7@dhxkǁ.3/zµ[+1ޟ>Tj.?TF\&QMG2EH@,JP˧l8?FFLi?K`O!cڻ$S~ t ;+-xK>7;C!컷i;f]j4YljQ'򐸃m)rW(Xw:R71ghTS9Ṳ8%)_nY2vzD6/K윈9F=H?Sg?)UWU`bPtp!>,SkMroS9 ,f9yɃ /$Q^u$~E۔�qL"-GJnl8xg1L ؿ ? )CeF5YG"B]&<MHY@^RړԙY'B9/PUGDa)pbx>Ɣzl8%,;Krpk?+j(UvR.SGT򁓌 ]+Z5lx?_)=?TuX3 -%Ip%t>{�l MDYy}|NXO@p]jgEj4K eM^}%+A]r<IaU'g>" !|<5" Q?ɕ|.yN*!--Z]rؓ$";MgQJެTZ$L`.k'gڎ)G'Fi/IϖYeOʳg6ֽɀ6 ךǠ%q6I͑qf-QF BَUHK{wCl(_)M0?`t*]/88 Qw|5yhBAY�*yHpQõ@Z :(nPDgVr=1 ,dJJ?ׁ]7k0_fnJ[8#1fXlkOBYV:M-HaxF^7Fx5/'~ urGG�sէ6#HߵYJK-b:&PkB%>܆LH){NJ2ֵET2U\R&l뗚?7@T׀?ѯ'*Q.&:;A|)ES1FJ>)r<u#MHQM5"GBǵvDbۆl 0Cl}beO(J[. (~3)F:U7䲕)x|3u 5nf+I#Swu4_v?\sL ?RsaKǣ|QHQN%uj)[p|dy,Nfر{% r(Pi[Ȳ_#TJ )oH>T:V54t:uÑJʢ%GBm_7;<Ejzt)!ɯipOruDT|}}2/-|mB;'vbQƂeUK{3m!DRг?}܉=9VzP+o1@MJ]hƋ>K)VƧIyzo&wa+$Ʈ;0/ȦHߊ\n6$ LLLrfDJdn{(kcCuݝ%:sQ֚޲JdZIb5%;!CO'Y}剚pM.gʊٕ3ELR'7EEȻҶE'Rmuu8>jjMu0NQTZ`!,<P9{^_ir*L.:Rm?/�/ jW(3T8R Y68mj˜ i q;,KAGLFDR}�CAU'Yknx->r?~2WNSj-y66t!drY)|ľ~ԾxZ!_KdEfGF|0/벥\�e\L/ؔ%{VCc>!Egs_|IUNk3Z`N=I,dИL2-z.<FBBm4o֖V)YսVPX :KQX=(RP?f^Ъ 7܈,>`H*VV̠\9ƴzً ݺ�X;;YsU<}-8<B߇E.76ÏO#?@=N3|x2#%pP}yzG2ʰ)|V5W&&2?*А4sjDYGc^3([6?R]T"?.b" vAT[qh6}q}ae{ܕꅿ:؆y^PMPQo&zI~[RVo5o:~?]Fİ>Bn]!>roY$ni Lm!q^$$>)D1q9 { ^j>U`7%m~ 2 SFwr]D^ |kopm~+JLLxxcq]w'p)+_Xl^ylw42�NBBvK(r`&@FC? #!kݭѹXݕ'kV9CM$5*o&Ө,W'Yx(" YQ/aG@(a|y0=<.޵UuUlszv IU,H^L�S}H}[T*Gx"9f+v^t 鹾Ŝξf@&uv-)mr)<a#!LHs#lɛLE2?o 7„+wQ>;w<UO&s+W[Py ZD_f!Fijǚ׋1`rQ]t[tՄpA_@XC#[٭/ڀ֓]Ʉw@}AUkmp D1@ 3<}ɍs,Jk .qKo~;{[N 2hcW{-R!Sj''Z$Db{Q?l"Օ/ۓ.Yw8]v#NQ�|1"C` 0�Çy+D&@3m<CoZ `Y&%�T^fLIv""/&4:)ƶ -@˵Lv<ˠ~8@F܆x<w{Q7k,CmtbITU`Ls Qq7q^#x_�_x_ 3W=*ix=:W;Y jXI| c|Kqa:ٹN#5z- ,+f]u�qEX#ZU8UZY%#y狼(P/gpfk a |Ѵ[)LO[۲/ƽhbi>Ν[k I)@ w^tBHX\k&OE�X:MJ߻Ђokţܢ#ۢrP7ݠL@فYRL,Y$DsgqM=pwen>gKlǓyCZ\DeUUPSMG&3aw �<Xr4@Va A?h' dm)6@ > )\IQ_Mf) 5MZ r}5_aMb3C?_w[fkTc ?fvq^\! :p)?I[,.y[BC+jEo0@!,qXekOumٍ&I�=u:r'RySfgpʿ_UCÎFP|iLKa6"F%VCرS;GPXP)'i$݅I-Inj;v0[<m-L) Xif=|H2%`ɖ>$Cm,;Tb,M;ى+9@,LsM}A"c]�FA�)nkja > LpeE_BWG~?gg'?^Bt�nps}2{;\h{I/k r$4 ,sg+ v+?q�44*KIm|G@,đ:=i^W�bxx1+bٯBޠY~c<RA&k950}6r|8zlrE~<iJO�iW')E  2SjRoc状JO ǼIyxiXe8�n7[WdMδb 3.a-{cp^d8{<vr)P%OaYj첇Wێ/<4BuF>,lL!C5K;[{v%KTZivl RH!tuv\@ ?OSM8s2aFb ntX YY3 MEbd+1!xaQc�϶$ %2;ՋMp;:{YBTȉ"+%{/x'($/#?AV.XX5fWҋ<*d%gLA'u/%Cx#iF(y#>{+/d'GZųn`HaEgbY! tw8~~£x{-xX+F=vnж0=nJcH*%ZSŔԸ&ΝQh-qJ1!lDz,!?@yYpJx T'ziT'EcO^3TsW[~0$±^$Lid(~zdžy0jU=hKigBI͒(-{K\H%1U \fs7fd멮3a0Ci&mʓ-zjn{wV$YYyI뷇8{8A3mj))k t`IJF4j@(zOnd#YYGptR�@9^ A|r ?3pξ2dH,?XIoD"qX^ӼS`&D,2T}Uv`Kmw&4 omBoƤL(w[ά2 9a c7*z+ ӵ 7 ZIdyd=4=>-ߔK;⸻oWC1Pɝ߯|Z!RᘙHl-J! r;EVx](T;?+”J Q�0IJ؄$Zngmq3aQCsR(t$ulfΩ[I=O)DӨ[-Q&KDFO\mVd/ �g,|Ij }`$3R@=j~Έ0ߎEN;6NGB.g:PTx]wh>E'TJ4"bY3~Vh.!�rJh bvXAtɃҖ!wU*wf|sHt O%bRn޻n̈?!8b鉭w,{6t>A^f�nfkR?rY:=1iv$Aק` %^BDKī ,e [9IrAbǨEԝ*^Xs= MHwH rE7j/r^`mymy9eJ/ p #@$ 'ٰ&&MLkF0}?Q\~K۰? 3KDKau뻇&(Stq0+8yM#\b*jka6̌&Ĝ|WOP$9䍂@dI˒ʴJ%gր?{}۰ i$c љߏK2։.M;"KڷOWSwR:,n[]3lOV1{-ORd`N4 ("QfS<X@H<`!b_e5 Xh~\,Q&:Q<ڈ?U='>t"ݓ}k*dXڄ_MEg"ݚ%* Qc/%HO7x򧙤[=oӫʤ AoBwM<QZt]o`o!BB㡾ȒN_角7C,5ιĖm vLBީdUQ+L,'vʎb&!iX{1RVy8[�Y)SJm>8jش",K@ͧ<ǖ_qɼ5lCw1hJW-\t.'N7/&}9c\X;@Fl&9lUo xf{oH -\+ ZVZ~A1V|j7=0U5$[Kw? ᅠj&mS '9zJ!iBn,SX1> h&!Y]GB=Av -+qX | 9-AjrYqe2?ƁJ/wT!~c[V kt�w.xK]0>!CΓ7Yz 88v f@%Wƃ�uimN̸X4Q͜*Tk2 >S߶cM?-T#ڴ7ȃƷj̔1ti]M $Eys,b6ff`}sO]TczZ:5j3vZ0„3^l5+Aݞ"Yc9LcY`4swࠡJ9փ<@ J)c9u5jg_B5}SK- njF*7Rېy;JstA c-|Zǘ*5؃<6%*]E/RDt{:E x= V d\ yJ֗(HHQJăk\h$G 3ܽM%YCc lWPi/-Jϡ`,.q }V@Y&͹|r]a_R+9ؿ+me#HG wl0w3Gar%|)'Xz!ɪUG8\*D#m㜓X"<\ŧgѾ|GAc΀uU7ǣ|7 nlĥQ+jrG ַ+hy͹SFjTk/|6dzN)}n ^^JRƾ^x *5/lUֱ⚡Y`HW(xL,Xwsv="ӿg%\ [`59?ql"N<v_@D?`Y$> B}o cݸKPfg3ULK'B;{ ٙTA<xt!: %1CrWE@7p5ҲXh F_^)Y1 &t9E�/Ump3N CϙzuKqg[r lW,\JGψtS٘fյ΢bEm�09ƥL)_mTT)9czH9gpQ4T]kE,@P퐅�gD?55_wW'0%1I#ڔִ`" r)_;/չF]d6]9S^Q{E#Q;/_/{PAڜo%Jth4ķ WramվGZdIT.URe?cHR6UF#`LnA,tCA4t~[i1:nv˄6^I+S0*At#)%stXD'@ޡQLWANYb/bƜJjKpySu t>�rFH܆z,pKԪy\>Vpd@YfO T6d3-LYi C"713Zx rXw lՌgZCpu&h$}x(*<?bT(A,+p?I`J|?CTl<rYt5Uu 8euZd h,TC2X3zZq=BtQeY#SEmXGw6 )}o6C֭ݭF#ʂE)ȿؒq9G+Kb#%d%np4#GkȒ9  Mmbf hnUET6 #G&W C:IѳgeEԥL(VVfǥ1*}0Sd2j\L%I2٬l#VSmQi|Nb 2K_5hd2wq)d>XmUaW{,nv¼‡[ s}5blônˆM9M)- R޾f_<? K/ kt2}z[y2* z3DHCA:FnŀE\6+*DɑբZ+Vk:,P-kZ~ jn;M56KfgKGC] 9txXQ䰿"ksH.,tdY AVH-zԐT@sHus{}%ռw9Uv=A+ #%Ӟe4bJ˿d &0�Wx]3?bmrp;Ią'7u?;4r"W\j=rjX}~3 }O|`aTU%B%/i_5l^r $}(f%fjWk\<lNոڋڜOv@DnK?.uVyL 4b7�zB䈦nj JJtxvԾ~kJ"I9 @yͮ1} Jgh7^foˡ>K# gEraG8#@\cx\>jkXRSG|#h>U~F $j=FiK>i`y } nju?ȩݪ$Q.hx}cgx]j4YqL+qtp̨hjZJ5fI=Vsdӻ۳uˇ~ <*w AzCIa @InȎ`pWtBXZ8,"2x-Sl%! q4Fw18h(֡؁8Y�b`Yl\Eh^$ɛ&[oFsv ҫϭdN|1U*˔<l-v9*:ǘ9l?(#%Kg$iP ܑSYNA跒Xps=-ISQ-[AQ0Ih+d 2�0+'b?}+(Sq0O#n9x5;tʺSQNoڮG!֑nV{,i5Q=;&R`YlKa#9f'&1b\ʜ-A^Ξtm 2^f z3Ҳ}$b=1Z{r-W�n9ͷ#C8;/[#naG[r%{Z-By mj S^r}֢Ֆ7ϐgh䏷7O\l2*&9M_Oן(+3{ș޲,dAP8`ٺXWkQQ'˾S2TτPSG?<`s,2Hwwď2n[A�ʑ HO{Prd+Mu/QH1>-^-;\ {88SDETM_^޵[<zYL1qoW@e I1 qJ~nwIƹ4&].֓ jWu15`wvu>iK.~ {fS[TL;iԀ'"8´ k p(Br&&gßlڊTz^<ICB<oO 5TwݟWin� t7Vj�}s1uw!LziO8Q;&<oRYսk6Wy=Y| G;e|dYga֞� 'n{f2zXr$W>3Wk8#m,CpYgjm*{ +E;wt*XY-0]ȈH!o2@7!H ,K >.h[ <#ΈO!NJZp;Tn֙b޲0.hAI+_q�5yHM`IčԢ+]W@Fmn6ôHO؀-# 'oH7ê3#Ss|cw[qߴ#s?$ @Ŝ0&hz*S3Bݓ.EepW,*"Y58`jIbp*kA-&J"W)sc&J)KlT9{q0:{y{i_ -@~[-TM[2&1_žUI4M>Y۶_S%P=y}Tyq`vC/onwKP{ qAJV޶x_TpJUfܾ+u7a'mTW3zRߜ嗽ղUpO4*u*;佪:opd(EyW"R+e#b d+#IA3_7�F[w2{d~4 ۠RiU<~ߪha㈗}ĝ7X(xQQe,x<u~gVI\'vo؀Z5U7s묏v1[kh' Cp[cUo�Qz>ɠreG#\Jo&bj��z�����&VxY4[Xx{g 7|ӄro`ڽXK1C!QCtry&W6hrwxN0ZvLd雹, ȵ8ZZ2a@&$bR8"dtK͌Z~CmF^<WmXqm"5/zuٓwG1j )&1e~{? Ji--m < }H .V8 "K1?VZYu{p]pOunR_wd%tz-;zZ,xQ+t#}#qEO`8R~;W *؞mP#=>G*%zGP֣ 5RJO^| )(4m[{ gð5- >E7fmOhM`uR}YuV;ni[AL,%{W}(AzO*Ԋ=?tMhU~ҡ@軟5lBlntt!N\\h֩uZfԖ|ַ2Fɛd}H?VtZ6m6 y'6FآQ^d>G\\^! YRsPFbOp%Y4՞7hY(B_94O:zWdm O2ij+T>v~3]_S9{@�*7z^U7&ijld+ϡpSN`T0WaV /i^(QP7:" U=3aL5`D~>ˍl5.֨X?ETE+^)w}gx1Q< YB7\,o.=;=MA^�Xi/ tfMJ﹯;֌oc?V\HF{[~t,coX;ekd8;(_Za1D7|w{s+G808 6z9b2CVR|qUዓK[(LP9Bd.*BhJPk0DҗL7Qn}E:ԀtinHfTsbHG3K6aYS ^ܽpZ隟.e=薏77^x99?귄b6fY}.nU.8O\& 6YO, ωF$X#" p~ۨ˚2^Xc\^F!sqenCTnC61կ86/*C:WS 03l9]Z5aPv/;g!fLX YfӬGst-:ͼ!ljn hQk#^ɱ*pЛ76{:)ѥ 71bZcOy�+lo˂ߤRZ9Fx` 1nGe;؃ aA?\ECEmY.\[i;Mś,顈'O`pٚeѪy>\’*c^b΅ǝ ^"؜ uk)xZ4!tΜU⌓&VeJvȺkq[lxAivNK ]I*R;w/O1An wcA!]]z�%jRfK sL=5CR^+4;N쮂0|)!Xe;: 30@(Ns;.DČPURj8 2J _RExGNR06bw@2A]y?3IND9ImZy".R|@cח"}cC|$Pkh+oC #o,&XȅLqUvŠ$U+ !z�dɎ)2|gfYXrAUCRA!)H yڥxUAGuX8o4KWѤl!ZC49]>§]Z}%p?Ra@ǵNa.3U9D f5&._w:oT?3mĻ4jȆ7Ȣ :dg | cuͮ׌})8G9˂Z&hԩ72]+a \Z߹3Pv3( {@Lm;.2&&^kɫ W4ew̓.P>E][\~Yn ojAV/zW̱7vf(:eDˆ Z&LnZ9:ڦ!:Y:+)=ӞFse/Z2`0wK>%r]ߝ'q_7s%iLjXAW<KM<TB z4Uh vm {XtlǃE*>E4)x Crm;n^Oj7[qx>r%]ufNO109roej'%%@ S*_ ,ӐRPq[pnucݝ!0*M^j{zB2TIZs[w/>?")>I6a\Cj|kn0/?]arz<2ԓfڪN|/y1ݥ8[.j:3u`{"̲R,hbF,4y-z&i"Ё1*WI*a`+\v}],-HվoAq{ڐxpa.f] y%kg Zў2'4v�.azQ)AgԖ!@PDZQH; ~JTo(b(/Uw[2.8/)) P28[�Nkl�e: *372>þa- #1:׽`uebQPAc-hqhZJvt;)ǥB=p4r)|E 1h[Ymg@jzu^#_hJSyGj[<w;QkLFw8JN?E zGVZZڅCIYC$‹1^y_#7-UӴd.g:b[<Am$f͸;.QY%Y6 _M-]>rI0m�TȯO׼3�\J8}i@ho nt\!P{KʧEH]T~3]!. VtyobUīl:8kTK1"Bm'5qăI쓷/RIgݰRLҠ? h�0>M^t;Rn|(m%]^Ɂۄn%!MA0$ !BRcGēKQ:$oSf|nW:: CR~B:\|X(N[a 2 .mpQڒO$m�|I@^nRv,{cpF7s33^)֨@dłs5/.iC(DHۮ2 3 %e+|@I^Rx8!aKgib0B۔[)|^[Njp u.&)]덼8u[æ2P՗+O^ś$%R8ƲiN)uɭBr@>bfKMv9+_!䠘I"J"GI?,UŃ*Q_uȭ ccYJ]5 $ D|CIk5qNH#YjYVŸospٶ酷v72!񆚸ֹ .g7Ɔ[U ]4�#S0J z.l2 >q>ȉ`e]o d[uyXd9DfMi^@6/tV`I]o=l-�l�N}FPSu?ǀ Kj_/f_5|'Ra c= -!̛x߽ۻ%Wjq}|<-X3]-t66ڧ=._neHx^|+,OcSrHO�f-|=cݚ ^^ 7?2ڠ[g7 C=F$3VlHsK:vP>VFJ/|?FAX�])H?Ury[rOuA!zsP8\j\$>~g*{+ J`] ^ �W9F>%d& D U-b./yt2Qt{Α+obM\-;Mrt`CAvꢣZRY|TT7O̯-(~aueI1+%rY%0 "KnסaBOCHoTs-6c4 9>U6:򕱡+ koxxF--hdsulf ]{܇q-[BK0 95'݉@f}}(7Q6NX> sLKj&J(JG1=IdvmRѭʡoFMsW wgE]"f<Y: 2G>Ua9'j_u`eǵh}p+8%EoEnKPL!Y) 7ǔZlvf70\XԘ(r'Ѡgx1T~&1daR۔ *Q'UxH¡68ו>ArE|QW"4V"︗F;wIϙ'يAֲDy?EzX,2ނ,P\GH5 9qȹ{3Aw*4x$GՂ֌ GնYd +-t2dj&Ydebc`^QҖh ;Gۑs^DXϙ͏n ek-ZE2ª wOٕ0E 󥣈PBӛsnIl<_ .zP,]SǖQ7 0"HCC,j|j''^7_"~=&ߪ.yT& >?CSlўCAYKhL8�#8 %o~fatb![vvNjrd q֡>S܆}n  CXqw7M[6(q_*mj ?ΏSn`(D>>e{WuYS1. kOB nW:Eԭ"rgu@؛HY կyvgy/@P[Y@Ӿ?ցU~1F0;7T=Yx.1}2@kr2AwmmdNAUv5`Qjqps?V4"b3O_=aw.\,%Y8 u4r2;y#dϋ{Zۄ` V\)y) KG m$;Tp G{{ReGC:T47wd ]wQwg/Ku<| g}%U5BNd�6"]BCR3~n^I!Q6]cjb~] T j-Y 0 :~pJƓ8u_KMYʡ)±: ѢނyPB8[gIKt=PrVrMڋ[㯕 eU6 [| 5ҷ]ξ̃4ݒ,Sɤ1AjmAG6GtI(P#I8E]*^=!N-3t~ V^7Zkn}rʈhUǏ;LfM5α!6wWƒ+ǁ#jn"?Dڿ D�2s&Yc=Q( u4>nykgSD{B\SNj?T B:SRzh8�fS,bN@Z`]e4֦S!3w37Sw~6ixZ1Fj@ b\g,^cwi$6 sg6s\͉jj"ZvE DZ_+1]_@~lzssEcKdJv?KrTN뷪ŜՍMl݊B`� - HR0j$Ko'E [N�ÍC'Z}BVz6{LN1 e젳4)qKbDM_L fzq!Q?q`c4N]ef=DgwKC1*'g2=GB J$I^ݯ"f1̝} ㄖɍNޯp ,>fƶ�uDNa<j�S60GJ.Fz*F%O9͉vQ])pV4y@AB>2Lh# HвI*j1dC*2PnT/93i~|&;8, _%p\wB՗`MƋ Q"nU{0Y� p kZxjF ,T| 9dzoXc[ 76+cҳòscad6 `;{)�Qz2 /fƥ(Z?M wiCp|U_q O4 ڙ(_ڍ`l7 o26-ڧwm rN:R%z3XN^fHG:KlCMIaA/GY`<#Ց .nׇ3{d!ơTwP/e[k5$]eqjޜE F!5ߡ8mI-ºD\} vlKy؁1靖 ѕ4 AaghALml)ڋխUFvJWW/yL~ ?|\Jo^B *$=t'I>*e7 Az\L[`XP@=C|fk99­3WY__GQUE~)Μ;ȫWoTLSb{hӺ!*} \TH(!s$$4J~? w-)DC0~}' <˭*U]M �hѬ6*3.N_4r_|}h%GhgBm51 zҎ*l7d՗|\|+~tw? t.H 0^DI8FG@ޣzQZ* )>OQsyOXdo9tpL Tr/sͻOmk4O5"Q2#Zy&ϯx}͜.8"gCyJEᬠ ƲUzjrKbq�qh2S%cM~.RE/ :t&v| nf~I<KoSu%Yż3Ls�*׆o J]|Qj.hfn*:v_A] gNvR0-%я8)zg ɔ`iv6y֖K䯣ʥBƯDo!OTڋ|VY-3$ V)uW-{p|۰5n7HRi?OÇQ *yGtfVtkYC(+t0_eX&n�-h�YUa;̛e +A_:cMy)> zfᐄ7G+c3E$znPA3nWSVHIbp>F<l\�r*~iߝ6lpzE1Mer< oI3a<R\o9e\d,]y``ȑd$F) ܯV.(/zC rŭ/<ֶ00`)#j_^&[VĝHGG$i}͜qHH*O=7wm>=D: $=,"@hi_aCIdQrARv=tߟ,.rȺ8ћZ b ңKSvi/濅^j^A[MuB*r-Өv[{ ojPyƂo]pgƷừd7X0( ~|ZHveM2ז(v)*.ðSpР?R ΢~==ۿ�Uf?xs 4$C,y"&J@L?E&DNEi,#H[ @49b'@,`owXj4 :*}p*;I&ɉ̂&82\saxe")B <C%SbAC )e{6p97 hҟtfy YkxU|V?p4ָ� H ݫ <zX5>4cv9˒ASU!tYFXERoK4w#5p ͖v-33w+Mo`9DSp`S"ahv~!mKg;-,bF́E*0}휿:Ti>!.#(,BnL~!acKϞvB9xUd)uGrp׳>-3ԕU tuze+>^U3o3ɶkUXd"iov3NvIT!xxqampV:a1Ғ/!<(կJ8LzJ Ymm?0aSHe#lII YBL5UnoMcc0$" (83.8 Fqt+`sr(= L {ѤmDȈh nfAD„gq-~3E:Y ;-ޕ|jL{Ow7H0'Q=m?.3coF>ۨ(~Vb /٣V^\.�bG MzK{q.J[:@,Մ1_cWCNXmb=5)y=|H~/FgOY<^NŃakKq.0�6�Bk.ʮ• Y$6iJn<>1|aE4T+vCơD!4�t>\U0INoK8DY =;r><F[~rN+Y$icl1?4˕m1ay|E.N\a!|bώ5mry(lqJryǞE$<bfApO>CHR޺%HNW2p+Hp\>׾H/`I&Ft9JQE}WۇN(Ρ#I*/Z;t(E�O[F(6ixˤYp5]6{Z76"3,,4$49?DSK=ݹ*a>@'ˉFSgP@B~}ĐU3[`\]{= \Y"oHyH75XӉ/>:MGvX7Q\kg4!3 ['+^SWs Ji2l({-KN{^<rVP?' VViJ37 cdfI悚Vlqæ92ZT`[PґYv7m[ MT@XkMGo3.TD?w~pDGIW-*Vfmw\Frcz3pnLω+b倘P费Ɏ9d9A`+P8IQC_7#qD`@FC5l�6ݒo9zP}ku o:RoLYe5`ҊHm~!+_WFSkr$t[C<m Qf8gEv,cJ];Y"W|q3fxa*j #gyZkF"fiH �UMG*$__1 _**"(Kjz# lq[j487Եr~I@m|x{b'}$:KzbUb7/ۘD2,fRZrQ+da/FJՑy)9?8iT}J;)~3liV 4J b 7ݢݱ ]؇g#Xʒ#8Ak#VR28YOlTOcӄy$K%y~E7Ԯo;KҺ6Waf~Ssl'gdP-Lqt<p+Hc'l^,TA;i:'a@.Ux"J*UP2 gؕC1oL^%"xBo uVѨYD(mc *L>6[/p=5H@%<fN]4sK|D>Zd/Ï9b?_-!ɐ<h=mgt~rLBN>oL>ʢ?l qI\}Wj3kYuMofu0{qdIĮ ;eY\�r]yCvY* Ϛ,G�7w{8PlLŇE* gTuF33(c]g|M59lS?s2IHt& a7k6'z|BTVw{Kg@Wڡ.YQݽJTwlզ.uXk٬u5-62@ tCIF_ B!/0is_!~zϴUI2DQA+pS&kޭq dFH$/?-pxl\Ut y>ju`QaIQ=w>1 �g9c%\ Sf=g2ecx=6Ljl]f0g'=8Ȇ2]�%fckb1u5-wm9�Q,&?Tzw?5\%[<Ba[M�b+�ڱ>M1Slmb=8KPL*0HGS�jOVr[y) s|ts%P&0OD.d[j[Aቚ<*I�7G-X:-r@hgǏlw8?ͰS7QDpP~"};Imޥr5j5V.JcF6GA{JtW̛�5�X| L|7ӅӽqAsnDS 'EG[OH <%H̳9?W<rd6\PL6G NwJ+lQ6x.ٔ\='<x}B<aV{n2+ (鵮͝?% 9x|+ؔeAv,23e[U~@LY*/!e 6Vp1!i B"T/ftVp@o[[MH. Ҕx+́ΩwZLQ [%|rw57?5*T9tO -Gi YP&Nba\i%U-uV=B7.XpTMz3zg�(wrJcM.FpUcf ދ/lWՊ/J!ٗ;03>gݡ0-NY'x$f, BӦW=4f(y 5 c5<7M;|'%]app|+I3z$q0e ^S4G E:PbڇGL*vۋ5ӈ (4yj6U_d6ʦo+ܣ>6?q sj|$" ځZȖGr+Q'ðx/3s&BW%hk%.'_, æ�bxJL<v|p^}Y{<ZP3^Ԝܟ6pWtLB_YI)0xF!$ĕΰAW.Ʊ!;ܽ3l۳_\ҭ.ڒG0ܡ)0Ŏ>|\{1׊oB?MK1l`.d#h9ƼBgxٔ3½ τ</<z{)nı&r yFH񻺬:;f Geۦqې}?QvM OH-#ئu3C"6ic[E|tCz hy;E\b?(8AtYjZ > >E/|߀Clal趖&>?qwGƹ%~?�TKhJz|$-�0\4W~ض�֜ ьđ?PDPĩ/K %s|9v!w|k .9B_:TYۢb^dAȒ / )fYA\Q g'OnkAUVNN^?OՍq%vd6O Qʮe|LHgSDT 7+ LMݰ!3ЇW*Ǝ+u 꿈 eIl:df(Ez<}';d?}F%5N%:aw hF6!9qTQĄǓ_E,Ckc\9NsXMpumTV,*NyAD\T $#ϹDM[O\ -ΆSj!4I'VʤZ&* lwrzEJnȄY& &th'Thn�, ÊXN6E6Z7հAWYʁ:qcVb5L&e&73)ZdYg7c`)uB ڒ ̸* TCԛ҆xЌDhB:v=R{R]"4>S?-PIؾ1KR_68A Gև{ 8|'?QORvul>mZKS}ΥHCKML~�~JWK /p.;S% _,G Y̗i8eَc�~zݥK@*G!eZ)VFVC' =zKHW xV$Cr%W4T]YƪhѪ 0|8&Իa+тMy N t.CI2JurUMrke3tSTjE!i+&]WTKR:Q /P ). 8* d"' EȔ-_\SDwՈ5Wc Gnyh8O7Vٌ}L�3G#"rԾm:q}M2iSs{1P|gMLS2۽J0,O�S4MjؤӕKqƈ\/1+AKVMZb,G*綧7` U)da lJ2;〟}z:<̋'Zu سb*P7Ni̟3qLOyl۵so[ ){zV;9%` IJrgv1N9J4Ñjޡ}V--_CT /+xl[L=4Wt6.UmlR}Vq\|.Єp&ٹb]>hҔ>ό5zn"LKoL= ('|<Gū Oᧁ1xr |A2{`RׅEL6 �>%/Hpiq9fc�K/AuC%_'om3&6˰j瑕U'�9 f[ qy4wR'\yI_A]! YxEZ l%?[l0DǖA4¸B!٬Og- <.8'QSI#>n;Շ{N%[+f3E O 4NA`TWz~;1Q>P}fsIv߳A5p g %4@ֹS!++Q\Ƅ/n<(}NdR瓇h L.},hy8d^?�fko y^A?yJ*F+ʸcRDZWk „] l3̝~Q$A#@TֽK /0/3Clp6}ld"S>ߕ^&#s\H ^Z tX3vtmv A@sMĺD=6K=_ j{'4[Y<aN.F'말HO[u.SN")"x2Gp_\r�3Xs }ŝYjZ6Ṏ~Dg-�g E|8?>4H1^J,BW#�ֆx~u!iien5 Nɕ=(l<:w@>r#Mv 9̅aV"S$7)$5rr edOz[=hА=qpu/Vlϐ@ ݧĔreIyo*'ޑ bVt'ֳ14M!>^b0#} iyCUԖ++#Odڗ\i*Ps2"- ձq}\u(䥩PRM#":[+\}iBE@{a 0o)#eAۘqIzoj ' ;*YeUtPDE%>+24NЪR<x)Z ] }rnNfpY% YH[ˁ @hY"md'6WؙMq 9l4ަd8#s0y>@68G DKןy3sOxU=1#vt(9?!mk9l.mN}ƒ"<?fSӑ"|v=^|36W-/&!O׹m#˖[+jIomo&/e -hzV娣\jҝsqQD'U>P"g w͙gI92$;RE+OCQucȻ`QCs1`dV@u*<`;"/¡4M8VĈZj>ąa$(`y^TIɮBw|C_ߣne 8ex˃yo |gpӜu r gʃp<?{ˍΒX@x'\umϦm֨ p_Tb%5m@ ۵D% D(B<B(pqejh}.P7#&>a-|jsg iy+ν12kZFӷAԙi@aӱ{QEPTT|c2Z;`0~"'~ ۰~ƕiUsDDm( Ikm3M ?e7׾?/míB<Ę&8wYcw"m<PWhrMW'D}n|#L\xK6kPT [2zlhäj^V2meVsnc~:QA(`mF2)؊Zƶd;5^!+j#ڈX_u 1MW!GiO+/hI8jzf lAWL]~cwl"~͠ꮠ93 ݑ\`#Ez[]_yn֔M՘5\g߆aqMhbCz/o/4]߬aN_Uub᤭K3ի!;9ޔq:F${Ci-g.ª)jo*%8 pDꟆ6ĈtqG98s8(k3u/|3'gcsK^PR�VBo THQj﷈Xq'|p=׌;z-kj8HSc\GS(&o/B0+vaȆ2OhS>l~VD_?ϩV}v̖eNЦIjhMk]E$xcNΒ@?Fӧtq?vuF;|g=ڑ<51/]'}TGzfU*30T'+ojfo >GӎL%ܟds1�&M~!4-dalo/'RcEGssu3뾄pKoq1a B)h>9Y"'J6+musWw4Tp&F˙fX$TS^+&HW& `l}m,h#SYy3D[*+񀖹b!r2gsݿ,$b DZ[ X3oQ5�Q"ٳѧ;VqᴹW&eHGlcPc`&+AUjsO9|TR :w%Q(k~RŻt[<,gcY{O#T dJM< +oOhF }/ Xl$8~\"RU4 c$->] (�GHYmۮӚ)@&ʋ:5� WÖ0JU+4>|� J.#t( USy,Sjc Drךxl='4bWN�XpnUILvWmFkDYv9?`V5<}a!EYH'yq>NrmtQ'^޹' lI9kA-(e墸ź߱4+o\p6Ң0D808=ie퓘 :t7ܭy~4 g4tj: +[@M=:P& $-@$rlDž Z AgxRY6gfEb pw#ѩ`#;?:@iadWRp8rRg,wݕG frꈃgVዚ~;gyHy|b7Ua$$v+o[qnΌɒl<ӕΒ!-G% g M26ԦSt;rr+R&WS݈V moBzך43loa%TE_~)�~R![�l9Jalڑ0o J @p1`@@9aHS1TH8Ny) jOx,Ό,Q&+XJ!Y<יх�.-zDlu&o_=yj ҸLx`, Ɍ[jd3GXRq.(6e dRBe̗H P&#k؀:pPC!"d XUZᢠnʤRD[_) 3x%YƹR HK A |0.YC%o4VT iA2y qlPuGJة2';"i8U1cmjȜV,oނ&Ъ$hA<oVG^,m& � m?X|S*,TH 0,/9ϴ@Oڽ~$/ح]%yz NyIbWBi-MM=s=+D4 S̖ݠ=V7g%IS%0+<RVU]T?*㋴ QeF2�YVdKepӘ4*\)C3!W{wM3N\o<;I̲ѫL96ڑ8O&ǵ&ZFZg8P K/[cR׊R!vBn݆ʷs &V>C~d>Cɳ$%Oolu6Zi' ~.v4V>x6^.N깖ߔA?ϴ72$ߦ+B=m-k./Nܰ'= |@7<3ew[Hn%fU5TC?8-h,s!"`W9+I sH\@;d\A1LJ $ɦ$Mq笹8mR 6%/qq`PwzdǸ*@mj6,[ܓW;1HZ3o*N/cLEv&ɒ AE^[ԈoY| #e뉵V(G{'Ŧe/~X w@GR:$^ hz8F9^E垣s6Xߔ6E)KB ó0`#.xC%T[u[n:}^6ORV`P ;W8[J b w>=Z Rĝ"hLH|Q"VQiSy ѼGT63<$ ǼxSV.Eޠ]>&llm.|(H%w*$~xCx3>H`fˆ mlUS& ?k˖ zS}W63_0ǭ~p~Қgw$- I q>Q@\/4^. b4&jzRŰ@UTR> ?D&I2{G<̒�% ;BFs!'մޏOMR8=ju[|WEO2�5�+|6vwj]REc8e:aL*D*Hy6S4H G pŷ(Iȴc:}4VLmO@V+>Q½ˈ/nsVw_ fڬKC;DQ8ya6˻ňx'km(s!`T}˩]~[baM*YA@y=+~fp.Igarfdhӊ&}B >�EY{ m*?9xwC2mo<,w#b ǣ0hE_P]*@:J<7?|ؕ2-[%'w_$H;zIM `J~%R!#.}NLۼy 6T7'T %�v2;eNMib]axiFڨDUXp14]Ԙlg T݋.΢^0B29uDt|]kiF.ЯbKqU$^4e,r+7k} dZkE4r!߈:jG?5S[�M~4ClA2cْh%N\ fyɇw:Ԅ r{D zmޝ ZAeEAљ2fQMpR9O>W$VB/>¼ğI*PNqBeQqN^A\` PU5Iܝ'mhXDZ Egdf|8G/NF/FD :==iFk ˬbX\,~.redMhc~C@`OpљB1 lD>q KB~_VPtC"<ɹ b/Zx !|̎O5t:Z4Eqm~+{Iؖ{"Imf Hh=;:_PlRF;Eh*o4L7"}Uz6^6 쮑Ѣ)$5U}8V݃N yB=Cul0:�1}*V6{`ΏBI9VKfHӨxr$SB%ֺ5yO]-%҆'L@9ؾb靿ç�Ra@$ vNȢs $jBқ92$׋tG *"NA�;^e0a:'2?0xP<%>mB7vkf~MC;4fe#bmQϺ${0E;w�E:և`&lfJC!!=|FmNuB_Bd;c8&bjk#j,b GQ04kգ͟- _SI�};<z=!3ɧZhC8'~/W6@Ias՚HP"R&Vi<[֜JYWIOANf'A9L y\yz~mvo �%R '8G<yf�U$Uj"k=hDFo*4%iR@nyhaSYW6ys7PW?2_|">Zi76yŷD V{2JhrXX%DI*nZ uڵs?#k,D;p>H_.2 ӍW6W'Բ7K74utV)m7;[WgeJU_g6s,۴+^?hˉO(*~0fXnT(v)W%j,Xg/ dԮCrr"V7L pk, ?!砦- L'W菃!`SEn7_'+1;[:(Y%KxAr*AS 1 [zi[.‰q V"0E (2^@u"WK?}lvFL[-C;>clþ) D. W(M[V=ZSڈ})2>;0uVN匥yόP#1`0*}1A+%{r?<L+.]YzܴZmy\e`WzĎT${ٿ$13߳7{]UVp0=L袁ԬEV AB׷J� 5/4dA5SKBa(c4] LRmx#3!ǎ�&n靖/K4�ғP`kv|޽TV=D`SJgPH_dHuST'_9J ] ɦtagX'Ej~hӀhq8lP괕n'dI |Lw|N>#_H}KjyWMҗ7= 7z;c0 ˕]Kׅj8r%~w\s ͯp>I!6U;-{Q!M֎c獖仄ZƲ/l?e!F]ek"IltKKjs݄4{C^WZmjg骛(.|Q>7ťgvjhKtr#f 0 |8v4K{3ŢPz=h`h_P27lx=rՋ3iJ2�3 ۷[pqNeQY[v|yBShl/?Mfys(GDБ_a$cX.\+.懺ߗ"OLp=RV` ]8ah=V'Vq8'E@m;Ȼ)cBJgVa_-Rf/Z~.5~g_dQ-t@& 8ueC3hoo_0(g,hzMVUR*.y[1@uvĐg Z^f]Jy]/[\ۯJ0ZCC附fM[A0E#F$٤Tz:θx-ԭ}G{3 =%j4&>I0o2+6@B%Ao"T )6X u&Z\Wȷ3dB5ʵl;p6U޽0:lV8 gqRŔkl^IA\�BJT3^N!kgȱWw8,-F wt. ;N ^ P72P| s*߆Şo . )2'2@%[C^ݴAoFYERkH*I/ۍ$(Bw`V{6$4K;9DU'%R܋j,7O#mn:h@LUS%VeO)o(m- e Ul"W ̻h$k�MI <\& )zƝ5dL6 @q=%zpڻ#Վx߫|'k1DYjȝ-%{+BKg#4H/d.> n)1i29h|ѭ#MeE+ME@oASDqYzh\7=gw{Z>HaS"xȓLKz`XK*$p߈ޣ>:/E屘PNI$V -ۼ2 K#jil$oɓz8"pL@[19YWY*gV)If@OsNjjg}c5Ma8ss6.Tw%>#]5w Z#qU0wO?(QlM`S/F'ti]3LE#Z9OW5hOap1ؐK'fEAL얳R'u(f:WYd5Sf:oĉY_n3ʓ'j"ZPd<83eh(`!KGfK=t�8Sǭr:ǫ74 +\:9R8�VN?ۗ*#~Ճ${)7 9mGbv |zn_I8 /j ZUN0P2bmez2f@a5H#eWx (d~# M!H=N[痺=|<Ъbkxso,8%Jƍ2aʕl*Xqqi νHW{{ ~ɞ`3'A1^K[3:B&?\iIDy_t~�\+z/ɧNוp\됭4p=~6\|/l>T)|Sl痨-1}a+L05"~%ǪFE7Gu}mG"C!.߉Ʈ]%8$.\@)8En\l܏VGh!vK,e;h#[S .su))~l>m(2kG6՜Xࣿʮ2|-vv3#dJ|j)OuEDrL+ʠ(UV#$)5B>|`\3.zBiwP+ԅ{zU Omlɤ_Sh7WqhWt 7B .YRo,-k! E$f$�u:va<l dQ]gIoQvE*Bo[};oC618NԹ; <hUp؀&AQDOX*4MsY1jJM wɣNC1}fGөBV:Rfo+"7H;lȊᦞ'*+ۉ^֨Gnt%:i:2I߆r̙`f/hhȠܞZh^6^ I0gh!^k]r['UvRϏ.{ t-f1zuAT/J`T%gc֙,2ͤW+2nV#&5! C\xyV &y0Q U+A7#ȄP!0<M1y:UXܿB@藜&Ut;Zɶ b͏| if5RA[ cqo`! mttwA< +ϹVHWjs6mxKmQk(XMhu*TyPi^a m<X[~@pTK${Z 6"òΔj裋 R%1|D BjZRL﹢r`P9$7+X 6; D;~ޡўoߺ|{q-iͰܫ̭i^(/y�Mn&]rv9Av:IԣNg<@; P%ҍsjWwwC#?tDFMcWʷ~.�77޸X3fAT<[@RoAvc� Q:zAI5.a ?oc!2GÆKPsb>F2׎80|B7<cwW x"-\٩iVu(6I'gsBL3[`f+F:ӿu,u:컢m)t8DT%r >w۰ K5$*\㵧"8vDA(t(Z@$u0O15?̺Jq99v(RQQyD~h Sk(eECYm!Bj8"x;'q,Y4LJh^6[c."<3n]mר*1f<OiI$DbD5~h*MyF8 e.KnqjUa]8vsn!o.Rek $62QjFe7ԝ‡܃Ь vq5d}`rTw_< $ #=^?67Ͱ}o7)i1fzW~ԣں"_g5j.WE ໓uBi(,>p} n^c]F{! G N-7.Y W9&&78_z*?E~Ȇ@jM%Ob"eUkFعΙ<xO< @5+m&X����A �����}X�s=띔JkDqithf`ZRU[1}wMQj)q%+2/:fϣnćL'~ HQCxhLIm28yTvQ#7fEڌޞH\oe`suGp%GhƆ] ܙG$;借#<DPDl`/ vv}o Hj?dg9e.cp0cM,5lJԸW09/2y/1WȧD�vJjt{DZ.oh  rW,%{jٺ8SdA˕;iv^/r`ufw|댨GɰzBO?ȟ0$"̋MX>)*ե=ElٌQP?P ?g~ȷ ȐUǶXR7~,iV%!%3UAxG!W�e]Brg*_ouí9d8Sɬ7/<fdV/X= �6F{ ղ }iZwƛZP#nY #5o`HSЗ]y͟X {vj R+q{,E([GC>BQz #1ށFs҄#GvfnqnqYb@ r@On W*`7G&�]Yǿ9[URpJZ# qR?$HUݤ t\^vŏnyu[+&LFd+ `*V-,ĝ' Ę{)9Sǐ ^BWTR$ѷ13/Š[B@<+1䷼ X*"L<f/peU+iyTc$TT &b\ ʚbr}E1$E_sɊlC`悷Jd}Yܹ<GBxB&@x`e댃Hzr k.}ɍQ;C-4 I;_BN+:k 1zW߼mkA>EuSS6?su?H% LKw *r` 5&[4$k. �MRB_PI)/; ǯ]9EmZ.j? =kt]o? ;+7aCPSx#s3L)3rf;+9V-46Ʀ*?Cm8.DZ@e9XIDh'ov^d_ ݽlٍ9#DPe?)3CzLL3Q|Nbd w*e#Dn_9 @=BQ}wDZ}NG>^]G:^Gx lz=R<TRhy\sYfI­WPRa�dvs6+`fWiA)"847N9N58gF85n +46_M.pX1{LHP,xro6)+zEjWu6\:7@>OwMoGQSO}5_ "+fHvYǓ9CUg8+'?2TF"V6MIKmBl/dy�="8k,ZB-hx go݄ƈ< ChJxShp8]rPHc+94Cr-Dczi> ^8w{Ch^O< d)3ʢ$ 2؃(]aBHk.,c0&D Yv"+ 6SZR!(|YkvO& 6e¥m'nʩRz%62i!@ F\+ܥ; Bl dk< qLUu{^aXz>ʁי wb O_Wi |Άq 8 LO F<v<րRҌKVEiGMW4+C q*'xE˔.K ٖv#CI3|wuF2"NzO&d=v�}vnbD7޷u2}{%f8 B^8a'}֌Yx0$=EU[#+ =3'uXW"*s4qDkc]\B<}=:fm}D,RBFX]�QVeip뺍7ZL}`{=̌ f1)XG|p .4aGQ5uqE/1Ed| \!M$*=ee/ H*=ޓ\Y:\va[1>9dX$@N!�L#Ӄ?+TK!iE 6ap ,g{Ғsq~@^QUgwex> :]S^<ѵ _m\t [Zzk2?eLbAwoP : (6A.)JjjTZah\yΩ1JW`W2(!md:tTD;Y]2iwff;i]31,_T|BpWBa/8OpvwlU�V#ndL3F76zksSۀ==orR]hq+[hdA^ A9v\_!F~2dw +{ ?"a7Wx[56FgUWJX5sW=v%(΄+L;/*eO*ڲFmv#lk`!xd4n�DTo2`%zü;G*ao&.߾|,hmݭ �J!XWR_8'6[IYm>qr@43-�5#'~D 4OmT*WE8UK&A6پ6OA.k W*BUL$8qKR˅(ikx9OS�|��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/mpeg2.mp3�������������������������������������������������������������������0000664�0000000�0000000�00000040000�14662262111�0016456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Xing����%�  #%'*,/2479<?ADGILNQSVY[^`cehjloqtvy{~���:LAME3.96r;����.[��$B�� j����������������������������������������@ @��'(Cp�&D�¢ 8ke8/L̜ "N�@0e@9\E� H71 �P`]X hMCPP j00Ч~@Cpŀ` %B @4S@,<5xI)0ceS &LR\J JL*lR*F#R�`&_t٠&sf)wRT`Ji-AYAr7w250ű7;ۿ>O <nmԋ~7鄇ZBV?{jl&yD?V"s.rO׮ Xp&:` tUn'0[ĞOV|(D冺ή9-T/ i 5vK9K�( 0T,~e3:N57k?Nt_d - BZퟱ?\{W9o̺9?N�'8 `%J^`kj!H@vVvo>8`D3ע&TqD)o,J\)=5k{;t HO8ӔC\A`,xՙ @UFm#v2|&M4?) ]uּz@M6 Ǥ']@U� + mdP iyǡ!zXֶ8d{\9#kh c]:zƧ:LU8BPb^_֮4.ڎA�Yw5[2YFԮ'/�!!$s�0b͞0 2Az9W5"X1ȤE QϷQ0A 00e0 xE1�h:O+/^I-KOSSCۖ2R V/¾1)ŰR Gi|"<oUc2i>~  Xf OtrARг$gK�ō#VAU{P40 P�FXv%f]zB֦ڼ4ˠXGb+skLdfU6D2Yz P{  `�xpRBLQv3rS2 LG]�aܕ0 ~�x|˝hwa8~q"IEsX.uܺm ob^Qqdc0 �xxOC"m0,1ϒfT/* Z5bw޺T^$(9 bς  H�`LeaȡE#Ե:4Ѯ0+I0 �yFdgt�϶ZYv5684*0VhT:HDUH=[( 9֩< ؛@3M>  ˜zF$zO7PB04+SЊ RB#pN0(xd"!Foqx6@(8<l ys jqJ7GRnՀMp.m0 I2JtM]:1y$;wiڇKCyGLg (M8]K>%E~**@F F �"V0d l6۟" qW[$+<4)<\)F|3&8d&ҫբuT4!Ee#0@ * Ls8L.~L} z|9ҕ٨;pXDd` tB2#x |: cqEEh)3WL{oЈ,dqE@Y`�AXA$6X@a&(^zZD& -Z'!�fV|'Z4Wu.-{*�  Բ~C I`&r鿏+ѐ *22@  ܆LM2³Jqor0DRf7(66�X P2>hL7 � VJZJLij2tEPdqqX`"c8#6AAzV I@1 -�G~n'b˰ñ$;0J'f'{21oҌC\PpIU U3}e�<{ '{f{r8x@C^HnvL]f*k }qhi*j}iF$><Zaq$Imdձn<P<W ٖyE0*^@o ήa v*WuyeB~Iу"D(_j\"B@bp3 nѕOue02{ X|mq7y,ˊ]u7ԙ%ՖԅHr % 7UøkE 0  y%WY P g )yP0Q%LsgJǡp<&BMRp2H ��HFa'w,l.'+Of3*_=xؖu1n^C00�HTˊO3'6G[(gu=H5H2,TJ%e* :K&(ْF70 � "8 CL}Ž@0`e\8t14M*z.�pV8б$90�34¡j)uӎaz%6 %dNOGH~mJ=BWpFU@qŔT  � OW'I4כL:Dv#6n%a溟jJ:t0��N` t�@d3 (M�&ֲUqtm3W@jF<Ea #  تzJb}J9$�)pSm]lR ЯEE0^`DZTm�T"6t�8T*RiL!U cԭ(i8p>:;UV@ SPjVxؽo)es53FưFOoDn},>g!nŸ&{ޣ'֧!R i<:3f!P0<$pAaр(f@"j0TЄI":SCu݈H1qx."tGvwC:Qn?Fw'$sE2N6aWt>ШRbuajU�1@iټL=cH�F͝׎zآoJo*-p�RsjU[�Bi Ca˶[d�r%aqBHa1LEg"0^y"> k d{2׵Ö<S@T T@BD G m},& �"є@IX>y:ef9<efg}Hw]ݱt0 �AL_c:LC k|N=out}%r2 ]Jh�8�r\@ fն@LqJf6Gǧ3܋]71ٞڷ|]iA%i`ɴa$ڑLAFx~r5IxĂBVߔx'̅90 ^xF=�p )JE[ p$d ĀQAU S6%Ldd  x̑�q9]bYhB23g.60-80 xsn[2\m*V/<$~6 #5od (mqo�*U 50  @F^ G1gPD䓜 k ϗW\�1y`pb¸�z1VUHmCc e=@JyX}U83I&q_rLȥrT`. Kl61;aqn)YوH:*mI@ C"~n�h.#|gY7%Joťg h0$B46$oPb"{"�Ֆh�]ABR,:hy +^C9nPXPYnŅ7{8Pz2e0�>V{h� pX�`Bxy1w,&,cZ4{BT�V{#୾d0 HҼ(+>8_$-a}D-l7 =Ƕ'7UEw=/Y0 6Lϡ5FZ܊r @]uv8`4*nKn�1@x[iQk)nx0;@G.%[^d"> hS9:q)0ic %266!6pMr7@2zL=҈ a0"ɓ[)@ �ɤ@HHC9ٱd_lNG1,&Ol�"^_%/E960�b pwb�Z}Lk եe6곎6XSKQpvUE>,M<~i3u�'<@d0 8{ h0S( iv {C3lLC(T*'|ޓPu^ŊU5 iR68  &C }1N�J҂{#p3 5WB̋ �01ryqFLI Gƹ$"`d0(}ӽA,r! . @0 z8`Ҿ'B US̻܇Z!R,7~ ŻeOjI^�yB%,<dNZ @>`ĖI*waMW>,1[xTxJ2[te0�pڸb�>,̘& b &vqB͙\\qT4%F,hYMBwB*Ip+0 Dc@՜}hqلh*'"N+YkW]aV~(7 ,u�k9&9N)0vy"dY#O$(Laޞ_UQq+۰}e4ynjI1WK Xhnt  x�ƌ,VKm7Z a Z%a_:7B׀"3B0 J4zA"HxpqhڙhO01wL}Eo{qX&ȸYm0 p�FHfr@ Rl\c+ 0Jxگ{6�;JU+# "~Wi$C,Z tzI&�&0[cdz<J~w#\Zb,E60 t�Ig 9⁆*e]ʏ_r64̈tvT`ÕԔU+ʂJ7źOi0xaR/ob1PUf1xVuak^ov!2tIY?c\jT}gB r-0ta|͇51~wFF-ϴJ5`s^=H$BQ& ֮.Y�y]ri0Ɣ0^M˹_[Fb!iהp uJ/ԤYGA a6+èk�UbR-(~&b9\0*yY}2{d)Y>`j{(^HDJ p?L&SKB he0 -I�Բ5@ƪQ*jE$.ajHn@`S)Xr^U�@��YRb*`&^l�Ω&͢2VW+4X]V9+o!zj['%4RA2D#Mc<5%Y,~V1~?{_?V}6У1Mnyuvyß �fA@” A;8p&bޱ�XF5"i_�߇'ȪnxDT<N37'ZP@BT;]<g>i`¡Z%uNn&X6W qi)ѣ̝Li hn$k-hsBh@J Rv1̥`?'j0fM5ϟ{,;+Z� 3=TmS(@ESb\P* d�B)ZktaѦSOH'hE[Sj eJ֛R>L.@!�OeԻ^&/~ZM/L 4cV3(8㷹(*Бxq�@>龊 e�)HyXML(9&,So♨a\(e'[ϒuwD~\s�kՀ4RrO0R+^PE0|""ʿʋo#b8ψ~AO7cbz95EI1ۗo�=# ;Q$Ny }q^vk0q"^(Gjd[�;q:7#I_ߐHXfɘT3‡YQq95!0b.|! 1z_݄;9SA%C5Y w6PR01TjϾpNHɏT! o!8bW#O wtle�jxA=3 y^ &nM-Go~oAEs9v�:M*&3Q0A<,\C|_Zd ?KQĊξYc.[yM7g,{ª= .,40_r0 ��H' ޭxtvTΨc|�,$ƊX`GO;�@#:ROڌܳtY5~ Y�gtT9%(@!vU.BtA�'p&޹x�-s9N\ϋj{H֥>)Ts{j;ijwx,"kzP<^ƹ D>9o,dI V7 %a%{Z絨ޡd,/ì0XP+4!:r/_>D@`Řok T�0(]IjAOZoR+�Ӏf<}fy;zչ[jc fљ)@^RM"Vbr1<dbv  > !5'M܎oeKfܮ WAiވz2yjIۮĉ!>l6 0rc\yRJ N.S:d/@g±a<81�y7Kp!J]vU&3@�3^~R lTP8bd3]w ̘ Dcؼҍ<Mq<-Z"jFn{+& &;PCH�ͯ( ` d,J?5a %M7O^{0Q^PGN0%�Df/|"_҈G/}_-<-Fs)j� �0Y^Yİ fzFчLQ/�џ}JԡY %nPNPBĵ�.O@;}I0 4TL`F?@) 7`+X|S#Ҁ�X8`= c 9^(GnYi4E2/C:gey`qh?� h`0Y TĐb*�@e7")S7ᄋMHP '1JF d�;l0 S~i#FA0/ uf{vsCic}kk;!Qd/ ^iװ_}7<C|L⒱l�2U0�:2T<] R�##Wl. c�|Hl)_W'q?oп/IY �0 ;DڒPhdф? G(ʫH&P"";Wf~eUk�0ZG^QGS # EH5Q]kcg@ o)~oFToX]q50�0:3Ҁ+c( M8sѓWh p# Bk[oAF 2qABX8s0 8R�4nn$@D.`( @oBo`C@?PȊeXozV`0�fHep X%]<SF?섾+T ¡* ; $K֝payv1 `0D*e:�I+wc^)l\I9!֪�rH�0*^`Ĩ:982שּׂjj\2u3\mvjb\;-Cm}QNB{Wzjv#"8:G0 肬�(:@DbL|EQJ\]%P&8`e8Š.oƉU�30Gb0  (zD8tIW8퓬yf}o8@bœXyta}%0 J^2R�ђl�-@hdYkYT<s<=K@" ?$Ue¨5aTD�CG0 nV X8N~hFAiNҚpZ+!T(w.toT`p Pb`SFNI@9[K30Umz4Hvn$a|1A&k{vWI̜0M #nusn($9X7PFGo{` oA,2Viʎie,3I4VIQ3,cՔnnOVz֨ߴBp8- mO= 1ꭰyqirOڕ*32<R6i;p9*VХJl@jH̼ۂ0C%``_$KBwf_zwA""Q:,�lnmX~*GfpmH!Їq#0i+F/s!O=c!S`gruکWI=֨O ~tm/S%-09�Jx(n2MTFt>Σkaٍ֠�P\TPF{?=</ aFlؘ0g?E sqU"H:ְZp0HtCX/ow;7zGN=2{>$5>, "FL՟B°˵0 �Kƀ?!P 򈉝43!a�ԧlOrg�aNAw;)S9z )� ,ӨGY425ϞC:W[B~0 RH̐, Yoq0nSQ25S烥GIVTZZ{?L+RTyTi&6xJD<0&=al08 ֭Xq53*qƞakP&]Ix˅(TJ)_iJϦ$$)0|Ll  O@�F.j?`3X0OhAD }?�Px P Z.X�#j&,~C+*^KZmW-q;Pgc>ú~~YŶ}:(tSޫ-<܈jA$ M/\I?f*#`&x�JWG/}xu:]XS="1cKW%,F_bx\vJ@ʏ_fu2 DgVE50UΘwTc}W5m,E1@|H_mVÚwmgeяPib0X0�4sPd q�@3Qp,Ep#8 T[a`a1 i`ŬZuJ�&9e9@et0 EQ֒VfHUX?/a ^^N8FIUG' ץ™ͮ9*oŅcW0 0EQ{H1'F$viK瓖Tm2B¬u׻;VqC2Y  A�b% CrVrX+" AE {o0� �HG@bI2fG#,K[R^!Z& yFlU ?(,;-)s䥁&0 � Ү,E*|�iPc9Q @ɑLy>h_yj  �<1wOtBRECu3@>:>HRy}0� X,ܲ2 'Ê~r[_MXZx)MeWȉCՁ1*o=Хb0::Dtq[g=&o S*E=J;ufPBWj.@GT8F 1][ga (  ~8<LL}b~SHBWJC`T!$M~�0f EUUZ`"]m&L[u w1Kc{5K\uhͪ@P;9<W0 �� 0k$ô(u˹ъU7V4%|>0/h2Ě!3؄  �Ip86Z 7okyJi{3W"ov?ahN0 8 H0� Cc3W;ngYOe4d<<R5n'܌ hH}UD`&^ �>�9qVgWJi{zgwȴZCHSBԤ,bzer*aswMnKcz[oTX-T+z¦CyK)I?o׻{_3/@Pp'^x� I<JpSV 3$%8V>"7tG&f#l~ob~Y⮞o1 :c3D}Z6ӱы+qY2׋NY=5LӬE;;R1^~NhRnXd(H�V͠ښEc&h0�E o+˖sjZ,y_"IsΓ̾_&T+YkPԞ@40` 60_I7"گRzmU6M``SI'v㧾yfnk'-�$;4£0 T LO @,Wz~g9W:O llFs %CJC| Z pBUD R H L[vHC"^*!5L(JH2"CZ:n0� Y �I\O$ r!TiDkCu<7dN(R Ej1yS>m=HΧ%澠0 �A,QvB.r!2pL�&)t `jGDJK?mp uu{3  �{0B- w"@?vU{nA2‘R0 B�C,ݰl4un_'% />(DhIWJѶP |K4  (C ,9V(q(mPx4aʣrB0� .�,Z4 Eҽ3J <uFȺA!'JDZBQ#eVwlԏJW{0 *� 08 e2q" 4%I^=!j`ANvuV$)6+?]  g EJ_?v=vl ;~2u ujjLDzEԡA 0!j@EH@>i6 9VJ(YLݔd FVF5Ĥzd7t 040 Bc @V=|֓()[ y}&5F;~)Ez)U?jvp' `IC0 PF".m8o=^7 hEvz;V>A�g grXU@�a(l�06Ld�~"l.dA (*й`d {9*P W*!Ŗg'-jÄѾ; �zDl2B ,6oWl"S&B!<>P 09"T@Gn\pt,rP,Xq">hu0 D :+޴pBB +VM0 `E$x rmfL m�*&{+ x4{7ǰls䨤J0Kho_S41:6C>l;렚EPBP@&r5[ CPZ0 A'qZQYyF m[r{C/M_3'a+ih6B@ʵ8/[&#L0Y""HLȋ GLEUSiF#b0#I798p be$?[A`qq=00 8*@Ftܔ9?0. ,l BbیȺكK% }T.L1}s  n�`FSP"M]˺ BHhɤcЎa0%*-0gFe7-� Hru!L#dg!rפN�r}S Dz\@|J0 8:T2L�Ae&u$$H*Qy$`&4)$ki-GBwI`*)Cޔĥl  V�0F׽hm8ms&"X!eV [U#Rc0<{mEPIh 7wd݁z.3Pr�bu�]� 0XFX&ޅMxJO&B)yt9;DD-Y|>SU j@* g^0i3(N%ʼ_N҄Z$ <3 #:Y s18u`tYHB\:!]x0 XG RU08(B"=qpQy_pM %1剭  ` Zqj3隸I4N-Rէwhj`�0YP+m�M> @zB\h8@VƔfNο [Z3ʩ5�BF0 !TyX_RAFwgZ% aR\o}$8J[Kv3u Hx~ 0izJRY'2&2XF|qds**S-߭(s(:d�;�@bjPG%/�YsF`�\:0$?BݐiqEnGϣ?�>Ḱq@^"$2F�˒z)_ C:!p±q6:  Y �x̐(=]%@ b3},jpFe0 �xt'(TfZ|K70S*7tDh/@<-(zz)9,sŻn:+)@�xtt^S,GԥZjN^샅 qv^%7&ze&L o�6А[Q&c#@bj�y}[LϣoLFj#W6rN<8(4`n3t;ZNDY-RNէK*ñ!ֺ+f9J˶092^G2FpF~q#2X_2)җ8B_ J(]c& %Tр LJ$0 >V �hzCP8@?�n׍F$H3J( D[BUR8TXHI0�3 D1/q)iw$FUѷ9H,�^xZ-;ܙ  8 OTN,H!ɭe q Ї�M@0E2$ J *v3,L|L\>AA, 1tpwjQsٳ!; 0! OPn7 |zab(F<fLt3j[e*bj@���)[4\`*`'"�@b`3C8ƈ^!w Tt#cRc ABX+%e2ȲϤ2 邈%�u�l`NUF_<-R-7/t[{h,F-;(0 ^ �D)sϩQ ܳO#҆Q/wDQXT9Y nh֋a  4uƗ.xE2-4R"v0Nq0��|odmB wyhy0pXA04CcTF/E Tc":Ao0,aH�FPr=peL tyhEٜn{v[T&>aRꦂ0 Ψ {t}C TO!b.YEMS{J<8kCʪܓ`Y+N2BT  ¨�{ h<Kk[}oBŴ|:,1]U N4F069I�a2dl'ȎKưe4SS/!@e4-`H@ &LJc{` &x"$*6Is#V0\\Lܜv>9lfL.+H9/3Q?1|4h7}5SWH2ޫB󝏾%/K3{ㄇ�jtaglib-2.0.2/tests/data/multiple-vc.flac������������������������������������������������������������0000664�0000000�0000000�00000011222�14662262111�0020117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"������ B�zAf鄚0 <w������������ARTIST=Artist 1������������ARTIST=Artist 2�,������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y�k������Yl������Ye������eYb������)Yw������QYp������Z$Yy������cY~������YS������+9Y T������LY ]������Y Z������Y O������Y H������aYA������X YF������xY������gY������Y������Y������^vY������7Y�������-BY ������Y������Y#������\_Y$������*Y-������Y*������eY?������Y8������Y1������/kY6������Y ������FY!������i3Y"������Py#������XE������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/no-extension����������������������������������������������������������������0000664�0000000�0000000�00000000400�14662262111�0017374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/no-tags.3g2�����������������������������������������������������������������0000664�0000000�0000000�00000205357�14662262111�0016732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypkddi����kddi3g2a���$uuidmvmlԁ�'w�����5���<uuidenciԁ�'w����KDDI-KC�W11K����KC-RT19�KC-RT31����,uuidcpgdԁ�'w���������������������� moov���lmvhd����5쾿5�_�p��������������������������������������������@��������������������������������trak���\tkhd���5쾿5��������p���������������������������������������������@��������������$edts���elst��������p���������Tmdia��� mdhd����5쾿5��V"� ��������!hdlr��������soun��������������� minf���smhd�����������$dinf���dref���������� url �����stbl���[stsd����������Kmp4a���������������������V"�����'esds�������@����}���}����stts���������C�����Hstsc���������������������������������������������� ��������� ��������� ���������������������������������������������������������������������������������"���������#���������&���������'���������*���������+���������.���������/���������5���������6���������7�������� stsz����������C�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������stco�������7�� T��j�� ������"0��'��+w��,��1��6��;[��<��@t��D��I��N��T��Y$��]C��b��g��j��pE��u��z��|f�������������S��m��������z������������ý��ȿ����Ϊ����������`����R�����8mvex���mehd�����r��� trex����������������������mdat���free����mdat���wide����mdat��libfaac 1.24��d3,(@PlT @"g5/<3Yn]Ji#qztm6?xwi龅Ʃr Q<fGwt3sfv5? A1@ztD!Zz;3=7!ۖޗ0e<e=6L,!e3ߨp,*|/4n]9Ah О1MA R]J�߿}7K޾o�8HFP\6)u/Z�s*:tࣔ Lɞ)q37:>.z"AX*;!Q1Յ((xu9C AzFO6KF-T6Q|#g}V/99u}4~A 13ZzdmqN+ay mݠ�poM�D���o��pB6HXRdbqu?zAs}:roL'0F~8u˭F޺fr D_&~%~VJ{z"O >),53 0Ā߯SgǟmR^Udy'Hۡ䈾ˋ(Kc-ǔhYx)+J~7b{:Y JYUDAXWM�{N�<U��@�GF ,8" H( DA`$c}񺫗xMU떹ֵWK|W'ֽmyhgK/NO4/=9PR\9yw]dxJ^tk?whc"zw;{"̼~E\u(( 8^.x$"0x�&H`�"��T*�B @TL B,8 !0\Z[;uVe~}t~~.;1,9ǣӫCCoRE%:37l),6H <�^ NH n'hbEX_UF{108VW ѐiX ~ �p(�@��B( 0X(& aA0@T&IUq̓~jw9%pc_NI(4{o:ܾy$(OV=wҖ恭UС?P`tMճTi:\s߱*G(K!C=?KRJYbçCﯻ"@X&@����pB(0X0 `P& BPn)k徲1ϳ^-\NoΧY_\f]?77y{4dBs]Jxy7ZĿ $]qWԱ}T>x{z�Ƕܰ.xQ{@sۨ }(kT=.1LZQ-kW) uE$�@�a ��&��*�DW,8 p* PX(6A0E/?iqݯxqgR٥iy鿒% Gm}jW4t+@ ӟ " Ӈ@z\ H 9P8quB{UQU/d$!1X.jg|X8�L0D(g���`���JLF0Y)213SVm;i(&DM>.1`wC7:"Nf;vy=k~O.r5w+h �K5SqrSbB�ޠi=u*π]6[/R(\#6H9RH4 `Q8/ ՇdvX��-͖��XB�Z � T�LMi*]k Sokb1n)zߧ=XCُ~(xn4Bы=tr=<roV<sl֟qJ‡CB[=s{,ANAR"sIy)+5僊NR\N/%[%T ˕q�.N�jA���ը��pJL `H p.#B#0U]gY<K˽'6.w|x|~vohР{I ͮ>k,wc&P^O,|y-^q'#cBDX_GR ><=B +�#ʻ M�.��$p���L HL' @$ BApPP`L"#ߎ+Rz++cj's^߱.~Ka<\'GW-V O>׺; {P)L-c=q@Ƅ+>'U~\{jWz4"~p~q�(T��D���� �`H,P,$pH(`P0 B0UМSU7WzVdϝSoC-_$ ߷xQŗn|r‘tohgoןK|ڟku"KC: >aeapB4bhiCF�W�fAiV{�bE�@ ��"���Hl( @, A@\$ ƒ0\$b;g?|Wu\[Ԛ}w:&.{ִ ?]ϕWk~/?g͵IEj^ҽ<|~K'o7*ft! i\IZ=q vS <& �,�P�@��T��HL @T, @HL4 R^<ǝoWWƿ2og/z7yx# hI2"fPl>[cCj|q#~&ܰX2ݗ/idr7 4ey?w[PyurB$9 /@ @&p�@���8H$ AT( PT$ AA(PJ <g]~̮gw̼qvJN̖>w6SP=\?C>3;/02@]"D+*kɁLw~Щ oj7CU5[%=18� �a��� �L�H(% `H, @\, pP& D&C[^/on8<ޚ'k6.!F1i܇;gϛ#‡+ˆw ht4R<-4\rr6-!-ljFt`BKg ?S3-n;p^2$MO' 曤z�P�P��� �(�HWH&BA X0 `* X|'w3[|o7RO?^J,r9p;w>93i>_*ϡİauP|g=d ~x3 cNjl!% $k|oԀB2fTvB'ۥm]�hH.:)|@D@&� �0�" �@&H`Ba/Syu7hXN6収qٕ7u" UކP1"%F~]?gS/ڍ60^%yO.Kz%z`qRh,r]`"vz{%cZM>n=&I[:=/:ҾN=ԥ`b*`{<C�߼�X {t��)�TpH- 41t+ցZ`Ϊ1ƺ&Kj=kiK6H[S,Ҿd6v9Io=ӊT/QԧbsYF' a3q{Ww qܾ-t<}̃*ME9NW0E (k��/BR� � 2������8BPVbc's6A1 8)0_?&}C7/|hxUlF$~UEF\k!͉E&@bk>(zƝrm:@t .T`�X�k,� &���J,^+*RW!jp%K< /2yφѩ8{qnr-M(TIuMMa;﹛ Qny@�,a|N,y~fWPPQ{"��>}@� @P�T5\��}P/P��F���� U�N ,`Rhbuysq&8M=RTc;@ ojVT.ߚ</9U^_PAg{U{V&nuqٟpI=s@_dDҍUFQӫ\E(~,#Vz>$R9FU��� 65 �� s)@����F A@P, @P, pX$ Zvxu.gZIxs?Y}ʫƮH}XMYO~-}x/fRҹ(Č"*�&� �P�&���pL(8  PJ  XH& @P";S־׵|=-f G|G')4T{yU\]zL~wPx>xYz+ 8ko|]ƽy#Fh$׌_ rM^iwUA�,��,�&�@��(�\��pJ$ B(,$ ¢A0Pl$  Ux~kKZznU:ry.nfZOyyxJ<`rcm_jٌd0ӱݾ:`!lf)(c*%cpۆ`W;F *~3�@P��`�@@��JH&BP. PX00"xeם^3φ_̕k#s`@,%oWܭO@�JԒW"l": fu>(v9<MLTWV.)x暣-mA��@���+P �X�H$PP* Dp\$@P. BaE U7zfe^52=prPs/v~oknZ>);r/AmzaaGXX*)W럜ڭSxG gf~mK1b|B @&����� @�� �H$BA@TH&0. p\(koo8q_>2.Z?ӅCc[7܎<8.PkT^Q7uh7./gz!7]]Uh$b{k1I`M6<�\�,�`�����`J$ BPT$ H&@P* -}kW|=w^{:u^fk&[C.. o|. _͋؃⿽; ]#,KTqN{>J2 @w ~S^p*)ؖ ~/�j�"��H�@��pH$ !(H(QT. L.)y߫\۽s57_9I]~npql< ˷t&U/;:Ew'l JY۫{9΀Ҳn41!|U>`K΀7�� �@�@ �@ 8JW$ "(P,2 B( ZS|oֹo̯ s8I5'׊H=SAxqѲFmUD:O +uUnCY=nwyfL? l-s`WZ4b;7��&�*@�&�*��L��D�HnPR$MeD_N4n0VLT*/Fҿ:eRRDX'}῍+!}6tFxouvzT5qx�|I8A- |x^N?p[%v?椑 k|pʕ��Z�`�����X�F4*Xw3ik$Ii_~#�["kS%0եORL-̷- 6ȓaM|',ȽȺٺ~^uAD! ("b/-sٓ[~Ogĕ:#jp X4@6l���6F ������PB&0Q1Rk5788 ܳP+Wȫz #_YJz�9!5khR[ f.{?x ԉSi~Y{BohEOW�MUڝ3N/<otw^6r\'|aP\� @���ň���Xi�@�D0QBdk2:ZEG%�,;7۟^7[[9}) 7la/ZeSo|WM,F{NGR'-UQ_|/{Lϭy_w|8[Rίe tP##�sL��p ���5���Rj$ P, q TD H*(?jUu7ϞU/_ēM jusz*]ݹ?Iw>9tvfkՅs@Ljg,O^^ÿInիW! ዠ$k`Xx?`<p� P�@��� � �T�H$E AH* aL"A0__oUxn\&rC&NӚIGwa@l~.Γ^>[v?Hzc&h!#1C~ O_;%G: dP�(�`P�����H$D B0\$ A8H" BAEۺMwQonuw5^j&꣆l2+E9=-F߃Go#X&)sGb?n3<l y~y@~ڸLL.T�B @� @���L�* �FW$ ! ( DBPD$ ! Z{޳5{޹:5AOլ!ו(,29_NcOIͭվy<:|":X`sŢH7WGH�@%9 `� �� ��\��$*�D ,`T5|݌?Eg0u:mkK[iX[Kx$-^O9{b5t)Iwދvu?Qreς;xw9LR箕u.18>"bZvp.n. :S\T/0�(`�Au��<("dRx_P 0 J`G؅Nctl0זfd58e #@ሹz.3{(|ntOl!QEH!/!HT"$ȩ7(0)LP06qȀ,�pNK',T81lַ?ŋYwON5 i{,8Z557O{w'@P%uήq܎DFkoTDmXK qv_UvpqD�E D�� ��D����pP '(ԑWu]k$#@tqO^sNZ5M3YV5sjy_@zR&}ɡ7ۧvN˱w,nӬj=|`I=Ԋ*H_{Lr�B��@D�����@�Jh20_w(]{,yy_VR]qYp)1j _8H':Ul7lo�/ A eĘ/?kG.!5!*0<C ��1j�����D���DpN(6AT& @L"ׅqydeT%\ʚ];^oݲo~{_j"ӗE:T.x%;}]Q.ÿ.T53Q9|%@'9u܈ �p0� D��� �JH @PLAPL, b@P$!ixVs5yG.R>4~gP_/4z_Ƨ)RZW !Sx骶77aɯoI t(svI]%}9lX@lg, ����\����T��pN*  HL%  T( B ( XYk:{eqUS8ͯWOlJG|<<?\+ Z͆i=8Pyv^SfZkf[>2 �/.e!MH;ZN @ �X(��p��$��.PJ  PBBPP, Q z^ksk_9㊯ЅV~]ڻlNeyV!x:>e7=@Ctg_iQQU�Ƴ}3<Ē*-{C;SRzk]TޥB1 Q @�X�B�p�.�� ��FH BBPT,4 aP$ Pug{ԾKN9xoݎ65ȹx[<./o/زBQX.ʟ>wOTOmͤ>kJ@vw*]&;A%Z��$�� @��ˀH$ a@PnBa!X( B@Eo^q8*}je?y:iɶ<(|yCʕ=Pfnя]ByO9驡ء̙+^*՟"9!{>y/f��@.��( ��*� �JWj*@P, PT,3 Z9ճ[MSuU xY|_rq] duÒ*)3E1i߉ :C-?Nw6oeoFBy.]BTiRgMN㋚̬?{:�p�� P�X�JpTU㎵:Ǵ' >R5"*BR52nty(-fe>ziګcY 2@\0&<.}(É�wz0\:.f HSCH=ҧi"L$ 0�0���H��XT,#0.@T(E⒞\㙓zvyί*k/Qz ]~k%!oN~~!7hM/?>I"?{J ?!M~tira")IAK:`0�@� @�X�H��R( H" P( BA<UssZzu7[/ƗyUu {w~em}%1{baĚ3V4C> D͟<rF?zíGAĖ2?n$(^b$*j@`�H�L �@\�J(%  .A0HH' \~s޺sS5N<o|an@L GjLw]S{L;"?dtw\J MFu-St*]@@�@�&�p0���T(�8J(F P& a@X(2 Pӌ箷y/?Vn)S_Έ;AGwd[̝[Oط;V >.w7p9)�8$g{Al_ +}+׸v~9H!M%M� ��L�"���XP�8J( H(a T$$ 0X(%02otw;v-uq:]p9;Q4s.mYj]j!"RlenA|v}?QٸsFyKT3WI@iZ6�Vw@`����� &�NW(% PH( CPH, ba(H"U}yVKS1Ԟ} %ڼwa=- j?ROJ6q2ڇXyC#%}ܚ/ d&Jo=^|reքAu/J%qA)`��L�`���L�"�D,T*qƅk3q:l ǺRp#]^>M^t[EmahaU%W;]Tk*Y%!!*b75JR;^uFv0-QCHจ LBPm6kJii'hjpE~d@ �Z \�l�"@��L DNPEB�` H6pҒ Y=*kWۖмyb9BDQ)SH[7\`N)cu>&|u{di;;U1ڄDbi T B�6#3U@��lHB���l��� H 4,PBdkYZ=q?, lԝ-ps׷]u2T_M5m<WCqZ`7S�Osk'"ѡ,ѿRgz4O4_g/[QbI,+Ǹh9ϢWUNhz2"�(l`�\o�����H (,!RdcYVf@JP"o6Y$nlcY5Ra厫+)W}JG2l ek^k.oZvmg1yUsyv]dg2\<zlH3-i!0U0Թc09'H\&�r ��(����8P g*M ]b_׋%-%.CIƆ3M|aX-Mq6񷍼KgѕCTpE9j5LR6/mnuqzq8Wfs#$&b+bxjӣ0,X�& -)B��5 ���L�RH6 PHH BQ H& BaPYg +U&+۵d{n]%˼M񫾓 sv_B|)t`iu.]jCG8OJqЭ5Nw}*ܮQ~{M}7ԕ-{pj!P`P $���. �I@0�8Pj  P*"!0L$ QE[ b]O_}:6\{KXxUt8}Wvww0Ҫ/bѩ9n:N#OM$[B즂kp"R*T D$�*��&� ��"�pJ(% CPH( ATH A(D&c=LUuUĭ~˶SY:ZېiovecUgiYtr!^ߏd.R{<m !NJ]$:dDp#?&)$&QU+H@ `$��p�8DDB(P$ pH* 0[qz{W}lƴqZ_FCRr G>]VBts/-gǗɱy+mg{βZ4߫ 4V08� � `����H(% A@H("  P$ DaPH( a5knx槏ߋ\N'y7澝WK}e.޳z -'(qTF'wgV96Yx5G\U>%j0zK3� ��X�D��X��. �J$  Pj QHH bPS]\qʪu%ɪv pǻGl z;,>ib.<6JCٍxoM{̭ s9%IJu_@K>1L�� ���� ��X J(r a\( DB@D$!Da/dOn>ٝn|ܮ1i5.ct6s{osף�Uwo_t#^ZHS?G[}lMa4/;r$�$`�B@� ����H$ B@P&!"B#0^߳zg_m$}'7/B}zy[k_6_]@^ 6ÄS˄v.(qݠ2(f" 3QfP��@&����L���D�FWL A$# !1L$_:kN>x<q9+I?.]fqz0ݏþ尗w<<|4Eݪw<WA5)(RsX�$�.��$� �`� �8@.PDIjUu?v9 \W(QA4 Jfo}tS]y!G7hOjn~HP^DϘGbΤ &oӇZZ ӎ׃ !DLH�� @`8JE(KoЙ)m6NH)uR“kVW5l﹭~ynoQY\Pb QSUmtݯDɪjӽ֦hҧK)H?FD �7� P���@����J$ !(H  L* B&0Ϸy]wrI>?j93nqս {ӻݜ(ɢj28XMy8~{by hz;8_ ym6∈H4�"&�(�����F`� �H(5 A(H. PP" BAEr羉~^{wlԪR9;~ kyoWtƤ;u6}PqV*iq|eEXox.'d[mˁt��..���\��@��H$ PH* `X$ P$!Xssߍ5}_{"^_ưK3<:ȓAgB/E?kV(Z.:9Wq rCƸ)v }Bz>6n\<�� .`����T�pH(3 !(X PPD `N=s3~kuʃCׯ{j)Ucy9Q}z X$n=38jOƲj:9} N)|Tփÿw@?P"�� �D�@���J$ APP,$@P*!H"u5Yǎx}q*E_]}xH9 ~|NϭqWʟo2֯}Jk.㶛hפgAXa ޭ!&}u>ZSuE^[HJ � �����.� �D�8LWF !( B!(H""3*uCmnD]nZ$r>~-49uYx~kмZ|{m9yMP Z߅n&-L)CL4B eT. 6��P������ �J$*P4-S?,'E$q3"Zr"h.ܷPVܬ(9pp/xR/5)9SuLn?�\S�siGJ{&D!J� p����B$jM eku`sTj %k8k "Di߳h> 'I8v#عa-tn$h3;M5*Q[5xgC=ajPi櫺;;}S.[n.s6̓�=L�"s��08J )0TRȼǟYрĹnֲ5 MV݂5*$T~:yaLc^[8qc϶i9Ez~vUMպ ?_]LuZ Lrx8]$ ݹ<\A �(��� ��T@���t� 8P(XBhbUN9qMp?RkK}D| MqZC /5rٗ=Z5x[dMRC:%Bb/ŧNНCO(\{V3R7ǔ_YW^4cu5m=%4~/%aO;hXu������������T(F B0`(" A0P$ -f+>v^u%]T;|~ѾNyC47 W5rrct; `wXe=5-K| K[MGw{b�M ���(�@�T���0 �8F( aA(J q H* B L"Ǭ*dd֕%q,99l}>iݯ-1gJ*K~|5MnɇǟfYX=<hzY˛ikNdnM)_ YI PN@ �@*�5�"�� H( AX(&QH* Z羻UlN/*q/oʼ'|Ogr%2vYW)٠ǡC|65B>[xȮ*W=LV5h|_1:.LW/ J`*�P�T����D� FWh P`( P&*gZdUVW8M·i8}G^9__xn 6~JG|VzJ&vy8sU(O R٥;&A;D &�\��D�X���@.�X�FNYUj?ð4>ݘk[o }qm0Dl` ڙ9d΋:zR0jsXAjQ zǞu) G#ֻSʐ#D' ihhfd7y X|j65e $~6�X��^V�������pJM21*|qz�29bcWtUo.YSquF&4DlOGTTq eeSYxZ ߇K mXis/}QW%wآWIO7g.XuMP]奮v 8@ż*.�CV���jB�����pJ D,QT^oZή[31ng,я |{5MzvgբK*yti+t<$5bf_hϒ>Ӿr 킡,~ >+ x@b3T9H$dwpqL�R���k������J( (F `H* DA0ۭn_9ur'N?=f6tnϚ uoIG,]�KJWaEea̻bSSKN q[=xVQ<q ڑ!���X��`�H��&�HWL aA0L B@H0 @Fc98Y<kO Uԫɩw/'{*~WaKZ:?}/H5HQ_`78& -_?f Ǣ tv.X WoǢ ϐ X~ۀ���$��R���D-PV 'HXûhMj,1X:*%: �Q\Y7VU{³%gJeS+lڡ5J<ckkӪFWJ1=7[K w|jr`�D������iJ$  PD AXh @T$cs_<3띤oQ{5wI~޸f6s/G&js2*~w=ȾJ)?/!Ri)[Irc7P< [Ri%I%o\W]&-l�@@� ���$`��pJ(3 B@( A0T, -qu^gguFK~c{Fvo_XmsŚ vP oW3.?H K`;/'0y>IAǩŻ~)`Z<MDpi� �, `� ��� LL@( PT,  TH ZgG3z;Y3$|t99õGߋNzZ>lcR߾WpQqw(M_xZ I:g: ZL=_. pg׎ }8 6:Bq qsa " +� @� ���(�J( a X0@X. P(*{~xS8>׎ώ;nqs^&'u,ǿ|U}'B'=T}23" i_=blwi)(Fgy7}0%N#3<Ep"ntvrAM+#B� ��� ���J( A@L$ BP\, a X$k9ɚ+5׺kOGXtW7M%O3{$c۟SQ*͘jrj=HKGȘYBed�; ZkN9 S12ٖ 7  &6b���H�����D��pJ( @* AL B`E^y|[^J*j?Ū]p9<>d ( Vani-\=[> A2 7xNG]YSi N:`=o !!Q�2w`w)UiѤ?5J+@����(���J( `$& `P,1Z<VxүT.MioJ+䚛_>VQw. RS=}z|M�HП5t#W=*_5 ѓ ;]]EՒeru"Cܘ%`@�p ��� @�`FW( `, @Pl -qǷ|UL5ǫdrknvܷw}σ ٚp^eiF}y?,[Uԛit ^G&v#zP@4S'O}b*^R7YW)\`h p�P� ��0D(,ЩE:޸޸�4Ȉ@ai o.agX]*}<.=X}+jԺ텁5g>5ҡMFUX\Md t^Zu]7q_i[>؉y.e[3> E[��B 9d�����L���rL (- k*{�#CTԸ zw{b6:8 <Pw=Z9jSz.V{jqfNa(Ж؁>ԝMHfb)P2.{{Hә}\ (\,0eǬ@!AaH�F DH�X�P�B���pP( ( BQ . Uλ뛿/q&֪u<u˷{s6vj 6Itv%f~akL2,gi%gA͋N:d 0]q���`&��H��  X�pJ(&`& ‚`T, ¡0]沟~rT\W9jZ~8g^f}MQqoSBߧ;_?n_ZA^$&+Pvtp<Ss0> KA;vz" �" 0@��� �� �8B,T B`Pj BP,'  D& TTˬ雵E#W&4r}{4ImST8?[o¹+~*`Sy،>LIa7<F75J7'C[pU< $nf>K="X+5D�,@������Pl A0PL Ca $ 0L"U.sz8njU_h AZ&3q\Jt{ (Rugc+=][?ԟD6;i56gwױi]:}7{O:9in�`LD� �,� ��"� @� �D**`PL$ A@L" A\]~rssz_j No&|{PVlp5;s]u{;"!Z>l;+c6ބ3B$!:d;٦票_/b{~@��@��������Jh `(( Ap& B._2):zs3S/|,ָR49=(M:Fy(sqptφ%l3a _m,CMku𰭞ȲobxL^; h#57@`@@ �"`������D(Q0`* !AT(& C1W\ۻ<]V嬖9."~w$t9'B5./Ƃi9p=WpYUi{|)S{muhD7NdaQ6+l=j\aI����P\�� � �pF("BBHn 0P$ A:zoߦ_5 ,xE=~BԛC1Gm*ҍD_YumGn[V,D>A#J !j-$TMh��P����`� �FWL Bd(BQ "3˪~껻L+W>>>CϢ/=y^a&=.r}K?v]3!sAl䅫 ԤJ&|*Tjuٷus.F� &�& ܘ���&�8 (,\2R-ud"}b7.*3~^zSOCUx??~"].͚:d<tU > <ѧD"j#+_En-z4]n,Gјgfxh`|Jt@��w� ͛�&� HD,BhbEԹ5)ou<\K*&葪;܌.[~q:$Q?MǐyNGz4X7ߤ=G: F ,�ɺxsR궟{LY SQK+G'4 nsM87 F3 �&^s� �X\ ��'1B(PX( @`$ @(jz^^yȥw^k^¥q?;>'%UvPpsd9Q<>Fuy|l]Rn UA&B-IלZUsGzNƋplkIw �"@��X�X�LB$ PP 0H Ba8\}[zq<kYr5<2hxW|?<E8l;짍8?d ڵ^n͟^hku�R.oPSf<iMg. >~#uf{ =A @h�L��`� �*�B( bT( @(  P*X;Uonֿ%]p<\ ﷕&co ^Eɭ={ ߙ<~:T?/q-x5/pMv\9}|" ټT'qBγ �8\`�� Pb@��@W(&a@$ A0P~U^uY*˸NYgߺ?:QvSf;a#a 6gD}n߉<Aׂjba<̇ѨZF4ˉ|*>{Ө:. @��@�(����86FJU׷gUUCS'p?05Z4,4 kͫtTktVY.3<�|UJ蜫|SiL;H )SN"Z}2qgSj^7_cok7^kYPOX�jTl`]pP$E�1J$$ `T( a(2 ,p&^޷S&vݟbo?E�Ӈ=|(wofa8{KHuʹ΍Gםw^M~s۫:Kl&_7/<$?# <( :3,�+P��.�� �@p�D�HF"AP,$ BBnߏ髟]{2U.Mf58=?6u;?6-X|hE}<>U`egq9{ Lw7ʉt?^yvj)aɿ<2UTzO(W: dQ9Eo@@ �@�� �Z��FW$  aL"׶I^V[\{C6| ?-ɮ.?+[~=ԠykC#Zݫ? kFŪΨrj$!7밗CnF?*@6� ��(,��*���FFPQ 5"r؈8OL,TUpShm{D$0"Epű=1Η62Q^4-tz퉂A'$rk~ .,!:֮0wp?Al9C){(u?~;*@u7:@�o.����T+!P,8 B,$ 0P, AE-yn\ɾƷuWުkN+p6GK>w_w]Õi<k%_v _xT;tuÃ*1e{e]!wޫ~] EI!&uȖj,c!.gB1a� $�D�� ��F( `jA0J$B09rZe:]\ˁǫw~u|iMGo+.kyt^jn�CHa^6#[nb.90OZaǜAg;ZΆ7/w@/9C"t "-0`�H� �����NW,T`H a ,X5[tboYi.y&G/;WU};xBkJ ]FdzHwT9[~S/Qm $]7Uմ{69W#9!X^ L�`@��� �pD&0X j|yk 0;_7eU]LBuSV3t4A5%VX<?xQV_`ՕGЄ"@[=:ցSY4-<AEa$!XT80l/=M>97y3 txK]쏾#dy� f#9���6"���D,��8N,T`T( AP$ *g=_wW³ZLvk&8,v%Ҿg~'_&X t/PS�>jܯ9rޞ� 6标t.\H8#&Mw`oe}[\sW}'==wzg8ofyDȄX�X��B��\����D&" ဠX$ X. @Tb|׷vZJқ'%9_.:L@Ѫ<Ҫ_? F_5�7k~Ͻ̾=ʠ~e{uE9P/D|CSH~s1;M-YqFcgɚZIIX:l�@��L��*��J& @P,$ PJ PP"îe\o.LUy}-|8KB?&>,}3B؍:b=HwVSƠ9s*zÔ"\ki%tst(}z# Jl.0QK�H*����@ ��RW(8 @( B#~YMֲd/Iw:o'5G_r۫~fX(?}i&38z*꣺K ˻.:ĴK,^ĿxAȐs UZ&r�ŀ� (���H &L*_t> ˧m"t2h5GR>\)V M +|G)}g9$ml&A1~b^Ift$TRr`"S$pJ<JRLʈ5,X-ǒnB9<Ƣ=Qwwsl  8Z�p,��|�@��@� GL,$ƒ`X`XH' #0.\dfJjZk^ZB.؇u,R,Yn?h%.,#0r}U�]ꠧ;:J vs6{l}o3Y'YeNk._=d blg@[��&�`�L�����L,d A8`H A0T. B@E|feEIT_}Y壷!s?Lg{V<~e/G-Ļ�R ).Ȋ 88 8$? a 7Y-I;^b8 퐒@��(������T�8HLT ဠn@X$$ ,g|{wYs5WR*k.keݎÎ_ʟڎWNd7/>J}t�?̢/:vIif Ga-97;gR<ʅ _eE9ZFjUW`��� ��*T�8DLT`,PP,,~WLu[3dq֐r>v?]Wvr?_| 6׿k2J.W~wWз}D~\ѫ/0W֝lo-T&N1*X| A8���@`� ��J& `X( AA@X( ! ׿v煮MfurKhv}wos.onLJ,2ķ7^ /B IRmY?6=A*?c}VH]Y(%֖w~ ���� �`���J* `0 `H B0H"Ǎz]Ĩ߮㛫ky7U| ^6;Ϻ[Bąf%IW; zxv�>>r庯4Tga%vHIDMƹh$m8ɍAc(;N `Vv�\@��� ��\�FL B X0 APL3BAsw_⯞VUwz|x)G3{ݗ砏t:H=}T-$rUZ,`.?KҾč|c�Xgqܯrw:p]G}s)zn@вTp�\�`�T��\�@�@�FJ`XP$ Bp(% @EK2ٹn<uʻUk8p;M-)8\ח>GY>_%LbKùsYv#;E׺9jy ߣ7.7z [%5v?ͱ*tA 0Iԓ>[MP�L��&�.����1�H( PX0 a X0  [d+R]~rxGMZ96h `{P+DϺz7TNzSS#wyz˰ͪBs#/-q�Ac5N@OB vpmw"3NI: t��X"P��\� �pJW(AX("`(6 A@Eێ7xeIULy^8_' Oϟ|q0(y&>dGup5 LuK,%Ety:3+?M eL !8#*>;6_X`]1|<PZvVP@CT&�P�� � �L lԙC]q|8N fnuzsz-"iC\y%m] YB3C?o(u[U.2UDZL~CBMRTuH e&m3|vr=sZD;?~G3' �% ��̀����R.`H B@P*$*Zg:2n\lp<䟙<#з>ug@BZ'Oo =}}O0k:*sr'])q Rҽg P6nKh)Wݬd-p�Ȁ��P%$�,��L,' @X( q0PN B-gmnqj8SI8J#O ^oKAϮ/}%_oklcĺO\-{)h0`IY}S_8 ;cB6^3Q9]` !� � 0�&���J, `. @X( `"{jS/Rj{*49/~=i.K~?-C2uq~[m E$aT?:#kRu]Bem˥;4(q \#5ͯ?h.ÁRz‹MLu��X�������H(pT, aT,)Fa;굾*enf&4w*T9{?z9=FDzq?|Y-=c$%κO8xE�^?#N_8p#U2Oː_/$K7(�\� `D����@�8HL `P* a(P, @(1knT;2Z՚.uQ%Ge?;g?u>~!=ڸ8~GC:8=>E>(bjeb^ew#AZrQпs^9-'suV"GT!�@�(�*�&$�����8JWJ1€P, `*_:ޓ)rdZEΧ>IY~ΦܨI G6|\}h4Fz>7WIl $Iz"e_ 棇_6Jp/:6XQnH\rdjLOШ+�% @��€��.��P�@*0Y20"7|=W['>}=zucJTBxfIKKSlQJa/LyCoͶ(ƹC$ՅWLCšk {0Ez19@X[c.ͼözƅq. h D#" � X��� `k�JH `* b!$ VoD2Iͭ#8'_oողaw:kɼy1I�=}GM4g> ѐ/8'q?_7_ۚs! ?fݙ45)}d 7\a�Na $ �@����EPD$ Be X( B@P$=|R \2uǷ=ko]g|rWDײuK0 1�Ӎؾmirr#;%|nx&C Iif52вd-$sY?߅��(�P0� ��( H(& ),$* Au5TѻArO>T;n_LzWFj}x%dԻ OZF5\c Hfݡ$w?w'XW-sݰ1e1]\MŸߢ 1B@�.�@�,��*���H$  PD9 APT"k[ըELFk]eg$o,9??r ͞RW_"<n;JҚht6K㖇iя0X7ChWfB/h~7:iz{ Lz?lФMԌDr�)0` �K���@�HW( D0,4 EO?tuj_8Tj޹Yg\mgPrd[>upRTmI"Iœjf9K/^G7{\qqm c0C㥨9N[;Xbš1Π���(�`����@+D&F&U\dbː< x`.4$+E'n(0 zpF1`^L!F=Ca:(*JCQs[k 'M2'~xpQuQf^�BjCDZ:`^yC[׶Ycwe.P~���m.���P(9��J( AH(&2`X( EN>SX˙V_SU4;\.߮r_#YTٿ�=DGǃyKZ{7 [*/;pm B*5'!J땏aZskǨY6/>Z2V_ȴ, \U��.���.����J$ `(&$`h a5[Y^wʓrK~|7Rj;#[\9o0_Yv`'./SJ9I2Sev<[pgoo*phÈ4QW M5kST5^F�5<Zf~FL~#�%T @ ��*��(`�� � D* A0P0@\( T[ͩn"SۏYrw3Of#} ̫j5J$ ? SH|kFt%r^UjMK@EW!~Iε̆3Լ VVߊhtk|n IEG�� ��*�� �"�FW$ A, PP B@$ZwN%NzuΧ>qY2_uhv|<;[o'o`3O0%²p׌Bk'I(j^ȉHtz Q)"k:Dz^b JM!I|!�P�Ap� �&@��8H&.^ N:`,iSu$ V(Wܚ3=Ad Lu0<SEQ \sh롢+:7dƌCT2 ~yH1`t@'o^\7=::A[l|T ',7ײR�E��a�����pDMKޥϯXj `Zr4.evGX&5 b+1d;GŪ\%*fq>Qlk9nbB% rSxIp#d5Et`CEgD/i9f٘pH��.�@P\IW.$ ~$YX\KN0㚖zj1ЩsqMV<sG,BLVs:舘PHDK׌ 3%P/4�a8 UԶiYM|͛6F_Pș�O��4Ȁ��!"��� N- \3\Ozڵ뾦.fWjeOhGahNV?,z<rUn"{5im\uMWP=$/�ϭ_mmfD R7P3RMX=H0¢j^nJģ'6M�!H�jh D��`��R( @* !8T( @Hbd|UNzK9/5ZB!ӯ}8}~۫$ k /4<bXnd_s֩Z^j_/S^#݋3"f=��@�X* ��X��� L( aXH& ‚ Lc4sK׵9V\[Cq~o~?,]]H\<vP;k& ۻ TWDY[`tg޹VnЕF?iJ 1`X � � �\���..�L,' Q X( ¡ TH  E\]Z穛Wi~}%ImM~e-Zg:ͣEz7-7Sw]ESpR;J{ڝSlR >X{HÚ :lwN{KsT@�T���.��@�X�JJ$BA1(& BBPH~&Z*M/9Z׷_ZG_:u}㱩+g.5z'oop2[㬺N7gl16Ӛ"hv>/ ǘ50(x@ <5 C@L0@@�@�p�.�JN&`X. @H( Ba=sqw5Zוkv:)\V#mV`GMqc;vKj[Mwvzx-||Gt%cw:>v]=N+<q&:~ (Oj {Fkb*�p�T�D���$�\�TWL$ @, pH*BaEfsU.S}w竗.Ԭoz8P,)աWA(Bg6;rz8Γo#pWc%b%eN"<0�LP��D��$� �P�8N-\<v/]EOMGJ"7`PvKX73%^z,=},5# !5ʐ$-H>xgx֨J~c{ -nh8?x2配p;&Zs1V^/dAV_} 0� ���p�P�pJL4@Xp Q T." #0\MV㛽M~?Y#NcϟT~ Flz>N|Q|CPOzh*)z)<Z}8"KMNc&^ AwDeнLKml!@ʀ\�@� ����JL B0\$ p,QTFa7z;_*.?r]G{k9y7?q?a[%~=d.Ed #ݟ~8zJ۹wJ |hko,aQ Bh[va *��,"�� ��.�P�JH B`P `P* HB2sRN|kzgCdzÇ|~(CHb]>5]OlI޾o׊/m46q"!GP\11y_]jUe ȭh P����@��PW( `X.AP$ ,q}ޭ5ujq6 -7oV5:AF®}RHU :Jnn\C'vO>Z#?sĎ'A:N(QQ2(nT/��f�"�(@�@�`��B+((TK} SCmLtFכ %ӼR A9Dҩ&EFZ&)'a.H"D=]lOH[HGjaĄj//VZFҭkc@F+t#4L#Iv0zLGtQE ���N`^�� s�\�F(&"ph' B@H"[Uy5W2Juq?nԾjnCmh[@%NW'rl[NG:Z7HN(?C}W fm.hU2gl΢Ywv٭zkܪ {Un*DʋHn ��*�.��*�8J$ AP,% PL$AB\~uny{U)'Vrx>:y'?~)ۿCkF⟲0CU}T0sSQӆ1OEn2?B77cDV^7jq6&El,U’M �0@���� ��H( XP a, aPHHU_o7t8J~1(>ryp4NiDi!of &O,cj!C|/#Jn"Njo qE`׌)h;i 7{ L{-LQ2t�@�*��� ��H,‚a@Pl `*!A733k$ܒqN|G>|Ρ6sL D;E~Ch;yvG՝5_j XxZ)�ϙ,uY-,ֶbE|‭f���H�,T�� � �LLUP*‚ T(" ZY2(j.R:NKuۣ~u59ԁMjxzy,M~r%xՐx>l7?@*U!<@2VD �,���$ ���F@�!`FWL4+B0,$! A:UƫƵ"U䚑:{O>+^[ﵢRv׶^gjV K~uN?|kOJx+}oʰ4;yt cG$6D^WqI!0�*�����\�*�p@021SYs^WHI";>/WaW{/]똲az@%qMxՆ@KXB(dW$O^=J[ {)[A;ٓz.RLQ58ck@~R[b&V>h7s�Hx�h � �����"8R,D ဠH& a . APT"=ⳎWZ%ξԑk}ٻv?jnM$w;W:CȜ]/s!q.%PTDTfsf+eC7 B1y}XQ�&��.@� �H� ��J,c pX* Q $ X<Tq^ZK|"jn?u\:?@xR/3t9/7E] 4֌k%WS_<bb.~L0oc+A-j/yj#cJ^Q-L�P@�,���.�� J&" C00D`9ƫϫK72II:٣˄]MWV79f/@ىKv;(}ZdA.Z=,V vW\- Ҫe  zS)s|D]X&۸7�.�(� D� ��"�p�pD("`P,pP$ `T">=)WΫx<^6>mwbKCEz"/y>M"a|{!#* SqJn=fb ȌWNG9s9rJCiRuZz`@� �T����T�HL ¡APF$ a X*P"ymWzU=o\Wipr;7LG-q߻K)5;6sY>bNUvϔ>2yLW {a0nBpO}%Q/ξ�] }@ �!0��� �*��@@�pJW$ `XH ¡A8P,$#b7»󕗾'N?erw.Vݿ'vwFߎo<oɈ}}uzא5>nUJu`f.kt7?qU@udԚhYc P �X�T��P� B60XrDKcw?nojf/0 R!i1я ׏F|}%n4b\4KcJ8KՅ-R%<cFckudٜcOAmI *QZPP6D\�f��e���v����8D( P\0 A,PE}RuRw/}U]Ln?Qr4= ko;>ѓ_yw^޶rm[t 7(-oF[q>> 0!˸U%VPv+/ N'3VMt3ʑX)YSV�.��"�@��T��H �BW( aA`(APd B-kyRofu^~*Dյ)'wN~B�V|?/O/o&u!\k]}qzu3٢s5)cYKxԟǖ2ol‽T#Mn@g� �TP.����P`�8@PXRdaWZo^a?/vavZÖ}$GE-)1YG+ ܨE_ۮxdMp(lzGmB?s4t"}Vp?tʯWКt_>cۉah'V*Ĥ�"C@ ��028T(Aa!X( `P$ EMnx;Ƕoxԫ3j#ߍsE `_wlO37/`ܬM<n)@MttJVOk_iޢHsuѾUΒ| -: _:��@��r@`�TX�pJ(6 @ . DPH( En\uYu1Jn<ux?)ĵLbBbv{kU+芎!CbY͖gխC'wVAo،d2k$I�����(��T�@� �F(pPJ@P, PL"׷׫y^MLLuur^7 Ά;T_ _?}"hmsw _˽wH4-%`# 7Ո :$xRœs" @� �� �D�.JLD pPl A0P*) XuNnfqUjyV-tYt\e 8^iwW-+ժeNwW+k(tt]B;ѻ֠DpDzx=BQȠ.<`p��P��T�*�&�8J* a `*`H(% aEKYRk$*)牕;_~*ͻS֏$/HKy ~:-*U̎~y+?+f 2rKD;;DmrV׿+!:^n,JpkB p��L�X(�"�� � JL B0X0 a X(& A@yvTʶqA-[; :GiM z+^uӿ?._w[_Dߜz:0/5ۙ]gqaO\-”3>,+f[a" � ��*� �� �\�8L()a@T0`XHB @E|] jw}骮'q^v[_?ge+RS*woKWclVWcӰEv'G suid pNnTTz-Ǝ2FsP �,�p�\�@���P�JL `T(s  P*\|N5f'ڪW_Mǵ] m>>kh5qYH:φua;;ѩgZ^#u' Y'~vxVbџħB־"&���*����� �JLd0,)B50*dSԫj.jk-$XyFv۵�_QƮg껳$O?%.9<iIP! \|gi|W!-JLckI8 @ʱQ(��`@���H@NW(& `* @PFIZnW[x Rd?{Q弋zG>W:ÛA'ȧH= l?")561SW@*{ɽWOBcoZ#!h @ � ��P����.�pP (,ܩ"55ֽt|W~QNrz̳LA~Ʊ@:~aEJ2o.s�ʍ#�3Vߍ6FJȭXKߛ%n mHqo+D8⯢40@L˙0M�}���@���>���O�8L(-21j^N tY('Y?75<vo<7M4@'5uL N< Hv}U-O� R>2c`!Y cWsYY+fv<`vNtlCܪ-L+@"� Z��� U��������J&0P p, !1 ^k4w$k&q2UqF&Ǘrd0G}k3y< %wp�l}rN=VW7)4ScW3wKrqPQ(�(�� ����� �FLaAP* P`* `ϖqܒe+]*U^7=>a^uo8Na=_2X4F>%lڲ<D!MZLd2Kj^Buˀa�����D`J* A8P, B`\(^nN7y㎿Lbw'^x'Tj0_NH? v>K;6M7van hlMeg׉ڶwHFu5&'?rC@I�&0p�� 8H** L(&APT$1 X5^JfZy~%ڦbjo?мV@oվM+ o a&tdGEyrAo9Ya >(JԬ:<:E΂TjJ������H�HL$ B(PN AT$3!׿k9MŅirI49'UEO{Ki@*u\Jm54At"9Toɹr+bU*x|aoVSZ~ZjlBgqw鸡Ѭzr_ @@H�H�"��,��X�`H, AH* C@ "־sk)5Ԃ8hF>}yoi¸+$~5˹/.;pDO]<Y܅e#vKX^rZ z4V0mJ�L�� u@@� H& @P,CE9іDAw=er~o~:qwC7X4'Mot×k17o܉Ea/t7ŪʬWUq~B:Lw_&&)EO"͝%L[0�H��U����@�\ FW*2 a Xh% a@(!0jEL"Kc|؛)>I Jzq\]q^郮/{(pPޙ݌ \W Hc NmYBk t19q&oki ``� � �X��� �J&0TI_+o}Bcތ�Ȱl\0Be$ܚ(x:)IGO+'Fxܣ95{dhM˓|kĠ 9\VV{l;ܷ]7} ki_f_Fyh�,h�� @�!" JD,&a8}; er{ODyY/u^ꚪU IOG~ qjze\hHo9@J%M4'Mߛ\ZײZGAѲ A F���$��`��hJ( aA(P* A\, QP$k̽s=p9}˩WMoWwH'G7.8p}?DK :Ok76F6_]K.TzatΣޝ豇&``@hk.BҴ*�"�T� �(�`@@�8JH& A@PnPP,! ZfLVzkU%8X4984i\ \ja5'(�e';;7칋t9w>"'IsF"xҵt&/詐Ks^o2+@8B@`��� ����@J* A0P`J ~׬9ƒIӧ|{ Ew7{nhVmn܎G#"U``y}${f8Iлɼ-j ~u )a8�4S"4@(� �� �p @H(& B`( @Pl AE|s j$TXy{`ֻV6✏_9 m}~gH惊m%3/<t }iVٜ 9)G~(n'M 8|Ջ:βۜ�`�����X��HWL @P0 ( A3uN;xTqZY&~7Z|o2KKu wۮ-sPE﾿>ʖ;l~ѝpkz۾N*;~;6pm5-L{,8.AQ0" �P ��*�H&h 84} 4 g-2ٓ>7e] Pn SiYHjdox-Np~^)[zhLȯ ܼo#rkQ^y5\6so35һhľC&TPH� sj �����;��&�DL( BAX0 @T( B@D&kzyۙ9VMe]>KhrxWC/}zx,jmNq; 79>~ >{g~>IR}+[S })1|6{->/5Ң.DbLp��P��(���8NW, a(XH2 H&ͥ<"JK*k'n.\mJ{or'7,u?gz}qxY}\.,BO!zA�5}q{~S-z/Dc$STN�*�p�@�H���N -TT!N*UUswX<vS=ss?> j*!YO{j8.Բ xMt2| `". R + #љ h%MF{i^Qj@YR$K盫t} �< ��� ������Le$LE5+&ݔ⑹+TrF$�zs෱WQ xRׂ t^|?.(-Pg½@r*%yKg%G3 @7Ⱦڝ*1 � ��6��� DN`���L(*!a T,$ HB5XYuZL9-Hī7rrzzͳVӟIrOퟺU~Nz+^%|)}#ӳʿ!{^=S--<}'}"^%l$ 60S4g e�@������J( @X(6aTH2 \dֲR]Quc;W/8?%[R|lAuڕю}kw΄T|8<|:h1T|d8CMSm)[e  �*�����B�`J(BA0* A`XH @T$s]~-.rj?ZO)௃D_}zڃN>'촶1nݑ YOrZ.>7{]E{gwt$a��*�aP@���@��8J$ @Pl p,"A7J_|4RUҪ49>?{xNOo Ah[.תm|7*t1ټ]0';ݻZ&ؙ)$&  P"�@�T��.�pH(' PL* PX(B BAʍw2 w<7W%WDjlN<=zWѠ7ӛv3O9=+/'<26t˟F }j{6'ϩ HI.?0� P@�����J(6 bXH6 Hj T헾z_UIyJ$'ݺ57 .:t#ؼS/ۂ(Mo: +su3 y{:=_CEeEsA7;EHFi[{Qpp� ��@���LJ, aC0P* P"3]wK-YV^~t}Tݰ=Ӆu~_/|qnkG#e?;{}UJ%WxJg!WuPw;o"M8j0j=%p bzǸ Q�`3�T��P� � ��8J( a XH& `X(  H( X抺s%]\o\i"䫩w{yW!פÈ a i|b%B%(sK[Emw>Ptp65w=7ٍ.JD)3F#r�� ��l�.�LW(@L$`(  w;]KQvu0UI}u_NCWi>߆s-M eߎMu0Hfd޶q @=vE?`o2"u&j]KAӑ?W@.���\�� @hLTf.Z_]1f]iOzky%]LotI}^|ö6ӺZwكR W=afW7C E[�3ts).|iҮ7;zsi(� ��F`\�� z�����H$ `X0 a@T,A@Ԕ]qTOn?Ӫ76iO;#~%^xĶb?nȤ9A�>vev?wIHx3~W>:K^I{\.be h,� �P@�D�@���D�pP(& 0 9C3u4/ZsgIW_eXOxqCv"Z&" u1?b`}Joh'gm wo}eSh #5G:Vz7GM ~u |@�,�(�����uŀ�F( 0Pl( A0Pd ,zVy%$tSo9<o/}^f߿)&KlށoqN%j<QQUr.v,wڧzz_'Hd;yihUK; "cE:pTOi� �@ &�@���8HL4 @T, `P*c\7LZV]U˷TIsoҏמO;+'Uu=]Ӄմptg T:|ϡz`>? o;5@MtYT~1*3y _&H{�@�X��H��H\�H(P,T ET"2" kԒqٗ/nu_<$C^VZH#$MmoImUF:Ro:aܵ72|7U0#}D K@LoSxr ��" ��`�*�(�JL( AT( B (%[s.TSy<TҬv<6|۟H~}O/>@/ےIcoK.];Cc:H!DSq5sQ"H//WgBl8b*qkut P�� D�� �.���LL B0PL! C7Yy&eJV8/_nN}5m=߲?ſxZ"/G/e_f>N3m̓7<dIx%ԃbNc-'_kB%ƑR�&�,�@���\��8J, @LD! "|x73*/MO?n5;l]|�]JG8 -mo6 G~ =_g2&u/z|;3=7."Z{BZ=]fO5F(YW'Ȭ/\� ���,����� �XW, @PJ1 AE>u7zȽY B᪗|K?m2t}BOMK]>5U-֩;wќN{ƪߗ}ʨ> ȡ�GF%0e->oGhW -ϔ"D�0 �.�L�*��*�8P(M iQyk] Jr-.A(WF)f+/,zjhEnD@7c);ϑew$!KυZg�{W .^3G%rC 'p8paحF'CV-D D@��u� �.5�.� HLDB0T( @T0 AA _;}m*wySoZ_wSBھ'aOE{gá?Mf;Hzʜ\wP#tv[9ߐZ)|q` `Oy7fOݔE^h뚐Ԍ` �l-�T���@��J,$ @X( AX(% BAH"{S2U[%"^}~*Vq~Q~QcN@S;8M꿀=5-{K2AƳB~ЄL՜gҲm'٫Z͞kFX##RAPH@�@����T0�J&)`( P(BEFֽ޶RK{}\.Ҿ:]COvߡuxaj֜g_fCEf<>>AD`d_#UBK%jX+jWGǶZE5L2Fkh=�Ȁ�����J(*aX(& Q L. -z/ M"/+U5}{ RAɰuާ˟x?O?_ӏt.݅w_fϥp#;]&Qώ'=D79wDWl&w4;%ϕLbsI5d4,~  ��(p���@pJWL3 @P,3 P(".̺Le{FCKsӏeF3|k?2Rw}k1jo#_,5$oPa/\kĐgg(C*ETvHlܛG:5 4(ޖc(,<� *��$�\�� �J(,21j^ۋzO*=K]cJq_z[HYR zD9Z)6{ gի]BB>Ԏ�#�HuPAQy]]P%;+*7V.d5떓pӬC;Fk@8 �+  ��+@��jP���pT,c`T(& (# X<1k7jKz\K?^IϞ~t SqVG/3(NOp4Wڽ ]яn ebfB׏UxqWEֵa[ٿIHםSr(iY��e��&�.L�`�P���pH,$ @n PH* ¡A(L"I:ܣwl\ǟt:{ y oQuߡq3szԩ?%^<md8u*ȟWWw k\v(jjv4 wJL&w� ��@�p�R`J,D PH, `TH Z ԱU_w&kοz^AcsODBݵ>#k?@F~Z]7U!BGx~V%c,PZVȿ7l-{w#`i�%0�* ���\��@J,H `T(7 ‚ X$ վ<5"|JJVo ~Kh8.yAE5\aӀ^&Eń*ߤ靹tg`[ZϫMnR8P��H�@��(��.�*��8J,e @\(" AB(L$1!PT̽Y(T:y~.rsMktzB8>b1s>i);$ml 'x-xnwQ̃\rQ&u'JfW;&eLP<@%��@�B��,��L, pX(`P,A@EtKJ]e&jeC?O7ukE;/=b[JB[huS@zKEڝ aИߧ8cE<&z6=Xyp{^1トђejߡ [.mryiqPsD�� \����"@H( @,4 `X(rVs^&^]^Tip9?9~u=~N}՞A|-MLz/OWE\ڐ Az.p[?9'yH'.ъʺFn\ V0P|@��@�*�p@0�H,4 A0* `X*A=LqT󗺙kܼT˥ߟ?QcU7y·+ӧ{r>^ ;=gOWn$o#<�x͠_2p7ʢ ߩq&{ \!*@-- !� ��P ����0�8J,d @P4 An|nkRRKyc9uM[tt[}Y`#lSV.X%GBCp(wvFFsw:ߧ*E-uw,LRxïy7V E` �� � �`����J,H @PL AP׭~x<$*&o7ԯKdߟ_sR?SMQK%$ +f[< ͣogj.WY/n:l6M͋0L,w�D�`�(�p�J, a!T,JAH"ϟZngU]dk+Ƿ"%+z_n< kÁ o79/z$c{0nbm! ݂)ɘɾu-m=5g@ŧ|fZf`Q�H�� �@p�`�(�pLW, PH6 QPBljwzLdW e_DYk'~4~i՟"֎|M?7z u{e{/ZR0rOO߷XK=%FQ PknM =oҧxehċz�AP����@b@�T ��L0XdHĔzk�emI-9 Aj?W|gtbeWe=h+8wx,3X{uyQVԢjz@TDgZOvPPBႲf/=#׎W<1`PaN;ތ um XP-";d0�A9���羀���z��D��^R,A0P, APT(&  HBǻx pSTZM,~ߪ|uԿetOA_ס+3}{czx]񑝊`sp|aRL둠7EJ<~6++r1>B)G0I6����@��X�@��pH&@l `h AEO5lj*/d"Vjh,}C'sEB_([?_kw?̸O➏@A  ;k܄A6O)7~|aqO'k6yz5nX!I(6^| � ����.�P�8D,G †b (& Q L(V龳[ޗSTKj8 ʥٛW4m}}v'eh~]+dۯ}'%ҼkTf>`�ξ/ gֵ׍!P>W<Y[R\ktԊ<Xtp0�`���L�D�pJ,D!@P,D A`(! T|ڳ&Uʺ񺸕=*H;W]"o|ǏsjCzd .>][</K3ɽgd:";T(/vƵV^}y!|i< l-B@� �D�����P�HJ,7 @P, C0PD X3ޓw*.R[QVrӲ}mj}V%tOg4i-폾8t}~~:o|.%Ab K%DlUskaHn2xiƒ[k#jh^���@�����X��D�H&PЌ% ‚PP X뿏Rb*)RMN<nI,u_{rދS$X:+wџ?,v9jv=]0;a`xpIO2St};aJ+#m1n]pǞp뒖'hSE,(@b@��p H&!@p  HBǫsX2U֩5/]Ga5tSi[<-ru:5:1v(%vqż}~Tp9 >ž9W隹W|yVJfc7S h1/@"������P�@�J&*aa ب `*kN*$RUqTZ'noqwO:ѿ Ǖ~7wOWĊ-DqrϯoAiGĚX{dH v7RD6Ƶ/lP- b` � �X�H�\�J,( aPP, B`Pl8*{沦k]‹UI(,r[ҿ$ ]FN<'FLEP豒ՇsQD릀Oc~}!~{c` Z|N^CV!'}4|nt^JY뤺X+% L*��PZ`��@��J,( @( `H aA&P\o|MɐWyQӏ.eKG7 >h�O'C~Iѧ_cP7z?G~ߺ�3HӋewse -~nQ>_nt|-m2Uv \���b���� `J& `P, `X(5 U8Zqۍ*[5,rLhsd[?fqҚ5p/wYӷ!c쾰 WdLO>5*m%tDځ ~) iZdN><mp>+@���$` �$��� �pH&* `H(Ba>[}R)tk3Y/_X<M&~:Nxw{uTܟAT<] ;co]jzNgyN# Ml\9ǖL𡈽+F5WT:hfWqRCԦG.TZT,����� ��@J&@P,d @X(2 a=f^fPv u7}Eh[tW´kj#CW}ɽ[iH9m�kw;Zwx>y4pr_f{qK PY~W *7T/�v�.�0���$���pL,T ,% P$~:qܺ^$UY5<J1>%:W*i*\wJ%:{f{J8etMw)zUϡ̉Iެw&jץH7qIw ͍WY-Q}qHL�\�D� ��&��F( @P,% Q0P,% !BaE/Sȩ"J=ʪVCGgi\II<'⾭jo8[î�?Xܾٙ(6[;]6Vr_ñ;#4`܎Z#/vf1ش/4Em+tUM/ZP٩PJ3�`P\���P��PLT P,AA0PD![UMomy2ZB矯#5>>>Kܟt|_҃5eQyp xY _Չ1d$>CsȏyȀH )<mĝ#Xn�@�L �H���,�L,TÀ,D @Pd 73(4?5I,rU{x]'g7rCMVh &YzCtY6b4]g,vBڞQMf h c2=@Lay]JrHy$l7E%NVG!P06-AM�*�@  ����NP&PB#).Qm6U{'㉡quvEJ^lƪ&K?;}99=c ]C^hefcBc�<L5u\h_$ʟ -:7GzBU4l]`s�.�*��P��D��pF&1A0P;A@L"<Ҳ!-Yi%UӇI8'囫ûF]\Fy |߬$п&gNcF_Iϗw)}?i>fB){pFɔ-]đw}e;oH.T@�� ��H�L��J,( BBa 1C@Eg&]K⥸~@I+[qt>;eM会!q{BQ݃z|[!H{iPEkPֆ(҆EÓ \jNj[)2LU��� ��P��&8T,t @Pl!rμ]uS#YRҪH⽾?uC컧׫vw[HzM(._9_@5K9˿c7QIm R3+2]mI󄮱t"CYot䈾_  @� �*�B@R,$ PXJ pPj ! L"qjS[. V]|6u79y1g�R>Qz`kc\myW6!v$ n%fVn \o9`7ǁh<L+'�R �`0@$��&� 0L,8 APJ H(%)w[^\xdLTZnM~F:piFc43n_?:$ p;)0ހ š9: />7%.MM^uۂ*� ������J,(  @JAAE~=}Wkq-S{Ԙ>v|<_wi:xn[ ^V~'Q_l[j_cPP3J [wj_S~vA|ۯ@D,�#P*�@P�L��$J,( p`, ‚`H2 L"=w|GrsZLt?慬v7ӓӥ5/X]bosd]ôy 8FX^gZ ־[Fnz65#Gx>J ?랼\ؤ4-sO9@ B�p� ����L�@�J& @PL DP$c^Vqs{ڬe\]LJOEǏu.i~n=Wf?c?]L?Qn>Ř<B59%s_ezi281* iiR%1L!P �p� �p��,�J,T!@X&  H"|Spw&T5<Jݩ!+QYd"^ 57^l?T;_jæU z:m `u3R+;c/2vQKO'N9 w�,��!P�"��� �H,4 b X(B Qc|.Y(^K܍cWԉ,v'Nz\~:?hF?`~Wj0u9\1{ۄo) �7?]=vpm6#)[Ev3�E �� �PT�J&0,D @T$2Ba<_]MꪵRFdEU˃W u.{Y9VޯD?M >rv> Ρ#Ou˟q~^7v=2Y\֠tlHܺzБ$/ED΀ ���*����@� �HW, `Xh H(% !0gqzʄTzw|XH[>4{Af.Ţ*_'Doo.n'~p,h-J^N?O `fwxs$%~~u1cQP)@ �@`��(���L&�Hhؑ21\ I~D/2>$^x Er5v`8|2κ?wj.$¾LO�؞UU*x}7? qfdPkU]0`d"HlGO2fw dif(��� ���7��o��8L (La"21%zƸھRYNǶ=o|Ʊy{_s\(#[wg{|X"ԚJ|kx|駺CU1VRZ6C3pSNW K, $=JK^Z]+RearAEۗh5uP\ dH�`@���  �pL, B0l `\* ,sxX\-.uJc')ҾsCUk~�g#_dթGߺ7-WC1=4,0Zh#|!1o 珜|f �,��P�� J( BcX(" p\*2 HaW<_4\n]$^.XS75;鲴mN q=?&V_g6yd_<j18M}i$D.NۣFbiH,;Uv%jZjڒc&@ w�  ���@.�@H.@(* BAPۍj]$V9sGO65Z;k}awMߡ{ 1럷ذK4?*Ƶ|.3g-G>I&nŜuk;c:JD� �@���\�D��J,D  ( @T(1)N}x<sRWU/N>^M^4g?~[s6--H~O/op+!U<q?5t^_JWd;Ք<D^E^z ٶ ^WSwxrb{ĥp�H � �� ���J&a@X(6 B0L"񾲫[k~w2Ie^R^kawzMZS_YF_37B; ZS !6ËF/>e1y6*:CgbYa}Rnr[:mW4yJUX]=-35e:`���0� �,�L �J&c h  H&S=rUFy*eHyoo/jfy|tm_1rUz!Wk mGY(mlbF=5ߵȳ6�m::][./<pUgN�2"��P@��D�� ��QpHLT A0P pT"[M_ڨnr%EFY󗗎do~w(QTCOK"+kh:8^;u"{G-Ǝ`_5A0-jyяljX[yBHl8ŕEKq܀���@�@��� �8FW, a H`X(% BpT"oV8%uܩs_bo?|h2 B~xkRIi3ƯdI 9#yGTh0m?tf\΢Rٜ\Kɣ䀹*QX)@,ÿ5cA-y�� @�����" �L&PZjP)< ͑4CLB}Nm/'Q|5u|*VzWUziepڃ\,JqG%wJcII@ KZ"ZPC4# ]X;q��5�� z���5���"�׬N-PΧӋI֖G{UJ$I[,o1.H)aJ+ ٢>< 'Ük.r%fv}곭='@3}�0zCb}Dj<ҋT8(0]ֲjZSs?6 hH́$ @�o���^���J& B0l @X* ,zf]q1)y*ƴI/CMg+9{1N ψŨyc`]\Q:8$)܋R̾1w>81<SC *oS}p%0� �`����,�HP*aA0(6 P$ X%]dVV&\*8=? xuY{wg VMYY"C48~)h*1J=Zc q@a𳽙gdTRfSc|~۩VKk[Lv@(��������H�pJ,D# @T(&  T$$ XβKS5u$rT9?~cUqݲG_jmH[u?nϝ]j4{8/>}.,[;~v_zGr;'oe?X%I.��"���@�`���J(*a ( @PJ BC@W[㚹Zqsp9;y_n٦t/qgwA*c K�qCz]iWKrfnPV=FT{7@?tN[>第׶l 0@� ���DP �8J,E A0P,@T( Fa@P"m9q Z[$?z\CvW?/?׵q~i~ίx~'uԌ {j]/ 8^ܝ{vPԱbfhɏvB$  �*0��@@ ���J,$ @,8 pX(!r[ʚ%%]͗Vӯ]{~U]/k^i^I4aKYH MZ-͇}y>.hSS1ұ):FJ[dS3u)U>mZ[k\�V�&���"�.���0�8H& a \, b`X( D&S5Ǎ1-fKqjI.O)ydO\J:6n#~|wxg-ZL�/$ï R]Ѡ1^8 X Y~SQFk2ؚ |V:(@.�PP� 0� �R �@LW,T A1LDPL"w(kTEBL2^ \#{(/==+y'n_+kZ9$$F~ m¯w"Ô~}<XKշ%,emzẗ]sH}\cČnٸ"EH�.��$��� ��DD,`kȲ(u�m RcY�/jlW OY'Eݖ4(#a ~(DیqMozI_7Bԧ!�DsM.u!=eYX4_\-ycS�d3Gnp^� pP�� ���a��T B�LhTbW~g׮�J(51xZua,JґuZx_][-diHu L~ɿcg+BQ ԕV^ IXYEDNXT,Qgnq(g򺴞=Ҿ3谌dz>&s���9E��pL( `XH& H( AA">f5)u&.Uͯ_ϞZF'[=^h]{qۣ7+|< [Պnm؉W_S|&9n: W,vߥM>,BN.G"Ic�p���`T �@��JH`H*  P$ BCP\2Baޓw_&oy̎yq޿u uN6Λ}v$c:psW.LGw˓oeWFg-C3M�|tjZ'|l*::,]-R`A1  f�!@��T��(�HLBa (& F@,%D)k5L&ƿo|7ݷ3;Gg,v| yB_GFGX>mQd ު8oٻo)?жbE;M5R��X�@�PP��� JW$ Bb8X( APD *7S֤U^yμ>櫥jO Ɨ޳J^=OPƽvD/k_{йnm0vxx+v|<c0!4Mw O/W 4gz)`7KLuc~~BP�b` �����$�FhXBaf~ֺTg;w|O3*xc7F!$7 J"ժ4dE6v$7#k�=x-x]g,cx::£nYň3[;)i Ωb#kOi%]^q<um]vn#׹|d0>�UJ��� P"���P���moof���mfhd����������traf���tfhd������������������trun��������������������������������������������������������������������������������������������������mdat���free����mdatFh\T13U8ֺ~%=<\Mi!|Ǻp:HȈg" CY_XؕYmQ N̅ ,${ޥeXˋz'qҨQ <wl*m<E;h:_\%p]>.aʤ|&G<6P�׬7 s�X��5��jL$,Q:2cHk|g39cbFi]Y)yeU@TS.��:gD"�4Ѧxz~duC|6;+֮j <o9]?9tl|]]G_'Z? }Odb-dT!H `yր�l;6��X�l������aN $j]R1q/?ky<xd`ʗ7D^ឪórnڤV 'y7%IW<ܧQjwXz,xܵ򩸖_Yq|c:O lE5F<m.S=:7#Ϻ�ժ��5 U����P @�@�pN $,PHƫۜ#]9[DQVxqe_VOK!yC'7jI=wtmeu%St97kݟ ڽD j*ZoRaCC{x$U˝!f?5[F;份u]˦| fG_�ur=Q�7�D@D&o�� ���GN ,`RdbT[\"]} | z%vg@<Fֺ:l8٣q| Mqt 7*<bm?n믉WGYu:4K"K:}^ �,B]4)ȫ"R؟E\+0�x��@�����N -q5]Z] l J\;RXah\;GM1k[rJq^7G{dHY ޻O(aOJ6E 7LfTw.`-( (LKN &@*|93ކ=N.YnT ���`� ��"�RH A@P* A`P$ RX0 Bbh[Zߏ=xo^tџso衚�=]`ϱaUonO5n<@u`1ŶHQa:,u{%aV_髨xj9O/tOumeK�����\JL B! H ApH* a&S~5/^2u<yu5|nur-O_tt?o+hԬm^yymcߘ13窍Y_WhPvпTN( �$6İ8=}p%n=RE7?*X1��X�X�P� ����pJ$ AC@X* A8T.6 ġ2 ʯ&?m+k:"?}ޔڻ:~eV:Q-𾯤*fh*]$@df>o_)Fմ7f]%0еYOV��@D�&���X���HL$C@XJ DPTNBaD&C}]޷9u^5-Ypv> \~6՞ʳnELn_l-wHܯʥ,Xdd߭QVBPVmqX./a.qϧ]q0� �� �`�L,( AA* X(' BP(:7s}s$ݳzq>3cɍû峣 m>CIhIFQA<,>OHTa'oj:~ {ۂmvK=F yz Ffmk rp�@��R����Hn`H& A(. #0=DsU^nnj_9Uܿ_qϧ gxzଜ!IˠWi /~>ʱ _ 'OgZќoQ:Rt.;`_biԷ^sL>EU�p�(�"�@�.��$�pHLD A(X(3 @\*0"UTwsסe_5ۛX<=<ǛXf:O_3~=P\>>5.`g LoTuՆe ~_ZRqUL]>@m ]ҡ� � �� �&��L� JLD#B`PL @ !1 Y.wml&I|y%԰t[[hˣmcwWygWGy˭YBHfi?A`Q<r3?ӡra[�Nqq`h�� (���`�&� �H(H& PT, pT& P{}k|ߟ޳5Zjrx썼Shظ YaAPu=Op;Vxn/#6E,א=e^¬$5d',o˱{ՅG ~rXHوJAc@ �� �H�@�J$  XD A, q(bxYd㙙uůr +C_QοoH/v=B}׹>뚇1bĝGe8=|e?/vlC<tnqƯjD؉33܋j+�`�� �\� P ���H( P$ ApP$ ¡`T$F+_{x;{jyu{|'%Ic;xu𵗵[_o}Dq.a_ZǪi`/i={TwQ4ُC<rz~YN|;t]uI9%je`)�@P� ���H�JF9A`PL% A@H. 2 ~}|s~}^j/˽'7-Iu- vOHLmӈ*>"~+,L-s?S?xU~>�RZ&}M(éW)*8w=} M,wQj_���p��p@��X�L$ !H( 0. BAP& B1 ~*~}7:殼ߝzGxMӌR m힐ړg_G0Fg=Op Iߤ~`\|j| ?SnSJGנ�z �$�*@*@��\�J( CQX* D \$ d$ BbP|㊞5g|Zm~?a~qǴ|g_O4r-#'G^LU#/'[ ˥f{QI[W! ->-oh+LgQ�w�s|`��� ��������LWL @d  T$PBڼߏ|ݒǼsܹKyq׃kѢ穚G]em}IԪ{}{8!P [v͘8ߓс*[@%/p ZI/��8�L ����T�L(Y)bԗY?z ,0CldT HFJtR +_:_}3x'y ԊʥxR̽L j7b'!oaPVi\:'՛tNkU$gnV5Ώ1g $ ��M0\*���Z��}��pF$ \%߷.Ht!1;]radTHk;f:oAs+mkԿ߇Ϻ<;kzn]Dv㍫)riۻ%UGUJA�L,� � j��8LI jz#WH_OzS '㸧KCaV%�nj ){oW^/YFqS7&O&|vrY|Z*dzȂWa/$ c����SP���!N ,`R3RM|ނXuÅ5_?BۯGY\5jNo|{aʊZ9h/~mK"`X*"`M1eIӳ,7SJ=`^ʃXH�.RL.�,��.��J ,Ъd`wu|X߈W1x_^rU'iW׏^{tq.6񷍿K~!AR՘ )NF* 9ux%Ct\%9.KgQɋ*a;,X <���* ��*����L(AP$ AP\* BaEs#YǞ7n$_kCtmW}0WmQV^�ʈ>I~ mƽ9%ݎ ӜEo5�H��&�"����@�H(&!`P$4 p*" Da337ϙ>$uJOO/>VП>[eN(fb_p~wdr�qk?`uPqGs/qcH8 �����p@��H��H$a!@h q &% DaEm\Իy]|SYu/%\#[;G .tLWsWZ|a\.^^J*b"NWǻ*il�L��@�$��� @����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/no-tags.flac����������������������������������������������������������������0000664�0000000�0000000�00000011124�14662262111�0017227�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"������ B�zAf鄚0 <w�,������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y�k������Yl������Ye������eYb������)Yw������QYp������Z$Yy������cY~������YS������+9Y T������LY ]������Y Z������Y O������Y H������aYA������X YF������xY������gY������Y������Y������^vY������7Y�������-BY ������Y������Y#������\_Y$������*Y-������Y*������eY?������Y8������Y1������/kY6������Y ������FY!������i3Y"������Py#������XE��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/no-tags.m4a�����������������������������������������������������������������0000664�0000000�0000000�00000005522�14662262111�0017010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����mp42isom��mdat��libfaac 1.24��B� 2�G!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#���mdat��moov���lmvhd����EL�_���������������������������������������������@���������������������������������iods�����O��ttrak���\tkhd���EE�����������������������������������������������������@�������������mdia��� mdhd����EE��D�~�������!hdlr��������soun���������������minf���smhd�����������$dinf���dref���������� url �����stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���� �� b��� stts��������������������stsz����������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���(stsc�������������,��������������� stco���������� ����I����� ctts�����������������������udta���qfreehdlr��������mdirappl������������Lilst���!too���data�������FAAC 1.24���#ART���data�������Test Artist������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/no_length.wv����������������������������������������������������������������0000664�0000000�0000000�00000001024�14662262111�0017361�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpkh���������"V��!Bw!RIFF$ �WAVEfmt �����D�����data� ����������������e������Xwvpk4�����@~�"V��"V��!Bw�����������������Xwvpk4�����@~�D��"V��!Bw�����������������Xwvpk4�����@~�f�"V��!Bw�����������������Xwvpk4�����@~�X�"V��!Bw�����������������Xwvpk4�����@~��"V��!Bw�����������������Xwvpk4�����@~��<��!܉�����������������twvpk4�����@~�A�<��!܉�����������������t������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/noise.aif�������������������������������������������������������������������0000664�0000000�0000000�00000010460�14662262111�0016630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM��'AIFFCOMM�������e�@D������FLLR����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SSND��7��������(xc-ZL qK<< ZA- x�RZ<�i͖ XkjK=\<5<-K�xL  �[Z�<7 x < jx Ko - x (K~ P�k-w ZF)2Z :Ku<T<.-;x�$ - "DZGKs-giiY9I�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/noise_odd.aif���������������������������������������������������������������0000664�0000000�0000000�00000010457�14662262111�0017464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORM��'AIFFCOMM�������e�@D������FLLR����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������SSND��7��������(xc-ZL qK<< ZA- x�RZ<�i͖ XkjK=\<5<-K�xL  �[Z�<7 x < jx Ko - x (K~ P�k-w ZF)2Z :Ku<T<.-;x�$ - "DZGKs-giiY9I�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/non-full-meta.m4a�����������������������������������������������������������0000664�0000000�0000000�00000011764�14662262111�0020123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����mp42isom��mdat��libfaac 1.24��B� 2�G!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#!�I�#��#moov���lmvhd����ELII�_���������������������������������������������@���������������������������������iods�����O��ttrak���\tkhd���ELII�����������������������������������������������������@�������������mdia��� mdhd����ELII��D�~�������!hdlr��������soun���������������minf���smhd�����������$dinf���dref���������� url �����stbl���gstsd����������Wmp4a���������������������D�����3esds����"���@���� �� b��� stts��������������������stsz����������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���(stsc�������������,��������������� stco���������� ����I����� ctts���������������������� #udta�� meta���!hdlr��������mdirappl�����������ilst���!too���data�������FAAC 1.24���'ART���data�������Test Artist!!!!���----���mean����com.apple.iTunes���name����iTunNORM���jdata������� 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000��covr���_data�������PNG  ��� IHDR���������Ԛs���IDATxc|�� Xo����IENDB`��/data��� �����JFIF��d�d���C�    "##! %*5-%'2( .?/279<<<$-BFA:F5;<9�C  9& &99999999999999999999999999999999999999999999999999���"�������������������������������������������������������������������� ��?���jfree��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/non_standard_rate.wv��������������������������������������������������������0000664�0000000�0000000�00000000204�14662262111�0021070�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpk|�����[������[��!ghwav�!RIFF9��WAVEfmt �����������datal9�����������������g��e����*���-/{��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/nonprintable-atom-type.m4a��������������������������������������������������0000664�0000000�0000000�00000031067�14662262111�0022053�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000���������������������������������������������������������������������������������������������������������������������������������������������������������������������������ftypmp42����isommp42�� moov���lmvhd����i.i.��'��>��������������������������������������������@���������������������������������ymeta���$hdlr��������mdta�������������������+keys����������mdtacom.android.version���"ilst���������data�������12��trak���\tkhd���i.i.���������>���������������������������������������������@�������������'mdia��� mdhd����i.i.��}�����������$hdlr��������soun������������Soun��minf���smhd�����������$dinf���dref���������� alis�����stbl���[stsd����������Kmp4a���������������������}������'esds�������@���������stts����������2������(stsc�������������"��������������� co64������������� l������&���stsz�����������2���������������������������������������������������������������������������������������������������������������������������������������������������������$free���elst���������>���������trak���\tkhd���i.i.���������������������������������������������������������@�������������Hmdia��� mdhd����i.i.�_�����������$hdlr��������meta������������Meta���minf��� nmhd�������$dinf���dref���������� alis������stbl���Xstsd����������Hmett�������application/transcription_2�application/transcription_2����stts�����������������stsc�������������������co64�������������%���stsz�����������������$edts���elst��������������������trak���\tkhd���i.i.���������������������������������������������������������@�������������Bmdia��� mdhd����i.i.�_�����������$hdlr��������meta������������Meta���minf��� nmhd�������$dinf���dref���������� alis������stbl���Rstsd����������Bmett�������application/audio_tags_2�application/audio_tags_2����stts�����������������stsc�������������������co64�������������%���stsz�����������������$edts���elst��������������������Vtrak���\tkhd���i.i.���������>����������������������������������������������@�������������mdia��� mdhd����i.i.�_�2�������$hdlr��������meta������������Meta��minf��� nmhd�������$dinf���dref���������� alis�����Jstbl���Nstsd����������>mett�������application/waveform_1�application/waveform_1����stts���������� �����(stsc���������������������� ������ co64�������������%������2%���stsz����������� ���������������������������������������������������������������������������������������������������$edts���elst���������>���������freeache/editing/37bb8ef7-90e1-4e5c-90b5-8689fdd851ec.m4a_tmp���Jstbl���Nstsd����������>mett�������application/waveform_1�application/waveform_1����stts���������� �����(stsc���������������������� ������ co64�������������<������<���stsz����������� ���������������������������������������������������������������������������������������������������$edts���elst���������>�����������������&mdat������@"}X---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------/06-t H@:! WWt-1@*4F\ DО_Ca}ih^i^v.zCz͐_|߬a;*QO}z6M8,ac\|EF"sfZ[s7)׀p3s-6-h?"Ğ9TRǠno ůA-l; )X8"6)Q]# Lk.$ ]=%q.~Qz6)wu&"<EƱHJ`N6,S[dY yٕEB $I`j3J)GkoKP. RP&]U%̭,-I..49IMݖvQ J\ho[5yATztvO_Џl #XM?6*3JkRZ"iT-omfNѳlQ@՗U`IXc$ݭP5PU^HcUASpBCTI*\T%qObRv\ٟU 9RFǀ,(GkS YO"pU~\3jjˠuk}Ak۴tWe$,{z&6-1}D�R{V\&\ЂTzX~-*ePlQis/zw,y'QY$"�*UhT&ʟU[f/,K:˨͙ g%CZNBf# 2/t.MYWZVs޴F 5 ĭ'zp0&|&4Y ,xQ | Qp �7ıkg,iǷ6)q $DҰ@+Vq9]D>$THPq;&bIB?C^>%{+DN}+c!$D+4]:&z,80m#$<j<(basRgH:j2.ȢIl8=J(Z %uS^ d#V\Z|6*PLKnI) �%H�Qd\~]8"- F78̔1nz b:\nWf߁>Ĥ*~MRtѨdFwacb[iD+e""$)Ч.9[ZԒz/2"&7C:":jU <WvRxDKXs�&G\k1 *{E"MfuC$=L@'Xp6*LKY"R,J=H"A5rQL1^j lֳO0 QD,K)p0H`e }O9p]WDSOIJN_PkZd>"MMri>Fa?;i[//=Y1*We:Runĵ1 @:kZsEGQ]-" A,=ޗ&6(4`P: uYR DUҳ眡4ˀՕ7kЛ[u xL5ưӈ`ZL_7 OJP:R^}^XciY=rGm<~2)V6~*=I#|iˇu ?}Bҁ|o`lqo5e#]6*LH @-\uTDOhK]P :9aث uW ʄ\AoǬlAm%9/ CǗY1ZηggwlcW)S~xL[r 5Bn(QB iW ]]C6#uc3[*MR6Mņe>(B;Rq"͎W tg*$>%^?Bco6)VNa )zy+J$=UWQpy.JM{r,UoZeMg#BIh)b}l- .헍q(*xWOeG^!LY/ yAS<x={P ÷eNlgD%GwUBKV3R{ΕwSjs.6)L�\yUԄ@H涶\amΌ),"7*uX*eE0wpaϘ <;5Zv~Do\뽦;Mc=$ߡlۧ}>dӆi ٦NG%07޿~q#7HEʆm:RdB(wO?UZROJ7�nk7˕T۬ǛL7|6,t~Bh,8j*暘"_ʹ;7*Ґ6d8f5\{5kߣk 6d2컍0zQ/dhlUh 鷔 F9m~jח缦m*p8x򡗖DзYn Dt+72!U8ƺiܘ^8b [pݢ{ l 86)1-; Dр\z&]D}B%V1p|-2=)E=ZnpbC* 6'6mu6iT٦U˿E!EAjHI`yl3@&5$nU5 W5l-n k&4ʸL.@O{TY$S6,v`Uj\nTZ$I_tˆ~_3[l `\+uZeゖ]\[fՔ6몯w%џw.-HLG-r^Gաe?8ᲘL5,w6IoR)m^\L{m>lDj $ ! "F6(v@Wƻ)UA*T{Xu)Zͻ,pU@ E^j 6¦6U+mMu L:_RHJe(}z3v)Z''zY%ǂZ5WHS$A|&=."H?Ė南xg=kN~ %>VvXf$ Ĩ/7!&Db6)=H�]eM`T PJ voY;tX,?%JX CE#, TH U7-�x`w `zx\r69(vNuhҌhRno�'Q@Ur|ce%n$3UGV-'IL2wl _-^~MLOaFTC>om%գ*D<6)D�1*%X Q)) hpDGry͟MEa1 e}J}Gû8PranZm*eL�j5֖Ib{*(U"V *gVk)fU$1x⑏s@RʍTK14~hT֜ȫ P3^c\T7ihFBt=gM5J32c}[8Wr6!JAh)uUu+S*.!܀]s=oƹu&4& ܫ)O(iL_΅usLSB2|jnu@veT)fD[>r<d̠A ,pt>lNL vMݯJ%LK[x6,PfAh:$!JUT!u?9NDM𕬳J[diL#tZGO@X])2eE[2P;xᲗji[u�fYZ c%kqccE ay," sek[Ǜ#@1!旕rn_,T36(CF�SUZt%DTU252@߼pn!ǼM׹1lA UڃoDfUh/zJ;˖9"2tfIO~-|۱xӁ6%2+;IdIy56� A6,\f$﮸̨$I_Bo*jZ.gŧԕO6!3 �.L*SAt<_14[]UNEU!Iz3?4xO|c $N 4ʭJ塜¨_ecmEZe؍W-H {W?fi%-n;qq�ti^r-S}W%e \TTCyɢQ+cx6",3F�|&0D@U{?cU;^oS;e[qV@2uhCZ1VU I( dTFβMbVTe1pyƉeb)_dnNc\43vj>B>Ny]ڃ <VR‰Y[%e` K Y3xʼnC6(^`@Zē"UHSQwU({γ')w &) a|C]esbYUEgU;]ԎevX;][i<"VuĩpRj2i5;-X+C&=5!VԅlxE.t3"$I+ll`6)xJӀMjuRBRP>fX([UJ+BUGP)X U.߼bͧp >{r$<k *4i5jzAzJ-e^Qogbs~1nދlҫl?_@>hz 01){6-pL@V7qR5UXkD(Q)a&-;#E˖֖W&ي_*~GLih!L!\̋4 *_M)2`%ꟾ{%|{'IȮkԂAAWkڧ+v^A JfWE$d k,B|tnR)۝%InӧT7@k,0HP"z'g�C%=EA6)LшWu2Te5%*g%W-h˜x %BnZԖT[6Q2U,Qid;UWR .'^1On32W^ݷ KyD([#3DF3"<Wcr\dL&X%mT*,$Q 3S۰*}Y.+ a|h5܇vp6* 'D�ҷ5V" >!J /566)/?mj຀H|r{Lm#U -* v+c*@Vq1W-2_C.YW躰*.*$g>K|aoU9el{FG g �DbJﮩ'pds.']cES)4DĪLDeHaUPeYW+ Cټ>r铝m6)F�SU*UԉG\ҒzVh<<'s%Ug@Է)G@O&ևyo޲%ko^ 9HRm5@MaK9UC\bh&`TiMNg B,>r`$  aػ.ImUfTuWrۣ_.6!l+D {^& *RI>~WS>d~v@B]m 1n[Eylj86κU1ԓ4}P!)k9e$&[zCSťN)$�;^j҅)jM $'R8'D.icte0:lףsѝ='%( 86) L�@$/JI%IW( {9%xv-xzGf_^mn]Rǟϕz)s0('AA ,;lޜaX'g`MEr iqH"AV@&Xm`tXUmq[x`BfE|/K*n9t6)m]2DB�Uy9*AP^2mj/50Ǧt¬˴Vh-iJ˰H 8 R$5ꮵ .@J`&�%\-,Uu!4YR@W9_r.5;URȡ=wR@)XI@ȟ-z9wSw-Id|6)  FҀW:"Q C u}B{Ujvgɒf A~HJsƭSlya%4=dӭ-ʞ"mCu&MF;&ŭ[N+"TKiKSna˚n3Hm~zj;1 4,aRcLljQ]6)#N^QeAhBHn_KEV0q131ўev# "YUͅe8>sEn,$KKt|,7ܔȡ{nUɔDW"KvưZ%;*8]$g g-Sq kW)nd6*�������������������������������������������������������6!Nf˥TA*T|TLBy=<oZHe@8_ONl͗]&'2z髺W;kZ&{e̿J/mu*\E?n9#Y!:oɗ{([е<oR<LItaZTK3H288E6)1-M�Z]Ң!WR>$/ 6|>TR6`ƗI_~|1OacC4d#--v4ɋЉDQ\sa8w`ק\̇1 $W-dr^5"Ԙ.p|DRodZjC@J $6)D�pȗH"*5Ax_y|ueXc׏.A!a= uɤmbŠ;kgK:|*.x. }ODX-Qili {v Z<% Uvs k }1:8lcTT2ʓ%$J: e&ah^CQcj5=Ԗs#xj N NTqK g6)pvTFԠ@+5WR="A'˘<6E= UCFQ;xTC\3r2jb'/wv4n{tWo]z{{춹6͎> )]k˃TZxQu}WLWjy "DDn9վ/J=/6)PFbh+jRtEI `acsJUJ)$[+,QS kA9eQd,H5by6+]4*7]PU{#jL^i~:e^jG (`ɳEM3[hzfp*L7d?hRoS8 a!<6"l:DKfJxi{G.~Fyղk2>XFM7*<P6 $Y9UZnL s.\cSXVj\NGkRvbKOBhجi/"˜Na[`vS$Ol+Nxe̛Kk|[I6۶&΅p6"lBDW/Uk.T">!�,?R.UpB;FB},ҹzk1:*Ys/G]=Tʷ Hhֈ;󵦰l%nu: mjQu6P]]V,j}$+ $Or ?zF?_6]h6)VAh-\Cz*H@ EljuC/tg!E�+o)))"Cm8Im`$?:<09CNa[Ut>֢VZ;ƴ[-iԫ&{ Fx}9 Hxd, , Am<BuSt$CHaqp(f8k6(WTVJ@zT!^KwV5%kDd1ڒIp \Ҧo_7i]jg{fmz;Higg|nXuȜr,9 zZ-fu Sp ٪6ѝux,5M^cʇlxH˻�Y6*},R�wIrHȩ%ԙ*_?囗3sv  {j6A*6䋴<<?dNmt L! @þ%F@ 2QF. =a7o>lq[$ӭ~E{o5u):YQAymnfV6,p^A�MM^Ip |J@~U?h{#pF=v8A֚hPbZCNdPkﰜٗm7: 5>3ntuJzqoJ9<uSSb2ӈ#lf1 oBY"'zlr vDey^] TmBMP�ڕIpSQ1-2F**!! QJM骥ʇ-86)DQ*Vb@!BGȕM\w6\-ʵM|d b1S74>,F"Re] 1*>t1\̮F\B鉷(9e|%nto% � N#@B^9Qt<& {]i63s^H)#<zcF?EבjλVe3S|;/6)mDA@*WeWKR*iQVG#'u18dZJrE 3eӵO+Fҹj5,{^s' -6Oe BH+S1TwznX69SS:=_i;"Ẍ́ ˌ^22g?w6)lt+JAѠ*mMSy@IP{ba(&ϼJXI#'J;ɖޱAeu:e8K1(k? ص秦zm33J \ atnDm+DemEp^I&X VpY hG9sD[Uc`ܵq2?PFĉg"6-F{:ɪTUzJSZ0 "@O.!_i{;~ Qğ1[âئ/b$y|[vM sO?d/MTlm~Dqޓa#y;pL+t(6AXOnpRa]S@Su6ճS~Z F)�6*L HҠ@'T8 W֋Tw9.~;**e!K..MPK)*Noÿe dȻ~wp/|wuZF%f+&<tfy[oRxqKID^4D^НX(:% z<S@.̼nl�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/pcm_with_fact_chunk.wav�����������������������������������������������������0000664�0000000�0000000�00000034644�14662262111�0021562�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF9��WAVEfmt �����������datal9��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fact���FILT��������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/rare_frames.mp3�������������������������������������������������������������0000664�0000000�0000000�00000020200�14662262111�0017732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����eCOMM������XXX�A COMMENTTXXX���1���userTextDescription1�userTextData1�userTextData2TXXX���<���QuodLibet::userTextDescription2�userTextData1�userTextData2TCON������13WXXX������userUrl�http://a.user.urlWXXX���*����http://a.user.url/with/empty/descriptionUFID�����supermihi@web.de�12345678��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������VBRI� �d�bۑ��!:����@6λ7Ϻg7׻Ϲ,58cZO4ķccmY*0Eλ6p7 Ƙˆ8`h0h088�Ƙ8Ƞ�h�hȠع̻p˵}X,˻fh*e\ phƘƘhh�ƘƘȠ�8ȠȠhhp(7����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������D�����K���� p����.��� ��%��I� B2|P��m�H�PЮ  קNn``0�7(}xD!����K���� p����.��� ��%��?ݹVjƀ@��1V-܀ 3�o,ɿ@(kv<] �/DB����K���� p����.��� ��%��?Nnc%���� �Wy|J�B� [c�P��@��@��@�d>c����K�� i c�$� '�\=P�N8] E#Lu uT;`;W@RincKDdnt�~�ƨ���`=��l���:dp�ft7?AۑէV)GH4 -Ơ,扖"&&& �&&Gdʣo��HA`�),�L/�� Cp�)FF#�!`ƢPa <<p S$-+Lf@`;2Hd!k��'�ˠ@�)� � C��y&4[>mܤ A�u{ T " [Qb縓n'1dZAv���ƨ���(<��d)A%apr2c`kQ#��@�D"M$BBIłHC�Aa)h])dN__.a7?yqcKd SȚdCM dM?KYcl mg o1J̰~zeG L Jx@iɷi@3G ^BCL4L(b QVh2Q[:}8%`TȺ<cܐ\7TLr!{d2SS͛zey=~vwW39u=e.d9%}�PPfZ)ɟqXAK�� )g ix֖J�,z]N Xp q+Ed i+H?rUbFXF @!7K@tI#$^s^{uZfR@�VANPB6(�Ȳcbe2CؠU%lu�F3ޛkC:ϲO+lo.WevI ((,xK/)-)F!tҮ̿azLPV1t9d{O](TˉUݔ]A^+d`+h+E ꁬ.֤e gB}2`i)iN"G_P?VB]0[u!  ,4ظ1d2/=/I]4(IJKd yF,MyeǒUL$*v1"wL٣qf&܂.IdMZz.DD n0`g`7fBbrjF_XIÁ_,<E2}90T)&}׽>4q�,r&@'vek�F2ɅP߁#q3R-vB^Q2v,'; [O !iUxܪ#ڧm.BNV@|=`IФ@~&{;3/) 钕mH9JaNQ#HZ:@�@ V9.QVNOilMBCS%A8N"q9/ JDxד8W$DnFuͶYb317]Csmm'̙5"9 o2 $i BXi Zo 8ɠU@JQ jxEa?41 TJ`\[a aV6,�(1[^ ZŪV.MYe67圚xLgw�A¤3Dfgw28<YB627I :8,LdU^^Vd#UVS,<IzaS/S- c&IZpPaH۾ !VkI)AҋE9phਛ?ݨH֢BJ ʴkpJσä]2<LbOa齋em=50Rv[@׻вM S9{HGBٚf+ӝX# �i< @L1_t4\qf/ \hONꋇk+gU^K|]B"b*n~ߥ[/AgFTݫ/tY>C8=9%P5ݷڇ95V~RTI 1(|U'Wl)Y8 }Wf5sJ< ^V_ d1(S3QG[W„I M ZEZTMrůVkeXVae쉲cEoy C*nQ :Z]C�_8 2)HN5$xiP26 iBzQF*4{_.Պ‰EšJS}pW^uE>fSj+E讠mt u `OEҖQ+҄W3 wmj7m  ZTC-8do JSd<i =a@Q5�:;iR'#ȵ$[#*44Iأd d@<CpGlz?4|2D'%-(tk!9F_�a:#ǎBm2Uq1ngbd$^ 2>^<կ'Gĵ2IY.6MnH$AפQ4(H^7]dnf\ OVmc i3ו6ѩ(t/\ljgYj*fRյ5 k/11 ߯ qU3 ķzFŀq@eȭ.tfOrdLrwp,2g%_%v-5aT`lĪK@ηD +5#bY,fE7Zxq^ů֝=Yֵ=5^1-SFؽOLb )'CFdB)L`PL& j.z/LBSJ95H.Iı3 [ZS7LKq[LN1(SX 6(Q33zR <Vxk/ )_9'GtO|8mx<c5ۖS_�$LB5dy�sOno@�Ɖ:�)3U]�*r++PT0 C`(XHA% \ xTMt,f $bCt0p4 Y̻{L0=aU$EMxaЇFA"xKM˟F'2; RlRw9ͤa^PQ- e:.ԍ׃g8͇ʟH"v Nv}7*@Suڵ Z=844AӹK!.ݛ*Pm6q TPQv8PFbJ0"bl3TzՈC$>G*@cl~eA [X`Iy"M؍kHRSN{kgV.qpMGlrM(QEIe۾Ԅn{SnM Z^͛lasv1?s -1K)†;$KDJ ۷mﮆ =0V<�0# !FscyԞ  P6iFb|Qv8<aa*v̆m۞R˳2\nPv]l3_Y%IWZԹf+M5;g ]~!3SXs 3ݟfawׄ.`ѿbPǣN0&tӳw<-ei3E#&hԚbUdW)NO-Nj O-jW~k 21*=fl.um~j+Ƿ)RG3}3;;q?5+4CaͺXn[ވ"m k$I)#�j:a5dO�GS N:iڽa&5!Qeji$[351-F^RarXns4Z;-GCR8p\3kf{m,^#biR_j%InVF^ɮDO~ێ ,X3fnw�?JKS,Fp%ZFXH]a`RjKS:Rq(LXї/a-مM> P&\~(=P:,ZF4:Xa-^Cڎ1~*2qYubP� np=7ff- ! l!3z X8 {ae˱B`X1RQTgj[@b<xKT(Y8N⊤̐ 1m`z(Ur*(?kY-}O~[}hqC)iӥ pTddOqWrmF? D$ᮎIP sь[R3%C˒!ehpdUb 3%IQQi}ԦpQco$&Yf͈IKGF\ϘBIn0|@ q Z+a2�Z&d?MT Fh aTIY:P5& P"QɜfJJ pHk0 GU.Xd𣧞$ZqR9\8 0i KlHYTʤ%3k+3 4hvӶFFBhֳtxŚh6 MH}7=6aIǨ[X2Oc<Of{' @4e\`x~Җw*\s*);.cP?f?ŝ4l DbK=}!FE9o|$|6γ X:܉J,8ˁǒ,YIo(ڪl!NrTb<}'}SrE6p Z'bn$ABWS)(kXh sK*>NG$�j+2j &iwAޣK/]Y,2ܷouRENņln 2x$S&Žƒ.rxqobV1/#UJ@szpQ]I됣$aJhx)EHTawYeZ*3WKJFߎ1 Y~'�}YEMވ=zDdAC�LUS/LZʽ=)!U=3j9j[q]0gAG@eg `!44e#ud+'ӧE0WԷC.Iޚ(ˣ l laP4ijBˇr•#RWQWdȕdH$iߐ @Պ%Bt3 $%z|<Ǩ2ɨ4\�"DIMM-ąCǔFT--h/RHZҸCQ&%;ŻH"x^U֠q2C̖g(<i+5콡 H(RZ - Pp>Oq"\IKĜ3j 49*\]5LDp۹#MYS^҇dubh g#x;*H# !F]" OLѷI֗||+k:%&<!!P\#}0h�%�<c$XFҧ!$F<U6  z bay*K;L dl݌Ojc^qӡK1+Ն5KlruѸZV*®*V" mZ�Bp YhDü%84(D�e+Lhբ$Cw` AAf9\1Nߵ:z �NZq߂6Є -B?\d[1�"Mk,ȁHe}IW=5b()"`ʢ)N@9#yBޗxz( "i]XE_O1޾` YIl뭇ș3qkT�Def5ljʡB.XOY�JYͧW*-9gXmz 5gmL;$-: J$UbX΀ ]uıiOG]oC @V% KtQ$]gBk)Tc,-LFpOm. slG:Nw$kkoS3(ಪS.tzOH,]H5 iE1t B㇈t8=9vY,M{5+(Q lRXZ}`0m} HlE0kl h4 ̈+TAG���������������������������������������������������������������������������������������������� 00000000 00000000 00000000 �� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/segfault.aif����������������������������������������������������������������0000664�0000000�0000000�00000000037�14662262111�0017324�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FORMc.AIFFCOMM�����SSNDc���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/segfault.mpc����������������������������������������������������������������0000664�0000000�0000000�00000000023�14662262111�0017337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MPCKSH�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/segfault.oga����������������������������������������������������������������0000664�0000000�0000000�00000000170�14662262111�0017331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������{^d���y ULAC��fLaC���"����� Ķ �j���Զw,x@� y%@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/segfault.wav����������������������������������������������������������������0000664�0000000�0000000�00000000036�14662262111�0017361�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF9��WAVEfmt ���data������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/segfault2.mpc���������������������������������������������������������������0000664�0000000�0000000�00000000006�14662262111�0017422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MPCKSH��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/silence-1.wma���������������������������������������������������������������0000664�0000000�0000000�00000105130�14662262111�0017317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������0&uf��blx���������3&uf��bl4������� �����t�e�s�t�����������ܫG� Seh�������CO;J)rJX������9.@ �����������<������������� �� ����_.� Se������ӫ� Se���FC|K)9>A\.��������s�k��� e�n�-�u�s���]&EG_eR�������ů[wHgDLz����������� �����I�s�V�B�R��������4������D�e�v�i�c�e�C�o�n�f�o�r�m�a�n�c�e�T�e�m�p�l�a�t�e���L�2���t E˖p����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������˥r2CiR[ZX��������������������������������������� �������.��������� ު|O(Uݘ"�����������������@�^P���������W�M�F�S�D�K�V�e�r�s�i�o�n������1�0�.�0�0�.�0�0�.�3�6�4�6����W�M�F�S�D�K�N�e�e�d�e�d������0�.�0�.�0�.�0�0�0�0��� �I�s�V�B�R���������@Rц1�H�������ARц1�H�����W�i�n�d�o�w�s� �M�e�d�i�a� �A�u�d�i�o� �9�.�1���$� �6�4� �k�b�p�s�,� �4�8� �k�H�z�,� �s�t�e�r�e�o� �2�-�p�a�s�s� �C�B�R����aܷ� Ser�������@iM[�_\D+Pÿa�� ���������������a���A�� � �����*�� ��u{F�`ɢ �����������6&uf��blv������CO;J)rJ ���������]����U���� ���� ��% "" w=  ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]U��U���� ������AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]��U���� ��+��(��AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]��U���� �� ��8��AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]U��U���� �� ��H��AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]��U���� ��+ ��X��AA" w=  ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]��U���� �� ��h��AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]U ��U ���� ����x��AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������] ��U ���� ��+����AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������] ��U ���� ������AA����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]U ��U ���� ������AA" w=  ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/silence-44-s.flac�����������������������������������������������������������0000664�0000000�0000000�00000143330�14662262111�0017773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"���y�+ B�zbܷH2ĺJ��l������������������������������.L��������������8������V�������a�������������vL������������� ���reference libFLAC 1.1.0 20030126������album=Quod Libet Test Data ���artist=piman ���artist=jzig ���genre=Silence���tracknumber=02/10 ���date=2004 ���title=Silence�L1234567890123������������������������������������������������������������������������������������������������������������������������X��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������123456789012�������������������������������D������������������������������������������L��������X������������������������������������������z����������������������������������� image/png���A pixel.����������������PNG  ��� IHDR���������wS��� pHYs�� �� ����tIME  6D=2���tEXtComment�Created with The GIMPd%n��� IDATc?�Y����IENDB`� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Y�k?O?s???????9>?3y?̟>?O????ϟy??ϟ??'<3??O>I??Ny||??�C3'?????????????g|????s?|g?yg????|ϟ???'?'????9????sϟ>?'Oy?4uYl??93??>?ϟ?y3???O?y?????9?93???<<3O???<9ϓ???9?~O??g?~?ϞNO'�Cɟϓsϟ9?g'??|?ϙ?'?3|?'?????<????d????g???ϟ???>yϟ?g?9?<??~?9?I???ϟ?|??Ye?3??>|?gg??>|~s<?3s???''<~???O<??~Og????~yy?????g??O?9??g?ϙ?y???>y?|?�Cs|??N̟?3g???>sg?L??3O?3y???>3ϟ?'33?~||g???O?|g<??6Yb???s~y<??O?ϟ?ϟ?g?????ϟ ?s?s??<?g???|g$3?f?真?�Cy~Iϟ?'?'O<<?'&?O3>g~gg?ϟ?g>sϓ9?L><??g??<~?<?'3??g?XYw??gϟ?|?s??y??2g<s??|>g???????ɟ??s<g?<'?s|y>>|<??3g??ygys?s~~?????$s???y?g<???O3?y??3'??ϟ9?????9?O4Yp9???3s<'?????~<'?yy????g??ϟ????9?O?ɟ??>?9???3?? ?s|???93???????<O?fss??>?~g??'g???~g???3g?3??ϟ??s Yy???s?9ϟϟ9<?|???9??|>~Ϝ?yg?3???f3>??''???|?'????$??'>'?'9??9?9|y'|??|@O?<|??>3s??&~3???????<~r'??s~???'???s3ϟ??ϙ?9?|'?N<??Y~???O??'<?????O???9?????|''~?y???3>????s?????9??93<?|??ϒ?9?@?????3OI????O???&s????ϟy~'?<???<?O?~??<???<?|<3????gYS3y9?'?93??>?ϟ?y3???O?y????????<<3O???<9ϓ???9?~O??�Cg?<3'?9gy?s?~'?y??9y>?>O???3?9?y|'s?<9O?Ϟ|9?~?g?3???zY T?y??<>yIg9<3??>|?gg??>|~s<?3s???''<~?dϟy3?~Og????~yy?????g??O?9??g???~?3????|??'???O???>?'???f??'O>?>s3<?L??3O???s???ɟ???y<????y?9g??s<=Y ]&gO??|ys??g9?>~gO?~???<?<???y?ϟ?????3????9&9I93?g9>?|g<??y~Iϟ?'?'O<<?'&?O3>g~gg??ϟ?>sϓ9?L><??g??<~?<?'&Y Z???y??r??gϟ?|?s??y??2g<s????y??~?'??????ɟ??s<g?<'$9'?󟟟'|???>~?9~y?yO?g?'O<s's????O̓?$??9~?>???|||9??=Y O33y?99ϟy???3s<'?????~<'s???????g??ϟ????9?O?ɟ??>?9??'??'?y??y??9??39gsϟ'??|?ssO<3'??3???yϟd~?yϟ??3???~??O@1Y H?'?????>Ny?<N??~?Oys3?3ϟg???>s?>9?ssy|~???9?s'???~O9????'??|ϟ?|?II?D<<O?<|??>3s??&~3???9??<~r'??s~???'???s3ϟ??ϙ?9*YA???|'???3?sO?s???????9>??s????9<'??$?????ϟy??ϟ??'<3@?????~?|9>s~?9ϟϟs??????3?g?'?????9?????g>s|~|???~sO3kYF?g9?<???~g?<?'y??g?s>|y?>3?'g?OO??3??ϓs?>g?y???'??|<3????gyϟ?|'Oɟϓsϟ9?g'??|?ϙ?'?3|?'???ϟsO'gL?Oy????ϟ???>yϟ?g?9?<??~˛Y???>O??|y??<>yIg9<3??>|?gg??>|~s<?3s??ϟ?'??????dϟy3?~Og????~yy?????g??O?~?g?3??????s|??N̟?3g?'O>?>s3<9?s~??$3y???>3ϟ?'3Y'y3?~<gO??|ys??g9?>~gO?~???<?<??g?????ϟ?????3????9&9@|g9<'?3g???O?3O???'>?<?~?O??ϟgy9??>|?3ϓ?9??>L??93?<?ssY?$3?f?真?O<?y9ss<?<?????̙|?<??d?<????O?<?gOg~?>???'??s?�C~?<?'3??g?ϟN???????f????g?????$s???y?~?93>y?ϟ????$?<??y??Y'?s|y>??9?f~?????'~????'|?'3yy>??ϟ??><?3<'y<~>g?>3|>|?????|???|?fg???|?ssO<?y?g???ϟ|9?OOs??<?Y??ɟ??>?9???3????s?9ϟϟ9<?|???9??|>~?|ϟϟ~'?3??g?<~O???|?'????$??'???<<O?<|??>332g>????9??<~r'??s~???'???sY�?|?II???|'???3?sO?s???????'O??>?s????9<'??$?????ϟy?93ϟ??ϙ?9?|'?N<???????3OI????O???3?g?'|?yO9ϟy~'?<???<?O?~??<?VHY ?????9??93<?|??ϒ?9??瓞|3|?3'??3~ϟϟ?y3???O?y?????9?93???O?~>O?D|~|???~sO3?33O9>?s?9???r??s~rs?rs??'|?'?????<????d????g<>|?Y?9?~|<?g??3?<???'Oϟ????'??ϟL??|???|??~g>|?>s$??????3>I????s~~~~s?|9?~?g?3??????s|???>?'???f??'O>?>s3<9?s~??$3y???>i Y#'~yy?????g??O?9??g?ϙ?y???>y?|?9O|?????????>?'?<>?~?ϟ????'??O@3ϟ?'33?~||g???O?|g<??y~Iϟ?'?'O??3?y?|rg?|?ϟ?>sϓ9?L|Y$33??ɟy??????y??r??gϟ?|?s~s?9?>~y&>|?O??~|?y??~?'??????ɟ??<?ss|?O|9'?󟟟'|???>~?9~y?yO?g??~???O?<??s's????O̓?$??9~?>NY-?s<g?<'?s|y>??9?f~??y332?'????'|?'3yy>?y??3'??ϟ9?????9?ϟ???ϟ?'?ϟ?<O?393???????<O?fss???gs'???>pY*???ϟ?rg&|??Or<?Ϟ?39?g>>??Oys3?3ϟg???>s?>9?ssyOϟ|?????|ϟ??>O9?s?|??O????9??~<?3?????????9???̙ϟ<y~|???>O39???gg3Y?9???ϟ????39???9?????<?9???????9>?3y?̟>?Oϟs???'???s3ϟ??ϙ?9?|'?N<???????3Oϟs??????3?g?'|?yO9ϟy~'?<???<?O?:xY8???>????3?>s9O̜??'?3~gO?y9?'?9??3~ϟ???O?s?g?sLy?????<|@y?'??9??y?y9?????39??<g?<3'?9gy?sg'??>gO?3??9?y|'s?<9SY1??~>O?9?~|<?g??3?<???'Oϟ????'??9ϟ??ϟ|??~g>|?>s$??????3>I??�C>?9ɟϟ??y?yϟ'??>?~?3????|0|??N̟?3g???>sg?L??3O???s???Y6'??s??|ϟ<gy??|ys??ϟ?????~?????ɟ???y<????y?9g??s?ɜy|??'s??~Iϟ?'?'O<<?'&?O3>g~gg?ϟ?g3??>OY ?Oy????d3??ɟy??????y??r??gϟ????|3?~s?9?>~y&>|?O??~|?y??~?'???@?9??>L??93?<?ss|?O|9'?󟟟'|???>~?9~y?<~g???~???O?<??s's????O̓?$??9~TY!9???Oϟg'?~?9'?sg??9~g??9??s 9?f~?????'~?~y??????�C?>???|||9???'??'?y??y??9?|>|?????|???|?fg????????9>3'??3???yY"<?g??ϟ????9?O?ɟ??>?9???3????sϟ??'|sgg3|ϟϟ~'?3??g?<~O???|?�Bg??'g???~g???3g?3??ϟ??s???????????????ϟ?|ɟ??<?y?y#��jUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPTUU*UUUtTUWҪUU]UUURUUU]UU]RꪪuҪWUUUUU]UUUUWUUUUUUwUUtUUjUUUUtUUrҪuUUUUUUURUUUT]UU\uUʾUWʪUUUUrꪪUU]U)URRUUUUU%UTUUUUUWUUUUU*.UUUUVUU]UjUUUU]U*UU*UW*uUU]URUW]UTUUUJUU]jU*U.ꪪUU]UUUUP��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/sinewave.flac���������������������������������������������������������������0000664�0000000�0000000�00000176067�14662262111�0017522�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"���� B�cںޫ,,5'Kv��( ���reference libFLAC 1.3.1 20141125����Ɉ�#�� Q�C `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐ`���kɨ t�W#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@����!ɨ淚�O/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Rh���`ɨH-Ź͵}B#h '$$! $!$!$$Ba I$I?ݚfrlI I$HNI?5Y>I&HI$BHLBHLIrkVVWRi2䐙 !BII $$2t^_>d Ba $!$M꒤rII$! !$BMMfY7?$ B$$I $&C^Y5vHiNICI$! $dl|^dLHI$aII! Ly&ɯʾy9pBdB!$!I $nMgӪ&O$!$II$IrY$'o׮'I2HB$!8@!NL;'^UYf&IIBI$Bd!2I&I9 Y?S_6O&HI I$$&BY'ܚ_;%YI3HI$I$ N$!2dgg>L$!$HBp OMfίd9&I! $I $!&I $NCdɬUzlHNBBBBI2?_$ $!9BBI!Lne~$&Bd! $!2I$NLk͒I&I! !! L!2'%gֳ_Ii$HNB!$$2BOku%+6NI2IBHBHBHI$$I<75f$I! $! $!<9kWa|LI$$$$'$:֬d!!2B$HI3d=ff|9$!! ! $IBI%I%?%II 2BBI !$9 93dn~I$&II$!$I2L''%f~k! $$HBI$$$W7f$$I! $$B$$Mٓ_Y}r$ $&BHC$&IܚϧUVMI BI$!$I$I>N߯]}Y?'O$d$&IBp2BvOfd͞L$$I BdLr?f\l'&LI$!2I LO5vJg<fI$H@!$!HBd9?+5Y|&I'BI$!$$!2'4_rLBI$HBLI$ɓY$<'$$!2d!9!965 I!2HBr$$C 1I���*Ɉ?N 7�;knWS"~݂7VJҪDD$D")IHJ+U{|n)HHA$QDHDB!Q"*JTw_iJI(DDB"DDB"HJHD$__}MhD%  DDD$"DIR"*Rk{7[RI$DIIDHBRD""%*=n:]r$DD"$""!$$R"I)IDUsv.QZ$I!! $!E"KS/ڞ}ϟu)DDD"("E""D)$拏uٿV"QD DJHITJsܿTIQHDDHH,K[ulUJ%Z"! !$ItDUJ]ޫ$JI ED $DDDD+JϻzRJ"*$BD"HDB$$I$hқ\ VB"$HB!ID"Q*Ϯ}S]ET%$! $!$!(u[?o޽J$I"B$D"TEZR뫷|ms]+$IHA$$DBH "HDJnKn{7wEBHIII"E$DIHR%޾S۫uID"%! ")KVum+䥪DI""""$"!$HJ$D?}ޒW DJIIBB$DIW*\|^*RI "D%"!" !"H$E$T~~VT$!"!H$HDD%JDJV__utJDEB """DB"RU$_ӽZJRID"$"""BRD"$$UneETB!(HHD"""!$"JW[?*ܒBI"$"HD"H""B!))UO}uzE%$"!$! $!"IJJ%Mӛ7uIr"ID%HDE!(\~|K%*"%DHA)$H%?},Lۮ]HH"$H"RD%JUt~*"JBB$DB"DDDDDDdJ_4ۯ޷[gRU*$DDHH$"K"R^v%*RHR(DE"!I""""$\HV^}\VQ"B!B"$DH"H$EOL^n䤈J"D$DBHB!JU$~}wj*)$(AI! $!DD%t/~T$DBH "$I*җ]]o*]%%"JD" !""DDDIJB$Sr[uٿwR.ZBHHBH!")""JE".6ڞ_믨J!(ID$DHDH���c(ɨBMJ?�iCə$$0a 0&g9O)4r%98a$ɓ I!ad2g/<e$ I!I0†I&dJiRӧ'90ɒI$Hd&I!f|?ryr33$2L2L 0I ɜ/ҔyO<33$ɇ 0d$JI32r\<2rL$L$0 Ù9)R)2P0!$C$C&2LSR/'&$&C&IIfLRRRϙ̡d2aLaII'BfYg3& & $Had2Iə)M>ғL8C!dIaP):)fxs% ɒL$I&aLL(rP)>R<)<rd!&C$ I&dJ|)L3&d̒d2C!I$3<ҙɓaC&$I0$ÆaCBINK|(S)9ɘa2L$I$ $L2,fa3 2I&&C a >grv9)8d$ d&IP䡔R>99fC a$BI$ÁC ̔K*J}L&N 8a$$ !I3J_K)Lə2 2I0d3 sS4R,҇2IHa$ a3&Ls2Si,:JspI&$C$0$Ɇd3N_yC2I&$C $a L̔BO˞Ns3a$0ILC$3 %9ffIdda$!Ó9)._),yffI!aɆI$fe2ey32d!IHa  2s9) SSIC2da2BI$I$L&ḑ<._9JO&L8IL L$ ̙39̤)Х=/3C d00$$ &O2)Og3L2L&I$ $d2S}'M'93!$pC $$ $ÙSt"RJ$I0L$ $PJS| *yNRy30BBLI&!0L'3BKRfLə$dC$I$3%g9)y3' †L2I aI †22NPRs0dI $I!I&3%e.Y39fdL2L$$ 2|Ϧ?,rS3pɆI20&L 0C)���nɨVX Y.Y�IeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  ���Kɨ&u+0f5�X#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[����ɈLaO2 &Dr8Z!Js-ĺD&CLd$!P! HHKJx^Ea}œ$ 9!HL!aPsIQC!!$$&CLDtnDʦrC@ 2BB!$$8RR-W6*I$$@! !$0$2`d܄і<:VZϞBK0$$!$ $$$&Gk:2霦C$B@BII$$$īeXMC!RBBHB0 a s!V1nXR>$!@$H!IJrJpԉRf[ ! HI$Hd  &I )JEfI)$$! I!$ 䓞36.jǥ8P0$$!! BHI $ɓ iE$L2B!@ ! $BL%Ԧ6YML>LBHHI!0!$02ZOشڶ' &I!BHI e3,U9iL!  `a !&d-shSL) 2@HI!$ HL'6TUwlِBBI $BBa$ɞ}c9ZI fL2B`a $ ! d!BL9n':RRe2PCa $$ &! ̘YkUT\ !Bf0B$JP)\dbf0d 2$BBHd$%0~.+$ !HB$C2@%!)!Ҟ%בG:_p Na!3B$!Bd02$jTbd$HHI!! !!$! 82);ۭ#2$9 BL&HI$ %$U͊<i2I$)$2HBH`I $ 0!4e*琒 &HI !! $$$ɇ2yΌtg)'!2BBI !@'/&q*檙VdB &BHBm&U{[s$% a$ $$2HRB*$n4"TBdHHI@IBBg3==JkQiCْJd$2I g!HI0$By$͋e4&a!$! HHC $2dl9eĭZQ~}&a Hf!HBBI$!&Ii;)SS$τ$HL$ HI$$L!&̖-6BI0$HB0BCf_iNZ$2HBdBBBCd! 9|Kmm\詚 C $HB0$M=9]0d0Аa!d$$! &I32g=#GNVB BC ���ɨ �B S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL#D`119Z#9sL&b'.LOd\RF#֘#dxi !LC0Ge.Z'LM*# &Ȍ֫!6,`1}]AQ0.Z#$LFUZa 'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brjb521i0A2je\k# #JNĚ#(q8F9lG&FZCL˕Db+*UMD0 2F#hA221 Fb a;<FS+/AQr1I^WY]0b5eNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\b2L4LF`Aa9+b䴘j<!Ų0֝]5w#/'W.Zbi2ɈZbԲ..XN'41<$q O#W1|CZZLF0FXA12YqM0P 0Ay| S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL#D`119Z#9sL&b'.LOd\RF#֘#dxi !LC0Ge.Z'LM*# &Ȍ֫!6,`1}]AQ0.Z#$LFUZa 'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brjb521i0A2je\k# #JNĚ#(q8F9lG&FZCL˕Db+*UMD0 2F#hA221 Fb a;<FS+/AQr1I^WY]0b5eNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\���ɨ \ҫ�nNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\M`��� =ɨ Lkߘ]H^ˣPߒ&߈"DD"$RN}ԹTJ$$DDDHI"$DZVU:nrVJI"H"""""""DU$RREDD$DI"RII**NRI"DDHI$JIIUR$I"$""DH$rsֵUV/L$!HHB BC &k+V:&BI!$!$!!B!HHBHI!L鋊UJ=ԪTII$B$%U]]}WT(DDD!""$DI%R]_OϿW.QD$D"DHDD$DA$UkUors$$! B BBHLsUVFLHH!BB BBBI$IE]r*I$H$DB""HIHR[wJHI"DDDDDBDI%JU]*H"$DDHHH*$- mjū]wLHI$!$!!!!$dUUZpBI!B!!@ !HI$zTUUS3&BI DDDBDD$DH*ۿ^KI("HI $"D}UR)"HI!DJTUUJI(DH""D$Z]}եQ)"I "DHBDDH$DT){ﺗ*D"D"DHD*tHDDDD""$DD""DDII%Uu]}zꤊJHH""DJI)%EUW.ҥ$D$""$"H__uuRI$DIDD!IH*U]UJH"""""D@BI $riZ4Z*, $ HB !! !$I3.+WUU*ky$Bd$!!BBH@Ba9TQQTI"Q""DH"D$U.Ed"I""$D""DDI"D$r%JHH""$DDDB"$$J@ V5ZI2BB! @! HBI $&O닍V"wɒI BB!$!BI &f~[j&9)I"""$HH""I"DRURI"$D EDVu}JJI$I$D!!""D "J)%*ο}rJDI)"""""""D$$)*TujRR$$DDDBDD$DH*ۿ^KI("����UɈ 2-(#�D0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!Ʉ���~Nɨ YXWMU�aXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&���9ɨEILOO5�Ia.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&���ɨ %�p0Rԑ FDF0*F#Kbl!M!בN'MZe1`&44bpdרP##.\[ʥF-JV#)pʵqA O S#&FD2`M0k&&Uh&0ASa]CMu1 &'2LF.T# eɈˈkLGb2kA4˦!#e2՗-&&dFKbkU [0A׮a ( C-a &L#*ea \M-0VO2C:NLCBa0C*-+edab1='H#,l!22azV#arL#E Sbؚ(1:b<C&C2 ʴerFe$ЌF0 Œ(Ĝ!d195r14 i52\bh' bMea8Mq #\#]- !ɦ^FZɢi1r*Z&b#4 ֙z#W1 0h#)x C I/+OZ1e'HF#0Ūi1L#(Y[ ҘbvM2i!գN<FYQ1[i&&Y2ɨF&2C$14bs2Z0r F-1t^)aRbֈ`Jbk').1} &`DqML#DD0 01rZLLHbFNNɫ-14da -1jYp,C 'bbY8'!r&VL^##, X\N&Aqf(b b<aWq _)T&I &L#B20Q茵ddC(Oɮ0#W1tD1-8M+$atL^X21 MM^FbF]0b!eT] F)Se'#jb:Lbza A0Rԑ FDF0*F#Kbl!M!בN'MZe1`&44bpdרP##.\[ʥF-JV#)pʵqA O S#&FD2`M0k&&Uh&0ASa]CMu1 &'2LF.T# eɈˈkLGb2kA4˦!#e2՗-&&dFKbkU [0A׮a ( C-a &L#*ea \M-0VO2C:NLCBa0C*-+edab1='H#,l!22azV#arL#E Sbؚ(1:b<C&C2 ʴerFe$ЌF0 Œ(Ĝ!d195r14 i52\bh' bMea8Mq #\#]- !ɦ^FZɢi1r*Z&b#4 ֙z#W1 0h#)x C I/+OZ1e'HF#0Ūi1L#(Y[ ҘbvM2i!գN<FYQ1[i&&Y2ɨF&2C$14bs2Z0r F-1t^)aRbֈ`Jbk').1} &`DqML#DD0 01rZLLHbFNNɫ-14da -1jYp,C 'bbY8'!r&VL^##, X\N&Aqf(b b<a@���qɈSLí̗[OlG\ao#d.( b(J B(!a$ 'Yy92z5 ||O-p0b ()R$PBh^++5!9=M$e1PYHIDV%Kקm:::s֋F$"A, bAyI*r4Ѵ|vչ5-""%,BD"IK%˯ɺ#e-i,Bș ""D"""HHBȤ-/+.|k5SafIDLD !$!A!bHHD⚹'&Ͼ^&$ԅJ$DAHQBH$%s2̳'b D!DPDAHRJRpA}i4#T%AHHEHEH(EQ&TM'e6OYk1lSRq2!d($PE,RDZZ#h3-2$2  $(""BH""DP,)Dҹ8j!i4L*"- ,DHBJp¡QyYjvקoޛGQ) ,B$ LP% DR2Z/tuSuDQ"P "$EL01zwtz}yF)ADLB`I!$%pɲmnM3#eOSU%E%!$$)  dA2&Equ+__'ޫQF$b&BBDDPEI $JE"/)13,ifܴE"I")X(B)!!H.qZO/|z~d(b2#Ld(I  !$E""-ȚKi2̧Owq54JJD b($.R2^|tef//TJH D I"$$ĢryC2׋d.( b(J B(!a$ 'Yy92z5 ||O-p0b ()R$PBh^++5!9=M$e1PYHIDV%Kקm:::s֋F$"A, bAyI*r4Ѵ|vչ5-""%,BD"IK%˯ɺ#e-i,Bș ""D"""HHBȤ-/+.|k5SafIDLD !$!A!bHHD⚹'&Ͼ^&$ԅJ$DAHQBH$%s2̳'b D!DPDAHRJRpA}i4#T%AHHEHEH(EQ&TM'e6OYk1lSRq2!d($PE,RDZZ#h3-2$2  $(""BH""DP,)Dҹ8j!i4L*"- ,DHBJp¡QyYjvקoޛGQ) ,B$ LP% DR2Z/tuSuDQ"P "$EL01zwtz}yF)ADLB`I!$%pɲmnM3#eOSU%E%!$$)  dA2&Equ+__'ޫQF$b&BBDDPEI $JE"/)13,ifܴE"I")X(B)!!H.qZO/|z`���ɨ~F�Ha3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev���OɨJѾºm.XS$ $99ɞIO$39̜3<L3ɚLRaə$I2d&||<2sI3rdɒg䓟!KɜI$3g$2rL9$II䛓4dL̒O&2Np̜NO̓I2|Ns$9$̓drdd4̓90$$2d$rNs%%&Cs!$$I6d2s'$Le'9y90 &I9ɜfC&s'ɜyIII9B|>I>g'9L$$I9g̙2vdyII4$2I<ɓ'2L92Y!yNs'&vI I?2O'2|̙&~<$2gy$ə?32IIrIlIddIIIfd9$%ə&LN939$Ny2dɒ2O <$4O&rg&K) 3O'4Iə2dI3''I',Rs&rd'L4rdϐ3$$d8sLi$s$rI&L>fNdd3䓒L!&rL2I>I<%&JL̜'I'9 2I7Nrgd<L2Ns'2L$3'9$f%|rrfI&dg&L2yɟ9'32LL2rd$drg2Rp2Ld'Ld$r|g2s<y&M$93$Ʉ̓&i3')2S$fy%2L$$3I2s$$93992s$L493&L>g3II9'I9&y$͙ 3&yIdNL9>BdɒNrg$ɜg&yryrRd2|y&Nd9$y>OO9d2I2s>I'g&Ni$&L>ydtM9 >LO$d̓$Lddyy'ɝBfO̟̓&s&rI93'LI92fOL||o$2Rd&Y$dys9dI>~fIyfI92NLNd&vI9L2d9̓O $$ɜɒC!$rfL3Ls|8dɜ93<3 &���ɨ�_ �\F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP���*^ɈOKHlDA�F.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 't2FeFZbDa4F,-iLMdb1;&4̐dщh#D,Z-4,dF#Y^N!19ey NFi`#/0b1kD0F15i쌆0A8&SxFaNJa-&&Z`b1l 'dWM]˖Lb0,FK!哉1M1 O,C _ֹr֓+&/ `LF.V\F'L 81C b1_0+/e` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5*4\ LNV0\jk!I˓&#*T2idbebq5#15 Ce 2j˖œJɄ2#%15-bvM _kd0``L!0&B2.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 't2FeFZbDa4F,-iLMdb1;&4̐dщh#D,Z-4,dF#Y^N!19ey NFi`#/0b1kD0F15i쌆0A8&SxFaNJa-&&Z`b1l 'dWM]˖Lb0,FK!哉1M1 O,C _ֹr֓+&/ `LF.V\F'L 81C b1_0+/e` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5���DɨWXYZP�p$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`��� ɨ).38.�BjZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !���ցɨ�Te` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5*4\ LNV0\jk!I˓&#*T2idbebq5#15 Ce 2j˖œJɄ2#%15-bvM _kd0``L!0&B2.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 't2FeFZbDa4F,-iLMdb1;&4̐dщh#D,Z-4,dF#Y^N!19ey NFi`#/0b1kD0F15i쌆0A8&SxFaNJa-&&Z`b1l 'dWM]˖Lb0,FK!哉1M1 O,C _ֹr֓+&/ `LF.V\F'L 81C b1_0+/e` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5*4\ LNV0\jk!I˓&#*T2idbebq5#15 Ce 2j˖œJɄ2#%15-bvM _kd0``L!0&B2.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 't2FeFZbDa4F,-iLMdb1;&4̐dщh#D,Z-4,dF#Y^N!19ey NFi`#/0b1kD0F15i쌀���"ɈkLPԷ P(u #ξJE"HHHD"!D"!DI$I*UVo%t"D""DB"!HB"$IH*mn;TDI$D$! DDDDDI*Uw7noRI(DDHDB$B!DDJI%UW^߷꒴"("!"B"H)ZU>^7*DDDB!B"" H")).omWҕ"$$DHDBB"D""I"$Uϛ۶{o$D "!B"""I*W{?뿪\IHHD!B"$BH=߷ݴrHDD!"! DD"J{7{}RH!HB!DDRJUm6RH"! HBBEUTۛ(D"DD"DD"$$]WۮZ"HBD!! ""HTI^]o߾*TDI""$D!DD" "H)R{}:I*D!D""D(o%RH"DB"JEJW{߶䪑$HBDD!DD"""HHR{o}uJDDB"!"""(DR~tﮭ%$D"$D""!B"!H$I)%*{}W)(B"!!"DDI)"WtUITI"DDDBHA"""H%%uJ{>|$$R$B!B!DDUkWIR$I""$D""D!"D]vӺUI$DHBBDDDDDD%W}svo*$DDD BD"DHUUu{m뾩+I" "!""! "$IS~ZRHDHD"""""_}})R(BDDD!"! DB"$"J]\l_I*$I""D!"!""$W{oo7DDB!B"D!)$*~{K)) HDDBB"$DIR*׳ھ%$BDH"$DIU$_o~$R"D!H!Q*UUM~DH!DDB!DDB"HIIU}{}q"$D!B"$D~9RJ$D!"DBHB!$*ooӫH!B"!DH")JVmmRU)$!D"!$TUw}mJD$!DDBHB""$I$U+w\HD! HB"B"""$KU+����|,ɨFI�WLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ '���zɨI�SW#0CLA FS/ZYNHV&p$FYF5b952ee\&)YRj!)1CB ]ib5|C ؆2Yx' D0LA[(tb0\ Z&2őM)F'd&~YZ14 hőU&be,bk#+2CF'1 /!գ-1 bE&,F-hԦ&rM2ܝf4Gb4OC # X#%T F-`잴髹y<rILFqr0q8)!#byA.Zed0A2 ˈiGb(` F#ewLLa2dd4#*#^VFH2! Cb5zCLLOdC#ҲFDz#+H#i6,jS1LF!AZʥE@Ab50Zr0#da 'FɄ0Ca5--I! $dMDa# ҭdb4&4y4zզSaRa /I#JY1F'Y8NLFMzNe _]r1l!2ŰYda2ԩeb2aW O22dkdC,pA F2beXFa#!1˘Mud14S9rbxC$bJ0Q,LN&q#&#Ha bA<FS-Yr8biQ^Y0DdF&]ű NɱcAz ҌɄ1r&d2FVed;#(0@`C멭44 `a2NKRұxVVF/Sx4!j8A&#.S*Fb0)d4^0&-LOHbC#2d0-! LW$a9J`RM` (ŒI!N#W#0CLA FS/ZYNHV&p$FYF5b952ee\&)YRj!)1CB ]ib5|C ؆2Yx' D0LA[(tb0\ Z&2őM)F'd&~YZ14 hőU&be,bk#+2CF'1 /!գ-1 bE&,F-hԦ&rM2ܝf4Gb4OC # X#%T F-`잴髹y<rILFqr0q8)!#byA.Zed0A2 ˈiGb(` F#ewLLa2dd4#*#^VFH2! Cb5zCLLOdC#ҲFDz#+H#i6,jS1LF!AZʥE@Ab50Zr0#da 'FɄ0Ca5--I! $dMDa# ҭdb4&4y4zզSaRa /I#JY1F'Y8NLFMzNe _]r1l!2ŰYda2ԩeb2aW O22dkdC,pA F2beXFa#!1˘Mud14S9rbxC$bJ0Q,LN&q#&#Ha bA<FS-Yr8biQ^Y0DdF&]ű NɱcAz ҌɄ1r&d2FVŀ���ɨB/j*~?�9fC a$BI$ÁC ̔K*J}L&N 8a$BI0rs9>YM4̙!$L3 &C0'9?)L,(y3$$L0&a2d3)>S9' $2a2I$2C 2LfL49̡$aI$0I&P0̜M4*Zt39&2C $I $2C0χNYO.RSffd&III!93R)ffd0& dII&fS._6]?K3&NI$I0s'3P?J_?3&J&$$HdHdfIsRӜdÄdpdI00ɓ3JR S90 C 3 L2I02d(S,ҝ4K,s8d!a$I 0LI93%8Y)zRts<2Id0LI0L9J%?B%,d2I $L0ɒIeJ<P'3L$$da $̒s4)OyO)fd̙LHd2I$C2PsܿS92p(d$I&p(hS)))zo e'93 &II$!$aɓ2P_33&aI$$HB$ 2|Ϧ?,rS3pɆI20&L 0C):}=>s(s%$̆$I$I)>T2%&L&pI a0)0r|Kie9I3&C!I0fLaCNs~SJYePfI2I $aBL0ÆdəfS}9M%INrNI2ddHdd0̙isO(sCI$$HaL0aə9hTsfrL2dI0 Hda򜲞\<2L ! $C$8rg%%%S<2a$2L20$L̦\l~9O&fL$2I0 !Ba0Ng%<J~)(fL2L&HI$$Ʉ̓9甥9Iɇ 0ɐ0Ʉaa&g9:s(a2 f$dadPY:iYpɆC I$a$ rfJpSO8yd$a$$a$s2"JNJYC2d &Ia$0)JyϔeO)O&f(HI$$&I$hR)tS4ə33$ dI$d%?O4rdPɆI!0L3 0PЦRSR���EqɈwXWV3TM�Ra#!1˘Mud14S9rbxC$bJ0Q,LN&q#&#Ha bA<FS-Yr8biQ^Y0DdF&]ű NɱcAz ҌɄ1r&d2FVed;#(0@`C멭44 `a2NKRұxVVF/Sx4!j8A&#.S*Fb0)d4^0&-LOHbC#2d0-! LW$a9J`RM` (ŒI!N#W#0CLA FS/ZYNHV&p$FYF5b952ee\&)YRj!)1CB ]ib5|C ؆2Yx' D0LA[(tb0\ Z&2őM)F'd&~YZ14 hőU&be,bk#+2CF'1 /!գ-1 bE&,F-hԦ&rM2ܝf4Gb4OC # X#%T F-`잴髹y<rILFqr0q8)!#byA.Zed0A2 ˈiGb(` F#ewLLa2dd4#*#^VFH2! Cb5zCLLOdC#ҲFDz#+H#i6,jS1LF!AZʥE@Ab50Zr0#da 'FɄ0Ca5--I! $dMDa# ҭdb4&4y4zզSaRa /I#JY1F'Y8NLFMzNe _]r1l!2ŰYda2ԩeb2aW O22dkdC,pA F2beXFa#!1˘Mud14S9rbxC$bJ0Q,LN&q#&#Ha bA<FS-Yr8biQ^Y0DdF&]ű NɱcAz ҌɄ1r&d2FVed;#(0@`C멭44 `a2NKRұxVVF/Sx4!j8A&#.S*Fb0)d4^0&-LOHbC#2d0-! LW$a9J`RM` (ŒI!N#W#0CLA FS/ZYNHV&p$FYF5b952ee\&)YRj!)1CB ]ib5|C ؆2Yx' D0LA[(tb0\ Z&2őM)F'd&~YZ14 hőU&be,bk#+2CF'1 /!գ-1 bE&,F-hԦ&rM2ܝf4Gb4OC # X#%T F-`잴髹y<rILFqr0q8)!#byA.Zed0A2 ˈiGb(` F#ewLLa2dd4#*#^VFH2! Cb5zCLLOdC#ҲFDz#+H#i6,jS1LF!AZʥE@Ab50Zr0#da 'FɄ0Ca4���ɨHKSNRQ�V2&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bLZ02Ma2ab/De#$eFxMw!1!W&'2!liY# bhebjj4b5z|F# -eR ` 1LMDhC-y9CS2d0[#d W!02&0V1[a i Nq=rj)Ѓ00,#eF'&F#&F'2/rFU,0jTK0FU bx_'j252!C8 i#X12#E0 jCF2kd1<!b1rIS(VLF&\F'Zb8C] _0]1 #)hY14/,C"2^#Zb؆'dر vCaiFdha2aT#+bii2xb 0!u֚b0C0FQV'%iXHȼM++#<FFNŵy fa )# ұC a/bFզ'1F!2GɐUS+00C)&b0AaF$ 'ɫ! #Hɩr嬌'$`+Da8[h#, kaha FM22.VMITZ5!.1!`CDLN"LMEH&Yy\Mdev zшLC-i:FZ1AeX-TFIaDbN&F#iL, FNp4OŪؓI12ɖMDb15!Qфkb1iLʖ#CjSY9M&qNa3#je1'j!FbeF#0@vOZut܌M\i&#aiR˄dbNY8C̑1<^ Mk-i2b abebq4 #1C0@#2bL���,ɨB ]?�(BRծϟnmr+)I$$H !BJT.ݺwURQ$D"BBDUצ|-$HD!BB$UKZ;֥*)$H$DDHDDBDHI $(Rnv}RR)DDDDIJ-JsiZI$HDH$DHH(Rj|ӞsRHDB"""B$$JJ._=k]$R$!!""$$(DH"DRs_>u-QH"$$$$"D(DP$%~w:ҤIH !BDDI%IK>;|R%"$D!! !$DQIEӟ;zRD("BBDDBD$DIDERt{;j("HH"""""""E$Y))RݻwZ$I $H "D$RU%UWW>|m]EDI "$"B$$BDHeEW_ꥩID$ "BDHs}w])I)"DD$D"B$"BHEE$_w\-%"BDDDBB"$DHP"))*Z\H(IB("Ju]ZU%$$DI !""""""D$(Uu}߻wϫ]iQ(H"BDD"HIK-Vs߹۹|r)DDP""BDDH(H-Zv-RHD !$)DIRۭuU*JEDH!!"$$(HQII*U]zk;wRIDH  $(I%T_9]jRIDDDDDD$D"I"JU-_vnwU-%"$DDHHHI$Ԫ9թ*J$"DDHDHHD")%*Vw9w:)I$H!$"""$"BHI$rus߹ۻvIE"H""BBHH"$J%)J]w5Ze""BBBB$BE "HQJZӫk-*I$$ !"$DH$T}wsξ.RR)DHI " "DE$Qj}9|׮Z(I"$$$DD$BDDDI$YU+ZiOnws֩J,$"""""""$QE-\||ۻuKJRID"$IE%RUUu}svJQ$D""B$"BD$D(TZuNZHBHHH$$I$I.Zw:w9ҔH"$DBDB$"B$$P$RI)KK{RQH$$DDD$""DD (w﫥*ID"���F|ɨXƞ�M&SxFaNJa-&&Z`b1l 'dWM]˖Lb0,FK!哉1M1 O,C _ֹr֓+&/ `LF.V\F'L 81C b1_0+/e` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5*4\ LNV0\jk!I˓&#*T2idbebq5#15 Ce 2j˖œJɄ2#%15-bvM _kd0``L!0&B2.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 't2FeFZbDa4F,-iLMdb1;&4̐dщh#D,Z-4,dF#Y^N!19ey NFi`#/0b1kD0F15i쌆0A8&SxFaNJa-&&Z`b1l 'dWM]˖Lb0,FK!哉1M1 O,C _ֹr֓+&/ `LF.V\F'L 81C b1_0+/e` $І&QF(FZ2F^!a 'drCerbxC"&0&/,FZFY&#If#WjAb1  U*. #F2ד51LC&Ce=r0L xAC ijH a#"j#Nk#6ɦɧ&20CʓaxHRɈ18FZbrdb2kbs(b둈a .-eR# K+CeZ 'ex#X"d0C &M5*4\ LNV0\jk!I˓&#*T2idbebq5#15 Ce 2j˖œJɄ2#%15-bvM _kd0``L!0&B2.&C+'F!']Mi!0e!brZČҲ0M1zid [W a6rR0+0K&)i1lMZbzCb1!!yl eZe2# S2hF#aFbN qLAb b4z.ZrFұ4F&20N&N[ɮіd/#-rd4JʕEQ 1LbL|CN4O<b!\ReFWb 'p���NUɈ �KeNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\b2L4LF`Aa9+b䴘j<!Ų0֝]5w#/'W.Zbi2ɈZbԲ..XN'41<$q O#W1|CZZLF0FXA12YqM0P 0Ay| S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL#D`119Z#9sL&b'.LOd\RF#֘#dxi !LC0Ge.Z'LM*# &Ȍ֫!6,`1}]AQ0.Z#$LFUZa 'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brjb521i0A2je\k# #JNĚ#(q8F9lG&FZCL˕Db+*UMD0 2F#hA221 Fb a;<FS+/AQr1I^WY]0b5eNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\b2L4LF`Aa9+b䴘j<!Ų0֝]5w#/'W.Zbi2ɈZbԲ..XN'41<$q O#W1|CZZLF0FXA12YqM0P 0Ay| S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL#D`119Z#9sL&b'.LOd\RF#֘#dxi !LC0Ge.Z'LM*# &Ȍ֫!6,`1}]AQ0.Z#$LFUZa 'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brh���sɨ!j)#�LFF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCK@���]ɨ"c *,�l'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brjb521i0A2je\k# #JNĚ#(q8F9lG&FZCL˕Db+*UMD0 2F#hA221 Fb a;<FS+/AQr1I^WY]0b5eNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\b2L4LF`Aa9+b䴘j<!Ų0֝]5w#/'W.Zbi2ɈZbԲ..XN'41<$q O#W1|CZZLF0FXA12YqM0P 0Ay| S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL#D`119Z#9sL&b'.LOd\RF#֘#dxi !LC0Ge.Z'LM*# &Ȍ֫!6,`1}]AQ0.Z#$LFUZa 'de tu5  #`UjZV#2/J14bzO1m^G2YCDeeHF,Fű5i QbtxLd0Ai')LI`AQ8C2brjb521i0A2je\k# #JNĚ#(q8F9lG&FZCL˕Db+*UMD0 2F#hA221 Fb a;<FS+/AQr1I^WY]0b5eNFak!UbFQ015d#/2CF&!x81jb0$LLeQMdey8Hbh!e1:a!ZbS$2ŭNSI\b2L4LF`Aa9+b䴘j<!Ų0֝]5w#/'W.Zbi2ɈZbԲ..XN'41<$q O#W1|CZZLF0FXA12YqM0P 0Ay| S) &LBLFeDa jxQ4!]a FHbɉ b[pVH蘼/Deidb&Ř^_a)0C0#2YT0SQ&^NFu1 !0a &#0A#$a:UFC&C#&O\b4 #*L!i#iK&#k'ɑɯQ̡F#-4F\K,&Z,FR #j20!FLb e0a6&LLrL���ɨ#dI}F BN>Q�L`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ$`05d'J؛dHbudӉVLF!eI0M$`)da#-d19215191}ue˗2eR\!b2\FF`2<CTɑA !L&ɉa.Ih`&'+TbG.b55i]LC$ɉ *H™Dbb1218bb!2aLeDɉDaydtC1;&Ōa/C0CJ0C&DdC ʡXCKL!쌣ЁAb2ʱ9-KJbFEiYYF&LOI42t-K0hL^\xC4&1=!1 Nɐ<L22\)I4# 0 1'`Y8NM\FF 11FMLk-da9#iX# ؓDaeN'\C'-hCb2irhL\eJ։Ha 1uF^F! C 'b'eep1bj.RF)2k#+֌FblI2ш #-r1j0LS#Fq4&1LdefHb2uhӄ4OxGF-TLFĚILj#' M2'V#\0CL]dFT#Ri4ˌ_rvFC FS)<#Q 0'%b0\-R0G1QzӫejLM&Y1C LZ\#%r'd!j/bk\kI0F&V#+.#aY1/ACe20AɒhC Ќ#az#-Y#/02k ! ]21<! K`J]#E茭#,CSWسk5L 1axFYk*Abj#Dk!!2؞C&b !Դ���Ɉ$JX~YZ-Z|ZqZ IY6m.*ID"!"""$D$IU+mTD!!BD!""""*Rm.I"$DDB!D!HB"!"$BH|oRR""DDD"""B!D")$Uwo뮩%$DB$BBD!DBDDI$I*zIH"$D"D"!HB""$I"J_~VE"(BD!!D"$B"$IJ꫷ϛܭZI%"$$B$"BHH"$I$JWU{~VI$""! DB!"$D\{~~vJI$I""BDDBDDH$Urnogo}$Q"HB"DBDD""DD$T۟\I("""DB%EKIT))"!D!B!""$JU\{*H"D$HBD""BD$!$Q $v{{QD""!B""!$"JII}{{~RH$DDD""D$DTRWWn~I"$B!B""$It{(IB$DB"BB"JIUUujD""HB!DRoongDHDB"!"h~J1%"D$B"DBDB"DJU_~ݿ~ᄅJ$D!""!H$I%*m߿\T$""DB!DD!DHHW}߿*$"$B$"B"BD$DE"DTU]$DHBBD$BDHQ)U_vwDDDDDD!B!"DRI$߷m]RJHH$"!BHUU{߿$$$DHD!DB!D$DHDR=T$DQ$!!BBDHDIWoߟ7wZJDHHHD" " "DHH߿ﯨHI"D"!"DBBDIJ]ZH$DD!!"I(o|IH$DD"!DD"HIU{?~﮹$Q$DDD"!$!! "$J$߽RRR""$DBB"B"""DDI$J{{{{U$$DI!DD!HBHIIU_~}iiD)!DDBDDB"$I"D+$HD!D$!""HHo]Ii$DH""!B!"""$DDI$Ww]IRP"HD$!D!$oo""$DD"B$!!$���ɨ%v-P2!6;)�Bd4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#IhFF.KI#[( =iWr2y5r&,!-K.a9dq SLC2G5x15\ɋ`e#+0, PG !2j d4!ɄhFTF0eCI5܆j.\Ȇ%G d.FVF!lYbC0<#,J15ja a LGSɐlO\!1^`jZZ0CHȚ2FZilM2i1:2iɫL#B 2^&0Fb0Nqb2CDeˋaTeRe.1V##0A|!dd ȆY  `bdʰ$4F0Cb1 #1bi!rIʕ$aL1ZY1qMi1 FMw1~Ft0xZqdҢ0a xMj!bb0 !!!b2L!ɄeP!vFQ`ISZih@ 1FeXb1#"4#L^'i:Cp%M4F\T/Ja .RɄh!aL[V'LGda[&CVLHr Q0C,F'&F#S#`#&^˖04MlI0'!krke14\Y4M&.RQkDC S$b0#/_#j! e28Oa15)#eq5؃ F#]1 QhabQM&)e# a8SYNɦM22$1:bi'<##&# bM$&Y5FWd&NbF^CFZb!Ŧ.2L#*XZ LMd4e/;#!h#H���[y&'VU�v\tGRyVknIHzE1OuIdYfT=OwoJK]5KSXjO5Ovvv*Xޢkk5nkYLS*R%ek4b+xrGh9RTX:NIRtg'NE̩")܊JRtGt3tgk'If^e2ȝJ˒܊QY������3EO*+;ywEHLDvsyr:1D),XEek[.nuggL>GH\S}4'ȩ}YYS[S-OSR~޷y )<ݕ1̻K*Y>KQIN|;KSGe"V+]u՜EIRG7]I*+^ˑtGe#OԖk4}S���o�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/stripped.xm�����������������������������������������������������������������0000664�0000000�0000000�00000001132�14662262111�0017226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������title of song������������������������������������������}�� ����@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/sv4_header.mpc��������������������������������������������������������������0000664�0000000�0000000�00000000200�14662262111�0017546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������' ��.'RK߷S1fYLbeYe1B ,hn ^ٕh3隩*\˳K|9vqZr+dՏ؉�ZG,܋LvkJÜ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/sv5_header.mpc��������������������������������������������������������������0000664�0000000�0000000�00000000200�14662262111�0017547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/ ���v�ư@RJ)mj)fc,1B,Fc./y<a6WzDuu6W[_wywgrJW\ʘ+?=k ^]qmH7I������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/sv8_header.mpc��������������������������������������������������������������0000664�0000000�0000000�00000000162�14662262111�0017561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MPCKSH*)^�RG E}ZE}ZEIQ�SOD����APAPAPAPAPAPAPAPAPAPAPAPAPAPAPSTSE��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/tagged.tta������������������������������������������������������������������0000664�0000000�0000000�00000237633�14662262111�0017014�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����_TALB�����T�e�s�t�A�l�b�u�m�TPE1�����T�e�s�t�A�r�t�i�s�t�TIT2�����T�e�s�t�T�i�t�l�e���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������TTA1���D��z�)RLZ��RZ��Z��'��` _��������������� �������������������������������������@� ��������������������������������������������������������������@����������������� ��� ��������P���������������@����������@���(@�� � ������� ��������P@�� �����@���������������� ���� ��(����������(��� �P�� ��������P(���A��@!������ ���� ��� (�HB%�%B ��D���@��A����������PPP�`�(`�����P@I�P�P������������P(�@�������T�C��@�!PX @a� �����@A����&���� d(0�0 ��L ������������� ��@&" �E�(EaPtP`L���LT� ���E��P[������2`����0�������`������e����e��$@Y ��ˀ ���d���0lr1���I�&a2����`������*�� e������e��TI��LR6 �e�0��������&P �� �e�Q[����0 `�*��������&������`��eT�`e���0 ������` ����e�2�*��0 ������P6 ���0 ������e����e�@��� eP6������LB52���������P�L�� `R\!� ��&�@�`���`����(��� �&�P�2����jL`�ʘ�e�e� &�����Ap�0 ��P ������0�������Pe����L�� ��� �����$�e�0 �� 0WRI��(1a�(���I �dP6L� �C�� ��eS �����I������IL�0 Pm�2lj T����@6�L��A�  ��@5�� 0 ���@1���&(���I��&P0PF5&6$ ���(T������0��`�0�!L`� @Y5�������ʀ2�j��d0$����P)��ee!L2j�`V �@0P$0�L��L�e�3(l@�T���&e��L��ʀ2�`(cB����(eP �����L� L2 ���&@5 ���PV@5�՘���@P��( �2p'!M `������ �����I` ʨ��jE e �( � $����P�! a�`�P6\6'` ���*� �e@���eTC����@�T � ����P�@A�0����`k*aj�P6 C�2���� (����`!@ �0 CH`@(������ �&0Y���� ��2�P���L`)�����j@Ym #C1��($ �������(������ ��� �<���& (S`rɐ00 ��`�00 f �e���PF2j%L5&����P2��a�$@x!R2TP������`” ������'���0 �������&af�0�LA�PV6 C`�d���2�ef&$S!L�! L2l&I������T d& 0$�������0�����A�� a3 C���( aC �� (ʀ0����A�� abʨ`@�! �������� ��(��AJ������ �����*V-� ����� ��������x&a`Ն0@��0�����T �0�0 .� �����a( OQca��! A`L��`j ���� �@\m$�&a��A���(������0!yõ2@9aTl���\��0! Pa&a�L��������@6Sa��IՆ0e�I0$ a�eCLj#a0 � 6\m&eCjL2d�Ae����Pa(=̜ ����- y���( @5�0�� �a ���d�$ a A23��P�$Px�2&'I�� R6L2@! ��0WIx�P)0�P)0�՘dL�.d!L��0$3�Ax6u{�0��Pa����` Pa��������` CH$ Cr$Lep9 ddM��2 d0��pf�A L`j6'S[ɐ &$3����x8<$O5&gAI�PF5D0�2<'3  �����@6!L������eh 0d g&a0 C�A2C0��I1WIfH0&a�۬$2Hf��0eaA2C2HfB$3\MƩ@sj.r3a'dj�*aN<C���PL jC yS! @fN23(Y �mI"0 @$3d\$p2(0jF L��&0 rmd&��@$@ٓ!��� �f0 ��%0a$ aL&a83�&a83d3�� 0uIf!y<3'pm&C9 s5!L2 A��P69<a&gV d&��!Ppx8 �� ��(0S0IL�ʆ9g����LIL�9IF9I@"0��(0Ix8L&@f������A L$L����&7N A'0a83$ g& <O���`t>@pfRa298$$��\md& !0 3C<Pm$$$ 灘D���2HL�&arF rQ&&~֖̜@X��A2\M !g&���0 3�����&ŕ!e &C�&arf093PTd̄ ���0 3I�La&g&Lhd$393��d&g&PIQF&a+o)�Wb P Ù A2 j2uy��L 2L`|m d!L�d&g& ���0'$Hfr2ə �Pa @!A�0 3��2 3/<ʮf2d&咙s�䩏;9AJ4��F2$MKy `reG'�&ə��ᓧ!L232(f9G������`lO MNq!$'&��Ro9m9J���L.{5GT$Dd&'�������$393�����2yΙ !a�ZkkC!TPcAjfڮ�$393���I3��������D$Նd����0LXL'CjCxIjcΜ#!�fCޤ<M"cEC�����`LL ����(0\0\a�dx>v &Pae�sِ32O29 �3`393�� 3932C���(0 (sِ!L2C��`LL�L_z83 �1W{ ��0sfG� ����ˆs'A��������PA!��2��030sfC(0�&dR3?*V'3.0�`LL�0�`LL�������"!�F@PF�j 3a&g&eLL��00L0m0��@V M 3 $arf������d@ ?LL������03sٜmɑhE$ j$<����&��rf�e$<2sL"�2�����e3?G- �B��*Vl8<HxfrFF29�PFC� 393`<<L"��0jgC >[�T0_#P  O"@�&Ϝ?ơ���$#E@593` y'�&HxȄarf@8g�@ ZAC��ˆs~03`.{nYoMjbO1Ih`1HxHIyGʔSo �) *P[$Rf����0ayQA֖I����� 5C03'09y2sddp ��eEϜ3 3j@����`LL��ly8y+ 2,19����&kLq@��*sIms>A0wX՟)y&�9<�e31VE�BʂǾ��0fRTQE���|HF$p W6��!DTE`6syjD��L2GWA`p3g 3� 3k38���PFa,�2rxC�`91G@�*:L9�PV9aʨogY&y �`99@�� -'93���(#gϸVU��a''�(#0a愙9}Th ����@9<0 3���ԍ99 G�sp>!-a@mYTa1hc@f�M*"�`9H9zpH1V*"�0 V{sO*B��cA3g#3j 0s&��� 3��ϸH|8 �jTj�PFg8g&��f|au#HF|1��`朧�����@F�@2b 3gN0)?xUi10 �j��������Lxʮ8͢ 2B͙�L9̉����(#�fy&����@9<dv&y�F X5B�@3(bD@3'`̙��@9<L9�&|`@dppŪ!UG�Q,f<Kb 38"ky5;0 ����*c T��0a?qU#�@5u)2(ja='Cc$ Fx��fy*�șt;<P9<�P|C̜z �P9<��������v&DI3���`2·*.#Rgl;Ҁ` *,�d�Pԍ*(0C|mO>8����33@KX5B�B& -@�'̜3HfN��0a?:NَZ5 ���$#23���&68P6uC"F08<3ϙ9mcT8�������(#�3 sSC9aΜ�����&|15B����H|ꪣ1h`A:8U#77Ta@�,C}C��!���0gfU" ���fy*���X>YF�7=h��,H9<33kGc!����������9)sN�PU#Z/wi@��d+VBD GcPa@0V"3u�����@9<!���V9a�90ảap9Yi9$/w` 91�0F¤vvT!��PD̜��<�@F:Xe *زKPĪ *����F?gOZ=0R9fq]�b�BQ#9j޲ !b0mLErV2"Ӟ5�cPa@�BUqs �TPul€` %WI��f> �&Z9$����$#O VcPSvcaĪ)��aŪ�� 9c@3Ta@�� kTPFGle5HAԤɁ"T{$X 3, @c€�sx�����0X��PX!��Tg�*xS3��$0`3\X5B���:P���*X砊�� |  _�Tg���$|?V`;Ö:U#cmIP'Ϝ�8jy@�"\�<35|jLD 膧p:Aj����:j�����@OcU������|Xkݡ[vV �Lnha1Ta@��������1j � c,g>F,EDYT@��������@��UJM380S?Q��0bPe\!0*Y]u����z:kWX51t !I� |j"V��P9@@|j�����q,ޭ5z˂00b���8Y\8$����H,du1!2Ta@����RiF nIa.0 ԈFQN91joX)ӌ}*�����@8ͨªBTxge!e#Vh\X+}v1"���0.�����@���ƢbU:Jƈ߆~ْ,V!eXC%Ee왾# U81.=eF)[0bmg k}<֊*`vb@eBT1�F!rh=fע,H�BjCFa@c up:F L+1@����s!pp:F@1ƥǸjDc�������U.F����`q:Uq@08jfb@@#V�2I7U�����s9 �HVy@̧F�PI~V����Lx3.v*E@#VO9��P*FŚ~e*D���e3?��*v-!n3ŀB>�T_݁W|U)SU����dE@F`RMKc������*XuUA2ƨ �. hqa( :B����@c4PW}��0Zq@e:x4U¡8b1i���0����,p�bExY �����G]1q3wiBWLt1+-U:VQjBj���aq@�H8!uXx��@c|spr1jDS.<\ZA����@2QXc]z !��d#s�q.�񔍺!X5B�`CF��� ������u*~jE ��V�����R8" u#X1b1ZXcTXu#V0b)FĪ#��0ƫ>UG�������d.��T/ĊUhĪ#���@2Q���`1 �`QGisAxueP+����"vz�0b1 #FD:B!XuW}ejĢbغB4"ZCVbĪ#��cXWP+4Zq@Eօj�`6u<$��ª#c�����(ZǏx@��`q������1*jq fU 놮8bF}YUZpuQ8 h *Vz"b`ݿ^ǃDH(вA0Z11Պ��Ʒ ?LЪ1` ���1��PQGh٨/)F\uDcvWQ8[w>������@F)T#P->���HVX1ԊVQ1�w?E�P,���UG#C8PD#uSp@��0�18̙[!C-:úl}AěfRa8 ���P[X8V �,C[Q8 ��c Ke�EUG������q�$cq@@G#z Bj]UǨ9]j��XZq@��1 ���$bQu����@Z������ uiv jq1 @Oٳkǵ����������R85pu���c���QWjPxG\#pB(.A٬Q/:@����@(:ЈƤP+���cq-;_Hň=h Z eYX�@(V!���Fak;ή I���rX+��j1 ��������dG]PQnp@0��TUG4���ԭ~UG�� 0U#d !�ưVa8 ��R7 1B�Pţ�cUG(v׊�c9 � ,����8"����ԭO0Ckq@���W!� ���5j#�p�c��TT/��������?> @P֏ǶQI)aX+�p#c+~UG���PW!���Pţ������d����8����@2.U±�:<&Y,���@ l"H W!�1�����8����Rt^Z)U^g򺆩‚P:.]W�LkEP���� uG���� uO^:B��DUG kŁa2 Z6֗~DafUa`+N!k  *R0 ��cX+ uſǰI ��P \u�����$82g]-z6~{wĪGȲA�����@88 ��v=õƈ=/cmg*e/A+f&$$W!����Z�d<B ���PcE6~џ ���XEϰp�,h����8Tz xMl1Z�����4;��@Qq#29�6RƸen=HT#d֊�2EZq@�������@iF= ߺQW��nb@o Z��a8 ���8,Zq@aҳ8D�b8W$����Y X8i8в֭B �WcUX1p������ jc\2Ep8 ����@8F]u*:B���QqiZӬVP !�,:k!��G-L��8p8Z8d!v]r<`D������(Qu:B�����u#  ƈ^T�ٱȴp�an]rV!XA֊ !^xĊb \u�����Hሗ~K#qb0rX+�,ZcUGr��������:.qq@�HQ�ԭK?��QqS&��`֊`�����:C8f?_r"�`1&aR �� =&���JW!DĮƀP4ƈW �� fZ+4BZGq[D >A�í  :a8 ���Haq@p6T;a8 �ūZ Bq#8p���@E]<Ł���]z<$��a-z1���>^.J ��0!:B���dG]l 1�1x@eX+���><ZQN.v ����+EAe���c!*pu�������Qq�qFD#d֊Egz|�����������P. Xq@���:.���5QXqK?eưV`,^8uUG�_*:$Ue[I/6K$�p*Xqe@t_0�����0`)99@cXk ����PS֯gkAE\ ʀ�@(\u� ޺c2,�ZcD#֊��qiVkʼn ���L6;UC,Zq@���?`D^,H������jq88Mz\u*\uu?TRZq1G8pA����KCF]:B@Y ��� W֊a8 �@~PX,cX+�Bq#dY-㡫��!  P4U9d k0Kc#��� zv4'$a%nY %����������1 e!���N}���Q۳p8 ����@88 XDZ��Rqaή]C^H�Z1ezc$�){ ��,Zq*<jm@T�l+;] 1bhX/A��������@?ho@P€d z- +0]!��m6_rBBARk4 ��Z,Zq@�� u<-k!dbʪ^l������:q@��R4;+����|z��Lk�Ԩ :B�B0�� ���l ���Q- - �DNJ�RDZQ8𪃲:zj����HNJ)ƈWİV�2]��TW+:N1 hT ���^u��QqUG��������5c!��5«@ŁW8#�@(|z���Xq@���@E]|�� Y}݅z|UUh( h���`֊���F]|�겻B� a_X8D (z=rY"���:158#�@=;��b!P<5h7aPX* %�0�����:#�*/�������"x %`�*�Z,Z`֊Zƈaw c`:N5Y㠊 {`Y#*h6SfW=U � ���� uG!*F |zŁWfܸ.:he �������H����d<zAuU�õ� z^۳Zƺ�������G�Pu=BcV<@�z� ^۳x-z�Ƭ+| $�0�1,-ZGpx@���@Eu=B��cV|H^յ����W]9,Ӡ@K�KCsVh�������@dLC b%Z7+P@o{CxOt���\r&�@I+4�, 2R(Kf^l m[5' Zdi Jf0%Yl  6H��`L&ӲC^a ,6� i��MK@���%LPPvԶpM,6�1x[` ¶ RRSrk ����8G������Rk<�ŁW=Bt]������(QaVhN޶$�$hL^$���� 64K@������ 0-m ��@P/= $��0eI�0R+'+AѮFԘ0���� fв t]꺢gj�����BcDt�����PQ��������HqKʵ %M  ư,P0`��@PQz��1 u<OQp`8@��ٯ%�(-1\1+������:>(1�U ŁW=B���� (<ǵŁQtV=>BCFs&Aka� V@���0eRHXa E=W�aZ@� (  @ AErQi@���KNtDaZaZ� ���=ӿ.I�` [e Chia��������iIi¥LA0v2à0�HdV:B���p͎kK`xK ail��1P%='U�����@EU����d<>r2���r06,opƕؘ֖p=ņ 2&����� 2ò Qh%i�jg}g �p-i8UA8Z,ed!%$$������� _:WRþ3(P% ��8Z\.qZ@��������P'MKaZJ`iZ@���ƴ,iB++.d�`L˒Ro`�����ư,i` .7,[dH-L ����@09 �����@Ppѐ.e „K��XiNVf ,a����L1ZZ0ۮXW ��������*/cLж.6�( -&r$$� $���� ( MI-L ��@Pp)SJCPvG]q���K@$ ZBI@̘6<������ 0-npeP� .eRN'XYka����� 2%Mi �r �(eI�cZ! <P� #vl.6���m�����Dƴ ���D)M���� lP°iyii2`DqV E&֊��` 5����Jԋe"eI�Жa +oh2ٍm;L&�����ƴ,i���„Kw [!f ���Aa¥LA�����il `1\)xVFޭ4㤵0Y`p) � $����å $�nvL��TL���� 2*Vm逢ƈZMxX3I$���QW=B���@HK�,LpT ITs0�⤵0��Ŵ,i����@IK� }6|6|d4f ���\iJmAՆul���,eISh/K` $@Fl6�X Kxp{-1.p [vL��l`ih4�YbC(4f �ˆm7: $ U @���  i]mKzM z_ڐ�Hhr������ІPJ�@2iyii�����cX4A��cX4A�� l0b r �p(_VA[vIr��0ee1 ��ư,i���hNM�������Pb �� (CaY�������(Ulր%LJ+:B0@|hXD 4f � .6lia�����cؖ4A�\biT,;ކv57m7p" ��@{M`P{��( �����cؖ4AA9&����T/ 6e$��00�(a܊@h- m&۪9Z@���@aؖ0(m � (L`M$���0mI�a( [ ������ mK 2˵Z"`bl!Q]d2і; 8YZ@'K uܰ|=KXKzl8lia��@X1I$*m f^mGl[e ��� D��a[+[ bؖ4�@(/ i&����X ے&���������J*[8CP(W9j� wr\֦d��a E 6I!=H�������ʆK9!(Lv  n6! ���`1may9nw@0[ZLD�0ae �wU_(LR�\l"!.6�������� 2�������J(ndpr8d76bF⬁D 4&.6�������ʆ[90mI����Q-MH 0^�������(lC&ʁ���������%$��Z&�` ے&((LiHfP(؜l- �@ n6��0F{KtLpMH9Z@�H`6f ����1lK ��������B8NC�cZ0Q3(4�����c4A�ak0M$��„Wnh:/- ��������@ն>P�`1lK� vb ������ n�1,ჼD��,VR ukhp�[9LTa0`i)QM$@B3(4��Ű-i�X ے&��&\dwж0� 4%l"�����Ű-i -Lp*7,[a�8l- ���Jp+ ����� Es[APex]������PpXha`ia� '(�Y7' Km ��Ű-a��A:L4lY+ ���% &I .6���@(iZ4 wm)�H�Ts0��������������@T M� S{ne"8�,rR[9 l-Ll0&�P-/vDL @F\l"�bږ4��Ŵ-ѹA6������@T e [9���i[B ʁ�������@I!p&����bؖt���8,�@([wN-F0Le5\h`Zh[@������t _^Ʃ&K"XL"��&DbɾX-K@�����ö`H������iۆN � �% ����� q؆  l(g߶�`[4C i,i�0r ���������@Ib 0ʂԺpY@&H .6ٲAi+@@ .6����Bce; Lp Cm0$P,K@����� pM ����pM �@NK:�(%djQEs-i���@(ရb,CUs-i�������Br8$DBUlX~{Zx����@Cyi�eI#L�� N M"2��������p[2���@([9������8lá`XH �ŰmY 6 ö���B0IAscӥ���������,muϠQٲ���������LP(�aۆ99† |ML$8X4��Mp`ۛ2:% 2! % �@Fd"�@(�/2�b[H�����`1l0%"2l{`8X4����t-<A�Aۆ'y i,)lYҠ4X4�Mpsstit& �����bڮmK9e{ &D�eIdit& �������iۆ B @�������ӧrvvcö@q�@(H�ö���J8E �����(\Yl[�A �J8;\~a7wJD�P0esܮsTU2�����J86- "dt0\cpit&79Lp�'˒2& �="D˒�������()C���PR8E ��B {`=2ತA= Am)ZyR������%B nda�@qƆmK��bڶ4An2!M% ��%p+d"��Ŵmi=6H n2��� plY ���%"PhL$@U4(v Fу{Ke .,d��H,K0[4��5H+-rö`L$�������%Cbږ4Mp&x%s79ٰ]Nk7H�BjN[$����B @��,mˎ鶤AhrLZD��BIYl�2& ����`I�ޕئE>- eIDPŲ0��(NeHa/ Mr)d &D�@FDmIazyɎeIIn2MC{+)0+G|Lla 8/0����:&u����`i)6M$���XLmJa/L &x%+Ҷņ4ؖa���֒[#Jc P���������@(7ۗ'ى������tWXTm0؋L$���*Vm&)9Leܰc-!�����*m �IWH0�(NFA܏Mvq0ۙ!ܦC0!) E$"���������xaf n4@@q,��%" d͍]mI8 " ��Ul/e"vb&"���8Z8 " E2CF�������� ӢJNHF�$]4� &xqQd8X%/A@"!,qodI���`/2L�@qU9,���]/-f_`m�����bld/2�����xBIx���}$t0E&� K4b- ��@(7hya0(NzI0VH���@qrY����`1]1ݖa������Axކ#L,e [n6d[Np*0^d"!I`H,̮Zrk$[D�������,6Mb, `a6@����@ͽ ����fcn���( E&���� (Aod[������B7vx[B0Ds a%k68ФA"��.0( nixD��������� 6:2 `�������Z8mӄblJ:ȋLQ2 $2��Pl0Y5Ǝ$%jv6"!I^d"����@(ZY` C($/2X8ȋL$��e0���nξ VXT{)\ai ������ذmtJPA -L"�Eia$/22 7l[Za/d[Aa-1-��������@(餗v:U Z1/9Y8ȋL$��������� gܗ LI���%&`[������ i/ vXQa  ��Y8ɋL$��b-Cx, P�vކ�����p٦A؇(� IkYfWh��@V� d%T]g"; Ha����������"^`4dvvA$l$��������re æ)�����0M�X m:48ɋLFy�����,ձryDq- �@FrM$�E&{������`1\i��@(78}DqrKAҭ E �Y8ȋL$����������� ��jt& �,6Mbe\4�������jM ���Iov]LRWYD0��������D5N4��������(CD$r. ,A 2 b�d /0������� MOD�d /0��X caH"�����Io8Y %m^iӲd/b"��� y'2 ©\ 7H�����ݒl ��� a5H��t:6l}$h �d /2�����e&����blA������%v%p7:W]lJ8ؖvc"([8#�����Y8ȋ:m&)���tH�2 A^`"��%&�����������ZtT)p e��� tľ\`J"m77\.าPZd ���J: &�*$/0��� o ::D6݀3Ă #���-"uLnE`;~ @��m������pڦI,6:/U��������f)-}F����M򎛖] ����mnd���Iv�ti*/0����� t^a�% n0 ��,2 b���M� tW$-2HK����r$Odd�����*L%;&ݱr&%+)} IL×)D �����*w����@qRdSE���nKvLn0��P;~>@AѭMU\a��� e ��'*M x H$�Y0dd����`aTQ&������Io8.2Lb�2Lb�����������(NGFcr[2LPaS y/  ]0]\".7D-` �����bxLK{Jo6����Pҩcp[I ���,6Lb�d`/0!^ʳBPZdaoidaFeZ�������AI'"? nvIѢ*%@U0,&2��]6|}Y@����Xna{9| dQE9���������@VvA����@�@T0r h8!V2|/`"� { �@qpYI� d�����@(Tr& { �^`"��%7 #.0 �����%=`"�m& '{*.0 H]K2c/)hoؘdV$3f6IdRB��� `[b "s����S{[��`1ܶaS\a0þa(pe88Ld��2Lɾ2 p^nɤA*FCѠn$_(Efb��8T$bibFϵi [@qrYarYarYI ���, &8,1e& '{ ����� pd.p4W^)c_QR8Gb�������X m���Jz(_(^P(`"����+ E2��BIF)MD@N��� &2��* p7�����, hi 4zh{GU$BÙFXn !d_XE&AFE0,$e�����)Ӄ}#G6a �����T23�f :n������A Д2��bm : d�dt7��G"9 .6I$ ��������� (‹Md���vT| i-'H(Ⱥde&1����pۆILq>7C3�p^[$��������������� al LI @qrY Z80L$N� �^@������/}E ��G$LqLD`스(Cy1D 1:������/و"A,U4vMl!m@@B v022������a 21222��@P ibӊ;$zE%XKF@�ƙW(1����EeR`Q 4goi6 !������%</0�������v6,Q\AAF�2:Ҿ, KD��hb;H�hb;H���X6A4XtY&*6J $������B  $�Jx(_`"� & 6M"��dt7����� (| M�@(ipfs"dt77wK3a���d4 5 X₎jd@8XtY&BF{`U29nA`����+CE[R.i }QlA!%h@��d�a���� t-L`4(.h`\�ɢL$���8YtY&Ee"�Pam$6M"��� P����bmнVq}IMY0m%3f9<xS$P;-���ࡶœ���@%D$dI~ NaWHpmA`�U2BF���d4 de���, NEe"@FdD�Ja՛(�����������A[2 V]`e8VͩDL�ѨYMȈ�����06 i 0`"jEB������@ٻ(qAwQ I̊$I�hb$#�������� ��@&�������xx7�(6U2A H�&ԜeD@q6MNm&m&.Б]bH"���dt/0 ������C&�����������Pr[mY i_YD��������B a~M| ò|bo @y!/@I!��d2*.Ht$ڐuD���������J @�20m$�������, .cY&[e"����dP};H{Q$ P �(Nn]6e�P;%:ʊ��@qABg:` ���@,MF$ `.������"x(@qXuY&�����b0t$8yUCi�'mD���� P�� 3|," ��rwNJ_*IB�����:_Q$bEB�����'.D�������@{fp�(N6]������������PY4Xr&1��� ��S'@(ɾ .im&���4I2\/Ib@v$8tY&���@Ez*_@ ��X /4���@(} ����������Zةi$�%< (^i��˵[v or-</p QܶiP����p(_hn-) �����vH6m$Bʗ0CR8}A  ����������`1l�@ v8x(_@���������% N8z=M{GF@�d Q0P `D������� $����XL/pj���@2RxoI tbv ���'.D���������@]> 6Q$�� @����@Aإ Jqje"�Meb@/4PlA&q,q+��Pl,���`T| ��v<2�����d2�b3b(�� FŮ4opqİH(S� d3I!��@</#m<B7,D�������2�24���2;̚ ��� �(6UѨ`R\���p;8 ����UeJSl,���tcO_ N 2��T ݽ8<i*WPK&£;a7HHf"'[s!]UE[IIPܖP~a3 �нetL{[I�δ_Y P.4��������()<7( VUlUŎi/H����������,-]e"�@L( XU٠Yףr~ro$3`pQW%I����(N۬~hnHF"I�������� (}����]w/Ad"@"ݥiAG񚅈�@ �������� (AqC9VU ��P,��0 * |ShmKw�������������������������������������`@�������� ����������������@�����@���������������������� �����������������D�@�����������@��R����� ��� ������� ������(@���H � ��� ��P��P@�P@����P@� �P @��P @��P @@��@A@����@A@@����� � ������ �������(@���(��"��� @�P2@( �����"@����� �� P������@EP����<$�����``�(�������@ ���&� (�����I(�L��`.P&`P@� (@�L0�TL��$� `&�� �)&��((��L� L�����L���\���& �B$���$PHQ�I@(�BT�(@Q���I����D���2���$��0I���������������$e�P�L���I��������`������(�&��$Tc��&�$��$�����e��@��L��@Y��P$ )*!@ L0�ʀ2�ePm����I`������I��&T�2P&2�`0���I��2`�2����ee�C��(���2����������&P `B�����0I�@YCj��e����`B���A�������0 �0 @@Y��P&�0 ���$L\ �P�����L�����\a&A��2��(+��&)���������2��(&��@�@��`������$Pm����(c���(c� e@��e�����e�����L� ������������P� ���Q����e��P��2$����������������eL�j���P$ �0 0)j��e�LP�������P��P����PV���0�`�j�0(���@@�20������!����jS7 e����e�e�����@�����s5&00@��ˆ ��`�`C@5 &�2*Q 0l@��LB��&�(C���$���02���2�&�I�d������@P�LR���$e��Ap�( �e�$e�PP�eL���e�������L�`��I����� ����@m�0�0 L2���L���`2����&`�j���$@���(�Le\F2 A�0L @!�$�L`e .0 C��I�����$ a������IfHsjC��@5&@���������P��2�22��!Ce�������� ��&a3 C��tld��I0@�ˆ0� ���P$LS L�” <!�0��La�� ��&�����A!<02���PF0����e��e0@����@R[jL�� @�@e�e@������PF3 LIfr$ L`O�”A1 ��P�����������L&�0�P 沙Dl  @sd2!LPF�l�`I2C�(+� &�& ː& $@P�2��P �& L0ʠ����P�L`Ȑ!0x&a���������&a&j0 3@���I.@ epe&�0! ��������e�e���ee��@0�( @����&aȐB�`�&a0 C��(@L6� ��� Vʊ `�Ɠ̐2���2! 0 I������02�Pa&a���0 C�'g&a�� @m\mH2dH9:m��I:6 ���eTcLL2e<9$ ����eʘd&a(-a1AƄ TI0 C�� �Ax30!��&n2@P@F2Ԇ0���(0�C2��PaA��ʨ�(+3aC�<'L���ef������`$aIp� ����Pa(0O\ !$ I�0�ȀIxrx������ ��2�!C2! ���A����&a� aq51!ruI ����&aL'0�0!< !3''gd̤2�C�tpj! a��eCjf $e$Sa�$�� ������C�If������ef` a�1aP pe#=H2Lt&a'[0d0@��=-?\l <$@��d��LL C2&gjsxr����j#@0 ��� 'Ϝ@y0�� W{o$L0��L! Pa�'a �0! �A8 Ʉ2Hpm0epf��(Pm8<&@d�@f�0l83$L&a�����L L �C9 ̜ L „!<A53�(0��(d&���Tb p9 ���L&������@Y� �A2 px8L��������2H2��-��`&xrxB!LS a���L&2 3A2��(dd\mg<9HAmrMdo0&�� v0b C�� �`Bm�������@$C v 3 �0A�\6I�`CO<Lj#0aAB!�P[&�L`L2 &a@e2���23���fns0 � L 2��0!3�&aL�(0��& �������eCI3' A0 ̄2d&��L�3��Ifrf����� ���Ն���2CdgN3�&>-h�~83I0a3� ���2C����lIfNmpmZ4Yѓ;} ��� ���2ld& �L`rfe 沙 Qm8pF9Yo3Hǜ@f̜D�(0\Ɯk'5@H�����Ajeo<&����Pa&��@m Lj6�e _$S8sĄ0a0�� �@Ն\ouc& ���\6� R$M & ���L83$<93WdA ����ee!�lL2a&�0ɄI�@0������j<uc30[2s�Pa&��PL ̤l83ϟzC  0���L`8R!eE3`Ùp90�� ����d0a3sI � � 93&g&��frMadI0(3�`d&�L<EzHI����d&gd&g&���$393$39:e�C�(2HfSo$Zs'g&Pa&<GJ$ ���P!3O208 �AY ������2HfB$3- �fy�0a&gm� A �����A3<G�P!LL��0<S��0Af'yr�&p$C&<$DS6u3sI21`LΨA��s'̵6L2s13&3&$39� ��&03��`R6C� rmde���-z�Pa&L`rf��PT+K`323�mW |#��B0�(0& 3g&������� H`rfRa&ɇ���������frfef��Pa&���Lə09h$Cl>30�Ljxe L09l9g&�L$m���L5HjAx R3 !���|<Gx2HH  3930sF7&y 2 LL2�l>BxY�� .{8<*9(�Hf39Ph��� 3\7 <L a&' pGR[.#ᙃD��� ٧Y<s0LW� 393(#!�e9DE4bHx 393- σM"�0a&g <&$ 3j$<�rf��Ly9!LФO>I�������(#!���vL���0m��� <� <ժZ3?s&(#am'm A�2j 9r5&y`LL�Y[2,<9������P٪c\C�*C[z" ΐXɁ�0 I.)(;d��Hx&L��ʸZM6"frX[<g���LL�2eg��@y�(#g�ə 0\?��� r<��6rx&�m|`��PFkK0L�PVL�!|x>պ#(��`LL�03Cə ��������frf39303�0sݞ ��!���Lə ��P6̫}<0g 9h@��$#���& v<bDfI?9I��0sA0aL"�0\ۓ�I DM&<yK660 � ���HF PaXkE�����/{ҍI�fΙՀ2rxH9<2rxaP;<8VaHŐ2@&̜ə 3 3hEh�� 3&v08Lɡ-׆,w0x2H4�����PF����(#Lə @9<�&<Cۜ;x0 �����`朧�&<<|V48< 3 3k38`Va=#�ad83��Lx8X!�$#?3gI��`Ǣ<D`.6c` *E �����C 6!ik=U��ƠY-;Ġ€@2b!L&-|pU11v)b@U$`&1s&�(#PF?ayΧ.BDM"DHז<8�~p@��HF 1Aoc! d\Q:�j!$6 $#dD��fy&����PF(#9OE9<�����0ϳc,q@�Bs���*y/����`1c��@qU#�1X�kzjYlB���YA֖z<��@œ'!3����&3s3�������2𳎑fRa@ɈÃ`23gNÏ9��a9O�dd,;gR`1hp!�eeϵ�BQ(933<z"����ʸn59 &!30sL���(#'A93H=Gΰq@Ơ! 47"��HFa"B;59�IPa9dD0fA3gp28uÏF8j��@:ƪ2'd 3'��0m>�*L>GlTa@�����d9穠lɧֈŠD"3�(#_dD�f}Id<Μ����0sγ���PF|5B��`\ӬT6 -UDZ,BC����T#":@ �am$͉�!0aE|g%̉����� 3'393&1�fyXR&������*y'A#ԨAq@�@2rxO<$�0F .l٨_ !*eRa@���$#|���*RWx����T!̙�%QnMBH^��`g"ed;z0 �����P sΣ >bƣQ̜���0sΑr� F5 zX�y?8 4)bD Vb����$#s+u 10 Jpu<bUʤ€���!ÃP̣p@�̈B�ɈQ?�ٺ#V��!Pw\������(#����PVP~~"FȚ`& I�!����&̜C�z`,VcB5C���`4 ��QUt%U#��ԨF���:b }* @m)g=%:WijThEZ#!���j5B0yj�X��̙sX#$Ȝ#!0snxJ�̜9��0y?8�EDrxxfQ *�F��`̜T� Z# !�� IT*;-@(#$3|Pu"��PFϜ9s!S#(3'am8Pw .fqC�cPa@:ACFBdI2s"�|F2���0s2G:{]c�ˆ ����TCf�?W<V ��@2`�$c �TxuSPP?YAj@ ��cHwV%D@�,�Ro秳G- �����U1q� #0 �����Tn|fQC#**L(~TCk #N L4��P9<0x#V 2Iz߲aa��aAl����1`X 5���ֿ? e�` U`bzV # X:pkU!��` U!c+ V5C���@kP #����@" XPa3ҷ#�;aJ :5*1Ta[6=ò~ R*,C-5CڐA֖ ]!��bĪ1� Uf p�(Fs���q?��������ԨUXC!X5BA4P�S#'lV땞u!�- F,ň�����Pc *x[�੣F@8y<8 �� u+>u:T4b�1c[vCq]1jЈU#ˆU#�����(b]�����Pc\):["`@E@jF�����UX@2�� u1b8Dz�����*XuU:j���������Tpqpc�dPCd b)FЮB2ղ3ˆU#�0agX ˆUj!lG\5B����.�������)"{<HiF 0hW!���H8 c5j �*jj4p@�������jVQ��P Qq`����Pc!�@2Qj �`#,kN80@]a @cVDDCaЮ ��f:Ɓ�Quu(hbxD1E�����q#Vpb�P ��Q+V!����@A-qpA������j:8XZq@@T!h;C) \q@��XZ8 �T:SB��HC#b��10c \AW}lgc-������������1.�RWXh`U p@�,!Tƫcm3C`D]c@Y�0zq` jLQ1b�XZq@��Hg**�����:Ʊ>5��1b������3Oc #V!���d � u�Q+vvKqT%m]J �� ���*n-:ے(X��Bj Y#~]2E���01t C����$c: �1�uDž1 ���� 㨋6>4N3^C¡e Sj%0 � \z@�������P RFڍi6֊#F,ňXf9lN4b��R?4bHU� ƀ`j���P���Pc!�(:` .t@@T 4 k�*TkDDZq@�����HwUPl7o����C54Zqb�Wcc\6H������@z뱮#������d^{�G]eGe��5tx`Ɔ(�������㈇4h Mj�������cu5ja,CxfX+z PjňUG4zBqh Zq@�B�B0Wu!����$cq�*Vz` ���PS~`tG Z1e9ԊP ,Vqh����Rxʎv8,F:H�XZqC:B����� u.ԈW!������� LuY 1 �@TV���jx����zmZq�aB� c>KA0zmB\vʮ "V4@����0;tphᨷJ!0hU �cW! dgۻG, P+�����U:B���z#�Eg+*FV(k;kjѳxغ!RV&H"�����TˆƈjG]�nQW1�1kP+�u=��czgV��4S8 ���*Xxcu W:1v1.!<l ;E'Yai � V �Jahw;ꊇ4p�����*j#Zq�P(֙~ +vEAl�F6X-Ā���Z<� Ap8�ŰB� 4(G��� ucXIHSq3)D hY_k��Zce cuY6`A�!ưV<PDZ3XEȀ1B^Ȁft 8 ֢c���!:q���XƱ#��Xce c@8H]]֊�NqcGٸ~G0�������x#8-Ha�����@Ia;#�eV֊زQs?���X *9a۟=^nFe)Ŭ: �������P:YB b`]Ua8 �u=afvQl�زT]BG$���ưJCW~E ������"cHx Z 㸾qmcv}1D�ڞm8jHHqm@��$�ai�� Z _mTeX+���� l}i<BW!���1#q1���Hgk�eXQW<W!�Q#JJZ+��Dk=2v-U�� k���gV����*⺮xbBTZ1JZGa8 㸾C`bW_6% �Em)OX8 �����-Zq�W[������$4ӯmD��ڥL���ra8 ����:; Z,3=A`%!��,ZqUq:B��������;+�����㨋�RqEUDZ";qE�2vPvW!F,:ׯ,2J,���� jDTIcQqcUG�,Zq@��:n~(4GK\����R4;GJNJ!�0U+\uDŁ(:hT̪hPQ F( VPk3\������5q=mwE@n,Qqઃ�������QWЧ׆!A���Aqઃ֊����vH)],J!�Ł1��@t6V<Ƶ#T8/:@�����Xq@ mcK� p�:BŁ1ۭaD �������� Z+(Pa a,������6* ����HqE�$8%(\u����iv+ҁeX+�B` ١W!�����H�P.W}e QqUG�����������㨋�HZfkWC�����(:.��P.>gtwb@0W `Ċ:B�������8"�������jU?#PJ 8h+:B������H> QqUG@(1٨ ����Ԩ:Bau [p l��� *2Gx=V ��� uvX8 ��Q%�q<͎G%'A)@�*A���X���8����x]_G;&d2E lJ"���P€ $�*a8bŁW=B��!���������d1ٮjUh k��1 cXٱ^PB5B���ŁW=B@^�`,^>N1 KJX"��C[vʂƠq~ɉ �&r�����@T8QDZYC �������Q/u,, ���j -pUtI',����[n0I���% �ZcV ����uB��5,ú2+�2+��!�P.|sC5Tc8n���:6H����X Unh �ҁZ TW$mH����KNd���`1,a��ò � t UcZD@h���q��C[Ae4S�  $���bhK �����p(S2+`���������Qb1,K8ZW<䑖 :fMR6[2Qĵ MQoŕ@�����0UKjc[&���� n.,Kx@��Zk=ȅW-+EH�%C2i $������Aǰ]Tn@0( [H��cXp]6 ���` m%=V�Cka�������Y e0LA������a(XUCNk\le �Łz�1-���{Էch�P8* �Aq$��p[m ix���� E֊ x)T k1e-"�8qR>KMBDh)i ˅`CZ.4) J��h!H���R�` m ֊PLw$ ڄ"le �-a -|[hIX D^hA)0%Y,Q4|�,6��( BR e2,6 i0(A[hPB��@(aVPeX=cB@ 6H����X X1WhΖev:&H������, ��(Q^5u}LI^v���jih!�@TxiW=BzPx#��BqU�>z����ƈoݣ^񠢇,z켓uF8Ί)@֦A �^6Űqj]qð.z����MӲȉ*0���� 0�XLMpY������J ��$qGl4RA/;H�X\C{������� (iX)��ch[z HC 2�( ae���0e d,lm6IL $�kT0��������i*J 6����$r0�cZ0lBW9x��׿ ����� ]v)��06MKH&K�pH 6 @BB H�����UXC�,Yk4�€L ^�"�H,6$Xl �����Dƴ 2�^A}VVլ$ rv8tZ*/@�HP^$���0eIT 2$MRi����� ( )������%+>v0@ư,.6�1�A!].�[ *S��$q9,K X����TpQ�p!�t0RF-- � .e ���\*S` ו�WaY9(4n]`���HphW*/´@1,K@`  ����Aј]cX0A@PpiTJJ� ���*j(c}|*O`Z@ `@aX0$l ��ò ò "�ư,i` ˒&�� ( )��� 2Z6=L¤4U[y %Ե ǥ&T���T]8} %l ��XiJ 0q R'Xa0�p &��@B\l ���h./aR \l QUl� TaT���������%v,lK@0eI*6.�A6����; Bm�0l0�����dsr9 .FoS{q� B 6Hl6������� ��pJS������@d [�cXZ8 \l  ( vFsr#h41[[Ҥ40(MIkaB "!�� �����/c[ ���������\[pJ*a.WXL ������@ KK [RL9 V m"%)/x0i- �8A6bd}З $ s �������PQ?Ɖu�����Y<遊妵- 0-K@���+=^4P)H�e Ӳ  T= e�@aZ0 tt@���LZ{yRk&@h����� (L)0eI���Ș4Aư,i�cڭrVRiC�t]���QW=B�H;li2H��1+K@����҆AҰ���bX4A�����2-}*e ����������jLS[,i���d��PR8) jS�ò �4tBLu9L&�J[ � K ��0R` ��cX4A���% 2��������"K`]zٻp-i��++8^#xM$@.6���0! hHyCY����� fh]4A_2/Ia^2"e �������x�c|L)�������(^(SYP@BFlBIk�������Ԩ���RC_e �bxKy:.6���(N�������PF}[@��h���^ZXZ0�,eIB .6 ˒2l6��,a���B .e �KOt���JZC X }Lm��heul @f&h͆P(. 'Us0���@koa鶡�����1,K@0R$�0a[m6�� (L %MPPpѐD�Ķ7; &���1,Kò ��@b > D�iѶ ƎU Nat+Od3ZcsUaKaZ@@aZ )bH�@1Z蘶0��)kR)@qe ������7K fKcsڲ'D��������Y4-ј,i�Zm/`ka2H`&Г�27- ���1[[An6�[5G[ ������ʆK9���JVۮ��˫]$%p-/vL�������@P‹>F-- ��1lK ��Ll{+ :oiebH��±#N�@7yM$��0lK@��شB?M$��$0͞hSʣj_,1��T r.e"�����bcvL��aM斿52%��@B,,"P�I!Mf ���I5%<nd;mh[@Hh $�� p���p;.- �����8Ʀp���0mI9lK ��„[9&\hҾ}I�HhM$�$4&���c QVXh��� 24����0cMӃl"������34`/@7H����AT.SZhD��0mIAaMf cܴmi���������2L� ( hh 0\^������ ZyĕmiD��iyS}[Eˁͱ*6@HhʃRR&���Rp)L%Ll"�P,K6:Mhe2"��8d� .6( F &˒�H������ 0VA���@b = oi2� .2���a[a&���„Wlh]]6ٲ OJH��a�cےaFOP˒2̖% ����„[9�������1ڇm /6���( ےf �ľ}U!êM"AU�D����������rIƦoA\lK@˒" ��0C(4E&�����@da� 7������D0LPP^&�v-}H�� (p�H�Hn2��AရL$�����fa�m$�AရA�Qo3r˖�P$/rȰۦ$9ؖ!QxH!M&r,K4'ے As걹������Aa­&D��ö ��@P>ЋL$��@P8r9 In2( vKνׂPr,K&Ki& ����%Lxс�,mJA d[ ��``ۗhbn2�(N%mI���m@����������T豂%JBS�����5u4a�S{HxK˒����{`J"�,f{[A X ض������@ICx" �7q,)êe1I�/rd &D���@(z A~QOmgX,KD�����Űm$P/2�����$/Cf�@F$#PTKD�b48M$�@F#rp˒����������Qa �öM&DŰm�a��J8C& �������(\.p������r>ȋLHeI\'yhRm䲤�eI�����m ����@IKxBF#D@q,i������B ����DKyLbض4���UE����m �ö  pЋC{CD������������rI�^t ��ư\"wiŋDlͬ����rE&�2/CVX4`Y@&ɈeI���������"p&�`1l0A����ja+�PA/:!pЋL$UKAh N6- PhL0��PlKD ߮.K&���&D���@N ���Xl_$W`⠪Z\]a⠪V-&)�������p(:E&�x�_ ]Tym!�`Yd= 2M&���2& dF2As7\M"d�U//ТU/id[J0Л$F��AA�hRDZ &xM˖���p˒Pvs"I"���xс��&ꕷ4������p؆ -" .v~IOh8���2:M&����-;ے2a/d9,i����#v!�r56]]mIi0Mvgے2a/ Iے2̶% 8X.H 8% "��mI�������rыA���@I7���J8M �@(79 P@����z*KR� r:؋L$����+V�����J8bo��� pқ$��n���B 'Ɂ����ji,2|Jom(nNbO& (\nd !D pJ[Vcö �ё" ��@(iE,qpkOk&KJa/dcrY1K;% L$�����%&�������PB �������I_$ey.iYmwa ��P1[ \A2 "�����������j$ �iF\i p&xyQ �������������T.|!`ɶ 0a7 MˁK A����&�e(e{L�@q-ـ](2�����`anrIzY` A$+q54 L4H���PIo0Aar���pنA�`"������jit:6w۞/@B"(tH�����,˜xL������X.4IX-O�;H�]E"�����p6F\Y \AhH&yDq-Cٶ fےe��������`1<ZDdt/|ɝx!&ĐH,D d2rEѭK{D ���� ��������D5N4I����6,Z0p٦r,p3X'5e0J�ݱ.a#@WI|!3 R$[U `"dt7 U4hpՍI kT(T]g$� f[ ^ 0 L$@F' ϴ : wd3 �Q&+]E"�8ؖa�.0mnv&�ɶ.0ٖa�����bl$J8 6 7;L_\@FSuI����������M/t4@pH������@(7u.0l"94v �&��P1[ -l[A��������D5N4bM�lR4 "�.! /0���Ფd/0������PIo8 o-6J$�@Fy�I^`"���X e ^`" Y]ކbPZd ���X mD���@(᡽L;mv1II֮D+=% "��J8;nZ^ l[A�������MJ8 &PI^(c7v)���������B 'M��,6M%a_6: L$������%m6%�% }/0��� ʕX=L[ d/p8aan4bM�����@FyǶ_d������̶4'Q(m%:;�������`aXtQ$8t]@F8ն,’n0[.'ux` 17  LD o����0\a�X m N{aI"!����Wa'7I;Eـơ(17th&@0&�������XLmD�Ax8do����0]apl0���`1ݭMA��XLp SD�8va{9|0_@B����@qZT ��������hr[4StUieF��@qUQ$�������Z8m$eh o0kbCh o0X8LQ D�0�%[_`"���������@TM� �Tnp7 d �����0\i�����( ��B '},Wbѷ5_= B�'2 "������PC)-<DnzJ7H��, PA(I`"! y):I| eXl%�%`-- "�d o8bO⤫AA`"���Uͻs%&eqrYA {`0, ��beK߶���A`"e��������%÷ ��PI`"��������Q-<l$!tH�PI_`k +,mi.w%=�к5-$MTE B�b o0).3dA`P_T 1 I�P,,٢"����T}l`dVR˸=a B����@���J: L$Te30 8N@LDwL��������tpBf2D�����,$'*KaZ8L$���@Nc;So 1 ����AF@ ddQeYA&almi�� y ��,2 skpd em7~$9+(&n%!����'S*vxY>��r4U-i���%ze`o' 8YTY$B ���������JJ�m; "�m�md/6H�%[3 7ȂXafSr\i���n4����`1ݶ ����o/Cj0}E������D�������� e.E bmA��m; "�XNcso˧H�������������B-<)D����`cm ����JzX^NE&poBI2H,8%i}sae`bJo4������pjvmJ/2��Y% 8, ����m; 6cP$vl&V !MTE(Nt۾@ ��Y8L$����k 7cտV%$PݾYL�����Y0eEE"����E:s%"�����������Qa�StOlI(N.-&���P҃D2 "����B7vzs@�Y8e$����T҃QF`t.(#Qa �(.4L��r ��&�n- &2:ٛ:/4��(jA,i� jm([D������ 0I�����PFkui"ɢ���� K�M ���PC��@(| �tۦA��� t]`J"����������ڿ-2� Ndw(3U¢e��p7@@qN ����$–h���� P�������@&���j<^. ������Q0I%=/6Jv"��]qܒa 5I�����̲KyѪ8](U�@ 2��P,,������$�����ONbV~PFŦ(j ;TKЋb0@j�$$������rmA������^x:7]^N H���N 2 brY2 "�@/P�Tvh),MH lLvUdQѠLpP]D"��P1- .&ϩbLW$����d;H�������,L/ r[V-Ku3Ր,r��������U7D����`qM\yYFe9:7u#(w.;hT�TP"�do`"�����C ���,wk֞}LL(4o������A&4nnj&���@ɦ U&* Tޤs5%!������P e\'&r~3Nf" ��������]J;|(_`"���X_H�@ɭm��+K a5v{h@br[A��@䶌זi���������@ ~(Ŧ6 "���*"C2x~uo- br[A��TLn4����Mm1O*s!�Qn`0nI�����@І*C0- UmE$��TҩZ:7B �����1����� ^2:&/4�����^pjo0p*o0���rd2�Cx ���Yii��*T`t/lNDdo $���������ZzjoLd������ ԾDPCy ap[fW{JfC��SزAivvVR[ I*.C"; 2 bTҡt{ hAcD �������eB-d$QrI6p`S$m0��rP`b����������vT`9l �vIa ��×L@��TCyg���bL�, ������� sG7w(���* ;���Av0����If22"860H�B:طY���@Ex(o0mbp[AJxn"1μtݕD ��T ^mi�*᡼0- ����X-݈FWWXNnK �@KDTCy�������BKO Ld�@-=7�����E[:���Tl1yH T n4����lb'D ���@"1Ld�����^0um)0$//0�&j�����boB< NP������*` ���XN/4‹Ld���������2&2��b\u�t*o0ܖi�*oAb �������������bho T�2 b�����e;Lb�����eHO M%T\0- bD"#"x(@F��@{6HTRvE(0PXn$!Fukae 1��@EP 5A@"sͿ�������P<o! 6U92eݖi2:-D'P-Mb�������{iM$HyҲB �� &2��оM 0��m9:7�@qp[I ����˅o'"0UL3����;7,L @r Meb����*xEHR,6 1��������4,"_.Cy�tpX= 9+ e1{YI,-<7������%=_'<{R Zxho`" MeJ�����C{*^iP,$��Te0Sb6$! $�,xho4 SaB+ D���S{e&1�B Fe/47dj""����� (S{Ú/4�ĂQ"�������������P` ����ծEl�����iyC`We������X޺A&e���m&[ʇI8XUYY�@Ly$b���8XUY�VM2U;ʅٞK\0���@en' %hi+X-ip_P������������0Ɲ]F@�ȂQ0[8ٮHЩt 0p/Q` ��*E4l`Q$5k! ���@e&1P1xYI ���`9|UFe!5&mFnO ��������r6Mb���,i"������, oU0���XvU���X(������� lwܦ������������������������������������������������������ ������������������������������������������� �� �������������������������H�����@��������P�@�P�@��@���������(@���������� ��������( ����( � ����������� �����@����(����@ ��������������@����@!��@@!@@AA����@��!������� R���(�PH�@�D���@��������� �������H�@@(���@E� @��$�� ��$`(�&&)` ��`���0�s P@a��&���� (��LB`(��$�(�������@������(�I������$@ ������I���d�((��$��$@�@A)(��@A���U��(����L��P�� ����`$� &��0��E�������� ( � a. ��@Q������ �� �eL��(c��( ��L��� � `!� ��`(�� �� � &I��\6 0`H�`$��@�L�����@��P���e�������0 L2��\�����f�����2���`&�� ��l@jT���������0�����������������(c2� �`��e�C��� ����1 �e�T��l.0�0 �2`������ʨV� �$�����e�C�$�L$C`r�P�$C���P $��s5N 2���$��ee@IA�� L�P�e�L ����2��S &������e�C��ʆ0�e�0����A5�&�I!���0!� �����C����ՠ��`@P0 O�C���� 0L��� �P @�T�`�0&I�������!��P ��I(������!L�Հ���@�Ԗj��f$�����(�(j� ����LA���0��$���T.d��@5���`�&(C ������`1 ��e I ����0 ������Tc��!��LB��Y0�����PT& @5���`@%L ����&��� T!@�2��( 21 C�`B�����e����@�����@Y�@`@@��������&�A ��(clSap��(���`P6L�`�������e�e��P6ɔ$ e�P[P$ a`���e@2������ �C�&��`��� $$� PI�2��������&�0� Wd&L��PPV �՘�P@YI`��(``��0@՘Y L.�@�0 �Ԩ�0'`�&)Ce��&a�xL�C�j���������Tc @���2��2���Ȑ2@�3I$ a@�e@Y5������eՆ�����&a�0I(������Q 2�d&$ a�&aH�����H0P\!$L�'aOZ� ����IeC��`:Lm!L�@��&aH�Ld�� �(��L2�2��!������B��Pj @WR �(`T՘d�����L�`((T ��0�e�e������� ��������� @&�aL �`p̩m�s5e&��0�����LI�0 CI���@m ɓF `�YTC  a�����0$�����L`TI�Lf0�����L ����6C2$�����0&a��������0ɐ a�21� 0 @ʀ�`0�P&KIP6�&A`��P��������P)3�Q!L@���P��e0�0e!Tc�������2��II����0 C�If��0V L2L2 e00 j �@ ��0 3P 0�L0���@!L\m&, ���0���2�(0������0W0ɐ @ddP6jL�2�C\�� <� 0 dAL&�Lp�����������2�P6 CU A�ʨa'ala0bf2j@f`0�Pa� R �����A��A&a������0'a��@��(0$ ` f��d���0\3Ղ! �0! e$e�� a���2JH�d�LRF���� �# $C.{Nf8A0 Cf������IP dQ T<  e0��0l8$ A ef���`0���0 C�d(M5� KjLʆ0�0 Cj !���`rِ uj3��@2kb$ L“3O�����P6��0 C���� Ս� A2��& gr����2HZ 23����@��A2I y(.0 L a C& A LII($ ��@C1@��\ə'@s2O$2HF ea L���(̈́ˆ!L2df���� &0 ������# 6fee�0!L ��0���pjd,�L``0E$3��&a6d!g(d&0&0l42Hf����L��`3� �j2IlHfBڞ$B* ��I.{0 !9L�j3Ld�A2 5Hf$f8L�2Hfpf�LpA2��&a8$ g&L<j<9 弶sF'� @m!nL0&����L`8eIlsC2b02L��IL�2ȵMj`BI L0��j#eL$Y-/�pəa@̓0g Ù $ gb<s0sVb09<rdb\b�ɇ��@mÓ0sf�A8LT9̨A2�(jL!y�����3y>Mb ���`n3 �@YY W=(��(\$ 0#$32 3_O?X4R e4 *"�����&09V$0C`@ A2I932pfa0WӜ3P|I�Pdre'����e@fș 0g0 lCle?GBE �P1r!@&L�(:y ���P#ၜ���0ə 2��b2&e3'arfL&C6Hf�L`H|3)Cf.O"�@$3��(\$ ��� a8yma̜gL(d&0і(Hms&&GՐeL���� 3je 0��h��@HXF>�9��\L����-LL(dF < � $<$0�����2Hm0���� L2Hf2əPo L�(d&LفkSK6'D0��0�&frfPL�2\''3՘���@\�!<�Ty0(b&'gp <Ԁdmf 5ȵM�����f}jé a&LI����� r��L23jf����gIB�L23�@Yy&�!3O���efB՞`c.1�T0$2+Km8yLb ������!<L y(H$����60����(02ə!ə ���ep̤ C�&<$@PLL���� �03*921Lp0 393����������@�AxJ����@1��0mPsID������������L9'0 "LL�����*9&032چ3s0���032Ho3 93�03��Ifۣ<�dB��ee]H( D�� ��(C�����'|͠ebp귌r}dL��0M-!�Lə LN"�0a&g&��������@YY��&hˇRF%93�`LL`�@kg~򟪀�0QD��-X'b� e����P28ć&&���� uE���`T\y֨��C��03� rf���L3!�Lə epxepx�epx�� g&�Lsf<Q;C�2*B���ȩ[(,XŁQC8Њ���j!�� FU<� NC�\|5*NYh d1px�!Ƀ�ᇡ#PE09��P�����frf.C ��������&<|Аڒ3<x3IC*Yj ܯ ���ʸ~ ��|U�PF 39#Mn%s rCźЊUD��� .-=Xu2"4�Px6"��(#_WTE@9X������dfrf�����(#vdeGDM"�LI0���&\g`&'`LL�`u{h2rX��`¬vfa&g2rxees$ e\c!F*t � EX#VBL"�� 2rx&eL�f΄9HL"�ᙃG8CtĥA6UcD!b&�a9yp�U@G#1Xƈ �0sȩ7 3���e<MDTX"���@2b� 3'<L>k1��j L�0s0<&̜90ả��&̜D�&̜"fN��ۃ# ?k=bcPae51P5BrsdD0 IA���1F:RW!���Lj8 2X+���@0 �PF\s@E&̜9�eOīC�����(8V�$F rU#4C %1X��(#9p0`F������2rxL9����(s[<" 9<5PsaΜ@9s"�0 c����*(Gd5F]Z#d * XGZcj"$3'���`.j@3'f| jO<5B|dD����2ag�fy~C,rZ\Z(�!b]���! #FFTQg T����fy*�����p*"930dmb@�̜!f3B�PFO5rx%93���&̜30s>+|<#H<ՠ�0yN��If 2rx$3sAޠ fWۻ��B,6 С5B1 JҲb:j�� uMd1'f 0 �9D ��`LBfes>q}PZ# !T8�@9<\FBPF�0a朧��$Sy> 9‘vҀ \�@Ö�2IŨ Y5B dўp �ˆA�O<p BUNd AG=F������PAtj d`1Ygu!¡Ck���Q!B�š ���T1Z6!�Q5BD!̉������2�� 3<������Pc<J̙LNnym@0vxLc8<ھvP�T9 ��� '?q �������� >g1gn@i@�����Tht0vUPAam3 #?g` * �8PJ1V1s# �����`9OڒC����.#_eɈB��Tft:{H` * d`@M]XF�ACq1*��R1*)U#����| d���j#FڸnW83j#��9 `ԛ^]ԊJ�1+{ĭX��.k ��@B����@cF20 ���������#,�@��59穀9|u eÃ�9$L-?*@ˠ��0sN!��s�������r v&yC�gU#d `<s"��0ᙏ"aD~u ���T6 `#G8$3'�����@m�d@m|�����f>cձuQ !2If1hp ���� 3T��@I~>c�@!/vUqY�� * �����@Uc0@�U#��@n:* - H1XjA ͟+U#� *,�QU!�0g1!���TP?,^��!D١W}B(aĪ1 )Uk;*�0bU? ����` XT�8j��!X5B1b)"̉��˟��@2bp)K�@M݊F!�Ѳ^3;dgѰX5>Ve"f<5Ơp1.$E1Ҳ3€�FݵPEYaP)B����� y׸jR&-;xU#������$Kq1Ts! ԘpWЈ[7pa@���R8 ab- �����*Xu��ԭ:TB@o64e8$�����Wja@��c�Ht!Q6k<Z$l $')B� :Ƹ1!�1€��j~*#�!qc\lԅ>5��iɲcq5B���� "u<$�0*Fa_a�ˆU#`lRz˂�@v ui iĭb@!X5B��0b��*xfV����Hx.]q1��1.s+�TQkǐ�0b!X5B��,!,ˢ}) ��:*C���PA뎣#!����dczF���\X1JG�!ĊU#oguрP �����qE�PQU93����ΨzUZ8j� zjgZtTQ pMP#V�B͡Z#�@#V!^x# �0Z1ܵxˎX$��P+aĪ2*9JgP#V����@B��&[#$u<D�ˆUL���fc`� V=ZJg+>!��cGll>VLL���PUG�� ���HFuկW!��VP#V!�!+~Uf/CD1ԊF:B���������@Oz|=XYd B�V:FaĪ#����$cuA@:B�����$c\DKg#  j,|UG�ˆUhĪ#0bF:B����1 �`QG@+nݤK�k=Ŭ!Zی" eXDT6aEgX+!۳ �����!8�j�1u��aĺ=RؚKua2[P+s14FĪ#���*X�cc Vj� T0ba8 ��d#��������PQKgG ^4B����PAkab#4f8a,V���Zw|` YG!d+�����*Xu/hW}k$�!XkT+&jb@�`bx:FaZG���!tW}dc����cq@aZG���"@Y^!�0b#C8 �Ƭjq!ec]Zq@��������H8"dVU0q`8p@�PV#b#�����@Ez::B�����$#:ȡ #uA՞a-{p@�,C-{p@����q��j|zvA*R^0c���Zu#���Hac< V��;{E3a���18 18 �5bĺ=]8u$�����������P��$#:-[ d mp@���0F<#uFi_4EP+X4S8 �������H8"�H8"���H]Ǡ�, !c!c:T ۳Xl1( Y4`Z�C8 _=;$���bZG���q+��eU[}W;) @e'@ AXU����@2Q/iZG@(Fu�DF}TPv4jC,CV ��aĵP@DZCdUigNd$D�PVYJbDz�( jŠ݊C��au{k5k���PQ+u��@Eq����q:B&N�����H8”]HgV��� z` =[ b*=]8YPSvb 4bbZ+4-*h k1����� .��*x��z��!\B��� uS1*B�0'xZ���gZF����*غ c@���Tpc=B��cxE,��k=B����$8ꂌ11+ o!m K9H@an(#X#z�Ъ-{Ԣ SvG�PuH5etw@ʮ3q3zC@`����!#����� z������_xQ zL`+n(��2-펺@cX+��������@2^ �HqX�pŵ>#1=Ut���������@��\8^k2Q(Akc\rV���!qG0egkD�Z-����i '\-sA KahESV0 C!��#oG]���0k=B�����dGXz|,DKHa0h2U����k ���� 2!< kr KH�����"c pV)lw!qG!qG֊ ׊Z ע������HYըǡk=Bm}i! A+9Ly͵}l)H$����0������$8E���P`\!�8xê٨?ZjFh`Y<l � m3ى*J a)G1n( ZF񺤩V � S^1C[qbZе>@)\ P [OBS�H(Q+Ҡ:@�Z�֊xhі( $�chٺ4����@8V<qc]�)\uHe*9Lye@��������VQ8CZ9H�����@W\gqx@0UI*=+@G\A�J5eZ+������dG,�HfGE�qc`Z��p����PQW=4;qDU$�8Z#���c8nl]z ]ABfU��0��T҅m@cֽ+⠻�ሷC_,%㤊�����@2#z3z;` ������zGax@��X�)8pZ�����\Xq@�B8kA[H���Zx@tc~MA+2 `eRM#M���� Cx[ͦA����` ^ &@azLZ%¥ $������1&ʖNb6 maRtP% ]Z!R(tABB:H����a]Q:>i%h�� (ڃȁqtYҁd)Ҡ,e ܲAZB $������&q\le ��bhK̡-��ᶋ�� 8+|xA�,zG}@���Ю�������n'-aB1.$��-a������Qa X bk=ƢgR(4誃@hUG lU ��1�8W:H���������$x  �*xh[H�����"���@E]!��B!Xw\HB8 a@ahKx�����@ź Cĺm9R � ڔ l( $$«0DaPZ$,A0jD 8e:z!0&�6���lYjbҲР4(p&b^_u�eucq&�� (x)V< ������Pz��€MA�������Z�kfk)lw+ԸQ=L K7p˥:pFP ȰriJI}$BB 2�( J ���jnxp[7G4XQQ�����`kSPc%O$B 2àl F@/ �������6!(P���@deZ M 02*`) �� (Lڱûf'X@���[H0`kS` m RzƣV��Bq!�1+欻Ǡ%C[hNˎA ULۄ5@���`1AWCkð�'@  ��(:_SN'�BF 2�������ʂC�X   q6 ,u= %1���`���Rq���$쏯1H-L �����@(qզ �������ԥǠ.q\��HeXo^cX@����������@q<`҂òLA����,W���jK>e G|Y :H\6q uŐ1_����@xNk$�DucZu���,qØ޲QN1YV4n6+ HJ"� [H���X,޻JlUri��� ( Dm ���A!c%��������@dLS @ NwWXx@00hk ��@P)T ���e9 �$aV���Aa֦�#?h+� $�aY%L��+ u��!\xG1\ 6ŁjF�v5kZx@�@bB�����#< ����ԩ_Ѡ4h���% �ò^��8Z���,u��ڲW�������� ?.C0= -0ipF� (LDch[z $���&0jS����*z����������ԛ-L����Ș0A * $�� [; e\d[H����U찴)% KH��0e �����i ����Pq '-YL[; Mm4g/a*p[@U0�(NZ�����Ű,a�����՘rS p) ��lm ,e +X] �bˎ_@@qr¶=L[&e Gm,z ThFCk ��(B8mciZh����R�����&sk K_]ɔl) �������&���D4 e.Lے usRaYŞ`I$%�%X@�����B 6��X zIk,e ����BI)Tm=PH ���ikhNFQeB����PT>ԊNRrMf�������Z:6��X,a@�iYdNB�X4cf۵2HI$���mK@��������j |?pWB��,V0@vB'K�`1-K ��� qLȲx]Lڶ �%X`,e ����^4PNBX]q@�8ZWQ4ઃ����1�����Ԩ-ugz-a���@81M%փ�� T<Z^0,-Zt( Hj��>WH���BG]q]Px��Qk}�S*ҶE"- @�������(lڔZ�����% [&lm"eI1v4)�-*Ĵ`mc4Z+@ `MKH��B&*4b)X@�����`1<Q4_.6PJ- İB)5��% !a+���������DGyA:Wx@�`Z#�@l��%<'��bX�X ������Ca{X ��&2���ð( ��� 0akSB #vQJ @P!et) ���AaF1kp, AZH2uAZ8ZA.e �P @P%L ���ڛ>L6 n!h0à5@�XaX@$4R2���1���P6lm ���_d��ư,1mK@` ˒&����StsER��` &Aa֦ ����i 9[[�@ �ò����ҝ]y1y)T�����X4!��jaZ!Q@�������(^|)�@ Q7ֵ0"^L5(z������������T6m ,eI���՘4A����%6=Х $0h- �c`) ��[t9ZZLX,�K9HJ >ѭ $��$4RPh rL������ 6òIiXa0`ilNZJZ�����U}0Pn5@��0&� jSp`��cXa 6�`L˒&��� v.-Yka ����zMm/vL�e ���i 56zKH@qR�2�������%MP(aUmT.`0V2�Dmt*ȊHZq`8W$������B_# CvP Tt$�x૛��� LC%M 1,K �@bכ|�`+9 Z � (LDqeel+ �� v@% 0R`+Ҡ0( �L")V&����&\ 9r9:f$4 viD����������Ș4A���@\����T6\cᲄ�����QiIaY�`1,KٶrxM$�BV&� .���aYdKabj �%<p) ��i - BB-4jCTaK���������� ni&OT�(f9mkaB+P`�Q-7; &�� yZ W4@���%M,eI��`1m �Yֶhr%d-=�������%M��nlZ.a�����D5TjS��%M��. ��@���m/4����,eI�ò �X ˒.eBph[H�����):*Akq��Xfka��Gڶi].6����%L�JxR|@��������(^!��|C@ʁ$xM$��Mm ���Ű,i����PRp) ���@Տ4֠ (Lؖd[5G[ Ҡ0����,eI����T/|C� (]C �Ll{+�M$b$ m&l"���@(a¥aY�A/H��P2|�b XI$$4%<-o� �P������QiI�Aa tle"������� 2t� 0[t,eIX ˒bX(RX68a!2qV|LR!2,Gm0\D@Ble"�����Aa]mj0�IԵ%$Jp e � Qv�;nX0Q2��������@dt������"0&\!�a[� (LC„jUlX0��������A��C&r,-L �0&�&\VhkaP-KC�[ ��öY-JH` ے ����¡\a[&��ư-d28[Z@�����[K`[H�qE>PP,i[ ��������"㰤 (L7,00wHE%D���diltdž//�@8YZ@hL$@Y2YғD 'ud 4V&¤4Y D@Fle"��XLے % %@�HhL$@B,0ٕ7-L ��2���%@ˆ.h,KzK;Lv) � _OMR�2r&KP(4 p!$��a��@(a­H������Dp�iPpkŴma�������J(t(2t0�4'_. �����AaM0%=m_������ rIM$���0n6���a�`,\ȩ ōU/m ����3hŴmimas-a����PZ#�����PQ/h ���������A/me `Y$a[H�ö�2���@PBijIL$��!qn g˅1ٖ0`Y¤4X0��� wYeK�H$Lj@ D1l[J4M$�������`1Ea[!UzB6�����㰥Sѡ\´@hKBv8iYfcm#$,K@�Hh]lD����cض!Q&Da>&wk]@ٲ �HhMH@a`-aBƆr..���@Bl"Q,Ka,aB\l .6�����~&��@*vNm5G&��������`ÖNEl"¥Ka,a���ư]M&*4&� \l)mK2��J+:̰M$������@ [9D���� 2gcvnm"��0f 7xƕeasڲ'xdadd.76������p i,i%+2��Z貤f �( &0f Im0ŦPh !&�������0) C(4kMNeI�� v>Ћ8[4���������rML-a,� .6!M%M ���0m&pp&�� eI = EEg˒&����1l01lWaD&pyK�����~<������Aရ4f @P8&��XY^y[=] BΛږ&����@P8VlKaröI��������r4&'y@abNSn27H����� j`0 t�� D8k@Զ4���0AHhP 4: p0YI^l"������[6`4+oj4dY�$4Fm/^,f˒&����� /6bڶ4 aZ  pno��eI�eB��qI.I^l"�� vD������cڶ����Y8l����@Tˎ] Ҁi   n65Hd1[4lY���ưmi�����rK9ö 0�`1,[axyK0P]iz :5'T�����'Ւ&0����B t0&2���@(�����ŴmW` �<E{�_Ҏ{o8Y4A0D ����b-&2Pmov��'˒&0����J8VTqV $0I`br)Ci&���B x ���@N[:Mp '˒&I :F%M`�8Y4��XLۖ&0�����B C(�T=6'Q�Mp ���@Qm%KVږ&0P,Kf `7����ö f`itf7�^K-A<[H _.1��˒&02f n6!Mv� M`@Fl &D�d4./nm"SjD@ d����%)$xM6|;(,[eI�Mp*%M`�������Z ��aZޖa����m8�,mP��@(ဗrar`��N=`6|3��P,KtmK���aۆ ��@(tN=w\�������%›~;�� (ˍ$˒&1�������ʎ SF% P@ öID`ah7;� Mpgے&1�����cض @=V~k(;|;|hzỳ��@BVf ܶ'�.l����1,h F rifh` ����\w8̄,LD ��PtU(9vM,̺*@H:M#Ka ���P,Kے&0��%ف ���`1\4�Nb8ٖ4�������zeAZՆ6L`P^lk R2�P`3\B{y%h$&8ٖ4)N%MŦwe ʅo ,hE0 K�ɶ , T@8ؖ4d /sR[X���Mr ��˒FmK�ے&0�PAn2An6����e`����������J*Tz, T@�/BBܪctm{1Ku��bկT@d4M0{6�������@(ဗr fh�B ؁hے&1����%|XN7@d��8vcvI ^iӲl] nf"# n6�������mM��������P$傗rhyw7lŒT4e(ed��������P$o!�eb�����+LtxyNb��������J8BB ف ����������@ 喞K9��p7%Lb��X m�/2A3haٶD䲤I �h;�˒&12HmeI�LOf2�����X -Mb���������~z3�P1-I0AI3ha����`1\ pYi��J8;,K+/-4���������ʨw%|��eIr:؋Md�������ʉ ��,v ov ��PAj6XՓN!v+Cu@ TlK`[D؁ hR Z���,˒&1�.K���B irIM"Um_pH>H������6LPhdۍ %4�PlK����B D�����@Tˎ]A]Lm��������ʨwy{aXhizyN`�mI�amA0 Yii ��@q-i�6abf���鲤A^ A^d"���2LdM7-!oQA^d"�2:ȋ;F%Mb��������������� a8��`1<-2Q{%%8ٖ4䪗dd/չ֞ś#[aIln$b�i)Q<&0 D��eI��tٲ@������N4Io6��XLm8BIy ��������PRMD����`1]����e(�J:͝C׶4N d�T0���b����o,j4D¡H,DB ���Ȃ$#SafUp V"3M$`�����(N. &{ �����tL3nl���tr)������Z8] `~le1�d\c/h6*diB$6p����������%SZђȃRm&j / /2ٶ 5"����D.Oy Rn ']e`�����d"����J:M2�@_ޢRUd �Y8kVXZ.1m���d /2ٶM;Fm�������%zL"{Jwaf),����� Le&8ضa�͞"3�.DF��������`UD"#���"M&2������NjOR�p dۆ Lqm��� 1i ;'oz㢮 `���������.P0lۆ ����������4PB 'D`1\.ácqm8t9F$�]M`�@&U],2# &mn*0ضa�p7��,M&2�Pڇ22�����`a\ AdB],2Pt]���;P�j38 &]8(St]4X8ț;F*&&b������XLrE-& Nd�@quQ04ٛdd�������`a\J:���������_ʳcvulܢ @qnm3��D8l���@(/2�����()E1۶app,a� y;GvnlLB � f"E`Gp $̂ a�����tZDY84Tȭ)HDX8ț8M&2�Pl0D@ɿ&Ʉ)N.��������AI'}��� t9� tD!Ѡ^Y$Fm&1Pl[v ڇ;YRl!+_)������J:5Z8444D�8ٮ 0! ��'6Lbd o0!Lm��,䥜N���(Nm=I���(f6Lb����NA`"4}[fA^&J.&=w݆U^[7��&&D4�bea`Wp &2������� t DF~ )5Il`,,Ҷ&C����%&2���vɎm& Ȓ&1��ۗ �Y8Ld�����,6;vuH�Xw%"/Bq-ZZ@$HDTcQ!!a��M!dܗ L Z8K9H �, Tqrن%tLl- bi(oeM#W`F�wŜ1���=&D�����l m9���@qmS$FU,6!!9 .0��(Nֆj-0v禗߂���������� (/r{vW`�������E2-.$ ���T[D H76Uܸf$a�����brٲcrۆI �{M\ vIe ɾ,o7g7b ۠ZOUM) H���������Mr&Q"RA^M�TL.0�P y)p i*o �nl/* "dV&"���ParYP@R `Č��������KӦ &b2� QvqM�P da+1 Pat XP@Ą���͕"3�Pabde��0jmRbr٦I ������v&IT #����@h$_`"* _@����bg$*&c6  Ld�@% d,gm���J8E|+O]oK[^@ ���P1l$�����vm������o_)̀Ne^@ \a E2P 'ۀ2vFc�Q(♞ 2d: ������v&brنIanm+2$1����@(ط/I ����`! L5Qd ���͕"3����I&JarI ������VswɨE sm+$WZwf#&("��ParaV|.$2ف ����������etY&�������tb�,ep��@MP 'wOlιk[!žYHDl@B �����"BP '{I '{�e&1���XV_Av\"pǥ2Lb������rn[xHRl!,dor �P~Hdo2 "MA 6xfi)F ̂˖6:'^ZJ���������V{ -/1ui)  'b����������@҃~ ?m`���P<Eeb�������o0dv ) ("�" d���kB/6 "BE ������.@cG7 (��)Q.sWl���v0 `b;)Nu�����������E|\٤NJf*ɄP,J,)ڮ ���������XZHN������x*�`9\C@�����^eE6(do0 SapI �Jxh/d�[:wjob@�������K"���BK @wE"3km-IL>$&j2P '{������������d���Q�0ISr ��������) ���@h|S{[!",}���������D=.��������Pd@&V^uI" `%0�*d_@��������X&P �d_`@�4- m2������BKT nK8i , b����Ndbpۦ Zxhw73����@%;p vI����4@2���t깣͛`&D$0��*}b�����*L^,����`iz 21�������()|9r154 5P^2Q 0tqj]0p$tdlEc�����}Qm]!C��*d_@bpۦ Z88��P d������������^tԹC TM����Gؗ1չ喒E+[H d����@-<8P d���*FV,xu !lXL C D����ij-/Yt<hRUl%= ��@M��������0X/|\D9��������⚭�g��������������������������������������������������@� ������� ����@� ������������������������������ ���������������������@������������$������������ ����(��� ��� ���� ����� �(������@��������� �P@����� �����P��P��@�����P !����@@�����������@A"���@���@ �������A���(��(��������������� P������������"BP��� �P�P!P�d@ *��*H H(�����H@������P��� ��L�„�X�&(@�(&����`�0T �(��&��  0IA���� &� (������������P�*&LB����(�&�����&P�I��`���&JQ���� �$�P�P�s5$�\m�&a�(��RL2���� �� �(�PV0 �e����e�Le��C�d�P!e�L��2��C�� 2����I� (I���� I*�elB����� ���������j#������L�$���0 ��0`����C� aL2�� I�`(!��e�e�P����ML$����m�����L����(`F����(If�������@�P$��Q ���2�l C��`e��������P������l2����L�I��@�����:���e��e�0d�&I� �����s52��&I�1!�C������0��!�Tj!<Wc0l��e�P 2I�����������0dH�P ��ȀI��!�� ��d�P �6�����I��I��!0 ���! ����C���020��QP�P�L�u����`R��$ 0���0�������P6'dL�@�� ���@�@���0 ���@Lj0T++c��jL��L a��j#�$<I�T���0 ��� j����e C�������(!e�`!�� 2����!Cd �0 �$������ ��`��������ʘ( ��`Հ2�ɀjȠ��PV �� ������L2���������T*�L(������L@A@�`ʨ6@� ��`@j aPV������$@ �����2�(2Y2���`2&��e��e��0W9  `���T �L������a2����2� �2A2����&�a�`2WZ `�2S ���aL���e������Հ0 ����������((���d��0LjL�(��`2�2Pj�P���L�� �����������(+��I(P��P[�� �&�a��`�����YQ�e�� ������(��e�C0������0 lrm�a�5������&���e0��ula0�@������� �L�Uj�(�!C ����`0�0 ��&Z �1l�Ls! �!<I$ �� aH&Ð ����0 C�d � aA�0� a�b��(@V����՘d! T{8lڐ* a.a2HT+� @!<!� ��&0j0�����$<9 A @$3����� `��Mj$CjL��� &a$ 0�����0�� 0 *a�����&a2�P0��0�ˆ0ePc2�01%�0 �e�`6O< $<9L.ņ0��eePp��� cl�e0a Ա�05'g&a�!e82CjL���0\6���\mC�� L5�0 C0 #`C`C�j0P,C�! 4��@ )00����Axrf�� 2�a! �C.C �����e���!����dpAd& �LkeCD�`Cjd� aɐ<''a�(0OO�@! e@jm0 C����&a��Ld.�C �j@�\1BmL2SA̤ ���C�� ���0sL��e0ls<t2�0��������2\m̄! A! �$ I����T0� a����Pa`j �0 g&a�` a a0���P � � a0[M6jL2 )#Wl0\6d''�C2eC2=���&a0�(0��0 OO��0\$CuZaa�`��L2�j A�L( ��ef��������IP �����$e$ÔA2SVn2e��2�d�j3L����` IIR$�@ < C��'gVZ- e�� 0!L���\6eIA2p� �������A2 0 PL(d&�L`p�����00���ʘ 1'39L,pmCmHfAmHf��@$3�� |ِ3O&F&A2���p���P1B$3�Nm@f��A2 �Lp����������L`8 0�ʘyr\ ��e<9L`CPO5HfB$S$3����� �PL���(+`�&0&eTc &Sd3S@$3aR6e8L��&0��fN �@m!SG<%O$2Y����� yÙ 0<O0pFdfAp�&0 apFr̜H&2lҊ#ck{@2HfLZ<L�`×MP0a�3�`ep���2H LL͜|F��&arf��Am 6I2��Pep6F`ԀL63P $31'j2s1HfPL�����`&g&��0LL��d&g&�L2���"I><3��\s���063��&ə �eTc!����2Cʘgrk<M"ed&'�՞9 0�d&mr @mM=8@2��0LL�����d&g&������1'ϓmr^`�2jp$3�������(d& � m<I&_$���������`3C`rA2s':6<WD��L`rfesʞ|p @b#g��@s<קlD�Lŀ3399"L��&093d% ��e &093���� ~$P<LN"��@ulr!���PL(|ə A2���ʨ$z;dԘ0ə A z$BH ����@2|fș xyμn( d1c7=8"��PL������ ��� _o3~#I!�e̗=5�鑰hr0H3jGR-XAк8R"1 feh 9arf„Q;7<+7<:&Pd&@$3d<s0jf���ef2a&!Cy&'Ag&g&Y̘������ 3|x2ar���&k{fB8g.L`LN"�ԖdHmf�����PA�PV3( 0932̐3��� 39[NfH�frfL23��93&SFY s0�(��0C7m'eŕMa&2�&n9Ι'��� 3Ifrf$h9����IfrfB�@63s23���`$3dyL"��eqO5 3<ЃO-zr1��0L���eԍI!YDZ*B�����`dYLeǜ0⧹!)AIa&'�28<28> |4B�� 70 HI4Rd@.I��� pS3�28<jpx*�2$�Q32cTE��Pg�����\6W`B�`����H<yPk($B$I@L|3������@ �&yLCfr�� ���&�&<jՔo̙)>ƨ( 0E����Lf9REIfr%RIe&'GʙsfNd K@@DA=9PF��euN\oGc9xqxLЖBFr�&g&C�����`<��*ə ������e&< 39$px"4ehL͆ A!B94 3@0B#"fNŐ�`gLUǨm@ir+��d��TP5FUe3:Bdd1'uky����@ g&Lf��M>CcĠY`E!b&�`Wg{7F("��jT"0ả2rx0 y"<KM:�0a@9<yD(#ޢ@jQ�PFg=*\ZAL@��ƨ, !I�����0a朙�e(#�C3L>k*"`2QK>�ə aC�PF�2rx���̣K���PAU������N`e�F~��������@m,"?qT��Po a��0gOULKDpc G���`,;G�ar<9<��S_#TjƠVPD�A2�z yr`y7��P̙#�`��������PF&<IgByΙ �sf.F�٨€""�sf<5ƪI @9<2rx�ԖdL�0sL2<$9[fp �q e!y&�q5&y&����3���m bPa��0a朙�3 sL��0sL� 32rx��Lv>T��������|�*U8”IƠ€�Q#cU#�� C9 ��0sS����� ���/{"����C0axҡ5B��c5*vͰx`��CoQ5ց""F�`F����C����l䇡cv+a]X`Ú6&R 0F���d`U����@S"�PA9@9<s1rX[rDO�cЖQk<j1`tvX11 C!�1kG-I��!FE���@2R˧ޞӳ1P!�����j#g��!C-96Ül,š,g= J "DHf���0sL��ʸڐ@D���L _ã}"B�� 1.l38y#���Lx XYbz\L����!�lN'>1 9<2rx*�j;pٲ48�2r7�������q5z"F�@R;0so҈֠���FϤ99L@9G����9nZ"bb#0�&̜)Df�������@fyj2oQ� �(#9?cQ5j��0szCU>Zi&1I9g~��*X h�0aǨ[KaY=x�����T95 ��� 3糊`<TvD�Uf<@ɇ ÜTMb<xC10B�gFr����ec,bY rD�(#gfr������� eL�jKœy�sm(�@UcM��$#(#g�����eo^1 �` *RD1��^:�� 1Bn< Fy1!��B<u4(!Dz#!�0Vҁ�L9 ��)kCF4Fx&]c!Ds!D��� 3* 33B���*6��0Ft* ɈB���)1K!�� &3s��~Rv3$ϣt�ˠ�ٸ(����+(0j �@F-h������FUc-@F#95d 1X!@ ����������*jk$#W��@O���fg]VZ#cP�c*P("j��!QزCg<Z#�!D1ny ����q re2p1İ3Vn����j?Oc(D팪 � !�TPX�j2r��zՕfR��'[Z8 aq�ˠ+�@ňk!X\k!����� uન[Qk T$# ~? T8T8����qh]ˆZcd~*���TjA$#?�5~DET8 �!&)'!��Bx2! >P39<4@ xj���T9mkz&!YxPU������������ ux�� ]T���� 1�H1v3, Q4 1 ��\q>LmCF@Ly�nm;0P:n£k}�������>u/|T�BfRY12eqH�زH𐂺EpQ:B���#!X8p��HHk,Yfn=Y2c\k ����@\�@ꈧ҅#���H1Ƶ��RGB�@Zjc?���� c��@ꈃ+ u/)bX ���Haˤ#"rЃO�g�L3)C:P:t!���b1�b1 Xk �������T8�UˆP#C��0;p��� ���PXC���8֭B: �!k<ngc-P ��d���T8�T4;B: �RtXǣ4b1$�xC���@+1B#C��X(�L@PG\_nЅ` e�����W@k]q\#b1������Hxk:C�U`Ł2FB qA+"@ !�1ͮ+UP )օ1B!Xk+"TB[aZ����PBv+B4!6wkC������ "�����1.U:��:1 XnЅ������:k�PC]h p@�ˆ!�aĺp@1S-c1�q`X1c ��P/]�C4S-����\z:C-Z?�Pb-������H",,4w񫝆 ���XZ,mX_i�p�ˠ#1CUͤ+>Ƶ L"j0 pCkC@#:��0Z8 XZxHq\G`�1:Buܺ � ��Ef_1k !�!�aY]40#ԮHt�ˆU1kC�@ ��:1����@2E�$c\Xǐ1�����VZDZl dx}I����A*Fĥ����\8(א�� xq! ��c�QǡJ@ٸ0V{���R1c2ղgX ���XUR ����ԨUq ��������RG|׸4S-eBTX,C-<kC���QCb�P`b[Wz!b-h pC-��늀0VHd bңc����� B"JFu ���q,=tˆcaD])9JgpCDa`=C���0Z1"ふ!DcC_ѭ@T G@#:kC0f*1L_.5B��� 1^j( jP X*FZ���u|H�06CKzE ������qFu X5:FcPD��Hx]8baZF1F]C�F@W{c�����T8���Խ5>u �F07k}J=Gʔ2H ����@#u$Z1ƪ�"Bm6֊Hw` p@@#u 0b] hYѪ������`գFD\Aw+J9�B1JX�������@@<>j@ĵP ��1h67k|H�` 6;2J+p����cfQ/QG���@6����P-%:Ƹ1�eJz{vڽ h@a ��� Ċk0=0ejW ������@5Fĵr+>k +0 ,h ��� 1q/|��aĵe*zlw^x@��4 [QF\1�����@d#]륏Z CYx@�0Ƶpu](+8@����ak!C-< öXz�8> �P&1)~?RHGX��!����q���$c����1Exz!*+J3)S-< ��` @ ���V=+9dC? X,5>V$6Kb]I����,0A���PRiͅHX�� *Ƹ1����H#vW�bԭ<b, 8V _x\K dj����ZcFX����Pָ1Zf*ĀFF\k*%5��@T1���@1ZW|mJe�e���jԪ:� Ύ*@FZIA���TX-0VUv/< C-<ju<p#��j+F\HUz \B(F\ax%P *0��(0AA1Zhĵaa,=@ňkǂW�BH G]#Dau���BXqo#G\u(i_�!1C-< �����&TAGTestTitle���������������������TestArtist��������������������TestAlbum������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/tagged.wv�������������������������������������������������������������������0000664�0000000�0000000�00000225523�14662262111�0016653�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������wvpkN*����c�����"V��Y$s!RIFFT �WAVEfmt �����D�����data0 �BWWGVH��y�}�m�$�G���������� ��u���e`���_~1!"!!G9!0"01�� �G �Dl0[h@dFF�0�#1"iɃF�#"A hh` e H4 MFB#$A 4L$xc aB , a !5- `L&Ie21&1 0H�M&Hd=la`@HhAHi,d<[XA!,`<X �0~ �@LLDH@c,1zOƣG4 $@ !Z!` 0$ ,=ACFZbҐ XX~Z` H zX�< ෷ �A 'h i <iK4/k 0"@XBX�a^$KC C ` -<z5$Z�@/a= $@k�0 A A$`,�/Z4X?kA ,`A k@E�z=�� , ^^%/x ,=F"釖 2�k�h�!AX!ICd,1lD&D`@ �X�4_Kx, @C A�5xz`A4 H�`C ¯<4 , 0ZK�Ix, i,A!� x`aH�=! IC i,AK� ֐ a�k�Аk A� %iH xAXZ?K,=�-@!=% @ C�i҂אC/X Z C�XxH_ x�Ҁ4ZC$hKKC c!!h @�Ҁ_ H��4ihAZz$C/ika Kz`A HcHҀ�i@0A_Z^?�I�-@ ! @� _k �i@C€%HkHXIz<_KC %hAC <$ zh~@CX  K$҂�-=4H �� �� %a/,Hx!,`Akh0$`@Zc@� H$ -zA$a^ C!HX$k@C H-=Hk� KxaiHKCi, -H k@�$ H_/<  $H%a?�-h - CC!a@5 C xH AZ -CK /ZXC4X�4  $A � i�0- �XC -@/` Z^<^ aICZ���XxHXCk�?$ - XX ^c ZZ A/ր�@�� h�4X4 aaAҏz-<^  Ҁ� �Hih� iaH%/㡗$K$@X5� h/@c  =40^�^X$HZhA aA �0 !�_x� � c �k,!Ic ZCK, xA~@X~ �� = �I$A CK‚$ॱ$h�04%= h0�א Ic KXX,A a CC���ǂ0�4�!i,A KC/H^Z$K^�@k @�IXXKIZ01$`_Z�CK%/K?$` ^%H AxH�ǯX�` �! iHЂ Z$A% ihhczz`_x � ,,Hcെ A-`A i ACЯx`!c�I IX$ ?/a!-@C Cր� ҂_a/ a X�`!hh@e@=HACXz HX‚$x I C���=4� � iA XX$HZXK $ , i` @CCx,`4�C0 h xi_^$,H !5i� Z�-Az K㱤  @c,^K Ґ hВ-@i aHc X� Az`!,H4$HKCXK-@ !@� _ ?€�4!@!KC  @Ѐ,��@�� $HZ~< ,A�z` @4hhC^zh I?Z�IX �C04� z!@5$A$AX%H4 ���~a�5����%H iaZ�-k/A/`A ��Hchx~�zHa@c`X$,aa hz iA�ր�h@z@�׏%hhHCC�$,@ %Z%HxA�azH a@xHK?0I%KX Z_,za, xA�54�C Ax,�%hAiA Ic-HҀ�~_ ha`aHX^ za%�xhIC$K^44!a@z@�0 �` !� %,aIKCXX0$< �k`CKC!Ѐ iA’ƒ$aIЂ z@ƐƐk$h` h�!/=44 xHK $0�@CXk@  �=�,A$/ ca k`� ah�`ahz@C�ZIЂ$~i`I/ZK@€4 �c � `=%`aAǐ$H K4�=10$�� A, /Hca $, AC�4 � �h@CX ㅥ~ I hC!�� -!, , H֐/ ,iH^K, C��~ր!h i@ HcIC$ǐ A ,Ck��cA,�CЀƂ,K /%` ! 5 ���a A�@kH$, ?^ h X0! 1 ! hZ$IcI?h $<ZX�,ICKC !A4I�@z@0 _C!! hHX$,H_ $H �C�@z�4@CCzZ‚--Ǐ%=ւ IX A��A !5�� I~ aA5 X400h X,A~< iHҐ4��~kah $$-h -A X�z׀4��$hZ$-H?zc-`i,A =�4 Hch! ^K c/xiHCCXր /<$A$K㡅H$/AX=CC�ka@�0$ AX4aI~H ,�,`@�4� /< @Ѐ� H% 4XK C/@�!=4 %��0hA4$kA~Z X�k�4 x  �4�/@$ 0x X4 � %ౄ! !5I$%H k KC/K C �C�~ C0 A  -h,,KXZ� <�@, ih�Z%%A-hZC�hh@Ư_!HCC0 =$,HKxaA!IzhZ XҐ�AxH0=~0Ƃ�`ahKKC k/ iA~�A K�, , aH�~-/�H0 ,H�iA~< IZ%Z @_�`� �$HKCz kZ?^�$A$ C� k `!@xa, %/В�,AXi �Ɛ �C��XxAX@ hKCC�50��HZCKK$a K%4I!=4 �C_/!!�1$HcHKCЂ$  � `^ iC€%AX$ /,^�IXXXX0 1^c�AH` ha, xaA Ix!h  4h�ƿ~i4 `HxHHZ? CzH^_z�H$,HXXKX?!,= Z$5!@k$<$h!AZ$,,HXX , @c�4a@� < KC 4$AKZƒ Ax 40 a!H �KCK�- ǒiHZ�~iH=zh`���k@C Cx -@i=~HKK-H� az` �Z/<k@Ak`a// C Z , H�-4 aHX  ,%,@  K��� 0 ��hC�$Ic ҂$ZCIxA  !`�5aHC $AZ?~h  ICK$AX��Ck H0 = �^$HZXВ/= ,kZXKX0/!`@H^�I$A Hǐ1 0 a@/@KI zA?~% I$hIВ�@CCƀ=04$A’^$�!`�_K ahh@c $`I$ax,-@xxi,a Z!a`ca@X^0h@ H$,, ZkAcAk/zi � HxC� CCICK$IzC5 / z� �ZX �%%%aIKK%iH  � �Ɛ0`! xA1,`iH X$ �h@/ ` !�0 @c Ґ,KXX4$ A~� _�Z�$�%H^-=!h�?0� �€4 H X x%@4ւX!@�Ɛ~i��� /K$ $AC z@�C$IC/ ,I?kahK IZ4�k��=$ k�$%/Ak A$,,hA_ 4!! ICK‚$ ~ - ^�^IKCCx_k<$� CA^ IIҐ K^� a`C~-�/ �04I^?  A ,AzchHCk��א$H%A ��� 4!``= 4^ <^�I KZ$ � �@� @XKC% - ^$A ��c!h cACЀ4�$/H^5 ,H$Ahh` @�!_/K�c᱀! Xa ZKCX$ �A x,�ICK/A HICzH4 0H$,H?~AX ᱠ!54 Ư`H @KKC C%K�hcAׯ7cXxaa Ґ ^& KC , €`H_0 @Czi� A4$H%` 5� i@K` , A$/ Z�Hz iH 4�i�XKz`aHC$H aaacH Hƒ405 � zI C�, a ZXx!�, �, ,�zZ� �X ? !- K1ǒ��Iz� ! �XI$, zC/`aA X�i��%0��HXƒ4XKCK^,h`C! @zCЀ,$AZ $hA�h@a�� i,aAxA XZxh-@`a` ��ƿ�c HC,Kc Z^$H!��h@AkAC $`IKZЂ㡵,A,H ~C0!=44� A$,I^?!,Z$,h @Ҁ�@zh@^c� c zhX�~KcAIЂ@� a׿XЀ� �/X1 ca44$ ,<$��0 @�a$/AX IZz= A�c!h@4_10`H‚/Ax/1_%A$ 0Ɛ��C_/<Ƃ0$`֐IIkx-%` $ C�^Kc@k@���$%HkIk`A‚ ZCC4 h@�h@z5I$ZA $聅1Z �@CzhHA$ K?` ‚$H 0 z` @Я�a Z Zh!A4XЀ�aH�~Hz`aЀ�H $@/@xAZ4_ ? �@�H $A-hx-h ai,aaAxAЀ4 = �c,�k@c h�ixIC ~XK$ H0� `!/�@�  K$%,HKx kHXXh�`!@z!i X$5㡗5~I$X� �XC� x -@ �aA0^$ 0Ѐ� z^x I4 `- Ix^$ZА  @ @$ $ x @$,@ 11$�~=� �hiH X$%`Ix,-HC h X4��@�`^֐0$ -`I�Iǐ�I$ @� @��1� CX$@Z<K$` В� AH H,<a�aAXk/AXҐ ,HI1% 04րX hi% Ђ !�K5 =�@$,,%  aC��~h H$AkH<ki,x $KzX �CkҀ� a ,H$A Zc,-`K$A!@4 `5!@XZ$H ~ AXk,HXX4 CCk , -BA� !AҐ!! Hz=�@�A€4�0X‚_z` A^�@�X i@CK-HKC CZ$H/`AZcAz5�X�C4_ $Ac- I K�hzC�_,@C ZCZ$HC4, $= C� h�!C0$A XX$h <h � 4�CCKz=5i%/z K xh c $AZ=��`HXkhЀ��X XxK$zi,@ KX‚�� a@�45cz IXXKCK?~�/AkhA㥱 `h! /<cAC�I,Ck45$ 0��ƿ^x$,H$,,H CkHza/� C0_k�^ IX4X^AX !4��_! A0 H/HC ‚$=Czaih!i� z@� ? !H�IX$ X? IK0!i`a`Ґ_�^��,A5$� �^! @k�5$i,A$aa㱴 ` ai,@Az! @AxHk^a 4$,@  A= a0z @ЀaHKK$,hAXzA i,A$ @z� hC!aCHI ,4H A_AА0Ac,`ih Kc c-Kia,!AA���< �$<$= $H$H?k` kh X4 = h@�� z <�`=4 @ aAx i I�A�zX�!=@^X CK$axAX%C��hh5�XҀ� -@ 4Iп�aZ�C �AҀ� @xihAk,, x x iHX4 @�!��1-� !=�xa -Z1 XX,� �� � ?4A$/Hx=~X%H h�@C��hz %<$HX^XXҐ A X aHKhc4 H$,/K $hiHK  !�CXz4ZZ%<zA$hZ$<55�X�$`,@�I�- %<Z h XXxHi@CK�@4=$%-HZa X^��=�ah@_�$@Ҁ0- /hAKZCX$aA @�@,!kZcA X^C,,,/A hc� x C�@X$% -AH4 5@=ZC  �C h KXch ,,%`I$ A 1_IC�h@ -@4$% �,A�� =� 5�XCk@I%KXx/?^ , X!�� a��k��a x 4$?A i�X!`x` ` @�$,,HXX?/@  -<5�z` =/$�$ Za Zx%$`aihih! C�=4c� =$iH X xI0 ,=$�Ҁ�� -`@0 I$/ -AƏ%  , XC10!XA XK$A4�4  �􀆀/ 44 =� -?A $,! C@c�$ /A Z?H Ђ 5 !k ���45iAz!h Z �01�/a h @C�wvpk��������"V��2��Y$+DBWWGVH��y��T��A����������T  ��R���8 �}$A$H~K 5 c��== ` � a aIei , I$ !�6 А!!HI%AЂ-H5�C��� <��Ѐ%H%aI^IK$a 04�k�� , IzaIXoK Z� =c 4004AZ Z$hC�A$ 5`,`h=@~ihА4i,H%Kc iH �hh` cHzHCc�Ac A X1 $Z H4 �@^ XCz@ A, K1 H$I X�4k i`a@k` X^ i,a IXzH֐Ґ=`a@ ?�=0,i X`AZ^!0aHc1�^$KH? I-05�Hx0cha`!@I kx X,KKC�4I/= @a�^$H hIhA5 ��4 H CЀH�IkH ր$ I%`zHC� �= ,`AZ$C/ -@X$k a,@�� C�hcIXk@XX$`I 4 ! c ! @ I$$AxA^ cA5@4444zHz`А�H/@xAXX? h I��@�� ^� Ha/@KC A A’0^!ahC��ci@Ѐ� K4 kXZ4Ƃ^X�~- @C� `IC AK/` a Xc,45� ’ IX hAK AxHz0� `aHCC�z ai,A$,I륡5!I i i@CЀ�_/�C� i , -K-A IZC44h@�4���ր5$Ix�5H^�0 z0hza aa =�`AhAx a $i���x0h@ZKX$,A?k!h XXc�,< ~�@Cz x $  H h�@� � -, -A_cAC4Hk,HXC�C� �� Z^ ҂X0^X c,h�X�-<׀�K^-4c Ђ I$ !����aH �,A^ AZ?ZX� A�Ѐ!~�@C�$A,i􏱖�IЂ54 !�450i`a`Az@z �I4^ I , Cz�k �XЀ 1$i,,KZCx AkH4�!�Cz$ �@Ѐ5 ,$��, �z�~ cCЂ$,hK$AchI-`aA cAa @�_K� z�h` ॱ ,a -  A-ZhZ44XZC i%%A=,ౠ€4 �Hx%@ -/= Aa 4X0 =Hh!544HA zKC,H֐$�ր�` h!_ 0$ $,,/K%$ cI�-! 4 0�Ҁa xA Hi% 4 =!@~ ,�4 �  Iҏ륁א-`0X/=i@54ǒI!aaKK =!k�ka� Xxa/@I /k aAXIa�h@xI@�4!,4 @!H$`i@Ҁ4_CC @ %AX  H$,HX�@1~h�4 XIk<$ , �@5!`�~=ZX��a�iAX$ x$` i�CXC0ƿ^x�X hA$IXXZk`IC/` !�� � �€0$`i5,K~�kAC XX% C4!�~!0 =4X <^/ �x_� Z4  1�5�A%` Xxh � ! k��C���KA xA XKЂ@4k <�XxH! a XI^!i,A‚!`HC CА04%`-@$$$hA4! �  C 4^Ђ$,I?~zh$/�hր^! A4 ZcH 4I44~ �, ,��$HkHIci,!I$�h@_1 @ =%Hk,ac�0-`  ` !ih4 HX I4 CzX%H  i@~z! A�ր4%,`I׀4KCƒ �^/KA0$ I�-KC$` X 4 !�XC�@44@z@,ai, $Hz<$IcA 4�CCxC_/ @0aH^$ ҂ H!�C0 =� A$-H$@$I H 4ЀC x,a�@X$,hKX k $AҀ��@zAz��XKXa%Hx藆ai,,kZ  CC� Z��HK^� c Ђ C �~H��$` Kcx%` ƒ IА � h�czЀ A��I Xx!iHa C44$ �c �,AXXK1/=$ $a�h%!�@kHxA4X/@ 4^�X1c, HCc h  A5C$hK� k�ր� ,  AKz` i  zH � 0ƂaiA Z X$%% �zZ A <$ IЂ%AKc! $aih` ,<~a@ �XC‚ ZZXXH -I4�h�Xc�¯<$ zH ^ I҂!0 ,A i � �h�¯Ɛ� a,<`IВ^!-$,A XXc�04 !,@A!i5$haAChKz KX�!=�,�Xx�!𒆖 Ix^ ICKC/ iz�!@%ၥC0 ,`A$ha�&IҐ�AC_K @$,H$`i^X I X4!!h44h0A$`i, $ A cIc- KCC_�`� /za!-,A? x Ђ �@C�45�A!�%H В I$x4 h XKA€�0 aH$hAXI-`, ���¿^c AxH€0$/֐$?~h-X4ciK @�!0�,= ! A$ a�hh`a�k� xHZcz4$ ,haAЏZ ZZ !A,`C�HCz^ `I^ǐ^Z$0 0 i C`A ,ih $?,%`I �a�!=�z` �KCҐ$H Zch  K `Hk�Ѐ�zX�`=CЂ$h K ihA$ `0! XX�KC5XxAx K� � C C�i@z`a@ $`-@-  %h0�Ɛ!� xH^ $aaA%%HX��k!@C$I^4$Z � hC�k�kh5$H‚$A< 44`!@4�_k�0�@KЂ$%%15A$A0$= п0 ,iH -`a Zx HH, X4!h�x50$@~XC4$ICKC/@/@ I Ic z�i�Z!ҀH  $A   A�� i@IX$5$,A!I CX,@Cz� ƒ4$a /^�aA4$  `` �,<$�X4 AZ�!Iƒ IC, @^, 5! HKX�-4 K 5 ! !�!�$hAx%$/ -/�X�k � $H$a Z?X$H$ X x, Ch�HKcII x$KI$`HXCi� K, cAI-Hc- -@xA/,hA �CCC��%`@ �Ɛ H$, X/h,Z5�==Zc@ �5$hA�i X chI5 ,A CC � <ǿci`zHCCҀ $ h!?e AC!a@z@!a`�a% �֏^$K$KC�C�/  Zz $`! ZH  ZC! i@  K ?k kK � ,<$@XЀ@  C Ax‚I?K �-`A !!�5�A � ! Xh KI�1AH1KKC/ IXCk$%AZca! k@� �~-<0$ A�!I-`I1 A/a  i0C� I!hA^%c-AZ$a 4 ��Czz~=�-,K^caic-Hzhi!IcAkz�H ,�k@Ck�XC AҐKCZc @a x @Ɛ/ @4 C A ^ A’ C0�a�_�Ѐ� A$$Ic-Ah XC�4� �� aH��Z%`-x ǏҐ$wvpk��������ш��2��Y$+BWWGVH��z�}�P��?���� &������  ��W���1 �Q@ְa @ �?  A4=$A҃,��ޞ �ZZ$HXXǏ� K4$�k��� C€�=C$A$Az<KCaaAK4 h 1<m=FH~` 5$aHҀ� XACz� x$5A ,K hX4�zൄAА,hH Ak,,A҂0^, KcIx,c/ @�! 44$ I?$K -` 0k`^ 4HX% ҏ!- Ђ4豀5��K�� H$ X  /K?kIC-@Z `a� =!�%aK^CKЂ$H^ CCk ~ А�,AX$H~<$`AЂ-@ X @k ,<,<$<%%` H-H^�Cz c!<$ ZCXK^X/CK% C� Hx40ai,aAiK/$ C ��C�, 0 h$ 0^KC�hka@44衡!@Xa-@Kx�IZ@� �ЀkXz4�%4%AzAxh-@XZ$i�K@!�X$ @��hHZ‚$-㡗4$,` �Xx/�@zhH X$ ZX/@$A ! z���-!A! iKC/` ZKॡ cA ~1H4 �Ґ44$I�ICxAx h@€/H��hK^?$ 04$@KC 44 = C0$i,@^ Z�IX A��ƯXh@z`aaihA$AZxh A‚-@Ck�04א��� I-` K/I‚Ih�Xxz$ �� Kk/�5Ҁc! @IxHzX4H ’H �%H!�k�C� c,h4 `,�Ђ ih XX<, i,  C�~Ix �! $aA xI1A%,,H04�Ѐ^$��` K Xz$,,% 4�@z ah50,<A ,,%,ka Ki/ C$ @_�$= a0x   xiH^��= h@~XCC�0aAh K? H^Z�az�Ư_< 5�IcI% ~ $HX cA a ^4 !4 4^X H �ր0$<$��5X�A$Hk֐4%HƐi�z-=!��� _1ƂAKkZ�!��_1$� `A A X A$H$ �CCCC�i@cA, , XkH$i,IXz X4$=Akh@Z70$ � i, z<%@k` $,C0=4�iƐX /%,!I5 A  C�= Z A4$`�K�I KCK�/A�!&XaA X$ zaHk,A4 44 z!�I$KX1$ 4$,AXXzX�X44$@,��, I$ ,A~II,a � 4�k ACC404 �ixZ%`I= a@CC @�! z IX$IZ %K�a@C€405��kX I?_x�/H҂$H zHcaz  �0,HЂ I%H- @0�/iCk��@� AXK$a zA ,@Z%,ha�C�0 �K‚$aAK Z~ - %aa Z� ah@zCC�`᱀�IX Z<Z I X %@Z@ !Hc! Ґ,a C0 xAxA5�0 A�h,X ҂$x%`K%�a04/�, ,<x,@$A X$,a ? K ICЀ`z!C�4� @א IK^?K ,/H Xz=0 �C!hAX^ Џ_AX$HZC k`5$h@5�顱h ^ ?^XҐ xA$ 0$ I?$@��%K%% KXK�-K�4 �C�,A I zk�!,HX!CC `@ @CC��= KcIK?,x -@KCK�@zh,<$ z, H� IcI^ $HX aa@C� 5�_�ACC h,aA- -H% I^4�i�Cz A Ѐ4�!H^ZZ I1$%A @4�C!@c �!`H$ha -A/ ,,AXzh, !HC0HC!iA$haAz- ƒ4,� ,xA zҐ�i -H 4�k�za@!` haiH$ _�I IcIx,@Ѐ�`5X�A ’x%`A I$ �h@�!C�x -,HZ kkI X!h@C� `_/4 4 =!A$/$hZ�ac  Я54� aH^Ђ$�hx�$< HzC ,A,AX K?Ґ!04@HC~5�A$ॡ$H?h! ! @xI� AxH5$ iaIcIx �^ �kᇤiCC�i ᥡ K?~/�ㅅXz�ǯ!AcaH�@�44$,! 4Ґ Z% I = ` AA$,A~ AX KCc h! 4^4 Ҁ4ƒ�� /HX^~ iA4 ,A�~0 =!=hh` @X!-�Ґ$!! A’^�i!ha@@! ƒ I? AЂ4<п�h, i�/@ K^Xր!@¿� KC$A$Ix^! X$HZ$ , 044X ^zhAxA  @ KXI0 1 �€� �z4I1 i,iH?i�4k�$ �@֐0$HZC$/z K� 0  ahK^,ǏiHkH$ 4 iH�!aCXX$,Ix 0A! KC� H @Ґ�@Ck� % iA?~%$H^4 ��!�XK k?H �$<`= Ck@Ѐ^Xh Ђi? xICK4Hch=�aHZx@4 H$iH‚$ H$I , ,<!@c=$ H�H%%`I<X i,I `H�~`I @CkhhH^�~AH aAk,a ��@Z@CA-@/HC0 h4�顡! @�K% - $,ciA-` Ѐ!``_/!HCC  x,ZKC/ahA%AX��hXcC� k@0$ICZ ^C ?%�KҐ$ -aX z_  � IkAKC/` ?Z4$!H Cz0H0@z`a XxAC  �-0H?ǿ^x@ 4%HX$H c  ֐ �0 ! Azck@ �Xx  Xk H% �,�4���! HXxh C/= , KCX IC/@k@�AXxHҏ !�5$,,` XIZX @/`0��I IXX?K, $AXkhACZ CCЀ4�KK$ -Z^$AxA$!A0@C5�h`%@K/ah Ђ%h4!h��i�C K^`zaH-KX4!@ !@CC4$iH^z ?  I$<04^ ! aH KcaIk$@‚I‚Cz C€�zAK$C5$,H$HkX���44�4$iH$aAK~ �-,H hc <_/< hH0$< `A44C/Ax  = h�� 0h�  K1 A‚$,!�C0^�$� @XK%H ?�I$H A!~a<$a0Hc hi,-‚4, ‚$�XX^-!aHCX!A Zx! $H4AҀ�@~<4  AX ?h �^!a_I�  X%kcHz5$HX�@ �C^/=5�,Z%`/ - � Z𐄇 ! kX%`  Ґa ,@�H? a0 hXXIzAX! %AX�H a ?$@�hh,%,H$H%!IЂ$HCC z@_ǐ h@cC ,A <ւ H4 =!h` hC� =! , Ђ!- - $KX �� h@~=!CC$@KCXZЏ!I%/AxaA�z5 `H @KҐ^ҏ/@%H00 =!Z^0Ҁih X?!  Xk��_k  C� @ I$, %HxA!@_Kwvpk)����������"V��Y$r$BWWGVH��{�}�N��:����I*������  ��i����^/@X i,IC4k XҐi@z`ck$@Z XCZ x X�` �0Ґ0! =H-`Ikh<ZC$H! Ҁ44@ H44xA hAxzA@ �K @C0hZ$hA҂֏ւ hA’ �xX�!@�,AI -h_�%A  C5�4!=4 I!-A?Zk<^� 0kIX� $` iAǏi -x I�a hh,�4�-K$AC/ XxA �HA= i@�4 @ Ђ%HXXXx< H$C0=-C$,H4Ic- X$,H!h` @CXk X�h`!  ^$ HK ZiAX�@� i_X0 =$!-@ 녡HzH! @z`!h�z= �=‚ Ic, X^%`, Hc,�!�ih40! -x$,!H�i��a`  x I Ƃ,a,@ h�k =� �h@�5!,,z ZА H^ZX� @zha��,X%A$Ix</HCxZ xA44 ` =$`, AXKK `iHX 5ihHih � C� A 5/ aA4!ah h��z- ` h@Ck@4$a ~ -  Kc Ҁ4�zƯ�`C0% �-/?Z���z၅!�h�aih xK$=~hIƂ%i,  �ҏ54 h�� �5-ƏaAX^^�k@54 �zZ =�AkK5?aK^ �C�X^x� 4 @A X~XZ$A‚ HxHz` HH @Z Xxa ,H0�AA0_ AА5�4$K^XЂ ihZ5 臀0_€ C i %A xa aZ Aa hhz@x!H�H�i Ђ$5c C Ґ~,!^chhЀ^�^z,i,  HCKC X �H?_c,�C��K$`-@ ZX?^$i, ZC C,`���cI$ /^I � H,`h0Ҁ�hX$,,haA^Z ZC�5� ACX$/ihI^Ck%hAC �� `IZ^Xx` X‚��= = �zh`A,`H, H$// ^ KА0C `AA! H /chI $@ � �А€~1!i I?/HcaA 1^0 Ck 0 a aH� _khA$H�zЀ04!= h�4$a CKҐI‚$! kh@z<! 0i,, a ?$ C/@‚$A � Ư0 HH�IЂ%A/H/ �,i,aIZC/<ahHcZCX i,axZ`HXX 0$ ,<  i� �$<�@Cx$HK44$A/@ C�_!0!A^%/Aҏ^ KXCC 0� A��_Z%@/H� Ɛ<$� C���-A, ZX? 4$ 0 !h i0�AZ% CK$H �h04 ! �, I$i $`KcaAAHa_k 5 = @� ih XX ^x 4$ �!�H$~A xAX^41~ @-�/%H‚xhaa,ihZ4$�kX�KZX҂$ IXC�! ZXz@�C aI4HKK  A0~a@4 @C롇 I_!i,,` /H�€zH, H@$ 0-`I~�$H~AxA@4/<0$�XcH%KX$ -@Z /hKA CCCC_/ `4�$ 4-AЂi  藀0$�4 5%H%A AK@C z@$<CC��Iz,I,-I A KcIA  !�=�@�  ,/HXX x /K�^$IX4kh@ Ai   ?z XА�z  c��^!I %@Ƃ4$i,AxiHC�H?�  ,@��! iHk�!-`xi/H^i@��CX� @�!i,,hiHZCZ$ 5�0 _CCC CZ ,H$HHH�@_CXz0$XX $/�xA aaAi@�4 �X =! aA $I%K$A!�0^cH4 = = =$IIZxIC i%H  C4 �z4€/,ॡ%<zA$,@ a XXxZXz!@CA AX$ g H I�~-�C���xa,aAi㡗$ IkHI���0?z!aH44 A c-^I4!@C��AK‚%A/i`I�I�h` �_ C0 ,�/ IA/hH$A$� CziH0$ , h顗$hA4$ K4 =4�50ր~ CZ$<$� ‚$!I XXzH� �1$H$, xA$aXK%,A CCk�c���ZcA�Iƒ$襱 , iiHZ�@czh,<4X��i �4Xc-$%1, X-��!�Xk_4�XҐ H%Ck` KC4 =�, H^z�$ `A%@Z㡵 ^C � X0 KC� 1h@Z$,A^K?~$ -,  h AƯ'`4 !kH^- X?kIA $ CЀЯC hAI֐$/H^ CKK X�CCǿ^CCC000$ % �4� ^$Xzk C!a@cЂ h,A~<^ $A 44$`,� C4a AX$HX,A�@4� @_^X�遗A 1 X �  ��0_  !X  a X ?^k , a ^x,<$�H? X�aIc ^z AXz�k XҐ ,~X-? X hAC�X��X�� a i㱴!aa/z X� Hc@� 1^ -@~ -%HXX� �^cX��cX, �IXK Hx  !!CC �hzhhAC $ICKXx<iH 5!k  $ x -A$H$zHCCzh�4 , 1$= h@ I ,A ~< aZ H�  i@�!aZ$h xIx,?4 I֐ @C� �^40 ��4% K5 Iii Ɛ4�Я�Aa,aI~X 4$K��1!h` @z! Z~44H!h�փzx'`h hXC IC/@ Z~ Ixx �a��,hX�‚IZ KzC! �x` A�!5 X‚֐$A A $A�4 Hah@^@zHXcHAKcia$` ,�/ZZ � � !h@z$��� aA?Z‚$`Ix 1Cka@04,`aZ� Z�- A�!@Ci`!@�Ђ4 I !IcaAҀ4 !��_1 @� iKX  I1 $%hA�X !i@zHCCCC  %H%,xiH -�0 !� <0 A%HK$-Ǐ5$%ai H Ѐ�� a ,IXx/IkA5$� ` �� @XX Ki �o$k/�1$ @^` hca@x  -@?Z�KC�遅^!14,  ^Cc0  �A� x,��~  0� ZCXx $AkZ �4�a_�@$@KCZZX50, $@Xz@4 a0hh xA^- X  =�=h�^! i�K$A  - -`I =  ka A�h -`i%$i, KcC!�¯C�@k@i�KcIC X$A,z= I^X$kAK$1 ,A4! ��ׯ1�kh@CHI A녱hi-5@�@z`HЀ44! � $H ,  -k ,= =$< h- 4^kHiKX?zA,,HXi�k��hZ@�H $A$-/I$aHz聅��^C = � -$`-/H K‚%hA0 顡44 � H$ I^?! X ,<4h@_Z$<$H - i h   ��ƿz �€!hh AZXz,, I�C5 @�h@KcIK%� %` �h�0 �%@/%,HZ K�a@_H�kh  1 HXkH c C�C�4~0$��hAX X Xz- HK$a`a~`a =,H,ih �IcA‚ICzzH�ka�XXcA‚a xAXkH?^ C ,i��, , ,<?^Z z`  X I =Z Ђ �k H$,`HCCzhiHxI0$%Z�k�� _1`Czz hKZ1-`Ic-/ I ���Z0!@zA  /HC$ i  ZC~- 4h@I$H^Ǐ H$!�@cHx@x%�50 cIC ’ I?ZkKC XX-< =��^/!iX i�/H�%A0�@0=�� %KzAX‚I$ � cH5� _CX� C� i I^? $  i`@CCxH� = hh@$,A$A/_,KX X1ր4� zi �$hi, z !-acH/@X$ X�1X� `zh@xAЀ$H%/AKX/@$%�a�cHc04! ^ IC,H5$ ,<��4!�%%aAZ ICX ,AxA�Ai@c!/׀4��4$H�iaAx5K$AKcAC �_C  ��h,H-k, I- 4cAC�5�, i@�@$H I�  -kh5�!=_ � Ac, , IKX$c!, ^C44@���C0$�-@4!�A$ i ЂXր~a��4 �KI$ /ZC ׀!@� @_�� �ZCВ Zx�!,<44A$  I/ I5�44h�~I? 0�Ґ4 H  A$ K�$�! 0 =`$hA ƒ~x-  hA!= a�k`cz$ 0ZIXXox aA‚4�a€!~�A 4% 4KC ?aA‚$HCC� C€Ư~�KX4!/@I$1 i,aaA!5 ,`@�ƿ^c� ��H?$iA㡥!-%H$hHXxC/ �zH IaI ZxI a,Ѐ0��_k`hh �� � Z^$iA֐4% �44�C€~-!!!h@cI$Ic5$aI$C�a��~c A^I$H!_a- -`Aƒ �4�KCC� ,A X$h ^~ ci I$`@�h z / 4 x,h`iA-HXXЂ~ iH IXz�ր'<0A�!z HkK%<z=H$HX!� ah  Hא$hZkH㵤H,�ca5 z@44�z,H- xihihA$��@ ҏk�HA ,A$,h X ?zICkH%K!@Ck`HCkhH4 H$hKk/ =0$AX X~H�4 %` zA, - ,<z`$ ZҐ0hi-@hahiHX!@���$�Ѐ�`A%A h X�h@0z`!�k�K?$ Xk!-Hc- ` !@� �4X�րրH‚�/ Kca 4!h@CC�ZC~@€4A$ ,A ?k ! KZ`!��~%<��$A i%I C��ia�А�Zi// ‚IX0h`!@ HA A KXKC c � H1_K@CАi,IcIx / 腱H �K��_�04AZ$A XKC Z-aHxHCCXCz144� aA$^$ 4!X�A�4!h@$/Zc,I$Z$hA, H40 �H? IIXX?ƒa $H ���4ZiCzX,A? i,,HXX$AxHh44hh�k�A$%,A^ch ,AX^4 H !=4_/@C�%@ $, XZ㱖 Z�~Ak z4 !`Hc`Zx@ h@� ih % 4Z%A~5�Ck0 CZz, X^z,a$KK2^ h`aCk "I//�ր�aX_$,AX4 aA!h`  �Xz`a A%%H? I%A^�Ѐƿ^c@ <�X KiA xIch$,ZKh@��0~ ZK$A$x�ai,aI,X�k�I Z-�XaI541=!h`/�  ,,,hKX/ 4-h, xAC �� xX� a�4$, X$ XkH$ � �XҀ�h@$5$,iAKXz ,A a0!׿ 4 aaA I ,ZCZ‚1X᱀!@ׯk�Z �AA%@/�/A/iHAxAhHX�5 ` Ѐ�AI?~%%%`i@z@cAHxƿ^c!@54 %,Hc% a $/,HCC @5c �@$�-HXǐ^$aA$ ��ׯ1 a`0//@z= ,aaI^  ` hhhHׯc-x�$a, xZ` H%ZC�$ h= `,��h`A�!I  I X$H€0^ �$`Xh -cIXI4$ Czz@CCx,`  ^-C/H�- -0! hhh@_X^X$aA ?~ $H!@�k � = I’%hA$I?z!aK/H xa0@C� �Z֐$,5 <<i %`I ���%< I� !a`IkhA$$hc$ Cz_ @�ր $AZz~<K Ɛ!@i</a`c iA4,a5$,H�i�ƒ�� Zx4$�wvpk���������2��Y$۔hBWWGVH��{�~�\��?����I+������2  ��I���0 �ZH Xk@O? $AZck@4h@ƒ4ޞ0AҀ %ZZ, $K4@ x�$h� XCփ$@�-/`�I%i,AIc,@X~hXCi� hih Ǐ$AX hA!hC0`00$=@𰰀aA KCI K� =H/ AzHXh鱰44 A Ґ Zx, i @�4 hHII~< A$hZ01 _�!@�0�I$I?^KaA Ђ� i�!��ACk@ ,H I$AkI$,k I! i`c A1@!a hAX^Z Ђ$ X��Zc@�HcH$%@K?~aKC X5$`,�  HC Ix ‚^XKC$,,a,�!h� i@z0$ X$%`ic!Ici,,HXZX `kHC ��II^XK�^hihA0 CCC4 �4$Ak,AxA‚~<^X Hxa ICzH!�K?H4 hZ X~�/ X$A�0 _K? ! zCCKCz -HX� X  ,<$ HƯ_�Ҁ4 `H-`I%H?Z4HCC0��� �@Z‚$aIc%XX,A�5@�~A$HXXKXci IZ~HC�4 zC!ihA$/Hx% I �CЀ� _/aHc!@C kHI’ր IcIƂ� ׿� א%`!IK% K! !!@-< =� HX aI%iHC/@ $ i�XC4 C@ HX4 AZ~< % ��h�5�z, i,AXKX^ ZcA�XZx@А0�@ - I4 /^ -@CЀ4�0 K$A Z‚ K^ 0$@ �cACXЀ�H$H ?k$,za, Ҁ4�4 ! k5$,A?kZC i,A !��4_4C ZX4 ,` ^XC �44 @^/< X��A, XkH֐?$`A  4�i/<� zHz` haiHAƒ%iH4$@�H =50ׯ1aX H ‚�- -H? 44 XZЀ�_/�HX�i,A $ -,@^�^0�h�k~!!@�$AI􏡗HX XXx�@@$AX%H�aihK X�@Ҁ5x!h@�IxI4$ h= =,п^! zH$A ZCc 4�^� ah@_Z@A^$a0 4$-� �$ HxHC֯!1I ^Ǐ $Hz€4 @x_ hHcH�4-`54K!A ’ր�� xICҀ�,//HǏ! Ґ$Kz��X! H, KX%@//?/@xAXXXҐ �Ѐ0/ A�50$A‚% ,Ix ,H$A�Hch=0Kc C^$/Hka Zk /AZ C `4za� ��@ X Ђ$hAǐ $H�`H C%`,!<�@�-ZXkC�4 ,`-@Z0 zXx`a €AK$h/‚I�ah0 !� @45�- 5��Ck HaHXx,��1KC�CҐ֐$ IChi`IC!A�A� @%=!� Ck%H‚֐IX^4 ! 5x!i�X  Zし$=,h - �04$<$ x@C 襇a X =xI A%h,�Ɛ�Xп� ,A c  Ikh`! /<5aHz@4aA �H,K 4k= � C4ƂXII^KCK4$ X� =hC h@ Z$  A c-@ C�!0 @ci �� 4 $$Hc aIx,�� z<!@ z@Ѐ4�IX^ HX4 !@ @_ HcH HX IKXЂ CЀ0!���54 �Cc- Ђ$H$@4$ !�Ѐ4c� �C�H %H֏0$ih kIc,��,<~x[c@z0H'@Z~ @$K @ XЀ4!� Z$,%,AzhAЂ� !@� _ X�4!@C $hAx%AX$A X  i�xHX4 Ax/I/H^c CAHc0H‚$`IKx- ҐIC0 !k@1$=I Xx i< XC$hxA!�i�4 AВ-x�Ђ aaA   4,,`A$H ^ZZ�^ h�za  C�CC� K KX$,H!� z5��4H5c~ HzAC4�=$X�hca5%I�Zz, a X/�XC%5@ z A%,c-H X�!4�_z@Ѐ4 ,HX ai ikX �Hza@ׯ14$!i`hAC KX ?$- -`iH4C h�0$_1H�@! c $H^$IcA%`-`   <_ AAc =,a$@ XIҐ!a@CƯ444%aI$,~A$H�@Xk`% i` h!= `i i,44$ K^$/�ր=4 _zH�a/ZXc Z/�%@X0$�Ѐ5i@�h$hzACzACI�hhC�0 `A%<֐I4ր4 ��h/!@C iAAK�i�_!€4�Ђ$@ ?ZC IC/K ih@��h a4 c I%?ZZ K04 `Hcz!=4! X AaIx-HX !�4$�@/ahA~i %,%k!a@~  CC� X Ђ%Kƒ$,  @Ѐ4 @^ ?0 X AҐ$A X�iAX$AzH� @�5Xz,h@!=aAC%/ iAz<AZ$Ic 4`k �0 HIkaa 1!, izz zHa@ zz@z�ZC!- ,, H 4 = h@􀀁z X� `H ^ i@/`A4 k 5� �~�i�XxC4 , xI/@XC$,,/� i�i��AX�@ %A^Z`-X-H4! h czh@1�  �I X ^kXi,aAKC/@�=�_X�4@Ѐ�,A 5$ %H4 @0 ׯ%=ր4�h@C XKXzK֏_%,A C4 �Ѐ/�A HX KKc IzAc � �1a�KC!  A$h XcHX$5 �k��@4�=h`K^�ia, xA!! @��K� =XI$A/H,aa� �4 h@��҂$hA’1^CX,I!h�XC_~ 0hh@ z=KxXK$�z@CCz@0 $�!a/H$zAciZzhiHi� @z` h- h�KIk IX$@K/k@C�@�4 `?$�@CzI$aA zxzX$AK4 @C��C  i�< KВ^z ‚ICaHa^X� $,AX$H҂X%  h0=~ �X$AXA4 X�CC� C^^z!IccihAcI^1$@C^/< hHXX/ $A % - IxHz` �/֐4 !-@xA?k! I��¿�5�aH�Ґ khK '5IА�hHi@z �$ 0$`,AX�-HxK�I,C! HA �$=��C4%H?֒4אhA ��X5$�k@�i ,iH%4_А$K i �!�^C��I KxhA^Ck!A �^c@4 hCcA ,AX_x!H !C��z^Ҁ C X, Ђ^AXZaHx,a@�C4�c %Z$XKCI5$`Xz`H�ր <!5 HHX K `ᱠ^^/!ACi@xKCK,iHz<,@�-=$ hH�Xc�H -� H?$ah cI^CX�XZx!/� $H$`-cH4 Hxa! A �4 5$i,ACKI5�!@C~-`H�  H ǐ%Hc   �Ѐ0AC�aH $,AZ?wvpk��������QD�2��Y$sLBWWGVH��|�|�K��:����%6������ ; ��u���, �_aIX4 h` -h@� X%%<�I�%!4 Ac`ǿ~1� @%/H?K A $h@z@@KC�  �5$KX$Z$ xaAZKC� @z_c i %� A4!�C^�!h i� k k=I$,H$A�4 @Ư_c Xz4%hC$`A$aI =@40ׯ�!h AkAX<A Ђ�i i@0 =^054aЀA H ~k4 hA k H0C CX xaAXA4hXx_$< K? Xa 1 4�Ҁ�z၅!� @cAK-%axhIC ր4�z��h�4!HXkH$<A!�0@ Z׀�i A i%Z$<~h-�K  K4 Xzh@Z4 !h�0A$`IkAx~AC ^$hihihC�4$hh%xI%A!CK$` xA$= h@_ x  4!H$ x K? ,-`I$<5 `I?K �`! Ђa k�IX � CzC � H$/@ ’ <^/ IxiHx ր44�a� ICC0@$`I CKAK^�`=5z , ,``a@x kHЂ$xH‚ %A4 @X�<� ` H%H?%X$,iHCЀ! �€ !h/@’Z%CxA‚ 0 zca@~@�X%@Z _X%,���X�zA€1$i,A$i KC/%HX^444�_/ hH�hC��i~< I$ Hzh@~C А4$A CK4$ha44$<h�-C AI֏Zk<ƒ a%H0 Cx0  @�@ ‚IX?~ %@/<$=$!@��K�00$ Z%k$,�/@% I� i�롡5�z, H�� - -Kc / ^K^� h A^!i@�%@K$?Z‚5$A$ ր4Я^C �$  Z! ZҐ4Ҁ4�, x^/X ,%/ i  hZ0Hx-A4! 4K֐ ,%, I, AC0!@5 @CaA$H ?$z ,% Ck@�@_^x,@Ѐ� %Hxx_z - -h5 !Xh!Ґ,, _X$Z0!a@C@4 @z@ ,Ha-@Zx`- K�HcH�=!�0�@�4$aI/ HしA‚�h` �0ƿ~=�,�Ҁ0 $A ?ƒ ZkA$Hz`���aza�~HCX-HX!-XX $@А 0 @���$H$/haaaI x ,HC~H!@i k  =z@,,%H$I1  IX�!�c!! c,@C€4�CXX _¿�~A /�AC�Ck<$@zH0!@ i,aIcI?^�$AKzZi �` @X_C CX� @A I%ax%@$H44i@��Z C�-@4$x! 4^�a�ka�aH0$I^ ,IZ,H 04� h�Ư~@X!XZ$I X?Z hAKX� @C0!, Az@�Ax�-Z~<0$  Ak�~-< $A !@z@�ak @4-`IKX-֐ h$`I~HX� ?$ 1$<$`ihA /c- HxAx � ka� �4 cI$Hkh/=AX ,ihA�@��hZc i  %@!Ix!I֐�H` �C^! ,@Z$/H K㡗h I =h� a5`a0 %` h ?z!HA  C�!zkh5� I$HX_�א-` �!�5X� � aA$ i <^k A 4 Ѐ_A4CZ‚,K$ �x 1 44�X!aaA/ CҀ�4_ ,  !Z4 HCxA$A�$ AXx!HI%A/ IkhA0$�0 �^4 h A $IcA$ Z �c遅0!Ck! H4$ IX^ cA$` X h= =0�Zx`aH�@Ҁ-@%% Z$Z5hh`a@z�z�iC�  IXKz $AX�c�CА� z A IЂiA!@CX�_1�4�! $a C4 I%@Kx,5 HCC_^��hHc,@K%Hc`= I^�@Я!hЀ� `a $HXMZC �@C! �$=�I/Kc-�/AXH^XkH iЀ�@ZC! HA�-�/A <z XcaA ,`H�4=�C @C ,Ac,-HC4aIz,�Ҁ� Hhz=0! @C0 Hx ZZ/KkHiZ KAC� =X�$ ,<$$@ Xz ek`$@44�@�0$@C�$HkZxh-�K$A A�Cza �֐腱$HZcK / a X4$� hHH,,%ZC hK$k�H?�!A�@C -@$,%c H А0K40`!@� h I^% A , CXz ��!@XK ,A ҂1$ IXX�!�C_/< 鱠5X^^CZX/ 50$�@ hCCzH$ IX zKCai, x Hc,@CCzKA!` X$,z $aIZCX���Zz% �@k$HX XXc`-%` Ђi Az`Ѐ/��C�$ZzihI@IC4!@? 44 -@I �H -�zh�� @C =Cz,K$AZ?^x4^$ Ҁ4��4 k@�� AZC$AZcXX$A A�CC0k h,`,54 A%$h4�H CzHC =  `A-@// ,iHXXCK hXA=0 %@/Ґ!i@�@z� �!`,<$A ICKKƒ%/KҀ�CX��aH- KKK�,H$H @zX$<55!��- I$Ic ‚$H �!1$<ƿ~ IH�x X -?%!i5$A Ѐ� z�` A%/ -aKZzHK%A ^4! C HxH!I�-,HZX$ IcA Xx  ch AC$KiK?Ak,,/% `, Hx`!_ ,!5 `A5$H ?$$HKCK�@Ca�?K, , ! XC$%KzciA4!!@�€� �c x$ ,/%A 0hHcH Ư H0,A-/ - ҏ^ ^$hHCc /���@! xi,aaA xaA ziH xihiH��az�_/ zHC0�-`IǏ`h, �Ax,5Hע"�@z�@$,KK֏Z $ X! HxHǯ5`CЀ0� ’ƒ HKCZ  H, `a�ZcA �HЂIchaxi/HI� h@��� AC$IXX X ^ �����Z @Czhh@IX$-h^kxA   H�0!4h A Ђ$H‚iA C0_0ah` �AҐ$AZ襇IkH  A€Cx�A`a�  K?~H 5׀�Ck 0!=� HZcH!a X @4�^z� C AhiHK%-@zz%Ґ֐$H4 @xH0Zch@��- x $i㱴 $h/HҀ�@�4�� �X ?ZK^kH @ @x@z=! 4$-@K_h ^ 4 A�% 5 ?�!AxZ� �a@z`, � ! I4 ‚?ւH$`ICC ?$=$��a@$hZiAX H^$i,聥X�%C @ ^@KcI%A $`H�50h-X0$@$,/H~ i,  �4��z=֐�@��^C%h//HЂ�CzH_ � , �,H^0k$H$= ��1 aIcI’ 5h �=0@¯4 a X K^Ґ ! Z�! @z` �Ư_c@�AAx A $<~,-x I4 �4_Kwvpk)���������w�"V��Y$kBWWGVH��}�{�Q��>����������c Q A ��W����Ɛ�, XXk AXAK, �@CCп А4,iA-?/-@aaA’Zc, AC 0Ѐ�A aai,-@KZ$,`HHXAz0�AX ^i,HK �k�H?��hhihA�-hzCK C$HkH �k`CCC~hZK-@/ǐ!-H=$hCCC0 4 !z,,A%,/ -Ax ZҐ�  �4 A4 ! A$ ` ^zKK�0��!a`!`HHC /-HZX$i, xiH�4Cǿ~1h€� xAKCxh Ґ$` 5 @ �1!a`k�$` a ^?~%%//`��1! @C% Ii//chI$`-HX�  ҏ!a� h-aI?%hH-%!4 / -<H�@CC' 4^c ^XzH   ��@c1 -@X? A$H1`=!i!=@0 i%HZc4IK!!! @�~-`@ !=` $H‚$Ic4$AЂ%`-��hh@�0~hX0! X㇧% A x @54 @1 IXaIƒaA$@�A�_A�4X0$HX0ZaKI!=! c ACЀ� H‚^xh XX^� i4�i�zAC-=K/Z !C aA   =4i` �$@ ,a$ XX�4 A_@CI5$,- X$`I�` @Ѐ^/<�АA$/%hAZc-= ~AX!�i�_k�4�i�X% K^KAX�hh@=�@hAXKЂ%!ICz 0@�~� C� ,i,AKc Z^ �!^zH Z$�𐆠KCKC/@c%@ 4i@z`a` @- XXH A=^HkHƒ!h��Z �k@C��ZXC$ %_ � %a�@�4   AЂI~<AK 4 h�4 �~xh �hh@� K IX/$AzhЀcH!kZC XX$ /@ZCKC Cր�€! ,  A-,@K^?aA k@C�� Z!@C��XIЂ-/I%@ ci/!!�0 z ,X !a x�ᅡ%`aA �CЀ4�`AC􀄇,HAK 㱖‚10�A?z=CX  <^  -%H4 А4$ I?0$HK$HX? KCKCK% I, �AA @%@�- ZZK4�HC�=$ 4 a�!A$ XЂ%KkKCx K�!~AC4!!AZkA$AihAX�, i@!Я5! <$=$ $AK/xA%�� k @�@אIkACZ-,5$@Ѐ�!׿ �遅 K -` Z~K$A‚$!=444~x,4   h Z%A$hAX �€��_4a�X4$,, Z -< z1$�   4 h ZX A‚5 @0 �/�A0@k, ,IzA Z^1$ Ac^C��a-@x?K I ahHH? =ZC4 @�%,Z$a ?^ Z ``CCЀ4пc5�hI^ HX ,CЀ04`�~00�,<% Kx hihA0�0_/ �azHZ$,,,aaAZ~�z $AxK�h !=z`X!!�Z 4$%<^KcAZ AZC_%b ^А$KCKCZ? K� `xC1  h`A4$A $4! Z$ <Ɛ!@<A�I$HX$aakAƒ IX4$ Ak�Cz�!= =$HX-ǐ - -zA ��ZC4 `, A ihiH5X AxA‚� CЀ�a@�Zx`��,Ac /ZXЂxI!cAxH ,< @Ѐ�aC/  x �a 4Ґ!@�=/! ॱ4 ZZ A H@C HX a KC5 -` kC0 @0� ,aI$,ր$!C€4�k1$ ��AKcIX ?za$A!aa0 �zX�h@Xc@X ,5$ Z II  hi��4� 5  $ -H?hIXҐCC�4~,@0 X 4x,-@X$� !=~ C0a/`AxK -xAz, ‚ @C~ I^-A %AX4 !@ih@K0ր$`!I K -iK ! @0 A�`AXXxA X%/i%� �@CЀ4�@��@h@kH~AX ZXch $I$icC104 `~@А4a@I%`ihAxAx X$<@z i@X� Ђ%Hz  IZЀc<$@4  HxA‚$i K, i` @�@5ZiXC$AxA%z  hA `!!!_@CH X$AX<Ƃhi,aA0 = ^0�`=  c ^C %Hc-a 0$`aC^xh � H%AZ~<K$,%%HK�!CCk`a  ~X cI‚0�- -,Hx4 @h$ ,!H !� 4hAҏK aK�@�A��I$a ^Ґ5$I!�€п~k@X�`%H ,$A?H ,=aHC c,/ @CҀ aA/ Zz%�I4 = ��1 @�%/KX$H?K 5$,H4!5 !@�= ` A Ђ%`-`,ZC I�4 €_/<  0^ A ’ `IXC 0=@ a I~X/$aA ZXҀ0�~z44�!, - -H=~,A-,= h@zHAx, -<0$h@XƋ@X?^ H, c€�Dz "=@ �-  cK$HXXXX^z@�¿�XZz�-/<zi aAC!  !@�Ƃhaa XZ? ` XIXC�hhh@� H^I-,aX$hA��ƀЀ0�� Az$,aA KKX,A � aHcH04_H��Ґ$,hAXk= A ҂�=�0= `H4 X ^KC$ 0$=$�44=�=0 %KЂ?~/=~=$HI^�, A  /A5$�!ci iAX?$@KCǂ  @c zH I%,,Hk X$` !��^5� Ѐ^�$ XZchih 5$ �@cCZ$ K_$ -=ւ @C�@Я �  CA$ $,4$aAX�= ax€@� =%%KЂXK� /44@�<hA�1 @ X�-=$HCK�iZ^�00�k@,%,Ak,aK ,zA^� A?44C0z,��-K$ZH aA� @Cǿ^x@!IHCX$hA$h I CCCC4ǐ0$<�%hAxAzA$@,�4�z@0!聥�I� KkZ҂ @�10_KZx AK�I$ HA�041~Xx,h��-H ’ IxcAX Hx45�C��@ 0�!Ax $hAKxXZ K$<XЀk<!0 H$A iA14 Z 0 c_ �K-%Hc %/@C0�ׯCzX�Ha ҏ^4$KK� C�@ @ i�^c  ,%z-�KZH!h< �=_^Kz 21LA$ HX !@� =ׯh�-  CKHkHih` @C zcKx,��ր%$@ ‚ I H$=4  �^10$=�hzH^zaآa $`Ai I?$`%=4$�0րaAI$A< A x -AK C�~ƀ @� h $ ‚$ ZX1 i,,H$  @��a@zx���%`I$IZ-ahZ00_ �4 � IK$@ !05X i@� �/`A%` XX?Ƃ5֐ i,=0aHAk@~� @,iHҐ?z 4$, IXz, A@ �~H���ai0 xi< `AK  Xz`a`CzkX�0�aH K i,!xxiKC$0 釀��0֐HxA�-K$ZC0 � IXx^CC � hHIiAX?zA/ HzX�п4K0 X$ Ђia AK $豠!@CC�!_C0�� H$i,-x$`aKC  ���_�@%A$IZXx5$ AC��k �~-=hCC$i5$-`iC/a,iH ! !4i@-H$a ZZ$,aa  @�/<Ƃ�Ha= = ,A%H_C ,i, X� � i@X�A� �^Z�-,Hҏ^k�/XI�Hc`!~ !@X$Ak,aaA0ai, ���� h z C�4�’0Ґ$A !�,H$A44 h�XcƯ_�ci@ $H $A XXC� ~=� ��AZkA$A0K- 45� `cazH  Ђ%$h ?^ $!�cz @� !%HX$H?~x{I,Ac�05Ư'� � i�Z X ‚Г A $ i=�҃A/ �~z4� -H~xXO$h h@04_&<H�ZX%Aא^ Z@X% �XcAc,_d @zHD A�-�/h@cC!@ % AC akZ$ -K^? AxZ0ai= ,!h@XCX4$ih X?$a Ђ  �x!4 Z Kaxh X$K%�0�!@04!I$,Hxi,?~! _x � @�A0 @XH K5? $ k�K, Hz=!!@�4 Hk,AZ^c,^ ॱ4$@XX04^0A�,<A�-=^�E@�$`H~  5 h,HC$%` 44Ґ$HX$ H�$�X� i�� xAXH A!hH4п H� �0%A XZ$=~�$AZcI @04��Zx`c44 ,A’%%XZz5 A CCzz` a4 i,AxiH`I$ -%H0@�_k ,<�4 @$haAKK$CK I� _/!a�X$iא%Az<Ƃ5 ,�`@zc!A CЀ Ґ-0 ICK�aHC� = zhA,=�a`%H녡H%` X�1$ H� ai,, -AX?%iZIhC�a~!�,A%`Ic%$% ��Ckh�@Ђ,hAxaA ?I 04�a@z^xHC ,` X $ zAƂ,A �� ~a��ca�4IX IX-I�^ Z~`a`1� x! ��$`I҂$aI AKcI/� H@z aha@z` =caA!$^ ���@ ~� �a@ c- ZxH4,H!ahH!i@!hh@ ICZ �hA aa X0ca�4 ��aI$h XKXz �/HxA454X! ,,H$h^ C,�C=$= h��IXK�^k I = =@kA�h,`AZZX֏~ I/Hk54�@Ckh`=4"= -$,1 hAx/cAC0H@/ `aHC~ C/@I$A‚А hH�@Ґ$H‚ h ‚$腁!5 `% C!=�,A ^Ci X%`-@�@-4�ai,A /xaZ -KXCC�`H� =a$HЂ%H%`K�XЀ^x@�4$鱖^?iZ4A = ��h@z@C ,H%`-HchI$A$5 = Z �@05$!iAZ_x%k,,%a �Axz�@�hhhhH I -%i-/AZX5a@  I$5п4Haa @zH!`@ 44�CX-` $c--� = i C =@KXIЂ< i,A4z�CC��ah%zh @4 5- K4,i, CX�Xx %,ॱ?ƒ4 !AzHҀ�a` hKz@ !ahЏ%`A4$ ?CZ  C�%<A� ,%cH�AC��CK4 �^$/zA?/ HX K  C4�%`I~� $%@ 44 Ah�!hhC^‚%H1A,AА@0%ǂ�@!ZXX$ICKI$-@� -<0A/` Z? iH$ ZC0 Xi�K^$, K X‚ @x=� h!`5А�a`C�iA x!�  �Cx � zH!`= - - I‚ҏz IK�,�C�%ౠ!h@!�X$ Zc K?$ K^��@x��,= h@Ѐ�,AXXX1 ^-`i, Z�C�=4 �XxH X -@KcI -KC^54��֢ �KC$A^! /@XaIXxH�4 �!! @ $I$Ik A^ZK0$��044~x,H� � $,  �^_@kh`=aׯ1 c�h@4IX x hA ~ai��4~0�A /ZC/$H$`i i�� h@�z Ac a,`H�4􂰠Z�^%� c, �~0�AzXz@�!!i -HC/‚ xihA44h@�_/�!�!@C4֐$hi^X,H$A �= ^A�CZ,/K?K -`IC/ ,= C� i@�4�$aaAk IX�=�ka@% ,��@$- hai, zaA  ,!_C��@C !Iƒ?~~<Ґ$H@� @4~- wvpk��������"�2��Y$;BWWGVH��~�{�M��C����������  ��f���/ �oXH4$HЂ$x,ICa- i`�a@�_C H%@ В^饇$A @xHX/`@!h, X%HZВ Hhh0� �aH,<$,KcIKCK<�I K^�XkhC$ik aIK^%ZKX <$=Ƃ�ih�h,HC , ,=HX^ @C�!ǿ^xX�004$@ XIX?$Z X4!a@CпZCC $HX~ /X%,�! 444$IIZ$A% A �Ђ Z^c K�^ X� k�А�i,Hc $ ǐ�-@!I �4 `CCX @�44 C-x kH ~<^! 4^ Ki@z@C%@�4Ƃ4֐$AK X /h5��1�hAZCxAcHK�^C IXX�4 !z=�i@5�`H$,H X @1 <za zHz@� aiH$,,,AZ?~%�/@ iXCC�Ư!@C�A$Kւ?5 ,a!HX��Ҁ z@HX%4$ XA0�0�CC��= =�$%H/,@‚$,AK0c h��0$<$ K/A KKX�I��c @C44�=^$A IXIK %��h@!a!`HcC� k, XZCZC/ ZK Zc5�a0zAC!x 㡗$@ a0aHƿ~ hHCCzci` �$A֏ IC/@  HX ?!@4H cIKX$` $�h�!h@K�� ! 0$`I5 A 5 @� ,!!` @�!HX ,A xx!I$H4!� aAX% a�AX!hH!@Aa^c@ A z%,% I‚Ґ� !@ZCC04 K^ IZ�I4$ ,<$�C0   $`IK^/A�-@k=i �/ @ 4!IC  /H< %HX0 z@k�_ @�!@ -K ,4Ai遥14^  C05I$�AA X!CCC^C�hCxahAx/`,H I0!aHk��C0!ZK �^A Xx%@�Z!��HkZX$ K!,=aIKր5 ƀ/! a CI^^ i/H� i�za@4 A _%^ai, Z!hz@C€5��A��,HX  -hXK A XZC/4�4��C_XCC$ -A IǏЂ a $,C�!�_/<X H04 XK c xA�AXk�^/!4h��aA4ҐI�4` CX $HX􂰤?^z ,h,AX @C! cAc,��5 H^-ch %H‚ CЀ4~ iH$%Z hhH� @k^�  $I ҂ , XC ih�Ư_c@!�XIKC KCK?! ZH � C~-!5�Ђ$A` kH X H� ��_^Ѐ0� H$<ZZXZ㡵4$!Ix=ւǂ�` �4�ǿCXx, @z@�$H $AKC^ H$a Ҁ���4ƿ~H�XX, Z֏eaiH XX5H0$``Ђ %,H �X0~�A C�% - II/^-`A0XxI` HH‚$`  ciA$HXҀ� � <ׯ-hA Z Z$ -AX!@�<$^x` �$AZCKC/@%x!A4H� hhC�_ h��@k,aa 41zaK i,a! @�A$@ X Z@/` xKz ih,�0! ��遅!z ƒ$HǏ aAXZzh @44~, a`$/` X? A�I, �Xzc-< A @XҐ$Z$ X=!@C@kAC! !i/ X�IҀ�Zx I€!` x ‚0^X c,-@אIƐ��@ 5@��4�-Z$ xi,Z-Z @�4�1z ր�za,hEzBD A‚H0k<5�HH֐֐-z?$hHKA i@CC0 � Ѐ4 =X$/%i 1,$,HX�  Z/�C �-,`-KZC/A$ ’4�0 CCzKҀ hh` ZC Z%XzaHҐIzA!C�=^x@! �` xI AZK$aA = �^  5$AX%H^ -` C4 A^x� CX4 �ZX$A IZCKH,<!` @ׯ5� !,ACK!-@ @Ѐ4 = @4 a`CЀ$ -,/ /H^4440~,H�! 5 ,A Вh X ��CЀ  k@CZC$ x ^aA$a, `!0 X! AK X‚5�h-= ր�h $zAX/i`I-HK0�i�, @�Xz@4�-@X^/HA Z0��C/ iHCC� @$ -IKX?~ c $,AXa@ A0 i HH-H’?~h-  /`!0CC C�=KC, %`=Ҁ^$@@�! �4�׿C@C� `hIca $-`I1054!��@c_k�€�@%H xaAaK €!  a iHK^CZ$zA$`! ��׿ @ЂI$,,aI֐ I�Ac~! c-@ZZ^$,-,/ -Ѐ0@!� =$�zHxЀ^h 4k`A$%/ C�ր4!hc h! hKC!aA X0$,xAЂ- 0 =4!54!h@/ KCK?ƒ_‚$�=�В!!`i�I$,Ҁ ,Kk�4I�=AZ KICZƂ�H @�_ 4 hh�% KCZx顥!I C� i _c, , ACk@A /` � @%hAk� aCЏ-k@�CC %,AX 4K$%�4���4~-=^�-=$ai, AZ?$@Ґ!a CC A� @X^Z$HҏziH �~` =k,@C�@I$,H!-@KA�@CCc^I�ah� ii, z A$%/,hHc� � ����^�X$, Z? ,%AZ5� <_1 aH H$H X H IXЂ C�! _ c`�@ci,iH $ I�h@CX A$H  A$ॱ54!�CCx  0%Kx/=5A% ���@_ !h,A I㱖 XkhA HH <� Z�k 遅^$ hAZҐ4X HAz450KC�@�@CIXIxhIXX‚4@xH1Ƃ0! ’%AciH /Z�h@�c!h�z?$ca` -= Z_xH/hHX$ 0$�X  ?$�ZcH$,A Ђii1^%HX �! I? !`A‚ 4$K$A0�€� ��Hi�Xc@kHX^ kzah  �AX=$4!HC $IKc C/�/@ X$^x@4�XcH^ ZX?^ZA�Ҁ� hH�,<$�X$zAЂ^4$ HX�H�HX^ ZXK-@X X�hhX�4�Z xa� ,AX$AZ^ I44 4��� ���Xx ‚-/4$Z 5$ �=10�hhh,HI/`i/h,H-4 =Aւ~ C� cACK$` $ih `azHk!XX%` �IK�-K�4� =H ु_%H~ @ X I! zh@Ax@K@��=A4֏^5 ,hAX� a <�A�44`Hzah c-`aA K㱴,K%� �Czh@_c@0 @z@$`a X$ !-HCKCZ^ Zxk@ xH c��,H%hA/ A , X‚ �c!h AkC@zhzh I֐$%Ic IiAXi1� C� /X^ciAXz/x Z^� hh ^a wvpk���������2��Y$_:BWWGVH���{�Q��;����~������ ��l���5 �w@�HkHX @_ �0� c %H?k $HҀ�� �ka iH AXZ^c`- A X$H 0�Cx X�0!,A$ / hAƒ4 ihCC�C4 !�^/H�iAcH -HXxCC!4!hk I%Ka Z �Ch`=�=ch 0$ C$H-Hxп4� h= !i��C�0=�- %h 5 �AC ` H%@/�Z$Ick A4 ,@�i@�4i�0$`Hzhhx cI^Z� I4Ak@�@C �� Hk$Aƒ~K$` Z��_!@zHxh= `, -xihA$IXch-@Xi,i@ �Я�Hzi,$AX�4k�i�_IX?~,HX4�!Z�a�XX a x Z㡗4^�~AKX!?ƿ^ 0 � $`IZCZzH !@z�az �C H-AX􏡵IX�CcC� k <!a`!@CC H %xh-ЂI  `@�c�@$ xhA^k �0�zC= ��!a ‚%,,,=~<~i $H$H CC� Z^xa@�4i -K^K%�0,h@XX ? �a`ih ^ƒ$a�  C��ch�遅!h5H, /<^C$HA�!a@CҀ!`H/�IXk K�^�i��hhC_ cH4 @ hAㅥ!-HK?ZAxZhH0 h_ �hhihA XxHZIC/@K��`��0$,AXXXЂ�=~%A‚-/@�@CC�4C @C Z�5$iX cI- C� �=x,!aH44$! $ƒK!I‚$AZcAc444@뇴� -<aH K$KЂ@KxAc!,A @04==x,hHC�@zXZ I ’%�K i0$ �I4�i,AX IZВ�-@ xAXzHCC4_/< ��a 4%ǐ @KCK xAz,�XXx�,@�= IzhAX$ KCK4 `C_c@�^`A^KaK  0K�@44$A‚ZkH /H !HcHƿ!A @ ai, , Kx,AxAXX�H`!0!z0$`IK$€^CIXka! AcHcx�!h� % KЂ- K4ւ4�04Ɛ�aH�4�C/�zx  @ % / A4!= = c,@� iHXƒ ҂1,Z iA�~`=,A% /Ƃ$%H C40 4 @zc @ HZЂ􏡅$H �@�50HЀ�@ %Ax-`IK?K%AC4�@CЀ$ $ 5$@KZ�@��a`zᇀ55ah@zZCh ?z $H^CZ0$=� C5X�A �I , Z֐%Hxᅇ@zh׿�@�@$aAƒ iA1A X aHi@X~-< H �khz K^~<^CK %`A0~�0�� X4-A$,Z AAkhh@�_ a ,HXXǂ~I ,=5�  C0 I aca,HxiH^444 !^!0�H Z,X�IxhiHX K$aHC~� 4@0$h%,KKҏX Ҁ !h�z! ,=�`Aa ^0zAC  K%A �aCC&= @0 %-/? ai, C4c =_k�k`4!$H ^ X$, i@!A€^/<! Ѐh $AK@Z$AZCKЂ��zh-�HA��aaA ׏^CC끗0�H/ �I$ᥱ$HI4 `CC��_A�h�k ,` -㇖H I `` `XCzhc A Z%HZK-�/Z i�CzЀ05�@�!,HK$A HKIЂ< ,`h�<  XX X$a_,AЂ^C �405H�@$HK kha -@Ki!=� k ,<$ , A‚ z%@Z1$Z$`i`,� !@z,/@ X$AH^cAZ� �0k<@! X$/ iA<kZ i@Аz,AI�А$AA  X%%h44=X =! $4$HC$hA Cп4�A��� I%$A iCz�XCz, X$Z Kc`-`A$A z�1!aHЀ% KXXchIK%=$=0 ! i`,A hAЂ$,?%h,%/,Hi@!�X��i AaHZX   ֐$HXzXi@ ?� hK/ aAЂI%` A0 �Xz4 AC^CxA‚$ 1c �IK, Ak5�i@4 = @$ ^?~%@ $,h5! א !�A !-Hc-Z?$h hA X^xHC C_C =�^�, ’ a=$,H!HHz@H H%/AKc! X$ !!�4ZXz!@A$KZX0$H-HX5$-=��aAX? ,=4K �0�!@$,H-H Z4$A$a K~`a�k _!HCC�@ Zzi, zZzX$A0 ᇄ! a~ @ X, 0$  cCXxC4 � HcHzh $ KCK^k`A4 a 5Ɛ�,<$�!a $<~%X II5 !5�Ai ƒ% ,%akҐ,A��@Я~H�$ � H$ -%I%h hih X$�c k��/ ��HX$`I$Ǐ!I Iƀ4�0~=�cAkHҐ$ KcIxXK�� ACih@^H�  4ZxAC/�/ -�44�ca_!�^C $hzր$H!� oX/�  x @‚�%A$K40 ƿXhZz,a_E0HCA% -, �遅 /�4�@$/Kch h!a@�5 � `  I$-<x! k,$ 4�@� ��5�ACk!��!II?ZkH$`IXҀ�ր0�c x5 a!�-@/@ c-HҐI� �@~,�!!=IKCKcIXK%,HX�@0A 0$ihK^KA@�C��@@��€ia ZX ` $IXx C� C5!hA𒆖-CK I @4�hh@ C��0 IK^�I$H4ւ� i�� �54hh@~xiH �iZ �zHC� !�^$ hIXXЏO/ %a@XcHXk<54a , 5K  H h`a�a^׀ �C�X$Z%C5HX A�14 i�z@$ ’�iAX A$hZ�@�`0= !i�=АIK֐ CkA/%ॱI�` hhz <$h�Hai, 㡵�, ‚-X�zx =4$,H%h?ւ$-@ � Xx~!HihH$ xKZ?K/H4^ �Z!a,!X$A%`I/?,h,%H� C�ka k`,�caHC�K hA� AhAx !~a�440 = Ґ$H?Z4I%@��4ak A��Ґǒ��-A   z@��_ x`!hC�^X/K -iHX_ -@� Hƀ �C�444 ,`a - $,?^ %A  i0� ?,`HC K%$a!a᱄!=$�~/h` �� IC!-@!- aAza, x���zka aaAX㥱 ha5I%`4 C� @,<$  -`  <Z X$҂�1��~XC�5�@CX �iA$!AXKK�hh�=4 Ck�X�^Kc $ach  xAcI �� k0^ I4 -Hx<% $hA i@1!�= @khIkh Xc-`A‚$�� A�<�X�!�0$, x%?X ,aKX~h  Ѐ��$HX ?^ AX�@zC€4wvpk��������2� 1��Y$4BWWGVH���}�R��=����������  ��^��� �@0H^KX Ґ A$,KCC�@Cz-= hH4!`h ^KZ$Azz ha,aAXX� = !@cHЀH$/hA/ h 4^‚$@C4 ` cH � -@$AZ4%, K�h@€0 C�� `H^ I’$?Z5$IZXCz!�C^C$ H0Kz KX$5c�I$A h1�@�$,/H$ K? 5%,/�@z@�� ^c�4! a i, K$%^ H ВXxH��ZА�@Ѐא,A$-HǏZ-@$i,I4 @顡!<0�@ƒ hA%`Ih4$AX�0a�@_$= h` � C Z�kCK_$ iC ,4^ � %K?$@I��z- �zHXXƒ , xHI$�� h�Xc@_k h@€@$A I=X/hhAXIC�  <$ �za<$ ! -AK$iAx%h K%AC�% %h xaA 㱖%@$A�@�=!�Cz�, , 0 A ’?Z kH$h4!�� _z<HX��z,`aZ4􂰠?Z! XX � Ax/`i H  A %c4$Z, Ѐ`Hxz?X0KCIЂ^�-HX0AX� I,! A-@4Ƃ^C 0$k! 0A ` ‚ -?I HAc%�! ` X, X$^!-  �CzzAA� A A 44$ZZZCЀ� @��@z5$ H ?^  ICKC/�k@ր0!=^c@�HX!4 aA%xi/KCKCC A!@ 0$@$` X$H $ ‚i!@�40ׯ1� Ccahhhh4$ 4ǐ$a x X <z%KҀ4��-`A$hI1Z�IK !@�h�z� !�А^X~ -I%HK� @� =_x�H 04$ॡa ,@-H0��!`¿@h/H X’%H ,a/C��=�Ck,`aAƏ!a ! ` `z^C􀄇%H^ ?^‚ A�4 5_X!! aX�$H?^C Xx/ZX� �!,5$ - I‚cA A Xx`^CA? @-@ 4^ H’H4�ր�^x`  �~`ZzH XKX�AI$�1~5��$I iX$A!@z0`xi@�c @�IiKC$K$I!�0�@ k`@Аz5I%-@/ !@��H�aH0 i,A ciA a  �1�C hhH$ -iI^C�ICK!�=! � CCzHҐ Cz ZC �XC0!0H҂$?ZA X  ``a@п!AZXK_I !����= hCC�XX/ xKX^xZ�Xƒ�H `H akƂ�C%@/z`IIi%�c,! ��hza@Ck��^K^C/ ` 0�` �^  h� H hi,I<$ �54 @cHc�h@� , %CZ -@��4_ �Xc`a,aA Ђ$,IK -��€� @xC4ր!@$H ?^Ic K `,�0 �^C�@C�4 ,iH ?^!aA/@Z0 @z@^x�KA�!a XX~AX^%@ H Ђ�A ��`c iXcHX4aI%HC X X�ah -<$ a X$A _Ґ ,hAxaAci i@x^� A %`-XK�^/`aaAK 44�A� I,`` @c` ^  ,I�A4K? €� @C-A/Ki!I�XX4�0� C�0Ix^�^/@ ^� C��_k, A A%%%<% -%A/� �Ɛ� ~@�!`A$Ax!I֐ 04`@x0�i4� AK1C K4$ X0 �_1�Hh@^ X^^CKH Ђ 4 �/�A-@-A haAKXz� €�i@0 h xA %,㱴H-Ak i@z`a@C~kC4 ��4$A$I?z KI �4� �XC4�HC��? -‚-`-@ zI ~a -H$ /C/%X �5 z @C hh` CX$,,/X/X CaC  /@Z%?K�i/H! a C0� zH Axa Ђ$cI hZ�@_ 1�@�,Hƒ%AX$H zhCC_140 A?$A xi!ah@Я @��@�$aaA -H$$% Xkhi, � i��4ƿ^!H@XxH’ a x x ? ,`a/ -A CА0 =�@C%H$~<KCX 0 @� %=Aր$H4/=!A -H!! @z!aHC� ^ IЂXZC H$A�$ HC! 5`aX /` CK -44 `Hx�^/Zk�C� hhaKZ%A$ X!C�4~@�$ Z^X1a i�� ^/<HCc aHxiH I1$KKX!`!Cz�k4��h@XXKXKX^$H4 A� `HHhh@x_h�C^ KXƏ$ h xA�0= h@0�` @� % ZKC^XC !@C!��`_酇 @Cz hi/H$,A�%K^� @CǯH!`AC $%H㱴0%` 0Z C�0aI^X$-/~ ^ - - �4��cA4!�zA, $%H‚$ h@� @z=K4 `H�xaAzih aA `hCC�cA� Hai, XXI , -Ki@z@XXxH/@xAz%^� $h���k�^ 4AҐ$xI!-$, Z 01ׯ1k�XXaA 4E ҐhaA$h4Яz �4$ z@zHXC%hA1O-HCK$I�X H�h�4Iƒ$H / a X IЂ4 = !�C�z=��k`,` Z^‚$Ix%HC$Ak,h�44 `Ci�^CxZ /`I^��4�Hz1 4$a$I‚x�IcI XkhIC45 C, 4$H-HX54 - 0 zCi`A$,A1%@�A!� ? �CX%H -֐%H40!<4�i�XXKzi, , �� 0 @4@^zA$HXz�A% xi,!cC�a@~€$-@/ A $/�� k@Z@z`a@CKH I%!- -�CX0 ^x` Ҁ�=@X�IXI -Hi@�41�Za��04֐$ -㡗4ǒ4^x44=!! � A?$= @4 hA%i/`aK$�@A� H,,IcI !-�@C��€~-=KCЀ�5 hƒa z1^ I$, HH1/K <�@ K?0 ��Cր4! h@K-Hx赆%@5@zhH zC4,<$ A^%HXҐ$ A!h` `� �,cIC/h, �aAZ A a�H4 @�!ZCx$A ?w/@X ��aǿր @� a,`K5$_z A � a A00 ,�� @^iZ? I-@ƂXXxH~-< i `,@ %@/@ � 5i@Ҁ�~Z�$h$,a ?k HXH zH�A%< h�AiHcAZ$HX AK^@45�X44  45-hh釄! AH�x @!, ,XA^C  !Ak=4�,� 4APETAGEX��g����������������� �������Album�TestAlbum �������Artist�TestArtist �������Title�TestTitleAPETAGEX��g�����������������TAGTestTitle���������������������TestArtist��������������������TestAlbum������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/test.it���������������������������������������������������������������������0000664�0000000�0000000�00000001204�14662262111�0016343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������IMPMtest song name����������������� ��0}�������Nd @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@���D������4�������>Y ��>"3 ��>��IMPS�������������@�@This is a sample name.������������������ ����������������IMPS�������������@�@In module file formats������������������ ����������������IMPS�������������@�@sample names are abused����������������� ����������������IMPS�������������@�@as multiline comments.������������������ ����������������IMPS�������������@�@ ��������������������������������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/test.mod��������������������������������������������������������������������0000664�0000000�0000000�00000006074�14662262111�0016520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������title of song�������Instrument names���������@���are abused as������������@���comments in��������������@���module file formats.�����@���-+-+-+-+-+-+-+-+-+-+-+���@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@����������������������������@�����������������������������������������������������������������������������������������������������������������������������������8CHN����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/test.ogg��������������������������������������������������������������������0000664�0000000�0000000�00000010470�14662262111�0016510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OggS���������ۿ;����Zfvorbis����D�����������OggS����������ۿ;���9<a}vorbis���Xiph.Org libVorbis I 20050304������unicodetag=öäüoΣø���unusualtag=usual value���unusualtag=another valuevorbis%BCV�@��$s*FsBPBkBL2L[%s!B[(АU��@��AxA!%=X'=!9xiA!B!B!E9h'A08 8E9X'A B9!$5HP9,(05(0ԃ BI5gAxiA!$AHAFAX9A*9 4d���((  ���@Qqɑɱ  Y�����HHH$Y%Y%Y扪,˲,˲,2 �H��PQ Eq Y�d��8Xh爎�����4CS<GDTU׶m۶m۶m۶m۶m[e Y�@��if0BCV���0ĀАU��@��J 9ߜYJ9Hy9s19眢Y 9ĠY 9'yК*9q`9&y9iK9HyRK9s9s99眨9Oޜ9s9s9 4d���@a)h Fb2A0 Bh:%qRJ' Y���@!RH!RH!b!r)J*2,2,:쬳; 1C+RSm5Xk9皃VZkRJ)R BCV� ��BdQH!b)r *АU�� �����O%Q%Q-25SEUueזuY}[؅]}}uaXeYeYeYeYeY 4d���� BH!RH)s9$ Y������pGqɑI$K$,O4O=QE4U]Q7mQ6e5]S6]UVmWm[uۗe}}}}}]BCV���:#)")8$I@h*�@�@��(8$I%igy陞*@h*���@������xxx舒h+ʦ캮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮뺮 �$��t$Gr$GR$ER$GrАU� ���1$Er,4O4O==SEWtАU�� ������� ɰM%R-US-RESUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM4M Y ��S--ƚ $bjc R쥱H*g1^Q{$cA-)&TBc*RR 4d�p@,@,�������4 <4�������$M,O4�������������������������������������������������������������������@4@<@<�������<<D�������,4<Q�������������������������������������������������������������������@4@<@<�������<D<�������,<Q<������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������`!"��pH$ HM4eAӠi0M����������$MA �IӠi4"����������AӠiEi4hD����������4!E&3M"D ������������������������p��0 "��p8e�8��X��X%��`Y(������������������������������������������������������������������p��0 ��p(eDZ,8$ɲ�<D���(p��ASbqBCV�Q��ű,MEi'$I<Oiyi<4!hEQ4Mi*0MU��P��`��B�bYy'$I<OE4MSUIy(i,M<QETUUy(i<OE4Uuy'hEQ4MTMUu] i DOMSU]u牢i.MTUUu]Yi2@UUu]W뺲 PUu]Ye�뺲,����*ф @!+(��S0&!$B&%R RR)TJ*%Rj)UR) B*%R��؁�؁PhJ� �0F)sN"c9'R1眓J1sI)s9礔9sRJs9)s9眔RJsNJ)%A'9��T��`#A�R� cYyh$iy(&Iy'<OE4Uy(i*E4MUU],i0MTUu]i.l[UUue꺲 \ueٖ,ڲ���VG8) ,4d%��@B!eB !R ��p��0 �H��Zk@gZkZkZkZkRkZkZkZkZkZkZkZkZkZkZk-RJ)RJ)RJ)RJ�U8�?ذ:IX`!+p��s B)T1tTZB1$ZlsA(!b,sB))VcQ)RRJ-XJRJX1ZbI)Z1#lMjck*-c_dl-j #[,-Zk0[1>R,1\��w�D3$� � R1s9R9sBT1ƜsB!1sB!RJƜsB!RsB!J)sB!B)B!J(B!BB!RB(!R!B)%R !RBRJ)BRJ)J %R))J!RJJ)TJ J)%RJ!J)��8��A'Ua BCV�d��R)-E"KFsPZr RͩR $1T2B BuL)-BrKs���A���3��stG� DfDBpxP S@bB.�TX\]\@.!!A,pox N)*u ����� ���\�adhlptx|������|��$%@DD4s !"#$������ ���������OggS�z�����ۿ;���f}[� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/test.s3m��������������������������������������������������������������������0000664�0000000�0000000�00000001040�14662262111�0016427�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test song name��������������������� �SCRM@}0�����������      �������������������������������������������������������������������@��� ��������������This is an instrument name.���������������������������������@��� ��������������Module file formats�����������������������������������������@��� ��������������abuse instrument names��������������������������������������@��� ��������������as multiline comments.��������������������������������������@��� �������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/test.xm���������������������������������������������������������������������0000664�0000000�0000000�00000012537�14662262111�0016366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Extended Module: title of song�������MilkyTracker ����������}����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ����@����Instrument names��������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����Sample����������������������������@����names�������������������are abused as�����������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����are sometimes�����������comments in�������������(�����������������������������������������������������������������������������������������������������@� �@������������������������������������������� � � �������������������������������������������������������������������������������������@����also abused as��������������������@����comments.����������������module file formats.��������-+-+-+-+-+-+-+-+-+-+-+��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/toc_many_children.mp3�������������������������������������������������������0000664�0000000�0000000�00000026405�14662262111�0021142�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����K$CTOC�� :��toc��chapter0�chapter1�chapter2�chapter3�chapter4�chapter5�chapter6�chapter7�chapter8�chapter9�chapter10�chapter11�chapter12�chapter13�chapter14�chapter15�chapter16�chapter17�chapter18�chapter19�chapter20�chapter21�chapter22�chapter23�chapter24�chapter25�chapter26�chapter27�chapter28�chapter29�chapter30�chapter31�chapter32�chapter33�chapter34�chapter35�chapter36�chapter37�chapter38�chapter39�chapter40�chapter41�chapter42�chapter43�chapter44�chapter45�chapter46�chapter47�chapter48�chapter49�chapter50�chapter51�chapter52�chapter53�chapter54�chapter55�chapter56�chapter57�chapter58�chapter59�chapter60�chapter61�chapter62�chapter63�chapter64�chapter65�chapter66�chapter67�chapter68�chapter69�chapter70�chapter71�chapter72�chapter73�chapter74�chapter75�chapter76�chapter77�chapter78�chapter79�chapter80�chapter81�chapter82�chapter83�chapter84�chapter85�chapter86�chapter87�chapter88�chapter89�chapter90�chapter91�chapter92�chapter93�chapter94�chapter95�chapter96�chapter97�chapter98�chapter99�chapter100�chapter101�chapter102�chapter103�chapter104�chapter105�chapter106�chapter107�chapter108�chapter109�chapter110�chapter111�chapter112�chapter113�chapter114�chapter115�chapter116�chapter117�chapter118�chapter119�chapter120�chapter121�chapter122�chapter123�chapter124�chapter125�chapter126�chapter127�chapter128�TIT2��� ��toplevel tocCHAP���,��chapter0����d���dTIT2��� ��Marker 1CHAP���,��chapter1�������TIT2��� ��Marker 2CHAP���,��chapter2���,��,TIT2��� ��Marker 3CHAP���,��chapter3�����TIT2��� ��Marker 4CHAP���,��chapter4�����TIT2��� ��Marker 5CHAP���,��chapter5���X��XTIT2��� ��Marker 6CHAP���,��chapter6�����TIT2��� ��Marker 7CHAP���,��chapter7��� �� TIT2��� ��Marker 8CHAP���,��chapter8�����TIT2��� ��Marker 9CHAP���-��chapter9�����TIT2��� ��Marker 10CHAP���.��chapter10���L��LTIT2��� ��Marker 11CHAP���.��chapter11�����TIT2��� ��Marker 12CHAP���.��chapter12�����TIT2��� ��Marker 13CHAP���.��chapter13���x��xTIT2��� ��Marker 14CHAP���.��chapter14�����TIT2��� ��Marker 15CHAP���.��chapter15���@��@TIT2��� ��Marker 16CHAP���.��chapter16�����TIT2��� ��Marker 17CHAP���.��chapter17�����TIT2��� ��Marker 18CHAP���.��chapter18���l��lTIT2��� ��Marker 19CHAP���.��chapter19�����TIT2��� ��Marker 20CHAP���.��chapter20���4��4TIT2��� ��Marker 21CHAP���.��chapter21�����TIT2��� ��Marker 22CHAP���.��chapter22�����TIT2��� ��Marker 23CHAP���.��chapter23��� `�� `TIT2��� ��Marker 24CHAP���.��chapter24��� �� TIT2��� ��Marker 25CHAP���.��chapter25��� (�� (TIT2��� ��Marker 26CHAP���.��chapter26��� �� TIT2��� ��Marker 27CHAP���.��chapter27��� �� TIT2��� ��Marker 28CHAP���.��chapter28��� T�� TTIT2��� ��Marker 29CHAP���.��chapter29��� �� TIT2��� ��Marker 30CHAP���.��chapter30��� �� TIT2��� ��Marker 31CHAP���.��chapter31��� �� TIT2��� ��Marker 32CHAP���.��chapter32��� �� TIT2��� ��Marker 33CHAP���.��chapter33��� H�� HTIT2��� ��Marker 34CHAP���.��chapter34��� �� TIT2��� ��Marker 35CHAP���.��chapter35�����TIT2��� ��Marker 36CHAP���.��chapter36���t��tTIT2��� ��Marker 37CHAP���.��chapter37�����TIT2��� ��Marker 38CHAP���.��chapter38���<��<TIT2��� ��Marker 39CHAP���.��chapter39�����TIT2��� ��Marker 40CHAP���.��chapter40�����TIT2��� ��Marker 41CHAP���.��chapter41���h��hTIT2��� ��Marker 42CHAP���.��chapter42�����TIT2��� ��Marker 43CHAP���.��chapter43���0��0TIT2��� ��Marker 44CHAP���.��chapter44�����TIT2��� ��Marker 45CHAP���.��chapter45�����TIT2��� ��Marker 46CHAP���.��chapter46���\��\TIT2��� ��Marker 47CHAP���.��chapter47�����TIT2��� ��Marker 48CHAP���.��chapter48���$��$TIT2��� ��Marker 49CHAP���.��chapter49�����TIT2��� ��Marker 50CHAP���.��chapter50�����TIT2��� ��Marker 51CHAP���.��chapter51���P��PTIT2��� ��Marker 52CHAP���.��chapter52�����TIT2��� ��Marker 53CHAP���.��chapter53�����TIT2��� ��Marker 54CHAP���.��chapter54���|��|TIT2��� ��Marker 55CHAP���.��chapter55�����TIT2��� ��Marker 56CHAP���.��chapter56���D��DTIT2��� ��Marker 57CHAP���.��chapter57�����TIT2��� ��Marker 58CHAP���.��chapter58��� �� TIT2��� ��Marker 59CHAP���.��chapter59���p��pTIT2��� ��Marker 60CHAP���.��chapter60�����TIT2��� ��Marker 61CHAP���.��chapter61���8��8TIT2��� ��Marker 62CHAP���.��chapter62�����TIT2��� ��Marker 63CHAP���.��chapter63�������TIT2��� ��Marker 64CHAP���.��chapter64���d��dTIT2��� ��Marker 65CHAP���.��chapter65�����TIT2��� ��Marker 66CHAP���.��chapter66���,��,TIT2��� ��Marker 67CHAP���.��chapter67�����TIT2��� ��Marker 68CHAP���.��chapter68�����TIT2��� ��Marker 69CHAP���.��chapter69���X��XTIT2��� ��Marker 70CHAP���.��chapter70�����TIT2��� ��Marker 71CHAP���.��chapter71��� �� TIT2��� ��Marker 72CHAP���.��chapter72�����TIT2��� ��Marker 73CHAP���.��chapter73�����TIT2��� ��Marker 74CHAP���.��chapter74���L��LTIT2��� ��Marker 75CHAP���.��chapter75�����TIT2��� ��Marker 76CHAP���.��chapter76�����TIT2��� ��Marker 77CHAP���.��chapter77���x��xTIT2��� ��Marker 78CHAP���.��chapter78�����TIT2��� ��Marker 79CHAP���.��chapter79���@��@TIT2��� ��Marker 80CHAP���.��chapter80�����TIT2��� ��Marker 81CHAP���.��chapter81��� �� TIT2��� ��Marker 82CHAP���.��chapter82��� l�� lTIT2��� ��Marker 83CHAP���.��chapter83��� �� TIT2��� ��Marker 84CHAP���.��chapter84���!4��!4TIT2��� ��Marker 85CHAP���.��chapter85���!��!TIT2��� ��Marker 86CHAP���.��chapter86���!��!TIT2��� ��Marker 87CHAP���.��chapter87���"`��"`TIT2��� ��Marker 88CHAP���.��chapter88���"��"TIT2��� ��Marker 89CHAP���.��chapter89���#(��#(TIT2��� ��Marker 90CHAP���.��chapter90���#��#TIT2��� ��Marker 91CHAP���.��chapter91���#��#TIT2��� ��Marker 92CHAP���.��chapter92���$T��$TTIT2��� ��Marker 93CHAP���.��chapter93���$��$TIT2��� ��Marker 94CHAP���.��chapter94���%��%TIT2��� ��Marker 95CHAP���.��chapter95���%��%TIT2��� ��Marker 96CHAP���.��chapter96���%��%TIT2��� ��Marker 97CHAP���.��chapter97���&H��&HTIT2��� ��Marker 98CHAP���.��chapter98���&��&TIT2��� ��Marker 99CHAP���/��chapter99���'��'TIT2��� ��Marker 100CHAP���0��chapter100���'t��'tTIT2��� ��Marker 101CHAP���0��chapter101���'��'TIT2��� ��Marker 102CHAP���0��chapter102���(<��(<TIT2��� ��Marker 103CHAP���0��chapter103���(��(TIT2��� ��Marker 104CHAP���0��chapter104���)��)TIT2��� ��Marker 105CHAP���0��chapter105���)h��)hTIT2��� ��Marker 106CHAP���0��chapter106���)��)TIT2��� ��Marker 107CHAP���0��chapter107���*0��*0TIT2��� ��Marker 108CHAP���0��chapter108���*��*TIT2��� ��Marker 109CHAP���0��chapter109���*��*TIT2��� ��Marker 110CHAP���0��chapter110���+\��+\TIT2��� ��Marker 111CHAP���0��chapter111���+��+TIT2��� ��Marker 112CHAP���0��chapter112���,$��,$TIT2��� ��Marker 113CHAP���0��chapter113���,��,TIT2��� ��Marker 114CHAP���0��chapter114���,��,TIT2��� ��Marker 115CHAP���0��chapter115���-P��-PTIT2��� ��Marker 116CHAP���0��chapter116���-��-TIT2��� ��Marker 117CHAP���0��chapter117���.��.TIT2��� ��Marker 118CHAP���0��chapter118���.|��.|TIT2��� ��Marker 119CHAP���0��chapter119���.��.TIT2��� ��Marker 120CHAP���0��chapter120���/D��/DTIT2��� ��Marker 121CHAP���0��chapter121���/��/TIT2��� ��Marker 122CHAP���0��chapter122���0 ��0 TIT2��� ��Marker 123CHAP���0��chapter123���0p��0pTIT2��� ��Marker 124CHAP���0��chapter124���0��0TIT2��� ��Marker 125CHAP���0��chapter125���18��18TIT2��� ��Marker 126CHAP���0��chapter126���1��1TIT2��� ��Marker 127CHAP���0��chapter127���2���2�TIT2��� ��Marker 128CHAP���0��chapter128���2d��2dTIT2��� ��Marker 129���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Info������� !$&),.1369;>@CEHJLORTXZ\_bdfilnqsux{}���:LAME3.98r��������4$LA���}W���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��4��LAME3.98.2UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.98.2UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU���� ��4��UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.98.2UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/uint8we.wav�����������������������������������������������������������������0000664�0000000�0000000�00000134210�14662262111�0017154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF��WAVEfmt (����@��>������������������8qdata��||yirvz{~mzys|}xuv}}~pp{|v^UR[^{p_cs}{sozz}}rz{sqqz~|{y~~k_frtvw{|x}tsu~||orЀv[e]gyzgi~{wxxy~{negy}}xsuÀbTchj}~omw~|~yzx{utrqyxzrπh^]adztjo~z{zz}}qqonw{x}Ѐl[[aguwlp|yz|wyxqmq{|wЀ~\Z^bn}nju}v}}rt~upoxzy~ʀ~\X`cl}~nkv|x~|unv}vpr}w{ÀhWY]is~{im}{ywkr~xrt{w}lRYZgq}jiz}wzlmy{ru~z€sSZUgnyncu~u|qiy{syˀTWRhmry|t\rt}zwkxyy_[L`pvqs|_nzw|~}{xkvz{cT^quojsritx~xw}wowzj\Zbhgs{zst}~zzzxt~~wkZUixsgryxxz}yzy}}{~w`Tborrlvyxx~}{|~}zvv|icirpdf|}}|~zxzy}}xjay[Rfsvwyw}vkhw}||~}~~xvxvyzvvwxvty~vuz{}|}{y}}|{z}~||{|~|~{{}~{{~~{{}|z{{~~}~~}}~~~~~~~}~~}}~~}~~|~~~}~}~}~~~~~~~}{|~~}~~~~~~~}~~z~~~~~~}~~}~~~~}~~~~~|~}~~~~~~}~~~~~~~~~~}~~~}~~~~~~|~~|~~}}~}~~~~~}~~~~~~~~~~~~}~}~~~~}}~~~~}~~{~}}~}~~|yxxw~v~x|x}x}|~{yusllmkptv{~yuqlfzkuhrgqrnqlwjntz|}|}}xtpfijbnn~o||{{{yyxy{{}~z|smehe`ngqzvztj[V\ajot}y|wjxeqks^vcumva{tvt{spkikov}||mik`bk`}q|rzpz}|ztiYM~S\sfpmjsgzafeermz}|tlnrutytznvmtepdohobrjwo|n{z~}||||~tqpffiblmn{~wx~tl_Y\ajps}}xq{tthsfvmuavlyo|jzv{~}zyywstwz}|~}rxmho}e}i~p}h|x}vv{wwvllxowesognblb{drl|qrzy}zzxxwxwy{sookmmnrtxy~}~~}yyxy{zz|}~~zxwopoipnows{|{~x|x{rzovquktotpvnwwxtzx}|z~y~y~u}o}r~n~l~r~m~tuu}z}yyqqqlpopvsz||zzvprmmqm~t~t~u~|}z}}~~|zxqqnkplrtt{~{~~~~~~~~~~~~~~~}zxqqokplqts{{~~}zwpqnkplqst{{~~~~~~~~~|zuqplmmmrqvz}~~~~~~~~~~~{~y~r~pnkmlnqsx{}ztqokllmpqvz~~~~~~~{~vqolklkoptx{{wr~p~l~l~k~l}m}p}s~w~{~{yrpnimjnqqwy~|xsonjkklqqvz}{xqpljkjmo~r~v~y~~~~~~{vpnjijjmpswz|ysnmhiijooux{~{vnnhhjgnorxy~~~~~~{wnmjeigkpowy}zynkkcigiqnwy|~~~yymjkbighqnwz{~~~~~~~~~wxjjj_kdjrmzy}{wtfkeakcnqp|y~~~~~xulfi`gggrox{}~~}}}y}v}n}g~i~aehfqpv{|yvoeibbjeqqt}{x~v~n~eibcjesrv~|~vtkej`g~k~h}v}r}y}}}}}~~wusegi^nhmyr~rulanafqgxwx~~~~srqcijaqlq|w~~}}}}}}~w~p~phemcoqp|z}wqoifneprr|{~vqojgngpss}|wqpkgoi~o~u~t~}~}~~zspoimonwxz~xutssuuwxz{|~~|zyxwvvvvwxy{}~|{yxwvvvvwxz|~~}{zxwvvvvwxz|~~}{~zxwv}v}v}vwxz|~|~{}|{|zxxvwuvwvzv|v~wyz|~~~}~|}z}y~w~w}v|v}w}x~y{}~}z|tzuyqxjwew`wbwhxsy}{}~ztpt|~|{yxxwwxzyuzq{n}n~osw{~}~~|{zyxxxxyz{v}t~zseVSW_nz}~|v{ozhyiysy~yzz{|~zvtkcbjtz}}|{{zz}zxzxz|{|}~vuujZLMUaq}~}||{{{~{v{o|m|m}s~}ztonlkls}~}}||{{{||{|}}~~~qorgVHL[hw~~}}|||||}}}~yqrv~~{|}wrpru~x~|}}}}}}}~}~|~~~tosiYJ~L~]~j~x~~~~~~~utx~}~~{tqqv~x}zz}}~||~~|~~~}}t}o|t{i|Y|L}O}a~mw{xv|}{zxwvwxy{|~{trquwy}~~~ywtrorrryy|}osp`SL[hq{yyyqsolposv{~w{yvrwormsnsowowtzw}z}~~xttlkolqnstvveyYMWfmvt{zyszulipjmtux}tssvxwz{~~vs~shmmjqtw{yr|m]SRfk}qtqsrp}hmlkruy}~x}|rsxosgvnwlymvsuvyz{~|~|rsohnxlzn}sfw[{R~\ljols{~{tro{i|mmntx{~vwzxvsv|~{trpjmoosy~|zr\WXlkhik||vrqlloorx{{zz||{v~qyqtlrkvo}o}s|x}|~~}wqrmkfpZpZrexp}efgrzz~~xrqnkopsw|~}{ttwy{y}~~zrqolnpsu{q\`eogahly~{toqnlqsuz|{~}||z}v|vwvqyp|p|l}n}s~ux~}|ymrboipgnlmfreviwj|w~}}|yuonpnqux{~}zvwxz|||}~~{wsopnoruy}spsmpmjkkrz}}yvqopoqtx{~~~}|xyyy{||x|u}q|p~opruy}|s|vxqunpnqjoipirluuzy|~}}||x~tp~qnp|s{vyzx}zz{||}}}}yuqqopsvy}}turnojhjmtz}yvrrposuy{}|}}~{zyxyz{}|}}}|yurroqsw{{tuqnm{jzhuksorvq{t~uz}~}||}~~~y{vzuzsxrzr{v{y}z}||}}|ywusrvwyyt{u}qnmjilqw||xxvt~t|w{y|x}z~}~}}{yzxz|{|}|}~~{zxwxwyz{}~wuupnmijnrx|~~{zy{zy{~{}~|{|}}}}|{zyyz||}}}}}~~~||||z}}|~~yuuro~ol}k~o}r}w~||~}~}~~}{{||}}}|{zz~z~{|}~}}~~~~~~~}wuuq~opmmq~ty~~~}||||}}|{{z~zz{}}~~~~~~~}xvvsqrpptuy~~~}}}|||{z{~{}xz{|~~~|zwuuutuuvy{~|{{yzzz{|}}~~~~~~}{yxxxxyz{|~~}|{{zz|{}}}~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~}}~}~~~~}~~|{{~|||~~~~}~~~~~~~~~~~~~~}~~~~~~}}~~}~~~}~}~~~~}~~~~~}~~~~~}~~~~~}}}}~~}}~~~~~~}~~}~}~}{|{|~~}~~}~~~~~~}|z||}~|~~~~~~~~~~||}}~y}{{}~|{|}~~}~}{|~}~|~}}~~~~~~~~}}}|}}~~~~~~~~~~~~~}||}~~}~}}{zzxxxxyz{{|~~~z}|~vusqrousuzw|}}|z{}}ztqp}m{mzn{q}ruxy{~~ywrnpnmstvz|{}~|}|{z|~|zz{~z{usqrprwu{y{wzu|v{zz~}~}~z|yusvqtxtz{xzzx{{~u|qqtx|}{{|{}|vvwsrxrxz|uwzuxuxw{z}~}~zwxwurvus{uyzxvo|j{h}jpuz~}~}}wywurwsvzzuvzsxswu|x|}~|~wzwtswqwyyto{fwbwc|g|n~v|}zy{~}}w{xtsw}qvwryotp{svww}|{~~zw{zttwxqkvay\t^{cwkvu}}{~yvuw|w{y~tztsvnrlsnyrswz}yt|z}|uwyskv_sYqYx_uivt{~tzz{~yusuyyy|{u}nskvktnqsuyws{vv|x~~~v|mwasYuXq\qfvrt}vztzz{{vtsv|}wpwlkvltquvq}rwsxyu|y}~}~}whw[UvVs]uipvqwsxzt}y}zvssv|wq~lkvloxus{vqqwtxzu|y}|}}}~tzeYyU|W}_rlvxroutu{vy{yzvssv{xrm~llowsyutvnruszxw~||x|}~}n`|WV}Zzdqs}vsottt{xy}y~zvstw}xrmll}ot}yzsvsosttz~x}z||}z~ug\XX`kxwuttortsyzy}{}~yutuy|uplkmqv}|~{~surost}u|z|y}{~}|wj_ZZ`j~v}{}surostu{y~|z}w}uux}ysplmosy~~ztus~n}s}t|t}{y|||oc][^hq}~|}}svrousv}{yyv~u~v~z}xtqooquz~|}}~s}v}r}o~usv|y~~~~sh`]_fozzvuuos~tzsw{uzu|x|}}xurpqrvz{~}x}u~vqrvtz{{~}zoe`^bis}|~uv}uzpvuutuux||z~~zwtrqrtx}~}}{~xuxqtvtz{|~~vkd``elv}zywyuuuyxr|twtz||~{xutrstw{~~~~{yvxsu~wv{|}~tkeadhox~zwvvw{|~zxywxxz|~}|{xvtttw{~~~~~~~}~~~~~y~p~j~e~d~g~l~t~{~~}zwvvx{~~~}||{yxv~w~x~{~~~~~~~~}unjghlrx~~~~~~~~~|ywvwy|~}|}}}|{{yz{}~~~~~~~~~}vqljknsy}zxwxz|~~~~~~~}|||}}~~}}}}~~~{vqnmnqv{|zyxyz}~|{{{|~~~~x|tpn}nquz|~|~}{zyyz|~}|{{||}~~~}}}|~~~~}~~|w~s~p}p|q|tzx{}|{~}{~z}y~y~{~}~}}}}}~~~~}}zywxwv{{}}||{|}}}{{}{}}}||}yxxturtqyrtuxy}}{~{}~{|yzy~z~{}~~~~~~~~~~~~~yzxtsxu~v{~y}xxz|~||}{}~~~~~{xxwzswvuxtvu|wz}~}{{|}}{}zz{}~}}~}}zyxwtwyx}}|z{|}~}|||{}~~~~{xwwutyy|zywwwyz|~~{{{{|~}}}}~~}}}y}w}v~wtv{z~}|z{{}~~}y{|}~~~~~~|xvuvtv{||zxxyz{||}}~{y{}~~~}}}~~~|yxtt~v~u}x}}~~{~{{{z{z~{~|~~~~~~~{wwsvvv|~~~||z|z{{{|{~{{|}~~~~~~~~}yxutvvx}~}{{{}~}|{|}~~~|xxv~v~vw{}~|{{|}~~}}}}}~~{yxvvvx{}~}||}~~|}}}~~}~~~}zyxvwxz|}}||}~~|{zywwxz|~~}}|}|{zzxvxyz}~~}}}~}{zzyvxyx{~~}|~|{zyvvyxz}~~|}~{zyvuwxx{~~~}}~|{xuuvvwz}~~}}~~{xvuvuwy{~~~~|xvwuuvy{|~{xxvvvxz{~{|{yyxzz{~~~}|zz{{|}~~~~}}}|||||||}}~~}|{{{{{{{|}}~~}|{{{{{||||}~~~~~}}||{{||||}}~~~}||{{{||||}~~~}|||{{||||}~~~}}||{{{{||}}~~~}|||{{{||}}~~~}}||{{{|||}}~~~}||||{|||}}~~~}||}|}{||{|z|y|z}{}|~}~~~~}|||||||}}}~}}|{{{|}~~~}||||~|~|~|~}~}~~~}}|}|}{|{|{|}}~}}~~~~~~~~}~}}}}}}~~~}||{{{|~}~}~}~}}}}~}}}~~~~|{|}~~~|~|~}~|~|~|~~~~~}~~~}|}~}zz||}}~~|}}}~~~|{||}~~~||||}}~~}|{{|}~~~}zxy{||~~~}|}}}~~~|yyzz{{~}|||||}~}~}~}}||||}~~}||||}~~~}{zyyz{|}~~}|||||}~~}|{{zz{||}~~~~}}}}}}}~~}|{yyyyyzy{}~~}{}|{{{zyyzyzz{~|}~{}~~~~}}{{~zyyyz{{~|~~~~~~~}}~~}}~|{{zz{{{~|}~~~~~}}~}~|zy~yx|xyy~z|}|~}~}}|||||}}}~}~}}}{{|||~}~~~|{~|~~~|}~{{zyyy~yz||}~~}}|||~||}}~~}~~~}}}|~~~~~~~~~~~~~|{zzxwwxz{}~~~~~~}~}}}~}~~~~|||||}~~~~~~~~}|}{y~z}z{zzzy{y~y~yyz{}~}~y}y}v~s~s~tsuw{~~}|||}}~}zxtqrqrtvz~|{zyyyzz{|}~|yvqqqpsux|~}}}}}}}}~~~~~|~yvrqppsux}~~zxuq~q|p{rztyvyzz~z{|}}~~}}~~}}||x|v|t|s}r}r}t~w~z~}~{zwvwwxy{~}|{zyyyzz|}~~}||}~}}}}~|~|~||}}~~~~~~~~}}}~~~~~~|zxwwwxyz|}~}}||{||}~~}|}~~~~}zvtsstvxz|~}||{{{{{{|}}|zz{}~~}}~~}yuqppqsvy|}{zzz{{{|}{xvvvx{}}|{|}{uolklnrwz|ywwxy{}~}yvtstvy|~~~~~~{tnkikmqvz~{xwwxz|}|xusrtvy|zslihknsy||xvvx{}~{ywvvwyz}{tolknqv{{yxxz}}{zxwxy{}{|~x}qljknsx|~~~|}~yw~vx{~|zzxxy~z}~~~~~ztn~llnrw{|y~xxz}~~|{yxxy{}}~}}wqnm|nqu~z~~~|zxy~z|~~~}{~yxwxz~}~}}zsnllosw|~{xwwy|}~{zxwwy|~zsomlosw|}yxwx{}}{ywwxz}~zupnmpsw|~{yxy{}~|zxwxy{}~~|vrpoquw{}}{{{|}}}}~}{zyxxyz|~~~~~}yvuuuvwxy{}~|{zz{}~|ywwxz|~~~~~~}|{|~|~|~|~|~}~~}~|||}|vr~r~t}u~wy|~~~~~~xtrrrqsw}~{zz|}}ttttspsy}{wwy~~{}eV_syjho|zvy}z}eT[qwmkw{yz}wxzfPYtocj}zusxwtu{jN[uh]m}qnxzsrypS[ue`p~nkyqnx|^Ynv}gfn{jgupjvn[esuqhnsyujpwynrzk`kqsoorpvpiptqsnflnt}rsrr}|}ymvytx}jktxsrtv|zwus{|y|~omtuuuvt{}{wqy}wz~yqtxyyuyxxz{~zv|z|}|xz{xzz~zy}{~yv~{{}~zzyz|{}yz}}|vz}}|}}{{y~~{|{x|~}ww}|~|~|}z{|||yz~}~zv{||||}~|vpu|}}}zyy}y~{}{w{~~{~|{}}y}x}~}~~}~~yzz~|}u~z~z|~y~w|~~~~~}xzy~|vy~{{}y~x{~~~~|zy~x~}~vx}~|y{xz~~}}z|x~~zzxwz~x}|yz|~~zy}}w~yz~xyzz|{|z~}y{z{yz{w}~z{z}}}~}z||~|{zz{xz}{{z~|~~~|}{}zy{{yz||}{~{z}~~}}~}{z~{~{}{{~zzz{}}~~~}zyxxyxxyyz}~~~~}}{xwvvwwwxxz|~~||{xvuuvvvwyz|~~|{{xwuuvvvxyz{}|{{ywvuvvvwyz{}}{zyxwvvvvwy{|}~}|{|}~{z{|}~}{zzyyxxxyz{{|x~vvvxz|~~~}||{{{{{{z|q|j}e~dfjpu|{wvuxz}~~~~}}}}}~x~r~omnrw{~||}}~~nc[[`~hpw}voknt{yrooqvz}{zyz|te[Ybnzukinx~ytsw}ytsv{l_WZgv~slgnz||{{yvv{sihnw~~~tg__gu|vrquzvwz|~{nhjr}{z{}yj`^ft~wrru}vtw|~unlq{}zz|wiadmy}wtv}xvx~{wvw|}|}~qfcir~}{wx|zvx}~}~~~|||~~~}nefmw~~|zz~}xx|~}~}zyz}~||}}~zpikrz~}}|~|z|}~{|~||~}zy{~||~}~~~z~~yttvz}}}~~}~|smnx}vu{xy~{y{|y~uont{}~}{|||~~}|}}{|~qjhvswx|xtyt~x~}~}}~~{~x~z}vpqx{{~~~}}|{{~{{|}|}~}{|}~||~upn~x}|{zz{u{x{x|~}zu~}xz}~}}|||{{v{t{u|z}}~~~~~~~}|}}~~}}}}~~ztryyzy~~}|{~x~}{~}}~~}wtuz~~~~~~~~~~~~~~~~~~}~~{wu{}z||}~|{~~~~~~~~~~}~~|||~}{yz~}}~}~}~~~~}~~}}~~~~}|{~~~~~}~~~}~~~~~~}}|~}}~~~~~~~~|~}~}~~~~~}~~~~~~~~~~~}}~||}}}~~~~~~~~~~~~~~{|~~~~~}}}zz{zwvx{}~~~{|~xpmpvxz}~|yy|~y{rjnwyww~{vw|}z~z~}ojqxvrv~wuz}z{|~pipyvor|wx}}zx~~~sgq}vin|{||yy|~{zjo}wjm~z{~}~~}lrz}~qs~zz{wyz|}~}|op~qryz{w|{xy~}~up|~rs~w{{{}yxz~~~zp{ur}x~y~|z|}ww~}}r{tt}}vx{z}vw~~}yx}ywz|yw{}z~}xx~~~wz~wv|}ww~y|yw|}{y}{ux}zw{~z{}x{|y}y~}vx~yw{}x|{z|zx~~}~wy|yx{yy|}{}|y{}}~zx}zw{yw{|z}}y{|{~y{}xzzwy{z|~z{|z~z~{x|xwzxz}{{|z||zz|wxywz{z|{z}}}{{{wxxwzzz|z{}}~}|{xxwwyyz{z{|~~}|zxwvvwxyzz{|~~}|zxwvvxyzzz{}~}|zxwvwxyzz{{}~}{ywvvwxyzzz{}}|zxwvvwxyzz{|~~}|zxwvvwxyz{{}~~|{ywvvwwyyz{{}}|zxwvvwxyyz{|~~}{yxwvwwxyzz{}~}{yxwwwwxxyz{|~~}{zyxwvvwxyz{|~}|zzyxwwwwxz{|}~|{zzyyxwwxy{|~~}||{{{{zzzz{|}~~~~}}}}||||||}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{wx~ztv~}|z|~~~~xy}}}}}~~}~~~~~}}~~~~~}}}~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~}}}}}}}~~}zyxyz{||}}~~~}}~}wsqrvz}}|{{|~{uqqsw{~~}{z{}xspqtx|~|yxy|zsppsw|{wuvy~~~xrnosw}zursv|}}}~zsnnqv||vrqsy}{|}xrnosx~ztqqty~|{{|{toorw}{uqprw~}{{|~ztppty~ztqqsy}||}~xsqsw|{vrqsw}~}}}~wsrty}~yurruz~~~|ursw|~zvtsuy~~~~{ursx}|yvtuwz~~}}~vrrv}}ywuuwz~~~}}}~~{trsy~{xvuvx|~}}}}}}~ysqt{|yvuuwz~}}}}~~~~wrsx|xutvx{}|}}~~~~~~~wtu{yutvy{~~}}~~~~}}}~yvw||wtux{~~}~~~}|||}~}wuy}wttw|~}~}}}}}|{|}~xtw~}vsty}~{{}}||||{||}}~~yuw}{vuwz}}{z{}}|{{{{||}}~{ttx}wuvz}}{y{~|}~~|zyz{|}}~uoryyuvz~~{xx}}z{}~~|zz{|||}zqpv||yx{~}zxy~~}}|{z|}~|{{|~voqx~|y{}{xx|}zy{~~|{{~|rou|}{|}xw{~|{{}~~}}}~zsrx}}}~~~{yz~}||}}~~~~~~~~~urw}|{~~{yz~~~~~}|}~~~}}~xtx}~{z~}zz}~||}~}~~vu{|xzzz~~~|}~~}}}~~~~~wx}}yw|}y}}|}||}}}~~~~~yz~}xw}{z|}~}}}}~~~~|{}~yw{{z|}~}}~}}}~~~~}~~{x{~|z~}~~~~~~}}~~~~|{}~}{{~~}}~}}}~}}~~}}}|{{||||~~}}}||}}~~~~}|{zzz{|}~~~}}|||||}}~~}|{zzzz{|}~~~}|||{||}~~~}|{zyzz{|}~~}||{{{||}~~}|{zzzz{|}~~}|{{{{|}~~||{{zz{{|}~~}}|{{{{|}~~}||{{{{{|}~~}}||||}}~~}}||{|||}~~~}}}}}~~~~~}}}}}}~~~~}}}}~~afsp]���AFspdate: 2003-01-30 03:28:45 UTC�user: kabal@CAPELLA�program: CopyAudio�loudspeakers: FL FR��LISTL���INFOICRD���2003-01-30 03:28:45 UTC�ISFT ���CopyAudio�ICMT���kabal@CAPELLA�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/unsupported-extension.xx����������������������������������������������������0000664�0000000�0000000�00000000400�14662262111�0022006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/unsynch.id3�����������������������������������������������������������������0000664�0000000�0000000�00000000500�14662262111�0017114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3���0TIT2���5����M�y� �b�a�b�e� �j�u�s�t� �c�a�r�e�s� �f�o�r� �m�eTPE1�������N�i�n�a� �S�i�m�o�n�eTALB�������1�0�0�%� �J�a�z�zTRCK�������0�3TLEN���@���2�1�6�0�0�0 �H�����I���� ,����^%��� >}G˿5�0}KR"J\ĻC圂g������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/w000.mp3��������������������������������������������������������������������0000664�0000000�0000000�00000001000�14662262111�0016127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ID3����%COMM���W���eng�Promo Only - www.promoonly.com - Distribution of this file is strictly prohibited.TBPM������128TCON������(3)TENC������Promo Only OnLineTIT2��� ���Knowing YouTMED������004099TPE1������Sergio Galoyan f. Tamra KeenanTPUB������RobbinsW000�����lukas.lalinsky@example.com____TRCK������1TALB��� ���Knowing You���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/xing.mp3��������������������������������������������������������������������0000664�0000000�0000000�00000020020�14662262111�0016411�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������d�u�=�@��"`�1 @�?˼N(|rrDO xKإ2Mw3ŕQv. sR܄$dyA�� 0�#����4�`��� ADYa ^O-c7 EbJ!SwukrA%5:=ʹjw0l%RPn@MD] uؕd oA`�� 0������=� ���[[6mes| !)V+ -.Xڤ4ϹR[" !mdj���0�����<� ���=|TVYrK>͎pu1D{-eRa7tfTb)JT*vdkB�@���0����=�`���PTdZOz SGm=F*=)2gK̘Y +ci7bpynd dA@�� `0������0�a����U^!62z)}H:]X7NqUlλcjk)h{ech辅*kTd(jA@�� 0������8 ��{T:TVuw0QlKJ48'ދ}D9<!cW=vEFbJd/z��� 0�����9� ���\E7i*of81CQIxD1,Q,<_/m u,V)JȆݕZkU d6SB�@�� 0������7 ��o*ikP)U1u(1wR+TQԠt뻋<޻ j29N؄[Jd>lB�`��` ������5 ��� صĨ3`:4\ȴ"bY Ӟ"0-d/uҥmguΔB(tndEyA�� 0�#����4� HG*P̿b7xxrmCMMm*̺t A"(BT.?8EdK`�@�� ������: Hm~˩2 NPrYЪ1E叼XRZa饔ƠmV߲-C8غdQz��� @0���P@�8 ���In͠*Y#V$.9sgnF̛?ԺbR\Vko*U(VIi{dXXA@�� `0�����/� ���,`rј MvX*<>YLzJ1ץ/;  ]&up+C dbz��@��@��t���:� ���:D\=XYzSsWN}x^w]!F U̐FY׭$FЁte Q;1F!-UPYoLdgt�A� �����4 ���j6VӔ*{۟8^(rm&^HXRFKA"Դð"j#:* dmv��� 0������2�`��̯ar-Xʱ+84ΕT1XqǸQs醘–ݩKe+ ]S*cRduuA��@ �"����- ���|Au�Oހom61W�Ϩp9 Xѵ9}$KS<Ӹ啇Bd|`��@�� 0������5 iud5'2haµ2$!/&p4  vZc9frdf�@�� �����;`@��M93bRqό]͹(u2"*+bխǘ?C\(tsKըV6']9[h;s=PdqA��  �����.�`��B2ELv<FT!ϩ$%smNwzj ` L$`SqcbdoA�� �#���= ���0F?xņ^aW+5B.cHyV)Bބҩ4U sYz!㏋]ja{ֳ`6pd^A`�� ` �����9�`��pKPlw4i= [qUJ1*rv_\VjE."ذ쐆+ lґn`ddA@�� @ ��@�=�`��ƺ#kQqU)8YкQI4 wUk.M R 5;Y;Rdz���� 0�����; ��� EzQuVTqW9v ^ر}N~HX|\6JyyNg0Udu���� `@������+�!����E5Nb2@m 9HFG.9WlutBihU K֭3N2 1o - #{SdfA���`0����3 ���zǦ^Tk QMI(M@@εxs 4uk=DP~53FW`Tx[<dzA��� 0�����1`���ʣ#AMoc.ʪIKn5P45+8?emלYww1q@$T)q.d\B�`�� 0������;�`��MBp*Pɫ֊iN7*d^D| aҟ(F~h[zQHͭHiy"dǏsA�`��  �����7�`��Ezv*S{^[byU k C'-[VHVQO?UBQQW̉M (7fApbŲ1w Yd͏iA���0������7`���04B4U[d7&?%sْ6|G|1/k{&]i˥)Ghl6>6dgA���  #&��<� ���(krqt))G yN Jlz C)Ňʹ3*79xdrA���@������3 ��h(Uƚ,r ET7_۰}L)!!I;Ifw=p^S _do����0������-�a����mWIn qѭ =gy+0V̪'L9 (ֵHsoZkklh0,T>d��@���#�A���9`���Y̎\fmy$'@WHbFbVyၢڌqel) g jd�����c�B(���G�`@�z'8jF@, ZY&&BrVI킡g?]}vwo:-[<R`dyA��0#8�@�8`��dI*T ؒ@τ(!rP:tTPU[аu~&$>8VJd� ���A`�?� @1i2iy/9q]UB Lː8bF@SX2r˫w~xyd���� ������C�`���wlx:iL)_d(fcSIKi&=A׳mR_|(ed@����B<@?�`@��t ;L&jVy#<Njkߒn_h=V瘦LcZ$Cd}��� #&P)DD`@��R]sq%k֢9KݗE~ A<t֨A䅥/4ZzMK,z2dA���#8@�? ��˽{<l|?hu>3|2mbb6STÙ vm <e(d6>*Ld���� ���B\�@Hx('p( i^ZÝNF 9<o!d}T\e-BdA��` �"�`�A�`@��qA!ր(}y V4<Gt3yM{G׻k+:lg%M&dwA��� #8\�L� @v틨>5 ^A)xf.pnJk*X|ukh}&dj����  �#�P`H`@�?l5=F93/`tIi&4͇aQr__{Pd� � #8A`�4�`�,1{o:+ile.Yn B)VdV2R.y˸^X-sgʿQd�`��A��:`���krFRN;vuHe8C-6*IlxH<+lqL|貔p9rKd@�7#$ �E`@��;}LPF͇ cLcZFVS:Vޛ :4*&⬏m@d@`��� ��C`��.]P iUM2sT0q  i00+&ctdC4dYhQ" d@����B ���G�`��Feڻ̿UU |[,aRmj"l 2<7H[2w2Ċ<uJd@�@ #8���I� @��N*Ne<}^qG_Q./My};(Bs.&Rd~��@�� ��8��N �c]Zx)jm6Dle(At: w)SEt4\95h{uOAQ !dmA��� #8h��H� @ :IgނYY;* }ܶlIr.;Ww{F=/ =MCdA���  #$ ���I� @��eG&/j7 (ʚ_](y9 b(.hRS⡋ k>,FXjqPldA��� 0���T��J� @��&Tִw&igE2l]Hkuu%ԉ45Լx,Yd����` �"����F� @�NJ(ʞPmUF@*vJC7wl;x`v'.X"eg81d@�"8��D @���.o&Sdɭ"+]"'סyh ...lP)C>AXSjHtXd��A�#&B$���F�`@�!aκ^Y%ϵC([uB7kCb]Ej)Zy iA&=*+isOd~����� ���D����I� @��R<6N-/ۀ;,?'jYMʃBBŦ^OJViMdA�0���\�J� �qnat9B QzU .*4Ir9<uof~ub+<ɔC-);Od~A��@0����?`��HB\)KmUE m \XcU)RJbQ"\(<:>X(Y_ed���` �� @�E� s-^8ysK!qVd:@(BR)`7,s8Caf%&\M ҙd����@��c�B$���<`���R[O*Y+T9<�v)iWj+nyuk1朗k8;,kv}?9_ldA��� ���$���E r>Ol]ڹ~ *zXA׵.JE񖔧LBjԊm,L> G{zM37d}�@�� ��p`M�`�"o㯘wș6sz}ۈ Kf( SIsݼ`vlYA&P]UE.d���`#&��@ ��mF m|MmQ7R;A-EkDEڻ5[tjwShCbI8_BdA��� ���B @�D`@�s+;)3gӭD o\kxFaeE`PjtikFiJr hqqZUdA������P`J @\t-{onAŚ[Zzw])SL"0_:y?RƤpo%owxRid�� ���(@�@ ���;o-)dy2U zBfQ ԲH׹'Զ0,fG6R̙TTR7d@�� ��BD��H @��9 0vPW6ANufm( r-x5ZuY#nʽ̎b@dA�@�� `���8�L�`@��၃d�bD-|r$$XJP-Yȓ29>IT7bkȏ)J:ud�@��@��$���I� ��jA/._Z0XY\J5*ck qΕk73}MUߺǨQ X duA�� ���A@�H`@�=k!g\/d,fg19ԭ <H,!y!vp"v1bInMsw>׿td������"�B @�H @�/Mc2G~M Pj]4iu`pAPB]1/75A�R%Qހ<xUdpA�� @�p@L`� pv8b*ӲyÈ NCOϣRc;ooAԤ}8sg^Xj_dA��� �#�`�?�`@��#i2Vt2KڅYas$Aık9ʠ)g/0wG_5ta c6d�@#�B$`�F�`@�U!.=j]| w8[e)l^lIDpUUrӣ+Uѭ35e)r76<d���� "���E� @��yOXing����B@�B@����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/zero-length-mdat.m4a��������������������������������������������������������0000664�0000000�0000000�00000010645�14662262111�0020623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ftypmp42����M4A mp42isom������Mmoov���lmvhd����>>�_���������������������������������������������@��������������������������������<trak���\tkhd���>>�����������������������������������������������������@�������������mdia��� mdhd����>>��V"��`�U�����4hdlr��������soun������������Sound Media Handler���|minf���smhd�����������$dinf���dref���������� url �����@stbl���lstsd����������\mp4a���������������������V"�����8esds����'���@���`��W@VH����stts����������������(stsc����������������������������tstsz��������������������l���e���^���������u���^���u���|���p���e������������~���r���|�����������������stco���������}�� O���udta���meta�������"hdlr��������mdirappl�������������_ilst���0too���(data�������Nero AAC codec / 1.5.4.0���'nam���data�������Sine wave 440Hz���Xtra���free����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mdat���������@A\����8�8>ODDؑI뤇 8)w'hh@�D  4 HHO~SM4M4M1o6璇H"D NV6 Kx^K)yz}�p.jC+/hZh'Y&JٜQ d`<}t0F*p�OqDfc@KNskFi(kѝ����+T�>֖("wWNZX?'}ԴyGueA ,~M:` 8TL' "E+J%�Ā`80�7@ B D@>[jC-(򏗈{ <,ygyaaaaaP"6[K2(!RbߥV�tc`�760 !�B Emc8#d% ZjGӳN;4ӳN;%YZ%YAFr T \3!��`V�560 *UT)%\L_ׄ rl|J';Cέ4[i/7٦ido&ii4.i\`i^a2'7cdmɧhjN\D]4~$Q2-˯{maV�t`.`�560  IS9dJ -$#rB'XUzv9sVU)TQDI@߫hc`ȁDo ˖)(@e2?"8+ޅgU5bQ ~b!N%/TH3h8�� `�&560 $& �"!2\�A.u2I[/d<(S]5]5]5]5]5]5]5fLFF<<y䥏0&H0sLʭ I`k  NaJHCN@u� 79 � @{KЋmC8#.2C=ߊ}(= x0yyJRi Er$N!t!`ӠP] &760 R})paD=`~E>׭/e]e]e]e]eB8@u3�$ "�ӬAXL*h@E%4PvIY]���,�&760 Rxd<kmHc8.)!~7| '=*\@<|r,aaaaaaapDDr)5keֱhXw\.Ad/tc*Uf �+�Ak�2760 L,-!x9伻RwYYYYg<$U�Z4*nN $;w!^Np#@�762 D@?s! ͎CizpiKK~QEQEP`Z X^"n.Vq Z�уQ�&560 $-%Lp�OנiU,$IIIIIIIIjQEQEH! H( UU5!PQ n+xP=P"W0$$4�� ��8*560 Ha2 !H OI+ 4F8KtMtճOO=o}9X<c{62R|'kIZr){p9fҲ{ gJQ<gJY[&tӠP]� 560 $*UR.]Pd>I'OU̾](]5ӻ^~wݻ_+;N###ySΩyypyΩΩJGWwS77)viKUu.4yi &M \떥/@�� .76' B�`@ [lCLҵ'G/ir%Ȗh&hBhBh&*f�?h$]|/ er@'R\Aj&U2CN @,�@760 (�3ĴpUD[jC8 (sp t)ЧB�DVH,#oJNbd e @-3n]ggM��0� 760 R!ˆQІ0#aX/ f>WN]:d,ΰBI դH/p@% RdC*F%A D^j5Xi(�`0�760 PUY kmHc8)f<?T%&x/9e}dxAddAo@b�R("�\* P@.Z*0Ner{��@Q�"560 C5&sA T8t-Y?>C#zԷ YQcE+7E]5ĕz%E~)O>𥗡,,˽u27,*8F1H +!y Є/^cOG?W̩Ik 7b6n Nf4p�u`�.W!,Rڊ]Yd;)!ŚԌa @a!^ BiS7MF<E~$?ƅ@V "DX֐DClȖ&X1"� �0D#mr!N륗z>?Ƹ4@ h@�Cvh@�&R6Jk'S>1h)h\oYO]r+N{S>KkLj4~4vYXuW`O=l?luH~%D2q6pЦbLZiE P %ty'S|e+ѵTWMU׆9! 禃M Qj#vE4o @0@ �[g|}@�0�������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/zero-size-chunk.wav���������������������������������������������������������0000664�0000000�0000000�00000002000�14662262111�0020575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������RIFF���WAVEfmt �����D�����data������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/zero-sized-padding.flac�����������������������������������������������������0000664�0000000�0000000�00000011124�14662262111�0021356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fLaC���"������ B�zAf鄚0 <w�(���������TITLE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX���Y�k������Yl������Ye������eYb������)Yw������QYp������Z$Yy������cY~������YS������+9Y T������LY ]������Y Z������Y O������Y H������aYA������X YF������xY������gY������Y������Y������^vY������7Y�������-BY ������Y������Y#������\_Y$������*Y-������Y*������eY?������Y8������Y1������/kY6������Y ������FY!������i3Y"������Py#������XE��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/data/zerodiv.ape�����������������������������������������������������������������0000664�0000000�0000000�00000001662�14662262111�0017207�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MAC ���� ����������Tc���������G`#geWj|��� �2��%�@���D�� ���b�� ��I����~� g�5!�$�&�H)�,�?.�1�x3�n&6�8�;�*>�nA�4D�G� I�TLO�VR�0MU�W�~Z�1]�})`�b�RIFFʢ�WEVEfmt �����D��B���dataʢ�Nޘ �~æ+i!\r 2-Ӡ~xFm00Pcy kj'@i Cb"Cҧ`Çw|菖S|b- N5u}i2CZ1nPzSe྿: g_Mn<���2%y1yp }Q^7KzwV([6S!YHh{ lj:<(e˱HK=+mRq)< }J'l:um5  <OQμs(5mǐLӹi(e+ꨔs ˘<u0ANb9SHrj1A 9J]b/S _VwQ&的7' a&N9hȃ3 <_i!)6`s&{ f3H`WIȠ/RpD2sUh3^8AA�|@0SbaQ::p 76j S8zŦ74zf7VBg+ldDH_ATHZvuHͳ| K||0(|^ec497 ggϨ8ru:EV鞛L]�m׮m umfr);DҙX_������������������������������������������������������������������������������taglib-2.0.2/tests/data/zerodiv.mpc�����������������������������������������������������������������0000664�0000000�0000000�00000000625�14662262111�0017217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������MPCKSHD�������Album�BaHlads�������Artist�just for test�������Gere�Ballad�������Title�mp�������Track�1����������Ti[le�d� ����c s8 est withs������Track�1�������Year�?000APETAGE_����^tack�1�������Year�?000APETAG)flad6� �����T�le�d� �����[i[le�mpc s8 est wittags������Track�1�������Yea�?000APETAG)_І����������ac\�1�������Year�?000APETAGEX����������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/main.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000006650�14662262111�0015557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cstdlib> #include <cstring> #include <fstream> #include <stdexcept> #include <cppunit/TestResult.h> #include <cppunit/TestResultCollector.h> #include <cppunit/TestRunner.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/BriefTestProgressListener.h> #include <cppunit/CompilerOutputter.h> #include <cppunit/XmlOutputter.h> int main(int argc, char* argv[]) { std::string testPath = argc > 1 ? std::string(argv[1]) : ""; // Create the event manager and test controller CppUnit::TestResult controller; // Add a listener that collects test result CppUnit::TestResultCollector result; controller.addListener(&result); // Add a listener that print dots as test run. CppUnit::BriefTestProgressListener progress; controller.addListener(&progress); // Add the top suite to the test runner CppUnit::TestRunner runner; runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); try { std::cout << "Running " << testPath; runner.run(controller, testPath); std::cerr << '\n'; // Print test in a compiler compatible format. CppUnit::CompilerOutputter outputter(&result, std::cerr); outputter.write(); #if defined(_MSC_VER) && _MSC_VER > 1500 char *xml = NULL; ::_dupenv_s(&xml, NULL, "CPPUNIT_XML"); #else char *xml = ::getenv("CPPUNIT_XML"); #endif if(xml && !::strcmp(xml, "1")) { std::ofstream xmlfileout("cpptestresults.xml"); CppUnit::XmlOutputter xmlout(&result, xmlfileout); xmlout.write(); } #if defined(_MSC_VER) && _MSC_VER > 1500 ::free(xml); #endif } catch(std::invalid_argument &e){ std::cerr << '\n' << "ERROR: " << e.what() << '\n'; return 0; } return result.wasSuccessful() ? 0 : 1; } ����������������������������������������������������������������������������������������taglib-2.0.2/tests/plainfile.h����������������������������������������������������������������������0000664�0000000�0000000�00000004425�14662262111�0016241�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifndef TAGLIB_PLAINFILE_H #define TAGLIB_PLAINFILE_H #include "tfile.h" using namespace TagLib; //! File subclass that gives tests access to filesystem operations class PlainFile : public File { public: explicit PlainFile(FileName name) : File(name) { } Tag *tag() const override { return nullptr; } AudioProperties *audioProperties() const override { return nullptr; } bool save() override { return false; } void truncate(long length) { File::truncate(length); } ByteVector readAll() { seek(0, End); offset_t end = tell(); seek(0); return readBlock(end); } }; #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_aiff.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000013510�14662262111�0016570�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tag.h" #include "aifffile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestAIFF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestAIFF); CPPUNIT_TEST(testAiffProperties); CPPUNIT_TEST(testAiffCProperties); CPPUNIT_TEST(testSaveID3v2); CPPUNIT_TEST(testSaveID3v23); CPPUNIT_TEST(testDuplicateID3v2); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST_SUITE_END(); public: void testAiffProperties() { RIFF::AIFF::File f(TEST_FILE_PATH_C("empty.aiff")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(67, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(706, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(2941U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isAiffC()); } void testAiffCProperties() { RIFF::AIFF::File f(TEST_FILE_PATH_C("alaw.aifc")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(37, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(355, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(1622U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isAiffC()); CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f.audioProperties()->compressionType()); CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f.audioProperties()->compressionName()); } void testSaveID3v2() { ScopedFileCopy copy("empty", ".aiff"); string newname = copy.fileName(); { RIFF::AIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); f.tag()->setTitle(L"TitleXXX"); f.save(); CPPUNIT_ASSERT(f.hasID3v2Tag()); } { RIFF::AIFF::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); f.tag()->setTitle(""); f.save(); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } { RIFF::AIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } } void testSaveID3v23() { ScopedFileCopy copy("empty", ".aiff"); string newname = copy.fileName(); String xxx = ByteVector(254, 'X'); { RIFF::AIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(ID3v2::v3); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); } { RIFF::AIFF::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f2.tag()->header()->majorVersion()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); } } void testDuplicateID3v2() { ScopedFileCopy copy("duplicate_id3v2", ".aiff"); // duplicate_id3v2.aiff has duplicate ID3v2 tag chunks. // title() returns "Title2" if can't skip the second tag. RIFF::AIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("Title1"), f.tag()->title()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7030), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(-1), f.find("Title2")); } void testFuzzedFile1() { RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif")); CPPUNIT_ASSERT(f.isValid()); } void testFuzzedFile2() { RIFF::AIFF::File f(TEST_FILE_PATH_C("excessive_alloc.aif")); CPPUNIT_ASSERT(f.isValid()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_ape.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000022453�14662262111�0016436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tbytevectorlist.h" #include "tpropertymap.h" #include "apetag.h" #include "id3v1tag.h" #include "apefile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestAPE : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestAPE); CPPUNIT_TEST(testProperties399); CPPUNIT_TEST(testProperties399Tagged); CPPUNIT_TEST(testProperties399Id3v2); CPPUNIT_TEST(testProperties396); CPPUNIT_TEST(testProperties390); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: void testProperties399() { APE::File f(TEST_FILE_PATH_C("mac-399.ape")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); } void testProperties399Tagged() { APE::File f(TEST_FILE_PATH_C("mac-399-tagged.ape")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); } void testProperties399Id3v2() { APE::File f(TEST_FILE_PATH_C("mac-399-id3v2.ape")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3990, f.audioProperties()->version()); } void testProperties396() { APE::File f(TEST_FILE_PATH_C("mac-396.ape")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3960, f.audioProperties()->version()); } void testProperties390() { APE::File f(TEST_FILE_PATH_C("mac-390-hdr.ape")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(15, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(15630, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(689262U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3900, f.audioProperties()->version()); } void testFuzzedFile1() { APE::File f(TEST_FILE_PATH_C("longloop.ape")); CPPUNIT_ASSERT(f.isValid()); } void testFuzzedFile2() { APE::File f(TEST_FILE_PATH_C("zerodiv.ape")); CPPUNIT_ASSERT(f.isValid()); } void testStripAndProperties() { ScopedFileCopy copy("mac-399", ".ape"); { APE::File f(copy.fileName().c_str()); f.APETag(true)->setTitle("APE"); f.ID3v1Tag(true)->setTitle("ID3v1"); f.save(); } { APE::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); f.strip(APE::File::APE); CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); f.strip(APE::File::ID3v1); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testProperties() { PropertyMap tags; tags["ALBUM"] = StringList("Album"); tags["ALBUMARTIST"] = StringList("Album Artist"); tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); tags["ALBUMSORT"] = StringList("Album Sort"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTS"] = StringList("Artists"); tags["ARTISTSORT"] = StringList("Artist Sort"); tags["ASIN"] = StringList("ASIN"); tags["BARCODE"] = StringList("Barcode"); tags["CATALOGNUMBER"] = StringList("Catalog Number 1").append("Catalog Number 2"); tags["COMMENT"] = StringList("Comment"); tags["DATE"] = StringList("2021-01-10"); tags["DISCNUMBER"] = StringList("3/5"); tags["GENRE"] = StringList("Genre"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label 1").append("Label 2"); tags["MEDIA"] = StringList("Media"); tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); tags["ORIGINALDATE"] = StringList("2021-01-09"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["RELEASESTATUS"] = StringList("Release Status"); tags["RELEASETYPE"] = StringList("Release Type"); tags["SCRIPT"] = StringList("Script"); tags["TITLE"] = StringList("Title"); tags["TRACKNUMBER"] = StringList("2/3"); ScopedFileCopy copy("mac-399", ".ape"); { APE::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT(properties.isEmpty()); f.setProperties(tags); f.save(); } { const APE::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); } } void testRepeatedSave() { ScopedFileCopy copy("mac-399", ".ape"); { APE::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); f.APETag()->setTitle("0"); f.save(); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); f.save(); } { APE::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasAPETag()); CPPUNIT_ASSERT(f.hasID3v1Tag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAPE); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_apetag.cpp������������������������������������������������������������������0000664�0000000�0000000�00000015653�14662262111�0017136�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tpropertymap.h" #include "tag.h" #include "apefile.h" #include "apetag.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestAPETag : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestAPETag); CPPUNIT_TEST(testIsEmpty); CPPUNIT_TEST(testIsEmpty2); CPPUNIT_TEST(testPropertyInterface1); CPPUNIT_TEST(testPropertyInterface2); CPPUNIT_TEST(testInvalidKeys); CPPUNIT_TEST(testTextBinary); CPPUNIT_TEST(testID3v1Collision); CPPUNIT_TEST_SUITE_END(); public: void testIsEmpty() { APE::Tag tag; CPPUNIT_ASSERT(tag.isEmpty()); tag.addValue("COMPOSER", "Mike Oldfield"); CPPUNIT_ASSERT(!tag.isEmpty()); } void testIsEmpty2() { APE::Tag tag; CPPUNIT_ASSERT(tag.isEmpty()); tag.setArtist("Mike Oldfield"); CPPUNIT_ASSERT(!tag.isEmpty()); } void testPropertyInterface1() { APE::Tag tag; PropertyMap dict = tag.properties(); CPPUNIT_ASSERT(dict.isEmpty()); dict["ARTIST"] = String("artist 1"); dict["ARTIST"].append("artist 2"); dict["TRACKNUMBER"].append("17"); tag.setProperties(dict); CPPUNIT_ASSERT_EQUAL(String("17"), tag.itemListMap()["TRACK"].values()[0]); CPPUNIT_ASSERT_EQUAL(2u, tag.itemListMap()["ARTIST"].values().size()); CPPUNIT_ASSERT_EQUAL(String("artist 1 / artist 2"), tag.artist()); CPPUNIT_ASSERT_EQUAL(17u, tag.track()); const APE::Item &textItem = tag.itemListMap()["TRACK"]; CPPUNIT_ASSERT_EQUAL(APE::Item::Text, textItem.type()); CPPUNIT_ASSERT(!textItem.isEmpty()); CPPUNIT_ASSERT_EQUAL(9 + 5 + 2, textItem.size()); } void testPropertyInterface2() { APE::Tag tag; APE::Item item1("TRACK", String("17")); tag.setItem("TRACK", item1); APE::Item item2; item2.setType(APE::Item::Binary); ByteVector binaryData1("first"); item2.setBinaryData(binaryData1); tag.setItem("TESTBINARY", item2); PropertyMap properties = tag.properties(); CPPUNIT_ASSERT_EQUAL(1u, properties.unsupportedData().size()); CPPUNIT_ASSERT(properties.contains("TRACKNUMBER")); CPPUNIT_ASSERT(!properties.contains("TRACK")); CPPUNIT_ASSERT(tag.itemListMap().contains("TESTBINARY")); CPPUNIT_ASSERT_EQUAL(binaryData1, tag.itemListMap()["TESTBINARY"].binaryData()); ByteVector binaryData2("second"); tag.setData("TESTBINARY", binaryData2); const APE::Item &binaryItem = tag.itemListMap()["TESTBINARY"]; CPPUNIT_ASSERT_EQUAL(APE::Item::Binary, binaryItem.type()); CPPUNIT_ASSERT(!binaryItem.isEmpty()); CPPUNIT_ASSERT_EQUAL(9 + 10 + static_cast<int>(binaryData2.size()), binaryItem.size()); CPPUNIT_ASSERT_EQUAL(binaryData2, binaryItem.binaryData()); tag.removeUnsupportedProperties(properties.unsupportedData()); CPPUNIT_ASSERT(!tag.itemListMap().contains("TESTBINARY")); APE::Item item3("TRACKNUMBER", String("29")); tag.setItem("TRACKNUMBER", item3); properties = tag.properties(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), properties["TRACKNUMBER"].size()); CPPUNIT_ASSERT_EQUAL(String("17"), properties["TRACKNUMBER"][0]); CPPUNIT_ASSERT_EQUAL(String("29"), properties["TRACKNUMBER"][1]); } void testInvalidKeys() { PropertyMap properties; properties["A"] = String("invalid key: one character"); properties["MP+"] = String("invalid key: forbidden string"); properties[L"\x1234\x3456"] = String("invalid key: Unicode"); properties["A B~C"] = String("valid key: space and tilde"); properties["ARTIST"] = String("valid key: normal one"); APE::Tag tag; PropertyMap unsuccessful = tag.setProperties(properties); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), unsuccessful.size()); CPPUNIT_ASSERT(unsuccessful.contains("A")); CPPUNIT_ASSERT(unsuccessful.contains("MP+")); CPPUNIT_ASSERT(unsuccessful.contains(L"\x1234\x3456")); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), tag.itemListMap().size()); tag.addValue("VALID KEY", "Test Value 1"); tag.addValue("INVALID KEY \x7f", "Test Value 2"); tag.addValue(L"INVALID KEY \x1234\x3456", "Test Value 3"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), tag.itemListMap().size()); } void testTextBinary() { APE::Item item("DUMMY", String("Test Text")); CPPUNIT_ASSERT_EQUAL(String("Test Text"), item.toString()); CPPUNIT_ASSERT_EQUAL(ByteVector(), item.binaryData()); ByteVector data("Test Data"); item.setBinaryData(data); CPPUNIT_ASSERT(item.values().isEmpty()); CPPUNIT_ASSERT_EQUAL(String(), item.toString()); CPPUNIT_ASSERT_EQUAL(data, item.binaryData()); item.setValue("Test Text 2"); CPPUNIT_ASSERT_EQUAL(String("Test Text 2"), item.toString()); CPPUNIT_ASSERT_EQUAL(ByteVector(), item.binaryData()); } void testID3v1Collision() { ScopedFileCopy copy("no-tags", ".mpc"); string newname = copy.fileName(); { APE::File f(newname.c_str()); f.APETag(true)->setArtist("Filltointersect "); f.APETag()->setTitle("Filltointersect "); f.save(); } { APE::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAPETag); �������������������������������������������������������������������������������������taglib-2.0.2/tests/test_asf.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000037002�14662262111�0016436�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tbytevectorlist.h" #include "tpropertymap.h" #include "tag.h" #include "asffile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestASF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestASF); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testLosslessProperties); CPPUNIT_TEST(testRead); CPPUNIT_TEST(testSaveMultipleValues); CPPUNIT_TEST(testSaveStream); CPPUNIT_TEST(testSaveLanguage); CPPUNIT_TEST(testDWordTrackNumber); CPPUNIT_TEST(testSaveLargeValue); CPPUNIT_TEST(testSavePicture); CPPUNIT_TEST(testSaveMultiplePictures); CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testPropertiesAllSupported); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: void testAudioProperties() { ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3712, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA2, f.audioProperties()->codec()); CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.1"), f.audioProperties()->codecName()); CPPUNIT_ASSERT_EQUAL(String("64 kbps, 48 kHz, stereo 2-pass CBR"), f.audioProperties()->codecDescription()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); } void testLosslessProperties() { ASF::File f(TEST_FILE_PATH_C("lossless.wma")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3549, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(1152, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(ASF::Properties::WMA9Lossless, f.audioProperties()->codec()); CPPUNIT_ASSERT_EQUAL(String("Windows Media Audio 9.2 Lossless"), f.audioProperties()->codecName()); CPPUNIT_ASSERT_EQUAL(String("VBR Quality 100, 44 kHz, 2 channel 16 bit 1-pass VBR"), f.audioProperties()->codecDescription()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); } void testRead() { ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); CPPUNIT_ASSERT_EQUAL(String("test"), f.tag()->title()); } void testSaveMultipleValues() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::AttributeList values; values.append("Foo"); values.append("Bar"); f.tag()->setAttribute("WM/AlbumTitle", values); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(2, static_cast<int>( f.tag()->attributeListMap()["WM/AlbumTitle"].size())); } } void testDWordTrackNumber() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.tag()->contains("WM/TrackNumber")); f.tag()->setAttribute("WM/TrackNumber", static_cast<unsigned int>(123)); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); CPPUNIT_ASSERT_EQUAL(ASF::Attribute::DWordType, f.tag()->attribute("WM/TrackNumber").front().type()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(123), f.tag()->track()); f.tag()->setTrack(234); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); CPPUNIT_ASSERT_EQUAL(ASF::Attribute::UnicodeType, f.tag()->attribute("WM/TrackNumber").front().type()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(234), f.tag()->track()); } } void testSaveStream() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::Attribute attr("Foo"); attr.setStream(43); f.tag()->setAttribute("WM/AlbumTitle", attr); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(43, f.tag()->attribute("WM/AlbumTitle").front().stream()); } } void testSaveLanguage() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::Attribute attr("Foo"); attr.setStream(32); attr.setLanguage(56); f.tag()->setAttribute("WM/AlbumTitle", attr); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(32, f.tag()->attribute("WM/AlbumTitle").front().stream()); CPPUNIT_ASSERT_EQUAL(56, f.tag()->attribute("WM/AlbumTitle").front().language()); } } void testSaveLargeValue() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::Attribute attr(ByteVector(70000, 'x')); f.tag()->setAttribute("WM/Blob", attr); f.save(); } { ASF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(ByteVector(70000, 'x'), f.tag()->attribute("WM/Blob").front().toByteVector()); } } void testSavePicture() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::Picture picture; picture.setMimeType("image/jpeg"); picture.setType(ASF::Picture::FrontCover); picture.setDescription("description"); picture.setPicture("data"); f.tag()->setAttribute("WM/Picture", picture); f.save(); } { ASF::File f(newname.c_str()); ASF::AttributeList values2 = f.tag()->attribute("WM/Picture"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), values2.size()); ASF::Attribute attr2 = values2.front(); ASF::Picture picture2 = attr2.toPicture(); CPPUNIT_ASSERT(picture2.isValid()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture2.mimeType()); CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture2.type()); CPPUNIT_ASSERT_EQUAL(String("description"), picture2.description()); CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture2.picture()); } } void testSaveMultiplePictures() { ScopedFileCopy copy("silence-1", ".wma"); string newname = copy.fileName(); { ASF::File f(newname.c_str()); ASF::AttributeList values; ASF::Picture picture; picture.setMimeType("image/jpeg"); picture.setType(ASF::Picture::FrontCover); picture.setDescription("description"); picture.setPicture("data"); values.append(ASF::Attribute(picture)); ASF::Picture picture2; picture2.setMimeType("image/png"); picture2.setType(ASF::Picture::BackCover); picture2.setDescription("back cover"); picture2.setPicture("PNG data"); values.append(ASF::Attribute(picture2)); f.tag()->setAttribute("WM/Picture", values); f.save(); } { ASF::File f(newname.c_str()); ASF::AttributeList values2 = f.tag()->attribute("WM/Picture"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), values2.size()); ASF::Picture picture3 = values2[1].toPicture(); CPPUNIT_ASSERT(picture3.isValid()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture3.mimeType()); CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture3.type()); CPPUNIT_ASSERT_EQUAL(String("description"), picture3.description()); CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture3.picture()); ASF::Picture picture4 = values2[0].toPicture(); CPPUNIT_ASSERT(picture4.isValid()); CPPUNIT_ASSERT_EQUAL(String("image/png"), picture4.mimeType()); CPPUNIT_ASSERT_EQUAL(ASF::Picture::BackCover, picture4.type()); CPPUNIT_ASSERT_EQUAL(String("back cover"), picture4.description()); CPPUNIT_ASSERT_EQUAL(ByteVector("PNG data"), picture4.picture()); } } void testProperties() { ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); PropertyMap tags = f.properties(); tags["TRACKNUMBER"] = StringList("2"); tags["DISCNUMBER"] = StringList("3"); tags["BPM"] = StringList("123"); tags["ARTIST"] = StringList("Foo Bar"); f.setProperties(tags); tags = f.properties(); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.tag()->artist()); CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); CPPUNIT_ASSERT(f.tag()->contains("WM/BeatsPerMinute")); CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/BeatsPerMinute"].size()); CPPUNIT_ASSERT_EQUAL(String("123"), f.tag()->attribute("WM/BeatsPerMinute").front().toString()); CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); CPPUNIT_ASSERT(f.tag()->contains("WM/TrackNumber")); CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/TrackNumber"].size()); CPPUNIT_ASSERT_EQUAL(String("2"), f.tag()->attribute("WM/TrackNumber").front().toString()); CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["TRACKNUMBER"]); CPPUNIT_ASSERT(f.tag()->contains("WM/PartOfSet")); CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/PartOfSet"].size()); CPPUNIT_ASSERT_EQUAL(String("3"), f.tag()->attribute("WM/PartOfSet").front().toString()); CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); } void testPropertiesAllSupported() { PropertyMap tags; tags["ACOUSTID_ID"] = StringList("Acoustid ID"); tags["ACOUSTID_FINGERPRINT"] = StringList("Acoustid Fingerprint"); tags["ALBUM"] = StringList("Album"); tags["ALBUMARTIST"] = StringList("Album Artist"); tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); tags["ALBUMSORT"] = StringList("Album Sort"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTS"] = StringList("Artists"); tags["ARTISTSORT"] = StringList("Artist Sort"); tags["ARTISTWEBPAGE"] = StringList("Artist Webpage"); tags["ASIN"] = StringList("ASIN"); tags["BARCODE"] = StringList("Barcode"); tags["BPM"] = StringList("123"); tags["CATALOGNUMBER"] = StringList("Catalog Number"); tags["COMMENT"] = StringList("Comment"); tags["COMPOSER"] = StringList("Composer"); tags["CONDUCTOR"] = StringList("Conductor"); tags["COPYRIGHT"] = StringList("2021 Copyright"); tags["DATE"] = StringList("2021-01-03 12:29:23"); tags["DISCNUMBER"] = StringList("3/5"); tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); tags["ENCODEDBY"] = StringList("Encoded by"); tags["ENCODING"] = StringList("Encoding"); tags["ENCODINGTIME"] = StringList("2021-01-03 11:52:19"); tags["FILEWEBPAGE"] = StringList("File Webpage"); tags["GENRE"] = StringList("Genre"); tags["WORK"] = StringList("Grouping"); tags["INITIALKEY"] = StringList("Initial Key"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label"); tags["LANGUAGE"] = StringList("eng"); tags["LYRICIST"] = StringList("Lyricist"); tags["LYRICS"] = StringList("Lyrics"); tags["MEDIA"] = StringList("Media"); tags["MOOD"] = StringList("Mood"); tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); tags["MUSICIP_PUID"] = StringList("MusicIP PUID"); tags["ORIGINALALBUM"] = StringList("Original Album"); tags["ORIGINALARTIST"] = StringList("Original Artist"); tags["ORIGINALFILENAME"] = StringList("Original Filename"); tags["ORIGINALLYRICIST"] = StringList("Original Lyricist"); tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); tags["PRODUCER"] = StringList("Producer"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["RELEASESTATUS"] = StringList("Release Status"); tags["RELEASETYPE"] = StringList("Release Type"); tags["REMIXER"] = StringList("Remixer"); tags["SCRIPT"] = StringList("Script"); tags["SUBTITLE"] = StringList("Subtitle"); tags["TITLE"] = StringList("Title"); tags["TITLESORT"] = StringList("Title Sort"); tags["TRACKNUMBER"] = StringList("2/4"); ScopedFileCopy copy("silence-1", ".wma"); { ASF::File f(copy.fileName().c_str()); ASF::Tag *asfTag = f.tag(); asfTag->setTitle(""); asfTag->attributeListMap().clear(); f.save(); } { ASF::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT(properties.isEmpty()); f.setProperties(tags); f.save(); } { const ASF::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); } } void testRepeatedSave() { ScopedFileCopy copy("silence-1", ".wma"); ASF::File f(copy.fileName().c_str()); f.tag()->setTitle(longText(128 * 1024)); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(297578), f.length()); f.tag()->setTitle(longText(16 * 1024)); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(68202), f.length()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestASF); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_bytevector.cpp��������������������������������������������������������������0000664�0000000�0000000�00000055354�14662262111�0020065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #define _USE_MATH_DEFINES #include <cmath> #include "tbytevector.h" #include "tbytevectorlist.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestByteVector : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestByteVector); CPPUNIT_TEST(testByteVector); CPPUNIT_TEST(testFind1); CPPUNIT_TEST(testFind2); CPPUNIT_TEST(testFind3); CPPUNIT_TEST(testRfind1); CPPUNIT_TEST(testRfind2); CPPUNIT_TEST(testRfind3); CPPUNIT_TEST(testToHex); CPPUNIT_TEST(testIntegerConversion); CPPUNIT_TEST(testFloatingPointConversion); CPPUNIT_TEST(testReplace); CPPUNIT_TEST(testReplaceAndDetach); CPPUNIT_TEST(testIterator); CPPUNIT_TEST(testResize); CPPUNIT_TEST(testAppend1); CPPUNIT_TEST(testAppend2); CPPUNIT_TEST(testBase64); CPPUNIT_TEST_SUITE_END(); public: void testByteVector() { ByteVector s1("foo"); CPPUNIT_ASSERT(ByteVectorList::split(s1, " ").size() == 1); ByteVector s2("f"); CPPUNIT_ASSERT(ByteVectorList::split(s2, " ").size() == 1); CPPUNIT_ASSERT(ByteVector().isEmpty()); CPPUNIT_ASSERT_EQUAL(0U, ByteVector().size()); CPPUNIT_ASSERT(ByteVector("asdf").clear().isEmpty()); CPPUNIT_ASSERT_EQUAL(0U, ByteVector("asdf").clear().size()); CPPUNIT_ASSERT_EQUAL(ByteVector(), ByteVector("asdf").clear()); ByteVector i("blah blah"); ByteVector j("blah"); CPPUNIT_ASSERT(i.containsAt(j, 5, 0)); CPPUNIT_ASSERT(i.containsAt(j, 6, 1)); CPPUNIT_ASSERT(i.containsAt(j, 6, 1, 3)); i.clear(); CPPUNIT_ASSERT(i.isEmpty()); } void testFind1() { CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO")); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO", 0)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO", 1)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO", 2)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO", 3)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find("SggO", 4)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find("SggO", 5)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find("SggO", 6)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find("SggO", 7)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find("SggO", 8)); // Intentional out-of-bounds access. ByteVector v("0123456789x"); v.resize(10); v.data()[10] = 'x'; CPPUNIT_ASSERT_EQUAL(-1, v.find("789x", 7)); } void testFind2() { CPPUNIT_ASSERT_EQUAL(0, ByteVector("\x01", 1).find("\x01")); CPPUNIT_ASSERT_EQUAL(0, ByteVector("\x01\x02", 2).find("\x01\x02")); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("\x01", 1).find("\x02")); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("\x01\x02", 2).find("\x01\x03")); } void testFind3() { CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S')); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S', 0)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S', 1)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S', 2)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S', 3)); CPPUNIT_ASSERT_EQUAL(4, ByteVector("....SggO."). find('S', 4)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find('S', 5)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find('S', 6)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find('S', 7)); CPPUNIT_ASSERT_EQUAL(-1, ByteVector("....SggO."). find('S', 8)); } void testRfind1() { CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 0)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 1)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 2)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 3)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 4)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 5)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 6)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 7)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS", 8)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind("OggS")); } void testRfind2() { ByteVector r0("**************"); ByteVector r1("OggS**********"); ByteVector r2("**********OggS"); ByteVector r3("OggS******OggS"); ByteVector r4("OggS*OggS*OggS"); CPPUNIT_ASSERT_EQUAL(-1, r0.find("OggS")); CPPUNIT_ASSERT_EQUAL(-1, r0.rfind("OggS")); CPPUNIT_ASSERT_EQUAL(0, r1.find("OggS")); CPPUNIT_ASSERT_EQUAL(0, r1.rfind("OggS")); CPPUNIT_ASSERT_EQUAL(10, r2.find("OggS")); CPPUNIT_ASSERT_EQUAL(10, r2.rfind("OggS")); CPPUNIT_ASSERT_EQUAL(0, r3.find("OggS")); CPPUNIT_ASSERT_EQUAL(10, r3.rfind("OggS")); CPPUNIT_ASSERT_EQUAL(10, r4.rfind("OggS")); CPPUNIT_ASSERT_EQUAL(10, r4.rfind("OggS", 0)); CPPUNIT_ASSERT_EQUAL(5, r4.rfind("OggS", 7)); CPPUNIT_ASSERT_EQUAL(10, r4.rfind("OggS", 12)); } void testRfind3() { CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 0)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 1)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 2)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 3)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 4)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 5)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 6)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 7)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O', 8)); CPPUNIT_ASSERT_EQUAL(1, ByteVector(".OggS....").rfind('O')); } void testToHex() { ByteVector v("\xf0\xe1\xd2\xc3\xb4\xa5\x96\x87\x78\x69\x5a\x4b\x3c\x2d\x1e\x0f", 16); CPPUNIT_ASSERT_EQUAL(ByteVector("f0e1d2c3b4a5968778695a4b3c2d1e0f"), v.toHex()); } void testIntegerConversion() { const ByteVector data("\x00\xff\x01\xff\x00\xff\x01\xff\x00\xff\x01\xff\x00\xff", 14); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0x00ff), data.toShort()); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0xff00), data.toShort(false)); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0xff01), data.toShort(5U)); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0x01ff), data.toShort(5U, false)); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0xff), data.toShort(13U)); CPPUNIT_ASSERT_EQUAL(static_cast<short>(0xff), data.toShort(13U, false)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromShort(0x00ff), ByteVector("\x00\xff", 2)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromShort(0x00ff, false), ByteVector("\xff\x00", 2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0x00ff), data.toUShort()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0xff00), data.toUShort(false)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0xff01), data.toUShort(5U)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0x01ff), data.toUShort(5U, false)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0xff), data.toUShort(13U)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0xff), data.toUShort(13U, false)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromUShort(0xff00), ByteVector("\xff\x00", 2)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromUShort(0xff00, false), ByteVector("\x00\xff", 2)); CPPUNIT_ASSERT_EQUAL(0x00ff01ffU, data.toUInt()); CPPUNIT_ASSERT_EQUAL(0xff01ff00U, data.toUInt(false)); CPPUNIT_ASSERT_EQUAL(0xff01ff00U, data.toUInt(5U)); CPPUNIT_ASSERT_EQUAL(0x00ff01ffU, data.toUInt(5U, false)); CPPUNIT_ASSERT_EQUAL(0x00ffU, data.toUInt(12U)); CPPUNIT_ASSERT_EQUAL(0xff00U, data.toUInt(12U, false)); CPPUNIT_ASSERT_EQUAL(0x00ff01U, data.toUInt(0U, 3U)); CPPUNIT_ASSERT_EQUAL(0x01ff00U, data.toUInt(0U, 3U, false)); CPPUNIT_ASSERT_EQUAL(0xff01ffU, data.toUInt(5U, 3U)); CPPUNIT_ASSERT_EQUAL(0xff01ffU, data.toUInt(5U, 3U, false)); CPPUNIT_ASSERT_EQUAL(0x00ffU, data.toUInt(12U, 3U)); CPPUNIT_ASSERT_EQUAL(0xff00U, data.toUInt(12U, 3U, false)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromUInt(0xff00ff00), ByteVector("\xff\x00\xff\x00", 4)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromUInt(0xff00ff00, false), ByteVector("\x00\xff\x00\xff", 4)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0x00ff01ff00ff01ffULL), data.toLongLong()); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0xff01ff00ff01ff00ULL), data.toLongLong(false)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0xff01ff00ff01ff00ULL), data.toLongLong(5U)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0x00ff01ff00ff01ffULL), data.toLongLong(5U, false)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0x00ffU), data.toLongLong(12U)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0xff00U), data.toLongLong(12U, false)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromLongLong(0x00ff01ff00ff01ff), ByteVector("\x00\xff\x01\xff\x00\xff\x01\xff", 8)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromLongLong(0x00ff01ff00ff01ff, false), ByteVector("\xff\x01\xff\x00\xff\x01\xff\x00", 8)); const ByteVector data2("\xff\x01\xff\x00\xff\x01\xff\x00\xff\x01\xff\x00\xff\x01", 14); CPPUNIT_ASSERT_EQUAL(0xff01ff00ff01ff00ULL, data2.toULongLong()); CPPUNIT_ASSERT_EQUAL(0x00ff01ff00ff01ffULL, data2.toULongLong(false)); CPPUNIT_ASSERT_EQUAL(0x01ff00ff01ff00ffULL, data2.toULongLong(5U)); CPPUNIT_ASSERT_EQUAL(0xff00ff01ff00ff01ULL, data2.toULongLong(5U, false)); CPPUNIT_ASSERT_EQUAL(0xff01ULL, data2.toULongLong(12U)); CPPUNIT_ASSERT_EQUAL(0x01ffULL, data2.toULongLong(12U, false)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromULongLong(0xff01ff00ff01ff00ULL), ByteVector("\xff\x01\xff\x00\xff\x01\xff\x00", 8)); CPPUNIT_ASSERT_EQUAL(ByteVector::fromULongLong(0xff01ff00ff01ff00ULL, false), ByteVector("\x00\xff\x01\xff\x00\xff\x01\xff", 8)); } void testFloatingPointConversion() { constexpr double Tolerance = 1.0e-7; const ByteVector pi32le("\xdb\x0f\x49\x40", 4); CPPUNIT_ASSERT(std::abs(pi32le.toFloat32LE(0) - M_PI) < Tolerance); CPPUNIT_ASSERT_EQUAL(pi32le, ByteVector::fromFloat32LE(pi32le.toFloat32LE(0))); const ByteVector pi32be("\x40\x49\x0f\xdb", 4); CPPUNIT_ASSERT(std::abs(pi32be.toFloat32BE(0) - M_PI) < Tolerance); CPPUNIT_ASSERT_EQUAL(pi32be, ByteVector::fromFloat32BE(pi32be.toFloat32BE(0))); const ByteVector pi64le("\x18\x2d\x44\x54\xfb\x21\x09\x40", 8); CPPUNIT_ASSERT(std::abs(pi64le.toFloat64LE(0) - M_PI) < Tolerance); CPPUNIT_ASSERT_EQUAL(pi64le, ByteVector::fromFloat64LE(pi64le.toFloat64LE(0))); const ByteVector pi64be("\x40\x09\x21\xfb\x54\x44\x2d\x18", 8); CPPUNIT_ASSERT(std::abs(pi64be.toFloat64BE(0) - M_PI) < Tolerance); CPPUNIT_ASSERT_EQUAL(pi64be, ByteVector::fromFloat64BE(pi64be.toFloat64BE(0))); const ByteVector pi80le("\x00\xc0\x68\x21\xa2\xda\x0f\xc9\x00\x40", 10); CPPUNIT_ASSERT(std::abs(pi80le.toFloat80LE(0) - M_PI) < Tolerance); const ByteVector pi80be("\x40\x00\xc9\x0f\xda\xa2\x21\x68\xc0\x00", 10); CPPUNIT_ASSERT(std::abs(pi80be.toFloat80BE(0) - M_PI) < Tolerance); } void testReplace() { { ByteVector a("abcdabf"); a.replace(ByteVector(""), ByteVector("<a>")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("foobartoolong"), ByteVector("<a>")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("xx"), ByteVector("yy")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("a"), ByteVector("x")); CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); a.replace(ByteVector("x"), ByteVector("a")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace('a', 'x'); CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); a.replace('x', 'a'); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("ab"), ByteVector("xy")); CPPUNIT_ASSERT_EQUAL(ByteVector("xycdxyf"), a); a.replace(ByteVector("xy"), ByteVector("ab")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("a"), ByteVector("<a>")); CPPUNIT_ASSERT_EQUAL(ByteVector("<a>bcd<a>bf"), a); a.replace(ByteVector("<a>"), ByteVector("a")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabf"); a.replace(ByteVector("b"), ByteVector("<b>")); CPPUNIT_ASSERT_EQUAL(ByteVector("a<b>cda<b>f"), a); a.replace(ByteVector("<b>"), ByteVector("b")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), a); } { ByteVector a("abcdabc"); a.replace(ByteVector("c"), ByteVector("<c>")); CPPUNIT_ASSERT_EQUAL(ByteVector("ab<c>dab<c>"), a); a.replace(ByteVector("<c>"), ByteVector("c")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabc"), a); } { ByteVector a("abcdaba"); a.replace(ByteVector("a"), ByteVector("<a>")); CPPUNIT_ASSERT_EQUAL(ByteVector("<a>bcd<a>b<a>"), a); a.replace(ByteVector("<a>"), ByteVector("a")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdaba"), a); } } void testReplaceAndDetach() { { ByteVector a("abcdabf"); ByteVector b = a; a.replace(ByteVector("a"), ByteVector("x")); CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); } { ByteVector a("abcdabf"); ByteVector b = a; a.replace('a', 'x'); CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); } { ByteVector a("abcdabf"); ByteVector b = a; a.replace(ByteVector("ab"), ByteVector("xy")); CPPUNIT_ASSERT_EQUAL(ByteVector("xycdxyf"), a); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); } { ByteVector a("abcdabf"); ByteVector b = a; a.replace(ByteVector("a"), ByteVector("<a>")); CPPUNIT_ASSERT_EQUAL(ByteVector("<a>bcd<a>bf"), a); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b); } { ByteVector a("ab<c>dab<c>"); ByteVector b = a; a.replace(ByteVector("<c>"), ByteVector("c")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabc"), a); CPPUNIT_ASSERT_EQUAL(ByteVector("ab<c>dab<c>"), b); } } void testIterator() { ByteVector v1("taglib"); ByteVector v2 = v1; auto it1 = v1.begin(); auto it2 = v2.begin(); CPPUNIT_ASSERT_EQUAL('t', *it1); CPPUNIT_ASSERT_EQUAL('t', *it2); std::advance(it1, 4); std::advance(it2, 4); *it2 = 'I'; CPPUNIT_ASSERT_EQUAL('i', *it1); CPPUNIT_ASSERT_EQUAL('I', *it2); CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v1); CPPUNIT_ASSERT_EQUAL(ByteVector("taglIb"), v2); auto it3 = v1.rbegin(); auto it4 = v2.rbegin(); CPPUNIT_ASSERT_EQUAL('b', *it3); CPPUNIT_ASSERT_EQUAL('b', *it4); std::advance(it3, 4); std::advance(it4, 4); *it4 = 'A'; CPPUNIT_ASSERT_EQUAL('a', *it3); CPPUNIT_ASSERT_EQUAL('A', *it4); CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v1); CPPUNIT_ASSERT_EQUAL(ByteVector("tAglIb"), v2); ByteVector v3 = ByteVector("0123456789").mid(3, 4); it1 = v3.begin(); it2 = v3.end() - 1; CPPUNIT_ASSERT_EQUAL('3', *it1); CPPUNIT_ASSERT_EQUAL('6', *it2); it3 = v3.rbegin(); it4 = v3.rend() - 1; CPPUNIT_ASSERT_EQUAL('6', *it3); CPPUNIT_ASSERT_EQUAL('3', *it4); } void testResize() { auto a = ByteVector("0123456789"); ByteVector b = a.mid(3, 4); b.resize(6, 'A'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(6), b.size()); CPPUNIT_ASSERT_EQUAL('6', b[3]); CPPUNIT_ASSERT_EQUAL('A', b[4]); CPPUNIT_ASSERT_EQUAL('A', b[5]); b.resize(10, 'B'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(10), b.size()); CPPUNIT_ASSERT_EQUAL('6', b[3]); CPPUNIT_ASSERT_EQUAL('B', b[6]); CPPUNIT_ASSERT_EQUAL('B', b[9]); b.resize(3, 'C'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), b.size()); CPPUNIT_ASSERT_EQUAL(-1, b.find('C')); b.resize(3); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), b.size()); // Check if a and b were properly detached. CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(10), a.size()); CPPUNIT_ASSERT_EQUAL('3', a[3]); CPPUNIT_ASSERT_EQUAL('5', a[5]); // Special case that refCount == 1 and d->offset != 0. ByteVector c = ByteVector("0123456789").mid(3, 4); c.resize(6, 'A'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(6), c.size()); CPPUNIT_ASSERT_EQUAL('6', c[3]); CPPUNIT_ASSERT_EQUAL('A', c[4]); CPPUNIT_ASSERT_EQUAL('A', c[5]); c.resize(10, 'B'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(10), c.size()); CPPUNIT_ASSERT_EQUAL('6', c[3]); CPPUNIT_ASSERT_EQUAL('B', c[6]); CPPUNIT_ASSERT_EQUAL('B', c[9]); c.resize(3, 'C'); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), c.size()); CPPUNIT_ASSERT_EQUAL(-1, c.find('C')); } void testAppend1() { ByteVector v1("foo"); v1.append("bar"); CPPUNIT_ASSERT_EQUAL(ByteVector("foobar"), v1); ByteVector v2("foo"); v2.append("b"); CPPUNIT_ASSERT_EQUAL(ByteVector("foob"), v2); ByteVector v3; v3.append("b"); CPPUNIT_ASSERT_EQUAL(ByteVector("b"), v3); ByteVector v4("foo"); v4.append(v1); CPPUNIT_ASSERT_EQUAL(ByteVector("foofoobar"), v4); ByteVector v5("foo"); v5.append('b'); CPPUNIT_ASSERT_EQUAL(ByteVector("foob"), v5); ByteVector v6; v6.append('b'); CPPUNIT_ASSERT_EQUAL(ByteVector("b"), v6); ByteVector v7("taglib"); ByteVector v8 = v7; v7.append("ABC"); CPPUNIT_ASSERT_EQUAL(ByteVector("taglibABC"), v7); v7.append('1'); v7.append('2'); v7.append('3'); CPPUNIT_ASSERT_EQUAL(ByteVector("taglibABC123"), v7); CPPUNIT_ASSERT_EQUAL(ByteVector("taglib"), v8); } void testAppend2() { ByteVector a("1234"); a.append(a); CPPUNIT_ASSERT_EQUAL(ByteVector("12341234"), a); } void testBase64() { ByteVector sempty; ByteVector t0("a"); // test 1 byte ByteVector t1("any carnal pleasure."); ByteVector t2("any carnal pleasure"); ByteVector t3("any carnal pleasur"); ByteVector s0("a"); // test 1 byte ByteVector s1("any carnal pleasure."); ByteVector s2("any carnal pleasure"); ByteVector s3("any carnal pleasur"); ByteVector eempty; ByteVector e0("YQ=="); ByteVector e1("YW55IGNhcm5hbCBwbGVhc3VyZS4="); ByteVector e2("YW55IGNhcm5hbCBwbGVhc3VyZQ=="); ByteVector e3("YW55IGNhcm5hbCBwbGVhc3Vy"); // Encode CPPUNIT_ASSERT_EQUAL(eempty, sempty.toBase64()); CPPUNIT_ASSERT_EQUAL(e0, s0.toBase64()); CPPUNIT_ASSERT_EQUAL(e1, s1.toBase64()); CPPUNIT_ASSERT_EQUAL(e2, s2.toBase64()); CPPUNIT_ASSERT_EQUAL(e3, s3.toBase64()); // Decode CPPUNIT_ASSERT_EQUAL(sempty, ByteVector::fromBase64(eempty)); CPPUNIT_ASSERT_EQUAL(s0, ByteVector::fromBase64(e0)); CPPUNIT_ASSERT_EQUAL(s1, ByteVector::fromBase64(e1)); CPPUNIT_ASSERT_EQUAL(s2, ByteVector::fromBase64(e2)); CPPUNIT_ASSERT_EQUAL(s3, ByteVector::fromBase64(e3)); CPPUNIT_ASSERT_EQUAL(t0, ByteVector::fromBase64(s0.toBase64())); CPPUNIT_ASSERT_EQUAL(t1, ByteVector::fromBase64(s1.toBase64())); CPPUNIT_ASSERT_EQUAL(t2, ByteVector::fromBase64(s2.toBase64())); CPPUNIT_ASSERT_EQUAL(t3, ByteVector::fromBase64(s3.toBase64())); ByteVector all(static_cast<unsigned int>(256)); // in order { for(int i = 0; i < 256; i++){ all[i]=static_cast<unsigned char>(i); } ByteVector b64 = all.toBase64(); ByteVector original = ByteVector::fromBase64(b64); CPPUNIT_ASSERT_EQUAL(all,original); } // reverse { for(int i = 0; i < 256; i++){ all[i]=static_cast<unsigned char>(255)-i; } ByteVector b64 = all.toBase64(); ByteVector original = ByteVector::fromBase64(b64); CPPUNIT_ASSERT_EQUAL(all,original); } // all zeroes { for(int i = 0; i < 256; i++){ all[i]=0; } ByteVector b64 = all.toBase64(); ByteVector original = ByteVector::fromBase64(b64); CPPUNIT_ASSERT_EQUAL(all,original); } // all ones { for(int i = 0; i < 256; i++){ all[i]=static_cast<unsigned char>(0xff); } ByteVector b64 = all.toBase64(); ByteVector original = ByteVector::fromBase64(b64); CPPUNIT_ASSERT_EQUAL(all,original); } // Missing end bytes { // No missing bytes ByteVector m0("YW55IGNhcm5hbCBwbGVhc3VyZQ=="); CPPUNIT_ASSERT_EQUAL(s2,ByteVector::fromBase64(m0)); // 1 missing byte ByteVector m1("YW55IGNhcm5hbCBwbGVhc3VyZQ="); CPPUNIT_ASSERT_EQUAL(sempty,ByteVector::fromBase64(m1)); // 2 missing bytes ByteVector m2("YW55IGNhcm5hbCBwbGVhc3VyZQ"); CPPUNIT_ASSERT_EQUAL(sempty,ByteVector::fromBase64(m2)); // 3 missing bytes ByteVector m3("YW55IGNhcm5hbCBwbGVhc3VyZ"); CPPUNIT_ASSERT_EQUAL(sempty,ByteVector::fromBase64(m3)); } // Grok invalid characters { ByteVector invalid("abd\x00\x01\x02\x03\x04"); CPPUNIT_ASSERT_EQUAL(sempty,ByteVector::fromBase64(invalid)); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_bytevectorlist.cpp����������������������������������������������������������0000664�0000000�0000000�00000005130�14662262111�0020744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevector.h" #include "tbytevectorlist.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestByteVectorList : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestByteVectorList); CPPUNIT_TEST(testSplitSingleChar); CPPUNIT_TEST(testSplitSingleChar_2); CPPUNIT_TEST_SUITE_END(); public: void testSplitSingleChar() { ByteVector v("a b"); ByteVectorList l = ByteVectorList::split(v, " "); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), l.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("a"), l[0]); CPPUNIT_ASSERT_EQUAL(ByteVector("b"), l[1]); } void testSplitSingleChar_2() { ByteVector v("a"); ByteVectorList l = ByteVectorList::split(v, " "); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), l.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("a"), l[0]); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVectorList); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_bytevectorstream.cpp��������������������������������������������������������0000664�0000000�0000000�00000010760�14662262111�0021271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevectorstream.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestByteVectorStream : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestByteVectorStream); CPPUNIT_TEST(testInitialData); CPPUNIT_TEST(testWriteBlock); CPPUNIT_TEST(testWriteBlockResize); CPPUNIT_TEST(testReadBlock); CPPUNIT_TEST(testRemoveBlock); CPPUNIT_TEST(testInsert); CPPUNIT_TEST(testSeekEnd); CPPUNIT_TEST_SUITE_END(); public: void testInitialData() { ByteVector v("abcd"); ByteVectorStream stream(v); CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), *stream.data()); } void testWriteBlock() { ByteVector v("abcd"); ByteVectorStream stream(v); stream.seek(1); stream.writeBlock(ByteVector("xx")); CPPUNIT_ASSERT_EQUAL(ByteVector("axxd"), *stream.data()); } void testWriteBlockResize() { ByteVector v("abcd"); ByteVectorStream stream(v); stream.seek(3); stream.writeBlock(ByteVector("xx")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcxx"), *stream.data()); stream.seek(5); stream.writeBlock(ByteVector("yy")); CPPUNIT_ASSERT_EQUAL(ByteVector("abcxxyy"), *stream.data()); } void testReadBlock() { ByteVector v("abcd"); ByteVectorStream stream(v); CPPUNIT_ASSERT_EQUAL(ByteVector("a"), stream.readBlock(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("bc"), stream.readBlock(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("d"), stream.readBlock(3)); CPPUNIT_ASSERT_EQUAL(ByteVector(""), stream.readBlock(3)); } void testRemoveBlock() { ByteVector v("abcd"); ByteVectorStream stream(v); stream.removeBlock(1, 1); CPPUNIT_ASSERT_EQUAL(ByteVector("acd"), *stream.data()); stream.removeBlock(0, 2); CPPUNIT_ASSERT_EQUAL(ByteVector("d"), *stream.data()); stream.removeBlock(0, 2); CPPUNIT_ASSERT_EQUAL(ByteVector(""), *stream.data()); } void testInsert() { ByteVector v("abcd"); ByteVectorStream stream(v); stream.insert(ByteVector("xx"), 1, 1); CPPUNIT_ASSERT_EQUAL(ByteVector("axxcd"), *stream.data()); stream.insert(ByteVector("yy"), 0, 2); CPPUNIT_ASSERT_EQUAL(ByteVector("yyxcd"), *stream.data()); stream.insert(ByteVector("foa"), 3, 2); CPPUNIT_ASSERT_EQUAL(ByteVector("yyxfoa"), *stream.data()); stream.insert(ByteVector("123"), 3, 0); CPPUNIT_ASSERT_EQUAL(ByteVector("yyx123foa"), *stream.data()); } void testSeekEnd() { ByteVector v("abcdefghijklmnopqrstuvwxyz"); ByteVectorStream stream(v); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(26), stream.length()); stream.seek(-4, IOStream::End); CPPUNIT_ASSERT_EQUAL(ByteVector("w"), stream.readBlock(1)); stream.seek(-25, IOStream::End); CPPUNIT_ASSERT_EQUAL(ByteVector("b"), stream.readBlock(1)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVectorStream); ����������������taglib-2.0.2/tests/test_complexproperties.cpp�������������������������������������������������������0000664�0000000�0000000�00000041772�14662262111�0021462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "asfpicture.h" #include "flacpicture.h" #include "flacfile.h" #include "tbytevector.h" #include "tvariant.h" #include "tzlib.h" #include "fileref.h" #include "apetag.h" #include "asftag.h" #include "mp4tag.h" #include "xiphcomment.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "attachedpictureframe.h" #include "generalencapsulatedobjectframe.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace TagLib; namespace { const String GEOB_KEY("GENERALOBJECT"); const String PICTURE_KEY("PICTURE"); const VariantMap TEST_PICTURE { {"data", ByteVector( "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48" "\x00\x00\xff\xdb\x00\x43\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03" "\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\x08\x06\x06\x05\x06\x09\x08\x0a" "\x0a\x09\x08\x09\x09\x0a\x0c\x0f\x0c\x0a\x0b\x0e\x0b\x09\x09\x0d\x11\x0d" "\x0e\x0f\x10\x10\x11\x10\x0a\x0c\x12\x13\x12\x10\x13\x0f\x10\x10\x10\xff" "\xc9\x00\x0b\x08\x00\x01\x00\x01\x01\x01\x11\x00\xff\xcc\x00\x06\x00\x10" "\x10\x05\xff\xda\x00\x08\x01\x01\x00\x00\x3f\x00\xd2\xcf\x20\xff\xd9", 125)}, {"mimeType", "image/jpeg"}, {"description", "Embedded cover"}, {"pictureType", "Front Cover"} }; } // namespace class TestComplexProperties : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestComplexProperties); CPPUNIT_TEST(testReadMp3Picture); CPPUNIT_TEST(testReadM4aPicture); CPPUNIT_TEST(testReadOggPicture); CPPUNIT_TEST(testReadWriteFlacPicture); CPPUNIT_TEST(testReadWriteMultipleProperties); CPPUNIT_TEST(testSetGetId3Geob); CPPUNIT_TEST(testSetGetId3Picture); CPPUNIT_TEST(testSetGetApePicture); CPPUNIT_TEST(testSetGetAsfPicture); CPPUNIT_TEST(testSetGetMp4Picture); CPPUNIT_TEST(testSetGetXiphPicture); CPPUNIT_TEST(testNonExistent); CPPUNIT_TEST_SUITE_END(); public: void testReadMp3Picture() { if(zlib::isAvailable()) { FileRef f(TEST_FILE_PATH_C("compressed_id3_frame.mp3"), false); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys()); auto pictures = f.complexProperties(PICTURE_KEY); CPPUNIT_ASSERT_EQUAL(1U, pictures.size()); auto picture = pictures.front(); CPPUNIT_ASSERT_EQUAL(86414U, picture.value("data").value<ByteVector>().size()); CPPUNIT_ASSERT_EQUAL(String(""), picture.value("description").value<String>()); CPPUNIT_ASSERT_EQUAL(String("image/bmp"), picture.value("mimeType").value<String>()); CPPUNIT_ASSERT_EQUAL(String("Other"), picture.value("pictureType").value<String>()); } } void testReadM4aPicture() { const ByteVector expectedData1( "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00" "\x00\x02\x00\x00\x00\x02\x08\x02\x00\x00\x00\xfd\xd4\x9a\x73\x00\x00\x00" "\x16\x49\x44\x41\x54\x78\x9c\x63\x7c\x9f\xca\xc0\xc0\xc0\xc0\xc4\xc0\xc0" "\xc0\xc0\xc0\x00\x00\x11\x09\x01\x58\xab\x88\xdb\x6f\x00\x00\x00\x00\x49" "\x45\x4e\x44\xae\x42\x60\x82", 79); const ByteVector expectedData2( "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x64\x00\x64" "\x00\x00\xff\xdb\x00\x43\x00\x09\x06\x07\x08\x07\x06\x09\x08\x08\x08\x0a" "\x0a\x09\x0b\x0e\x17\x0f\x0e\x0d\x0d\x0e\x1c\x14\x15\x11\x17\x22\x1e\x23" "\x23\x21\x1e\x20\x20\x25\x2a\x35\x2d\x25\x27\x32\x28\x20\x20\x2e\x3f\x2f" "\x32\x37\x39\x3c\x3c\x3c\x24\x2d\x42\x46\x41\x3a\x46\x35\x3b\x3c\x39\xff" "\xdb\x00\x43\x01\x0a\x0a\x0a\x0e\x0c\x0e\x1b\x0f\x0f\x1b\x39\x26\x20\x26" "\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39" "\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39" "\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\xff\xc0\x00\x11" "\x08\x00\x02\x00\x02\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00" "\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\xff\xc4\x00\x15\x01\x01\x01\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x06\xff\xc4\x00\x14\x11\x01\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00" "\x0c\x03\x01\x00\x02\x11\x03\x11\x00\x3f\x00\x8d\x80\xb8\x19\xff\xd9", 287); FileRef f(TEST_FILE_PATH_C("has-tags.m4a"), false); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys()); auto pictures = f.complexProperties(PICTURE_KEY); CPPUNIT_ASSERT_EQUAL(2U, pictures.size()); auto picture = pictures.front(); CPPUNIT_ASSERT_EQUAL(expectedData1, picture.value("data").value<ByteVector>()); CPPUNIT_ASSERT_EQUAL(String("image/png"), picture.value("mimeType").value<String>()); picture = pictures.back(); CPPUNIT_ASSERT_EQUAL(expectedData2, picture.value("data").value<ByteVector>()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture.value("mimeType").value<String>()); } void testReadOggPicture() { FileRef f(TEST_FILE_PATH_C("lowercase-fields.ogg"), false); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys()); auto pictures = f.complexProperties(PICTURE_KEY); CPPUNIT_ASSERT_EQUAL(1U, pictures.size()); auto picture = pictures.front(); CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), picture.value("data").value<ByteVector>()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture.value("mimeType").value<String>()); CPPUNIT_ASSERT_EQUAL(String("Back Cover"), picture.value("pictureType").value<String>()); CPPUNIT_ASSERT_EQUAL(String("new image"), picture.value("description").value<String>()); CPPUNIT_ASSERT_EQUAL(16, picture.value("colorDepth").value<int>()); CPPUNIT_ASSERT_EQUAL(7, picture.value("numColors").value<int>()); CPPUNIT_ASSERT_EQUAL(5, picture.value("width").value<int>()); CPPUNIT_ASSERT_EQUAL(6, picture.value("height").value<int>()); } void testReadWriteFlacPicture() { VariantMap picture(TEST_PICTURE); picture.insert("colorDepth", 8); picture.insert("numColors", 1); picture.insert("width", 1); picture.insert("height", 1); ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str(), false); CPPUNIT_ASSERT(f.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(f.pictureList().isEmpty()); CPPUNIT_ASSERT(f.setComplexProperties(PICTURE_KEY, {picture})); f.save(); } { FLAC::File f(copy.fileName().c_str(), false); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys()); CPPUNIT_ASSERT_EQUAL(picture, f.complexProperties(PICTURE_KEY).front()); auto flacPictures = f.pictureList(); CPPUNIT_ASSERT_EQUAL(1U, flacPictures.size()); auto flacPicture = flacPictures.front(); CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(), flacPicture->data()); CPPUNIT_ASSERT_EQUAL(picture.value("mimeType").value<String>(), flacPicture->mimeType()); CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, flacPicture->type()); CPPUNIT_ASSERT_EQUAL(picture.value("description").value<String>(), flacPicture->description()); CPPUNIT_ASSERT_EQUAL(picture.value("colorDepth").value<int>(), flacPicture->colorDepth()); CPPUNIT_ASSERT_EQUAL(picture.value("numColors").value<int>(), flacPicture->numColors()); CPPUNIT_ASSERT_EQUAL(picture.value("width").value<int>(), flacPicture->width()); CPPUNIT_ASSERT_EQUAL(picture.value("height").value<int>(), flacPicture->height()); CPPUNIT_ASSERT(f.setComplexProperties(PICTURE_KEY, {})); f.save(); } { FLAC::File f(copy.fileName().c_str(), false); CPPUNIT_ASSERT(f.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(f.pictureList().isEmpty()); } } void testReadWriteMultipleProperties() { const VariantMap picture2 { {"data", ByteVector("PNG data")}, {"mimeType", "image/png"}, {"description", ""}, {"pictureType", "Back Cover"} }; const VariantMap geob1 { {"data", ByteVector("First")}, {"mimeType", "text/plain"}, {"description", "Object 1"}, {"fileName", "test1.txt"} }; const VariantMap geob2 { {"data", ByteVector("Second")}, {"mimeType", "text/plain"}, {"description", "Object 2"}, {"fileName", "test2.txt"} }; ScopedFileCopy copy("xing", ".mp3"); { FileRef f(copy.fileName().c_str(), false); CPPUNIT_ASSERT(f.complexPropertyKeys().isEmpty()); f.setComplexProperties(PICTURE_KEY, {TEST_PICTURE, picture2}); f.setComplexProperties(GEOB_KEY, {geob1, geob2}); f.save(); } { FileRef f(copy.fileName().c_str(), false); CPPUNIT_ASSERT_EQUAL(StringList({PICTURE_KEY, GEOB_KEY}), f.complexPropertyKeys()); CPPUNIT_ASSERT(List<VariantMap>({TEST_PICTURE, picture2}) == f.complexProperties(PICTURE_KEY)); CPPUNIT_ASSERT(List<VariantMap>({geob1, geob2}) == f.complexProperties(GEOB_KEY)); } } void testSetGetId3Geob() { const VariantMap geob { {"data", ByteVector("Just a test")}, {"mimeType", "text/plain"}, {"description", "Embedded object"}, {"fileName", "test.txt"} }; ID3v2::Tag tag; CPPUNIT_ASSERT(!tag.frameListMap().contains("GEOB")); CPPUNIT_ASSERT(tag.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(GEOB_KEY).isEmpty()); CPPUNIT_ASSERT(tag.setComplexProperties(GEOB_KEY, {geob})); CPPUNIT_ASSERT_EQUAL(StringList(GEOB_KEY), tag.complexPropertyKeys()); CPPUNIT_ASSERT_EQUAL(geob, tag.complexProperties(GEOB_KEY).front()); auto frames = tag.frameListMap().value("GEOB"); CPPUNIT_ASSERT_EQUAL(1U, frames.size()); auto frame = dynamic_cast<ID3v2::GeneralEncapsulatedObjectFrame *>(frames.front()); CPPUNIT_ASSERT(frame); CPPUNIT_ASSERT_EQUAL(geob.value("data").value<ByteVector>(), frame->object()); CPPUNIT_ASSERT_EQUAL(geob.value("mimeType").value<String>(), frame->mimeType()); CPPUNIT_ASSERT_EQUAL(geob.value("description").value<String>(), frame->description()); CPPUNIT_ASSERT_EQUAL(geob.value("fileName").value<String>(), frame->fileName()); } void tagSetGetPicture(Tag &tag, const VariantMap &picture) { CPPUNIT_ASSERT(tag.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(PICTURE_KEY).isEmpty()); CPPUNIT_ASSERT(tag.setComplexProperties(PICTURE_KEY, {picture})); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), tag.complexPropertyKeys()); CPPUNIT_ASSERT_EQUAL(picture, tag.complexProperties(PICTURE_KEY).front()); } void testSetGetId3Picture() { const VariantMap picture(TEST_PICTURE); ID3v2::Tag tag; CPPUNIT_ASSERT(!tag.frameListMap().contains("APIC")); tagSetGetPicture(tag, picture); auto frames = tag.frameListMap().value("APIC"); CPPUNIT_ASSERT_EQUAL(1U, frames.size()); auto frame = dynamic_cast<ID3v2::AttachedPictureFrame *>(frames.front()); CPPUNIT_ASSERT(frame); CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(), frame->picture()); CPPUNIT_ASSERT_EQUAL(picture.value("mimeType").value<String>(), frame->mimeType()); CPPUNIT_ASSERT_EQUAL(picture.value("description").value<String>(), frame->description()); CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::FrontCover, frame->type()); } void testSetGetApePicture() { const String FRONT_COVER("COVER ART (FRONT)"); VariantMap picture(TEST_PICTURE); picture.erase("mimeType"); APE::Tag tag; CPPUNIT_ASSERT(!tag.itemListMap().contains(FRONT_COVER)); tagSetGetPicture(tag, picture); auto item = tag.itemListMap().value(FRONT_COVER); CPPUNIT_ASSERT_EQUAL( picture.value("description").value<String>().data(String::UTF8) .append('\0') .append(picture.value("data").value<ByteVector>()), item.binaryData()); } void testSetGetAsfPicture() { VariantMap picture(TEST_PICTURE); ASF::Tag tag; CPPUNIT_ASSERT(!tag.attributeListMap().contains("WM/Picture")); tagSetGetPicture(tag, picture); auto attributes = tag.attribute("WM/Picture"); CPPUNIT_ASSERT_EQUAL(1U, attributes.size()); auto asfPicture = attributes.front().toPicture(); CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(), asfPicture.picture()); CPPUNIT_ASSERT_EQUAL(picture.value("mimeType").value<String>(), asfPicture.mimeType()); CPPUNIT_ASSERT_EQUAL(picture.value("description").value<String>(), asfPicture.description()); CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, asfPicture.type()); } void testSetGetMp4Picture() { VariantMap picture(TEST_PICTURE); picture.erase("description"); picture.erase("pictureType"); MP4::Tag tag; CPPUNIT_ASSERT(!tag.itemMap().contains("covr")); tagSetGetPicture(tag, picture); auto covrs = tag.item("covr").toCoverArtList(); CPPUNIT_ASSERT_EQUAL(1U, covrs.size()); auto covr = covrs.front(); CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(), covr.data()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, covr.format()); } void testSetGetXiphPicture() { VariantMap picture(TEST_PICTURE); picture.insert("colorDepth", 8); picture.insert("numColors", 1); picture.insert("width", 1); picture.insert("height", 1); Ogg::XiphComment tag; CPPUNIT_ASSERT(tag.pictureList().isEmpty()); tagSetGetPicture(tag, picture); auto pics = tag.pictureList(); CPPUNIT_ASSERT_EQUAL(1U, pics.size()); auto pic = pics.front(); CPPUNIT_ASSERT_EQUAL(picture.value("data").value<ByteVector>(), pic->data()); CPPUNIT_ASSERT_EQUAL(picture.value("mimeType").value<String>(), pic->mimeType()); CPPUNIT_ASSERT_EQUAL(picture.value("description").value<String>(), pic->description()); CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); CPPUNIT_ASSERT_EQUAL(8, pic->colorDepth()); CPPUNIT_ASSERT_EQUAL(1, pic->numColors()); CPPUNIT_ASSERT_EQUAL(1, pic->width()); CPPUNIT_ASSERT_EQUAL(1, pic->height()); } void testNonExistent() { { ID3v2::Tag tag; CPPUNIT_ASSERT(tag.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(PICTURE_KEY).isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(GEOB_KEY).isEmpty()); CPPUNIT_ASSERT(tag.complexProperties("NONEXISTENT").isEmpty()); CPPUNIT_ASSERT(!tag.setComplexProperties("NONEXISTENT", {{{"description", "test"}}})); CPPUNIT_ASSERT(tag.complexProperties("NONEXISTENT").isEmpty()); CPPUNIT_ASSERT(tag.setComplexProperties(PICTURE_KEY, {TEST_PICTURE})); CPPUNIT_ASSERT(!tag.complexProperties(PICTURE_KEY).isEmpty()); } { ID3v1::Tag tag; CPPUNIT_ASSERT(tag.complexPropertyKeys().isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(PICTURE_KEY).isEmpty()); CPPUNIT_ASSERT(tag.complexProperties(GEOB_KEY).isEmpty()); CPPUNIT_ASSERT(tag.complexProperties("NONEXISTENT").isEmpty()); CPPUNIT_ASSERT(!tag.setComplexProperties("NONEXISTENT", {{{"description", "test"}}})); CPPUNIT_ASSERT(tag.complexProperties("NONEXISTENT").isEmpty()); CPPUNIT_ASSERT(!tag.setComplexProperties(PICTURE_KEY, {TEST_PICTURE})); CPPUNIT_ASSERT(tag.complexProperties(PICTURE_KEY).isEmpty()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestComplexProperties); ������taglib-2.0.2/tests/test_dsdiff.cpp������������������������������������������������������������������0000664�0000000�0000000�00000026055�14662262111�0017132�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2013-2023 Stephen F. Booth email : me@sbooth.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tbytevectorlist.h" #include "tpropertymap.h" #include "dsdifffile.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestDSDIFF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestDSDIFF); CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testTags); CPPUNIT_TEST(testSaveID3v2); CPPUNIT_TEST(testSaveID3v23); CPPUNIT_TEST(testStrip); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST(testId3InProp); CPPUNIT_TEST_SUITE_END(); public: void testProperties() { DSDIFF::File f(TEST_FILE_PATH_C("empty10ms.dff")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(10, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(5644, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(2822400, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(28224), f.audioProperties()->sampleCount()); } void testTags() { ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->artist()); f.tag()->setArtist("The Artist"); f.save(); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); } } void testSaveID3v2() { ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); f.tag()->setTitle(L"TitleXXX"); f.save(); CPPUNIT_ASSERT(f.hasID3v2Tag()); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); f.tag()->setTitle(""); f.save(); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); f.ID3v2Tag(true)->setTitle(L"TitleXXX"); f.save(); CPPUNIT_ASSERT(f.hasID3v2Tag()); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); } } void testSaveID3v23() { ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); String xxx = ByteVector(254, 'X'); { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(DSDIFF::File::AllTags, File::StripOthers, ID3v2::v3); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); } { DSDIFF::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f2.ID3v2Tag()->header()->majorVersion()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); } } void testStrip() { { ScopedFileCopy copy("empty10ms", ".dff"); { DSDIFF::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setArtist("X"); f.DIINTag(true)->setArtist("Y"); f.save(); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasDIINTag()); f.strip(); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasDIINTag()); } } { ScopedFileCopy copy("empty10ms", ".dff"); { DSDIFF::File f(copy.fileName().c_str()); f.ID3v2Tag(true); f.DIINTag(true); f.tag()->setArtist("X"); f.save(); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasDIINTag()); f.strip(DSDIFF::File::ID3v2); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasDIINTag()); } } { ScopedFileCopy copy("empty10ms", ".dff"); { DSDIFF::File f(copy.fileName().c_str()); f.tag()->setArtist("X"); f.save(); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasDIINTag()); f.strip(DSDIFF::File::DIIN); } { DSDIFF::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasDIINTag()); } } } void testRepeatedSave() { ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7186), f.length()); CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->title()); f.tag()->setTitle("NEW TITLE"); f.save(); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE"), f.tag()->title()); f.tag()->setTitle("NEW TITLE 2"); f.save(); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(8292), f.length()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(8292), f.length()); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); f.strip(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7186), f.length()); } // Check if file without tags is same as original empty file const ByteVector dsfData = PlainFile(TEST_FILE_PATH_C("empty10ms.dff")).readAll(); const ByteVector fileData = PlainFile(newname.c_str()).readAll(); CPPUNIT_ASSERT(dsfData == fileData); } void testId3InProp() { PropertyMap props; props["ALBUM"] = StringList("Album"); props["ALBUMARTIST"] = StringList("Album Artist"); props["ARTIST"] = StringList("Artist"); props["COMMENT"] = StringList("Comment"); props["DATE"] = StringList("2023-10-20"); props["DISCNUMBER"] = StringList("1/2"); props["GENRE"] = StringList("Genre"); props["TITLE"] = StringList("Title"); props["TRACKNUMBER"] = StringList("2/3"); ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); { PlainFile id3PropFile(newname.c_str()); id3PropFile.insert("\x28", 0x0b, 1); // modify FRM8 length id3PropFile.insert("\x6c", 0x2b, 1); // modify PROP length // insert ID3 chunk into PROP id3PropFile.insert(ByteVector( "ID3 \x00\x00\x00\x00\x00\x00\x00\x16ID3\x04\x00\x00\x00\x00\x00\x0c" "TIT2\x00\x00\x00\x02\x00\x00\x00X", 34), 0x76, 0); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("X"), f.tag()->title()); f.setProperties(props); f.DIINTag(true)->setArtist("DIINArtist"); f.save(); } { DSDIFF::File f(newname.c_str()); ID3v2::Tag *tag = f.ID3v2Tag(); CPPUNIT_ASSERT(tag); CPPUNIT_ASSERT_EQUAL(String("Title"), tag->title()); CPPUNIT_ASSERT_EQUAL(String("Artist"), tag->artist()); CPPUNIT_ASSERT_EQUAL(String("Album"), tag->album()); PropertyMap properties = f.properties(); if (props != properties) { CPPUNIT_ASSERT_EQUAL(props.toString(), properties.toString()); } CPPUNIT_ASSERT(props == properties); CPPUNIT_ASSERT_EQUAL(String("DIINArtist"), f.DIINTag()->artist()); f.strip(); } { // Check if file without tags is same as original empty file const ByteVector dsfData = PlainFile(TEST_FILE_PATH_C("empty10ms.dff")).readAll(); const ByteVector fileData = PlainFile(newname.c_str()).readAll(); CPPUNIT_ASSERT(dsfData == fileData); } } void testSaveDiin() { ScopedFileCopy copy("empty10ms", ".dff"); string newname = copy.fileName(); { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasDIINTag()); DSDIFF::DIIN::Tag *tag = f.DIINTag(true); CPPUNIT_ASSERT(tag); tag->setArtist("DIIN Artist"); tag->setTitle("DIIN Title"); tag->setAlbum("not supported"); f.save(); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasDIINTag()); DSDIFF::DIIN::Tag *tag = f.DIINTag(false); CPPUNIT_ASSERT(tag); CPPUNIT_ASSERT_EQUAL(String("DIIN Artist"), tag->artist()); CPPUNIT_ASSERT_EQUAL(String("DIIN Title"), tag->title()); CPPUNIT_ASSERT_EQUAL(String(), tag->album()); CPPUNIT_ASSERT_EQUAL(String(), tag->comment()); CPPUNIT_ASSERT_EQUAL(String(), tag->genre()); CPPUNIT_ASSERT_EQUAL(0U, tag->year()); CPPUNIT_ASSERT_EQUAL(0U, tag->track()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT_EQUAL(2U, properties.size()); CPPUNIT_ASSERT_EQUAL(StringList("DIIN Artist"), properties.value("ARTIST")); CPPUNIT_ASSERT_EQUAL(StringList("DIIN Title"), properties.value("TITLE")); tag->setArtist(""); tag->setTitle(""); f.save(); } { DSDIFF::File f(newname.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasDIINTag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestDSDIFF); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_dsf.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000004617�14662262111�0016447�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tpropertymap.h" #include "tag.h" #include "dsffile.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestDSF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestDSF); CPPUNIT_TEST(testBasic); CPPUNIT_TEST(testTags); CPPUNIT_TEST_SUITE_END(); public: void testBasic() { DSF::File f(TEST_FILE_PATH_C("empty10ms.dsf")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(10, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(5645, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(2822400, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->formatVersion()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->formatID()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channelType()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(28224), f.audioProperties()->sampleCount()); CPPUNIT_ASSERT_EQUAL(4096, f.audioProperties()->blockSizePerChannel()); } void testTags() { ScopedFileCopy copy("empty10ms", ".dsf"); string newname = copy.fileName(); { DSF::File f(newname.c_str()); CPPUNIT_ASSERT(f.properties().isEmpty()); CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->artist()); f.tag()->setArtist("The Artist"); PropertyMap properties = f.properties(); properties["ALBUMARTIST"] = StringList("Album Artist"); f.setProperties(properties); f.save(); } { DSF::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.properties()["ARTIST"].front()); CPPUNIT_ASSERT_EQUAL(String("Album Artist"), f.properties()["ALBUMARTIST"].front()); f.setProperties(PropertyMap()); f.save(); } // Check if file without tags is same as original empty file const ByteVector dsfData = PlainFile(TEST_FILE_PATH_C("empty10ms.dsf")).readAll(); const ByteVector fileData = PlainFile(newname.c_str()).readAll(); CPPUNIT_ASSERT(dsfData == fileData); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestDSF); �����������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_file.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000012674�14662262111�0016614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tfile.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace TagLib; class TestFile : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestFile); CPPUNIT_TEST(testFindInSmallFile); CPPUNIT_TEST(testRFindInSmallFile); CPPUNIT_TEST(testSeek); CPPUNIT_TEST(testTruncate); CPPUNIT_TEST_SUITE_END(); public: void testFindInSmallFile() { ScopedFileCopy copy("empty", ".ogg"); std::string name = copy.fileName(); { PlainFile file(name.c_str()); file.seek(0); file.writeBlock(ByteVector("0123456239", 10)); file.truncate(10); } { PlainFile file(name.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(10), file.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2), file.find(ByteVector("23", 2))); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2), file.find(ByteVector("23", 2), 2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7), file.find(ByteVector("23", 2), 3)); file.seek(0); const ByteVector v = file.readBlock(file.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(10), v.size()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.find("23")), file.find("23")); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.find("23", 2)), file.find("23", 2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.find("23", 3)), file.find("23", 3)); } } void testRFindInSmallFile() { ScopedFileCopy copy("empty", ".ogg"); std::string name = copy.fileName(); { PlainFile file(name.c_str()); file.seek(0); file.writeBlock(ByteVector("0123456239", 10)); file.truncate(10); } { PlainFile file(name.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(10), file.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7), file.rfind(ByteVector("23", 2))); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(7), file.rfind(ByteVector("23", 2), 7)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2), file.rfind(ByteVector("23", 2), 6)); file.seek(0); const ByteVector v = file.readBlock(file.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(10), v.size()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.rfind("23")), file.rfind("23")); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.rfind("23", 7)), file.rfind("23", 7)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(v.rfind("23", 6)), file.rfind("23", 6)); } } void testSeek() { ScopedFileCopy copy("empty", ".ogg"); std::string name = copy.fileName(); PlainFile f(name.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0), f.tell()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4328), f.length()); f.seek(100, File::Beginning); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(100), f.tell()); f.seek(100, File::Current); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(200), f.tell()); f.seek(-300, File::Current); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(200), f.tell()); f.seek(-100, File::End); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4228), f.tell()); f.seek(-100, File::Current); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4128), f.tell()); f.seek(300, File::Current); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4428), f.tell()); } void testTruncate() { ScopedFileCopy copy("empty", ".ogg"); std::string name = copy.fileName(); { PlainFile f(name.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4328), f.length()); f.truncate(2000); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2000), f.length()); } { PlainFile f(name.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2000), f.length()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFile); ��������������������������������������������������������������������taglib-2.0.2/tests/test_fileref.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000031414�14662262111�0017302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tfilestream.h" #include "tbytevectorstream.h" #include "tag.h" #include "fileref.h" #include "oggflacfile.h" #include "vorbisfile.h" #include "mpegfile.h" #include "mpcfile.h" #include "asffile.h" #include "speexfile.h" #include "flacfile.h" #include "trueaudiofile.h" #include "mp4file.h" #include "wavfile.h" #include "apefile.h" #include "aifffile.h" #include "wavpackfile.h" #include "opusfile.h" #include "xmfile.h" #include "dsffile.h" #include "dsdifffile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; namespace { class DummyResolver : public FileRef::FileTypeResolver { public: File *createFile(FileName fileName, bool, AudioProperties::ReadStyle) const override { return new Ogg::Vorbis::File(fileName); } }; class DummyStreamResolver : public FileRef::StreamTypeResolver { public: File *createFile(FileName, bool, AudioProperties::ReadStyle) const override { return nullptr; } File *createFileFromStream(IOStream *s, bool, AudioProperties::ReadStyle) const override { return new MP4::File(s); } }; } // namespace class TestFileRef : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestFileRef); CPPUNIT_TEST(testASF); CPPUNIT_TEST(testMusepack); CPPUNIT_TEST(testVorbis); CPPUNIT_TEST(testSpeex); CPPUNIT_TEST(testFLAC); CPPUNIT_TEST(testMP3); CPPUNIT_TEST(testOGA_FLAC); CPPUNIT_TEST(testOGA_Vorbis); CPPUNIT_TEST(testMP4_1); CPPUNIT_TEST(testMP4_2); CPPUNIT_TEST(testMP4_3); CPPUNIT_TEST(testMP4_4); CPPUNIT_TEST(testTrueAudio); CPPUNIT_TEST(testAPE); CPPUNIT_TEST(testWav); CPPUNIT_TEST(testAIFF_1); CPPUNIT_TEST(testAIFF_2); CPPUNIT_TEST(testWavPack); CPPUNIT_TEST(testOpus); CPPUNIT_TEST(testDSF); CPPUNIT_TEST(testDSDIFF); CPPUNIT_TEST(testUnsupported); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testDefaultFileExtensions); CPPUNIT_TEST(testFileResolver); CPPUNIT_TEST_SUITE_END(); public: template <typename T> void fileRefSave(const string &filename, const string &ext) { ScopedFileCopy copy(filename, ext); string newname = copy.fileName(); { FileRef f(newname.c_str()); CPPUNIT_ASSERT(dynamic_cast<T*>(f.file())); CPPUNIT_ASSERT(!f.isNull()); f.tag()->setArtist("test artist"); f.tag()->setTitle("test title"); f.tag()->setGenre("Test!"); f.tag()->setAlbum("albummmm"); f.tag()->setComment("a comment"); f.tag()->setTrack(5); f.tag()->setYear(2020); f.save(); } { FileRef f(newname.c_str()); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(5)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2020)); f.tag()->setArtist("ttest artist"); f.tag()->setTitle("ytest title"); f.tag()->setGenre("uTest!"); f.tag()->setAlbum("ialbummmm"); f.tag()->setComment("another comment"); f.tag()->setTrack(7); f.tag()->setYear(2080); f.save(); } { FileRef f(newname.c_str()); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(7)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2080)); } { FileStream fs(newname.c_str()); FileRef f(&fs); CPPUNIT_ASSERT(dynamic_cast<T*>(f.file())); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(7)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2080)); f.tag()->setArtist("test artist"); f.tag()->setTitle("test title"); f.tag()->setGenre("Test!"); f.tag()->setAlbum("albummmm"); f.tag()->setComment("a comment"); f.tag()->setTrack(5); f.tag()->setYear(2020); f.save(); } ByteVector fileContent; { FileStream fs(newname.c_str()); FileRef f(&fs); CPPUNIT_ASSERT(dynamic_cast<T*>(f.file())); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(5)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2020)); fs.seek(0); fileContent = fs.readBlock(fs.length()); } { ByteVectorStream bs(fileContent); FileRef f(&bs); CPPUNIT_ASSERT(dynamic_cast<T*>(f.file())); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("a comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(5)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2020)); f.tag()->setArtist("ttest artist"); f.tag()->setTitle("ytest title"); f.tag()->setGenre("uTest!"); f.tag()->setAlbum("ialbummmm"); f.tag()->setComment("another comment"); f.tag()->setTrack(7); f.tag()->setYear(2080); f.save(); fileContent = *bs.data(); } { ByteVectorStream bs(fileContent); FileRef f(&bs); CPPUNIT_ASSERT(dynamic_cast<T*>(f.file())); CPPUNIT_ASSERT(!f.isNull()); CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist")); CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title")); CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!")); CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm")); CPPUNIT_ASSERT_EQUAL(f.tag()->comment(), String("another comment")); CPPUNIT_ASSERT_EQUAL(f.tag()->track(), static_cast<unsigned int>(7)); CPPUNIT_ASSERT_EQUAL(f.tag()->year(), static_cast<unsigned int>(2080)); } } void testMusepack() { fileRefSave<MPC::File>("click", ".mpc"); } void testASF() { fileRefSave<ASF::File>("silence-1", ".wma"); } void testVorbis() { fileRefSave<Ogg::Vorbis::File>("empty", ".ogg"); } void testSpeex() { fileRefSave<Ogg::Speex::File>("empty", ".spx"); } void testFLAC() { fileRefSave<FLAC::File>("no-tags", ".flac"); } void testMP3() { fileRefSave<MPEG::File>("xing", ".mp3"); } void testTrueAudio() { fileRefSave<TrueAudio::File>("empty", ".tta"); } void testMP4_1() { fileRefSave<MP4::File>("has-tags", ".m4a"); } void testMP4_2() { fileRefSave<MP4::File>("no-tags", ".m4a"); } void testMP4_3() { fileRefSave<MP4::File>("no-tags", ".3g2"); } void testMP4_4() { fileRefSave<MP4::File>("blank_video", ".m4v"); } void testWav() { fileRefSave<RIFF::WAV::File>("empty", ".wav"); } void testOGA_FLAC() { fileRefSave<Ogg::FLAC::File>("empty_flac", ".oga"); } void testOGA_Vorbis() { fileRefSave<Ogg::Vorbis::File>("empty_vorbis", ".oga"); } void testAPE() { fileRefSave<APE::File>("mac-399", ".ape"); } void testAIFF_1() { fileRefSave<RIFF::AIFF::File>("empty", ".aiff"); } void testAIFF_2() { fileRefSave<RIFF::AIFF::File>("alaw", ".aifc"); } void testWavPack() { fileRefSave<WavPack::File>("click", ".wv"); } void testOpus() { fileRefSave<Ogg::Opus::File>("correctness_gain_silent_output", ".opus"); } void testDSF() { fileRefSave<DSF::File>("empty10ms",".dsf"); } void testDSDIFF() { fileRefSave<DSDIFF::File>("empty10ms",".dff"); } void testUnsupported() { FileRef f1(TEST_FILE_PATH_C("no-extension")); CPPUNIT_ASSERT(f1.isNull()); FileRef f2(TEST_FILE_PATH_C("unsupported-extension.xx")); CPPUNIT_ASSERT(f2.isNull()); } void testAudioProperties() { FileRef f(TEST_FILE_PATH_C("xing.mp3")); const AudioProperties *audioProperties = f.audioProperties(); CPPUNIT_ASSERT_EQUAL(2, audioProperties->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(2064, audioProperties->lengthInMilliseconds()); } void testDefaultFileExtensions() { const StringList extensions = FileRef::defaultFileExtensions(); CPPUNIT_ASSERT(extensions.contains("mpc")); CPPUNIT_ASSERT(extensions.contains("wma")); CPPUNIT_ASSERT(extensions.contains("ogg")); CPPUNIT_ASSERT(extensions.contains("spx")); CPPUNIT_ASSERT(extensions.contains("flac")); CPPUNIT_ASSERT(extensions.contains("mp3")); CPPUNIT_ASSERT(extensions.contains("tta")); CPPUNIT_ASSERT(extensions.contains("m4a")); CPPUNIT_ASSERT(extensions.contains("3g2")); CPPUNIT_ASSERT(extensions.contains("m4v")); CPPUNIT_ASSERT(extensions.contains("wav")); CPPUNIT_ASSERT(extensions.contains("oga")); CPPUNIT_ASSERT(extensions.contains("ape")); CPPUNIT_ASSERT(extensions.contains("aiff")); CPPUNIT_ASSERT(extensions.contains("aifc")); CPPUNIT_ASSERT(extensions.contains("wv")); CPPUNIT_ASSERT(extensions.contains("opus")); CPPUNIT_ASSERT(extensions.contains("xm")); CPPUNIT_ASSERT(extensions.contains("dsf")); CPPUNIT_ASSERT(extensions.contains("dff")); CPPUNIT_ASSERT(extensions.contains("dsdiff")); } void testFileResolver() { { FileRef f(TEST_FILE_PATH_C("xing.mp3")); CPPUNIT_ASSERT(dynamic_cast<MPEG::File *>(f.file()) != nullptr); } DummyResolver resolver; FileRef::addFileTypeResolver(&resolver); { FileRef f(TEST_FILE_PATH_C("xing.mp3")); CPPUNIT_ASSERT(dynamic_cast<Ogg::Vorbis::File *>(f.file()) != nullptr); } DummyStreamResolver streamResolver; FileRef::addFileTypeResolver(&streamResolver); { FileStream s(TEST_FILE_PATH_C("xing.mp3")); FileRef f(&s); CPPUNIT_ASSERT(dynamic_cast<MP4::File *>(f.file()) != nullptr); } FileRef::clearFileTypeResolvers(); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFileRef); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_flac.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000055431�14662262111�0016600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tpropertymap.h" #include "tag.h" #include "flacfile.h" #include "xiphcomment.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestFLAC : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestFLAC); CPPUNIT_TEST(testSignature); CPPUNIT_TEST(testMultipleCommentBlocks); CPPUNIT_TEST(testReadPicture); CPPUNIT_TEST(testAddPicture); CPPUNIT_TEST(testReplacePicture); CPPUNIT_TEST(testRemoveAllPictures); CPPUNIT_TEST(testRepeatedSave1); CPPUNIT_TEST(testRepeatedSave2); CPPUNIT_TEST(testRepeatedSave3); CPPUNIT_TEST(testSaveMultipleValues); CPPUNIT_TEST(testDict); CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testInvalid); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testZeroSizedPadding1); CPPUNIT_TEST(testZeroSizedPadding2); CPPUNIT_TEST(testShrinkPadding); CPPUNIT_TEST(testSaveID3v1); CPPUNIT_TEST(testUpdateID3v2); CPPUNIT_TEST(testEmptyID3v2); CPPUNIT_TEST(testStripTags); CPPUNIT_TEST(testRemoveXiphField); CPPUNIT_TEST(testEmptySeekTable); CPPUNIT_TEST(testPictureStoredAfterComment); CPPUNIT_TEST_SUITE_END(); public: void testSignature() { FLAC::File f(TEST_FILE_PATH_C("no-tags.flac")); CPPUNIT_ASSERT_EQUAL(ByteVector("a1b141f766e9849ac3db1030a20a3c77"), f.audioProperties()->signature().toHex()); } void testMultipleCommentBlocks() { ScopedFileCopy copy("multiple-vc", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("Artist 1"), f.tag()->artist()); f.tag()->setArtist("The Artist"); f.save(); } { FLAC::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(69), f.find("Artist")); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(-1), f.find("Artist", 70)); } } void testReadPicture() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); FLAC::Picture *pic = lst.front(); CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); CPPUNIT_ASSERT_EQUAL(1, pic->width()); CPPUNIT_ASSERT_EQUAL(1, pic->height()); CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(150), pic->data().size()); } void testAddPicture() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); auto newpic = new FLAC::Picture(); newpic->setType(FLAC::Picture::BackCover); newpic->setWidth(5); newpic->setHeight(6); newpic->setColorDepth(16); newpic->setNumColors(7); newpic->setMimeType("image/jpeg"); newpic->setDescription("new image"); newpic->setData("JPEG data"); f.addPicture(newpic); f.save(); } { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), lst.size()); FLAC::Picture *pic = lst[0]; CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); CPPUNIT_ASSERT_EQUAL(1, pic->width()); CPPUNIT_ASSERT_EQUAL(1, pic->height()); CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(150), pic->data().size()); pic = lst[1]; CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); CPPUNIT_ASSERT_EQUAL(5, pic->width()); CPPUNIT_ASSERT_EQUAL(6, pic->height()); CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); } } void testReplacePicture() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); auto newpic = new FLAC::Picture(); newpic->setType(FLAC::Picture::BackCover); newpic->setWidth(5); newpic->setHeight(6); newpic->setColorDepth(16); newpic->setNumColors(7); newpic->setMimeType("image/jpeg"); newpic->setDescription("new image"); newpic->setData("JPEG data"); f.removePictures(); f.addPicture(newpic); f.save(); } { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); FLAC::Picture *pic = lst[0]; CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); CPPUNIT_ASSERT_EQUAL(5, pic->width()); CPPUNIT_ASSERT_EQUAL(6, pic->height()); CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); } } void testRemoveAllPictures() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); f.removePictures(); f.save(); } { FLAC::File f(newname.c_str()); List<FLAC::Picture *> lst = f.pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), lst.size()); } } void testRepeatedSave1() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("Silence"), f.tag()->title()); f.tag()->setTitle("NEW TITLE"); f.save(); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE"), f.tag()->title()); f.tag()->setTitle("NEW TITLE 2"); f.save(); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); } { FLAC::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); } } void testRepeatedSave2() { ScopedFileCopy copy("no-tags", ".flac"); FLAC::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("0123456789"); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(5735), f.length()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(5735), f.length()); CPPUNIT_ASSERT(f.find("fLaC") >= 0); } void testRepeatedSave3() { ScopedFileCopy copy("no-tags", ".flac"); FLAC::File f(copy.fileName().c_str()); f.xiphComment()->setTitle(longText(8 * 1024)); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(12862), f.length()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(12862), f.length()); } void testSaveMultipleValues() { ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); f.xiphComment(true)->addField("ARTIST", "artist 1", true); f.xiphComment(true)->addField("ARTIST", "artist 2", false); f.save(); } { FLAC::File f(newname.c_str()); Ogg::FieldListMap m = f.xiphComment()->fieldListMap(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), m["ARTIST"].size()); CPPUNIT_ASSERT_EQUAL(String("artist 1"), m["ARTIST"][0]); CPPUNIT_ASSERT_EQUAL(String("artist 2"), m["ARTIST"][1]); } } void testDict() { // test unicode & multiple values with dict interface ScopedFileCopy copy("silence-44-s", ".flac"); string newname = copy.fileName(); { FLAC::File f(newname.c_str()); PropertyMap dict; dict["ARTIST"].append("artøst 1"); dict["ARTIST"].append("artöst 2"); f.setProperties(dict); f.save(); } { FLAC::File f(newname.c_str()); PropertyMap dict = f.properties(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), dict["ARTIST"].size()); CPPUNIT_ASSERT_EQUAL(String("artøst 1"), dict["ARTIST"][0]); CPPUNIT_ASSERT_EQUAL(String("artöst 2"), dict["ARTIST"][1]); } } void testProperties() { PropertyMap tags; tags["ALBUM"] = StringList("Album"); tags["ALBUMARTIST"] = StringList("Album Artist"); tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); tags["ALBUMSORT"] = StringList("Album Sort"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTS"] = StringList("Artists"); tags["ARTISTSORT"] = StringList("Artist Sort"); tags["ASIN"] = StringList("ASIN"); tags["BARCODE"] = StringList("Barcode"); tags["CATALOGNUMBER"] = StringList("Catalog Number 1").append("Catalog Number 2"); tags["COMMENT"] = StringList("Comment"); tags["DATE"] = StringList("2021-01-10"); tags["DISCNUMBER"] = StringList("3"); tags["DISCTOTAL"] = StringList("5"); tags["GENRE"] = StringList("Genre"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label 1").append("Label 2"); tags["MEDIA"] = StringList("Media"); tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); tags["ORIGINALDATE"] = StringList("2021-01-09"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["RELEASESTATUS"] = StringList("Release Status"); tags["RELEASETYPE"] = StringList("Release Type"); tags["SCRIPT"] = StringList("Script"); tags["TITLE"] = StringList("Title"); tags["TRACKNUMBER"] = StringList("2"); tags["TRACKTOTAL"] = StringList("4"); ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT(properties.isEmpty()); f.setProperties(tags); f.save(); } { const FLAC::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); } } void testInvalid() { ScopedFileCopy copy("silence-44-s", ".flac"); PropertyMap map; map[L"H\x00c4\x00d6"] = String("bla"); FLAC::File f(copy.fileName().c_str()); PropertyMap invalid = f.setProperties(map); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), invalid.size()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.properties().size()); } void testAudioProperties() { FLAC::File f(TEST_FILE_PATH_C("sinewave.flac")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(145, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL( ByteVector("\xcf\xe3\xd9\xda\xba\xde\xab\x2c\xbf\x2c\xa2\x35\x27\x4b\x7f\x76"), f.audioProperties()->signature()); } void testZeroSizedPadding1() { ScopedFileCopy copy("zero-sized-padding", ".flac"); FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); } void testZeroSizedPadding2() { ScopedFileCopy copy("silence-44-s", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.xiphComment()->setTitle("ABC"); f.save(); } { FLAC::File f(copy.fileName().c_str()); f.xiphComment()->setTitle(std::string(3067, 'X').c_str()); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); } } void testShrinkPadding() { ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.xiphComment()->setTitle(longText(128 * 1024)); f.save(); CPPUNIT_ASSERT(f.length() > 128 * 1024); } { FLAC::File f(copy.fileName().c_str()); f.xiphComment()->setTitle("0123456789"); f.save(); CPPUNIT_ASSERT(f.length() < 8 * 1024); } } void testSaveID3v1() { ScopedFileCopy copy("no-tags", ".flac"); FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4692), f.length()); f.seek(0x0100); ByteVector audioStream = f.readBlock(4436); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); CPPUNIT_ASSERT(f.hasID3v1Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4820), f.length()); f.seek(0x0100); CPPUNIT_ASSERT_EQUAL(audioStream, f.readBlock(4436)); } void testUpdateID3v2() { ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("0123456789"); f.save(); } { FLAC::File f(copy.fileName().c_str()); f.ID3v2Tag()->setTitle("ABCDEFGHIJ"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("ABCDEFGHIJ"), f.ID3v2Tag()->title()); } } void testEmptyID3v2() { ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.ID3v2Tag(true); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } } void testStripTags() { ScopedFileCopy copy("silence-44-s", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.xiphComment(true)->setTitle("XiphComment Title"); f.ID3v1Tag(true)->setTitle("ID3v1 Title"); f.ID3v2Tag(true)->setTitle("ID3v2 Title"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasXiphComment()); CPPUNIT_ASSERT(f.hasID3v1Tag()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); CPPUNIT_ASSERT_EQUAL(String("ID3v2 Title"), f.ID3v2Tag()->title()); f.strip(FLAC::File::ID3v2); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasXiphComment()); CPPUNIT_ASSERT(f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); f.strip(FLAC::File::ID3v1); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasXiphComment()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); f.strip(FLAC::File::XiphComment); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasXiphComment()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.xiphComment()->isEmpty()); CPPUNIT_ASSERT_EQUAL(String("reference libFLAC 1.1.0 20030126"), f.xiphComment()->vendorID()); } } void testRemoveXiphField() { ScopedFileCopy copy("silence-44-s", ".flac"); { FLAC::File f(copy.fileName().c_str()); f.xiphComment(true)->setTitle("XiphComment Title"); f.ID3v2Tag(true)->setTitle("ID3v2 Title"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); f.xiphComment()->removeFields("TITLE"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String(), f.xiphComment()->title()); } } void testEmptySeekTable() { ScopedFileCopy copy("empty-seektable", ".flac"); { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); f.xiphComment(true)->setTitle("XiphComment Title"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); f.seek(42); const ByteVector data = f.readBlock(4); CPPUNIT_ASSERT_EQUAL(ByteVector("\x03\x00\x00\x00", 4), data); } } void testPictureStoredAfterComment() { // Blank.png from https://commons.wikimedia.org/wiki/File:Blank.png const unsigned char blankPngData[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9d, 0x74, 0x66, 0x1a, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc3, 0x00, 0x00, 0x0e, 0xc3, 0x01, 0xc7, 0x6f, 0xa8, 0x64, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0xc0, 0x01, 0x18, 0x18, 0x00, 0x00, 0x1a, 0x00, 0x01, 0x82, 0x92, 0x4d, 0x60, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; const ByteVector picData(reinterpret_cast<const char *>(blankPngData), sizeof(blankPngData)); ScopedFileCopy copy("no-tags", ".flac"); { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasXiphComment()); CPPUNIT_ASSERT(f.pictureList().isEmpty()); auto pic = new FLAC::Picture; pic->setData(picData); pic->setType(FLAC::Picture::FrontCover); pic->setMimeType("image/png"); pic->setDescription("blank.png"); pic->setWidth(3); pic->setHeight(2); pic->setColorDepth(32); pic->setNumColors(0); f.addPicture(pic); f.xiphComment(true)->setTitle("Title"); f.save(); } { FLAC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasXiphComment()); const List<FLAC::Picture *> pictures = f.pictureList(); CPPUNIT_ASSERT_EQUAL(1U, pictures.size()); CPPUNIT_ASSERT_EQUAL(picData, pictures[0]->data()); CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pictures[0]->type()); CPPUNIT_ASSERT_EQUAL(String("image/png"), pictures[0]->mimeType()); CPPUNIT_ASSERT_EQUAL(String("blank.png"), pictures[0]->description()); CPPUNIT_ASSERT_EQUAL(3, pictures[0]->width()); CPPUNIT_ASSERT_EQUAL(2, pictures[0]->height()); CPPUNIT_ASSERT_EQUAL(32, pictures[0]->colorDepth()); CPPUNIT_ASSERT_EQUAL(0, pictures[0]->numColors()); CPPUNIT_ASSERT_EQUAL(String("Title"), f.xiphComment(false)->title()); } constexpr unsigned char expectedHeadData[] = { 'f', 'L', 'a', 'C', 0x00, 0x00, 0x00, 0x22, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0x02, 0x7a, 0xc0, 0xa1, 0xb1, 0x41, 0xf7, 0x66, 0xe9, 0x84, 0x9a, 0xc3, 0xdb, 0x10, 0x30, 0xa2, 0x0a, 0x3c, 0x77, 0x04, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 'T', 'I', 'T', 'L', 'E', '=', 'T', 'i', 't', 'l', 'e', 0x06, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 'i', 'm', 'a', 'g', 'e', '/', 'p', 'n', 'g', 0x00, 0x00, 0x00, 0x09, 'b', 'l', 'a', 'n', 'k', '.', 'p', 'n', 'g', 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77 }; ByteVector expectedData(reinterpret_cast<const char *>(expectedHeadData), sizeof(expectedHeadData)); expectedData.append(picData); const ByteVector fileData = PlainFile(copy.fileName().c_str()).readAll(); CPPUNIT_ASSERT(fileData.startsWith(expectedData)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFLAC); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_flacpicture.cpp�������������������������������������������������������������0000664�0000000�0000000�00000012477�14662262111�0020177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tbytevectorlist.h" #include "tag.h" #include "flacfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestFLACPicture : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestFLACPicture); CPPUNIT_TEST(testParse); CPPUNIT_TEST(testPassThrough); CPPUNIT_TEST_SUITE_END(); public: void testParse() { const unsigned char data[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x08, 0x41, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x0B, 0x1C, 0x0A, 0x36, 0x06, 0x08, 0x44, 0x3D, 0x32, 0x00, 0x00, 0x00, 0x1D, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4D, 0x50, 0xEF, 0x64, 0x25, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; auto pdata = reinterpret_cast<const char*>(data); FLAC::Picture pic(ByteVector(pdata, 199)); CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(pic.type())); CPPUNIT_ASSERT_EQUAL(1, pic.width()); CPPUNIT_ASSERT_EQUAL(1, pic.height()); CPPUNIT_ASSERT_EQUAL(24, pic.colorDepth()); CPPUNIT_ASSERT_EQUAL(0, pic.numColors()); CPPUNIT_ASSERT_EQUAL(String("image/png"), pic.mimeType()); CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic.description()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(150), pic.data().size()); } void testPassThrough() { const unsigned char data[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x08, 0x41, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x2E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x0B, 0x1C, 0x0A, 0x36, 0x06, 0x08, 0x44, 0x3D, 0x32, 0x00, 0x00, 0x00, 0x1D, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4D, 0x50, 0xEF, 0x64, 0x25, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; auto pdata = reinterpret_cast<const char*>(data); FLAC::Picture pic(ByteVector(pdata, 199)); CPPUNIT_ASSERT_EQUAL(ByteVector(pdata, 199), pic.render()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFLACPicture); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_flacunknownmetadatablock.cpp������������������������������������������������0000664�0000000�0000000�00000005162�14662262111�0022730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tag.h" #include "flacunknownmetadatablock.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestFLACUnknownMetadataBlock : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestFLACUnknownMetadataBlock); CPPUNIT_TEST(testAccessors); CPPUNIT_TEST_SUITE_END(); public: void testAccessors() { ByteVector data("abc\x01", 4); FLAC::UnknownMetadataBlock block(42, data); CPPUNIT_ASSERT_EQUAL(42, block.code()); CPPUNIT_ASSERT_EQUAL(data, block.data()); CPPUNIT_ASSERT_EQUAL(data, block.render()); ByteVector data2("xxx", 3); block.setCode(13); block.setData(data2); CPPUNIT_ASSERT_EQUAL(13, block.code()); CPPUNIT_ASSERT_EQUAL(data2, block.data()); CPPUNIT_ASSERT_EQUAL(data2, block.render()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFLACUnknownMetadataBlock); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_id3v1.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000006321�14662262111�0016613�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstring.h" #include "mpegfile.h" #include "id3v1tag.h" #include "id3v1genres.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestID3v1 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v1); CPPUNIT_TEST(testStripWhiteSpace); CPPUNIT_TEST(testGenres); CPPUNIT_TEST(testRenamedGenres); CPPUNIT_TEST_SUITE_END(); public: void testStripWhiteSpace() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); { MPEG::File f(newname.c_str()); f.ID3v1Tag(true)->setArtist("Artist "); f.save(); } { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT(f.ID3v1Tag(false)); CPPUNIT_ASSERT_EQUAL(String("Artist"), f.ID3v1Tag(false)->artist()); } } void testGenres() { CPPUNIT_ASSERT_EQUAL(String("Darkwave"), ID3v1::genre(50)); CPPUNIT_ASSERT_EQUAL(100, ID3v1::genreIndex("Humour")); CPPUNIT_ASSERT(ID3v1::genreList().contains("Heavy Metal")); CPPUNIT_ASSERT_EQUAL(79, ID3v1::genreMap()["Hard Rock"]); } void testRenamedGenres() { CPPUNIT_ASSERT_EQUAL(String("Bebop"), ID3v1::genre(85)); CPPUNIT_ASSERT_EQUAL(85, ID3v1::genreIndex("Bebop")); CPPUNIT_ASSERT_EQUAL(85, ID3v1::genreIndex("Bebob")); ID3v1::Tag tag; tag.setGenre("Hardcore"); CPPUNIT_ASSERT_EQUAL(String("Hardcore Techno"), tag.genre()); CPPUNIT_ASSERT_EQUAL(129U, tag.genreNumber()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v1); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_id3v2.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000206552�14662262111�0016624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ /*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <utility> #include <cassert> #include "tpropertymap.h" #include "tzlib.h" #include "id3v2tag.h" #include "mpegfile.h" #include "id3v2frame.h" #include "uniquefileidentifierframe.h" #include "textidentificationframe.h" #include "attachedpictureframe.h" #include "unsynchronizedlyricsframe.h" #include "synchronizedlyricsframe.h" #include "eventtimingcodesframe.h" #include "generalencapsulatedobjectframe.h" #include "relativevolumeframe.h" #include "popularimeterframe.h" #include "urllinkframe.h" #include "ownershipframe.h" #include "unknownframe.h" #include "chapterframe.h" #include "tableofcontentsframe.h" #include "commentsframe.h" #include "podcastframe.h" #include "privateframe.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class PublicFrame : public ID3v2::Frame { public: PublicFrame() : ID3v2::Frame(ByteVector("XXXX\0\0\0\0\0\0", 10)) {} String readStringField(const ByteVector &data, String::Type encoding, int *position = nullptr) { return ID3v2::Frame::readStringField(data, encoding, position); } String toString() const override { return String(); } void parseFields(const ByteVector &) override {} ByteVector renderFields() const override { return ByteVector(); } }; class TestID3v2 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v2); CPPUNIT_TEST(testUnsynchDecode); CPPUNIT_TEST(testDowngradeUTF8ForID3v23_1); CPPUNIT_TEST(testDowngradeUTF8ForID3v23_2); CPPUNIT_TEST(testUTF16BEDelimiter); CPPUNIT_TEST(testUTF16Delimiter); CPPUNIT_TEST(testReadStringField); CPPUNIT_TEST(testParseAPIC); CPPUNIT_TEST(testParseAPIC_UTF16_BOM); CPPUNIT_TEST(testParseAPICv22); CPPUNIT_TEST(testRenderAPIC); CPPUNIT_TEST(testDontRender22); CPPUNIT_TEST(testParseGEOB); CPPUNIT_TEST(testRenderGEOB); CPPUNIT_TEST(testPOPMtoString); CPPUNIT_TEST(testParsePOPM); CPPUNIT_TEST(testParsePOPMWithoutCounter); CPPUNIT_TEST(testRenderPOPM); CPPUNIT_TEST(testPOPMFromFile); CPPUNIT_TEST(testParseRelativeVolumeFrame); CPPUNIT_TEST(testRenderRelativeVolumeFrame); CPPUNIT_TEST(testParseUniqueFileIdentifierFrame); CPPUNIT_TEST(testParseEmptyUniqueFileIdentifierFrame); CPPUNIT_TEST(testRenderUniqueFileIdentifierFrame); CPPUNIT_TEST(testBrokenFrame1); CPPUNIT_TEST(testItunes24FrameSize); CPPUNIT_TEST(testParseUrlLinkFrame); CPPUNIT_TEST(testRenderUrlLinkFrame); CPPUNIT_TEST(testParseUserUrlLinkFrame); CPPUNIT_TEST(testRenderUserUrlLinkFrame); CPPUNIT_TEST(testParseOwnershipFrame); CPPUNIT_TEST(testRenderOwnershipFrame); CPPUNIT_TEST(testParseSynchronizedLyricsFrame); CPPUNIT_TEST(testParseSynchronizedLyricsFrameWithEmptyDescritpion); CPPUNIT_TEST(testRenderSynchronizedLyricsFrame); CPPUNIT_TEST(testParseEventTimingCodesFrame); CPPUNIT_TEST(testRenderEventTimingCodesFrame); CPPUNIT_TEST(testParseCommentsFrame); CPPUNIT_TEST(testRenderCommentsFrame); CPPUNIT_TEST(testParsePodcastFrame); CPPUNIT_TEST(testRenderPodcastFrame); CPPUNIT_TEST(testParsePrivateFrame); CPPUNIT_TEST(testRenderPrivateFrame); CPPUNIT_TEST(testParseUserTextIdentificationFrame); CPPUNIT_TEST(testRenderUserTextIdentificationFrame); CPPUNIT_TEST(testSaveUTF16Comment); CPPUNIT_TEST(testUpdateGenre23_1); CPPUNIT_TEST(testUpdateGenre23_2); CPPUNIT_TEST(testUpdateGenre23_3); CPPUNIT_TEST(testUpdateGenre24); CPPUNIT_TEST(testUpdateDate22); CPPUNIT_TEST(testDowngradeTo23); // CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together CPPUNIT_TEST(testCompressedFrameWithBrokenLength); CPPUNIT_TEST(testW000); CPPUNIT_TEST(testPropertyInterface); CPPUNIT_TEST(testPropertyInterface2); CPPUNIT_TEST(testPropertiesMovement); CPPUNIT_TEST(testPropertyGrouping); CPPUNIT_TEST(testDeleteFrame); CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2); CPPUNIT_TEST(testParseChapterFrame); CPPUNIT_TEST(testRenderChapterFrame); CPPUNIT_TEST(testParseTableOfContentsFrame); CPPUNIT_TEST(testRenderTableOfContentsFrame); CPPUNIT_TEST(testShrinkPadding); CPPUNIT_TEST(testEmptyFrame); CPPUNIT_TEST(testDuplicateTags); CPPUNIT_TEST(testParseTOCFrameWithManyChildren); CPPUNIT_TEST_SUITE_END(); public: void testUnsynchDecode() { MPEG::File f(TEST_FILE_PATH_C("unsynch.id3"), false); CPPUNIT_ASSERT(f.tag()); CPPUNIT_ASSERT_EQUAL(String("My babe just cares for me"), f.tag()->title()); } void testDowngradeUTF8ForID3v23_1() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); auto f = new ID3v2::TextIdentificationFrame(ByteVector("TPE1"), String::UTF8); StringList sl; sl.append("Foo"); f->setText(sl); MPEG::File file(newname.c_str()); file.ID3v2Tag(true)->addFrame(f); file.save(MPEG::File::ID3v2, File::StripOthers, ID3v2::v3); CPPUNIT_ASSERT_EQUAL(true, file.hasID3v2Tag()); ByteVector data = f->render(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4+4+2+1+6+2), data.size()); ID3v2::TextIdentificationFrame f2(data); CPPUNIT_ASSERT_EQUAL(sl, f2.fieldList()); CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding()); } void testDowngradeUTF8ForID3v23_2() { ScopedFileCopy copy("xing", ".mp3"); auto f = new ID3v2::UnsynchronizedLyricsFrame(String::UTF8); f->setText("Foo"); MPEG::File file(copy.fileName().c_str()); file.ID3v2Tag(true)->addFrame(f); file.save(MPEG::File::ID3v2, File::StripOthers, ID3v2::v3); CPPUNIT_ASSERT(file.hasID3v2Tag()); ByteVector data = f->render(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4+4+2+1+3+2+2+6+2), data.size()); ID3v2::UnsynchronizedLyricsFrame f2(data); CPPUNIT_ASSERT_EQUAL(String("Foo"), f2.text()); CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding()); } void testUTF16BEDelimiter() { ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF16BE); StringList sl; sl.append("Foo"); sl.append("Bar"); f.setText(sl); ByteVector data = f.render(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4+4+2+1+6+2+6), data.size()); ByteVector noBomBeData("TPE1\x00\x00\x00\x0f\x00\x00\x02" "\0F\0o\0o\0\0" "\0B\0a\0r", 25); CPPUNIT_ASSERT_EQUAL(noBomBeData, data); f.setData(data); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString()); } void testUTF16Delimiter() { ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF16); StringList sl; sl.append("Foo"); sl.append("Bar"); f.setText(sl); ByteVector data = f.render(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4+4+2+1+8+2+8), data.size()); ByteVector multiBomLeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xff\xfe" "F\0o\0o\0\0\0" "\xff\xfe" "B\0a\0r\0", 29); CPPUNIT_ASSERT_EQUAL(multiBomLeData, data); f.setData(data); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString()); ByteVector multiBomBeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xfe\xff" "\0F\0o\0o\0\0" "\xfe\xff" "\0B\0a\0r", 29); f.setData(multiBomBeData); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString()); ByteVector singleBomLeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xff\xfe" "F\0o\0o\0\0\0" "B\0a\0r\0", 27); f.setData(singleBomLeData); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString()); ByteVector singleBomBeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xfe\xff" "\0F\0o\0o\0\0" "\0B\0a\0r", 27); f.setData(singleBomBeData); CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString()); } void testBrokenFrame1() { MPEG::File f(TEST_FILE_PATH_C("broken-tenc.id3"), false); CPPUNIT_ASSERT(f.tag()); CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TENC")); } void testReadStringField() { PublicFrame f; ByteVector data("abc\0", 4); String str = f.readStringField(data, String::Latin1); CPPUNIT_ASSERT_EQUAL(String("abc"), str); } // http://bugs.kde.org/show_bug.cgi?id=151078 void testParseAPIC() { ID3v2::AttachedPictureFrame f(ByteVector("APIC" "\x00\x00\x00\x07" "\x00\x00" "\x00" "m\x00" "\x01" "d\x00" "\x00", 17)); CPPUNIT_ASSERT_EQUAL(String("m"), f.mimeType()); CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::FileIcon, f.type()); CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); } void testParseAPIC_UTF16_BOM() { ID3v2::AttachedPictureFrame f(ByteVector( "\x41\x50\x49\x43\x00\x02\x0c\x59\x00\x00\x01\x69\x6d\x61\x67\x65" "\x2f\x6a\x70\x65\x67\x00\x00\xfe\xff\x00\x63\x00\x6f\x00\x76\x00" "\x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff", 16 * 3)); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), f.mimeType()); CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::Other, f.type()); CPPUNIT_ASSERT_EQUAL(String("cover.jpg"), f.description()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xd8\xff", 3), f.picture()); } void testParseAPICv22() { ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("PIC" "\x00\x00\x08" "\x00" "JPG" "\x01" "d\x00" "\x00", 14); ID3v2::Header header; header.setMajorVersion(2); auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(factory->createFrame(data, &header)); CPPUNIT_ASSERT(frame); assert(frame != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), frame->mimeType()); CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::FileIcon, frame->type()); CPPUNIT_ASSERT_EQUAL(String("d"), frame->description()); delete frame; } void testRenderAPIC() { ID3v2::AttachedPictureFrame f; f.setTextEncoding(String::UTF8); f.setMimeType("image/png"); f.setType(ID3v2::AttachedPictureFrame::BackCover); f.setDescription("Description"); f.setPicture("PNG data"); CPPUNIT_ASSERT_EQUAL( ByteVector("APIC" "\x00\x00\x00\x20" "\x00\x00" "\x03" "image/png\x00" "\x04" "Description\x00" "PNG data", 42), f.render()); } void testDontRender22() { ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("FOO" "\x00\x00\x08" "\x00" "JPG" "\x01" "d\x00" "\x00", 14); ID3v2::Header header; header.setMajorVersion(2); auto frame = dynamic_cast<TagLib::ID3v2::UnknownFrame*>(factory->createFrame(data, &header)); CPPUNIT_ASSERT(frame); ID3v2::Tag tag; tag.addFrame(frame); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1034), tag.render().size()); } // http://bugs.kde.org/show_bug.cgi?id=151078 void testParseGEOB() { ID3v2::GeneralEncapsulatedObjectFrame f(ByteVector("GEOB" "\x00\x00\x00\x08" "\x00\x00" "\x00" "m\x00" "f\x00" "d\x00" "\x00", 18)); CPPUNIT_ASSERT_EQUAL(String("m"), f.mimeType()); CPPUNIT_ASSERT_EQUAL(String("f"), f.fileName()); CPPUNIT_ASSERT_EQUAL(String("d"), f.description()); } void testRenderGEOB() { ID3v2::GeneralEncapsulatedObjectFrame f; f.setTextEncoding(String::Latin1); f.setMimeType("application/octet-stream"); f.setFileName("test.bin"); f.setDescription("Description"); f.setObject(ByteVector(3, '\x01')); CPPUNIT_ASSERT_EQUAL( ByteVector("GEOB" "\x00\x00\x00\x32" "\x00\x00" "\x00" "application/octet-stream\x00" "test.bin\x00" "Description\x00" "\x01\x01\x01", 60), f.render()); } void testParsePOPM() { ID3v2::PopularimeterFrame f(ByteVector("POPM" "\x00\x00\x00\x17" "\x00\x00" "email@example.com\x00" "\x02" "\x00\x00\x00\x03", 33)); CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); CPPUNIT_ASSERT_EQUAL(2, f.rating()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f.counter()); } void testParsePOPMWithoutCounter() { ID3v2::PopularimeterFrame f(ByteVector("POPM" "\x00\x00\x00\x13" "\x00\x00" "email@example.com\x00" "\x02", 29)); CPPUNIT_ASSERT_EQUAL(String("email@example.com"), f.email()); CPPUNIT_ASSERT_EQUAL(2, f.rating()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.counter()); } void testRenderPOPM() { ID3v2::PopularimeterFrame f; f.setEmail("email@example.com"); f.setRating(2); f.setCounter(3); CPPUNIT_ASSERT_EQUAL( ByteVector("POPM" "\x00\x00\x00\x17" "\x00\x00" "email@example.com\x00" "\x02" "\x00\x00\x00\x03", 33), f.render()); } void testPOPMtoString() { ID3v2::PopularimeterFrame f; f.setEmail("email@example.com"); f.setRating(2); f.setCounter(3); CPPUNIT_ASSERT_EQUAL( String("email@example.com rating=2 counter=3"), f.toString()); } void testPOPMFromFile() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); auto f = new ID3v2::PopularimeterFrame(); f->setEmail("email@example.com"); f->setRating(200); f->setCounter(3); { MPEG::File foo(newname.c_str()); foo.ID3v2Tag()->addFrame(f); foo.save(); } { MPEG::File bar(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("email@example.com"), dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->email()); CPPUNIT_ASSERT_EQUAL(200, dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->rating()); } } // http://bugs.kde.org/show_bug.cgi?id=150481 void testParseRelativeVolumeFrame() { ID3v2::RelativeVolumeFrame f( ByteVector("RVA2" // Frame ID "\x00\x00\x00\x0B" // Frame size "\x00\x00" // Frame flags "ident\x00" // Identification "\x02" // Type of channel "\x00\x0F" // Volume adjustment "\x08" // Bits representing peak "\x45", 21)); // Peak volume CPPUNIT_ASSERT_EQUAL(String("ident"), f.identification()); CPPUNIT_ASSERT_EQUAL(15.0f / 512.0f, f.volumeAdjustment(ID3v2::RelativeVolumeFrame::FrontRight)); CPPUNIT_ASSERT_EQUAL(static_cast<short>(15), f.volumeAdjustmentIndex(ID3v2::RelativeVolumeFrame::FrontRight)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(8), f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).bitsRepresentingPeak); CPPUNIT_ASSERT_EQUAL(ByteVector("\x45"), f.peakVolume(ID3v2::RelativeVolumeFrame::FrontRight).peakVolume); const List<ID3v2::RelativeVolumeFrame::ChannelType> channels = f.channels(); CPPUNIT_ASSERT_EQUAL(1U, channels.size()); CPPUNIT_ASSERT_EQUAL(ID3v2::RelativeVolumeFrame::FrontRight, channels[0]); } void testRenderRelativeVolumeFrame() { ID3v2::RelativeVolumeFrame f; f.setIdentification("ident"); f.setVolumeAdjustment(15.0f / 512.0f, ID3v2::RelativeVolumeFrame::FrontRight); ID3v2::RelativeVolumeFrame::PeakVolume peakVolume; peakVolume.bitsRepresentingPeak = 8; peakVolume.peakVolume.setData("\x45"); f.setPeakVolume(peakVolume, ID3v2::RelativeVolumeFrame::FrontRight); CPPUNIT_ASSERT_EQUAL( ByteVector("RVA2" "\x00\x00\x00\x0B" "\x00\x00" "ident\x00" "\x02" "\x00\x0F" "\x08" "\x45", 21), f.render()); } void testParseUniqueFileIdentifierFrame() { ID3v2::UniqueFileIdentifierFrame f( ByteVector("UFID" // Frame ID "\x00\x00\x00\x09" // Frame size "\x00\x00" // Frame flags "owner\x00" // Owner identifier "\x00\x01\x02", 19)); // Identifier CPPUNIT_ASSERT_EQUAL(String("owner"), f.owner()); CPPUNIT_ASSERT_EQUAL(ByteVector("\x00\x01\x02", 3), f.identifier()); } void testParseEmptyUniqueFileIdentifierFrame() { ID3v2::UniqueFileIdentifierFrame f( ByteVector("UFID" // Frame ID "\x00\x00\x00\x01" // Frame size "\x00\x00" // Frame flags "\x00" // Owner identifier "", 11)); // Identifier CPPUNIT_ASSERT_EQUAL(String(), f.owner()); CPPUNIT_ASSERT_EQUAL(ByteVector(), f.identifier()); } void testRenderUniqueFileIdentifierFrame() { ID3v2::UniqueFileIdentifierFrame f("owner", "\x01\x02\x03"); CPPUNIT_ASSERT_EQUAL( ByteVector("UFID" "\x00\x00\x00\x09" "\x00\x00" "owner\x00" "\x01\x02\x03", 19), f.render()); } void testParseUrlLinkFrame() { ID3v2::UrlLinkFrame f( ByteVector("WOAF" // Frame ID "\x00\x00\x00\x12" // Frame size "\x00\x00" // Frame flags "http://example.com", 28)); // URL CPPUNIT_ASSERT_EQUAL(String("http://example.com"), f.url()); } void testRenderUrlLinkFrame() { ID3v2::UrlLinkFrame f("WOAF"); f.setUrl("http://example.com"); CPPUNIT_ASSERT_EQUAL( ByteVector("WOAF" // Frame ID "\x00\x00\x00\x12" // Frame size "\x00\x00" // Frame flags "http://example.com", 28), // URL f.render()); } void testParseUserUrlLinkFrame() { ID3v2::UserUrlLinkFrame f( ByteVector("WXXX" // Frame ID "\x00\x00\x00\x17" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "foo\x00" // Description "http://example.com", 33)); // URL CPPUNIT_ASSERT_EQUAL(String("foo"), f.description()); CPPUNIT_ASSERT_EQUAL(String("http://example.com"), f.url()); } void testRenderUserUrlLinkFrame() { ID3v2::UserUrlLinkFrame f; f.setDescription("foo"); f.setUrl("http://example.com"); CPPUNIT_ASSERT_EQUAL( ByteVector("WXXX" // Frame ID "\x00\x00\x00\x17" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "foo\x00" // Description "http://example.com", 33), // URL f.render()); } void testParseOwnershipFrame() { ID3v2::OwnershipFrame f( ByteVector("OWNE" // Frame ID "\x00\x00\x00\x19" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "GBP1.99\x00" // Price paid "20120905" // Date of purchase "Beatport", 35)); // Seller CPPUNIT_ASSERT_EQUAL(String("GBP1.99"), f.pricePaid()); CPPUNIT_ASSERT_EQUAL(String("20120905"), f.datePurchased()); CPPUNIT_ASSERT_EQUAL(String("Beatport"), f.seller()); } void testRenderOwnershipFrame() { ID3v2::OwnershipFrame f; f.setPricePaid("GBP1.99"); f.setDatePurchased("20120905"); f.setSeller("Beatport"); CPPUNIT_ASSERT_EQUAL( ByteVector("OWNE" // Frame ID "\x00\x00\x00\x19" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "GBP1.99\x00" // Price paid "20120905" // Date of purchase "Beatport", 35), // URL f.render()); } void testParseSynchronizedLyricsFrame() { ID3v2::SynchronizedLyricsFrame f( ByteVector("SYLT" // Frame ID "\x00\x00\x00\x21" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "eng" // Language "\x02" // Time stamp format "\x01" // Content type "foo\x00" // Content descriptor "Example\x00" // 1st text "\x00\x00\x04\xd2" // 1st time stamp "Lyrics\x00" // 2nd text "\x00\x00\x11\xd7", 43)); // 2nd time stamp CPPUNIT_ASSERT_EQUAL(String::Latin1, f.textEncoding()); CPPUNIT_ASSERT_EQUAL(ByteVector("eng", 3), f.language()); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds, f.timestampFormat()); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::Lyrics, f.type()); CPPUNIT_ASSERT_EQUAL(String("foo"), f.description()); ID3v2::SynchronizedLyricsFrame::SynchedTextList stl = f.synchedText(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), stl.size()); CPPUNIT_ASSERT_EQUAL(String("Example"), stl[0].text); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1234), stl[0].time); CPPUNIT_ASSERT_EQUAL(String("Lyrics"), stl[1].text); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4567), stl[1].time); } void testParseSynchronizedLyricsFrameWithEmptyDescritpion() { ID3v2::SynchronizedLyricsFrame f( ByteVector("SYLT" // Frame ID "\x00\x00\x00\x21" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "eng" // Language "\x02" // Time stamp format "\x01" // Content type "\x00" // Content descriptor "Example\x00" // 1st text "\x00\x00\x04\xd2" // 1st time stamp "Lyrics\x00" // 2nd text "\x00\x00\x11\xd7", 40)); // 2nd time stamp CPPUNIT_ASSERT_EQUAL(String::Latin1, f.textEncoding()); CPPUNIT_ASSERT_EQUAL(ByteVector("eng", 3), f.language()); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds, f.timestampFormat()); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::Lyrics, f.type()); CPPUNIT_ASSERT(f.description().isEmpty()); ID3v2::SynchronizedLyricsFrame::SynchedTextList stl = f.synchedText(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), stl.size()); CPPUNIT_ASSERT_EQUAL(String("Example"), stl[0].text); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1234), stl[0].time); CPPUNIT_ASSERT_EQUAL(String("Lyrics"), stl[1].text); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4567), stl[1].time); } void testRenderSynchronizedLyricsFrame() { ID3v2::SynchronizedLyricsFrame f; f.setTextEncoding(String::Latin1); f.setLanguage(ByteVector("eng", 3)); f.setTimestampFormat(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds); f.setType(ID3v2::SynchronizedLyricsFrame::Lyrics); f.setDescription("foo"); ID3v2::SynchronizedLyricsFrame::SynchedTextList stl; stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(1234, "Example")); stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(4567, "Lyrics")); f.setSynchedText(stl); CPPUNIT_ASSERT_EQUAL( ByteVector("SYLT" // Frame ID "\x00\x00\x00\x21" // Frame size "\x00\x00" // Frame flags "\x00" // Text encoding "eng" // Language "\x02" // Time stamp format "\x01" // Content type "foo\x00" // Content descriptor "Example\x00" // 1st text "\x00\x00\x04\xd2" // 1st time stamp "Lyrics\x00" // 2nd text "\x00\x00\x11\xd7", 43), // 2nd time stamp f.render()); } void testParseEventTimingCodesFrame() { ID3v2::EventTimingCodesFrame f( ByteVector("ETCO" // Frame ID "\x00\x00\x00\x0b" // Frame size "\x00\x00" // Frame flags "\x02" // Time stamp format "\x02" // 1st event "\x00\x00\xf3\x5c" // 1st time stamp "\xfe" // 2nd event "\x00\x36\xee\x80", 21)); // 2nd time stamp CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds, f.timestampFormat()); ID3v2::EventTimingCodesFrame::SynchedEventList sel = f.synchedEvents(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), sel.size()); CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::IntroStart, sel[0].type); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(62300), sel[0].time); CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AudioFileEnds, sel[1].type); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3600000), sel[1].time); } void testRenderEventTimingCodesFrame() { ID3v2::EventTimingCodesFrame f; f.setTimestampFormat(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds); ID3v2::EventTimingCodesFrame::SynchedEventList sel; sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(62300, ID3v2::EventTimingCodesFrame::IntroStart)); sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(3600000, ID3v2::EventTimingCodesFrame::AudioFileEnds)); f.setSynchedEvents(sel); CPPUNIT_ASSERT_EQUAL( ByteVector("ETCO" // Frame ID "\x00\x00\x00\x0b" // Frame size "\x00\x00" // Frame flags "\x02" // Time stamp format "\x02" // 1st event "\x00\x00\xf3\x5c" // 1st time stamp "\xfe" // 2nd event "\x00\x36\xee\x80", 21), // 2nd time stamp f.render()); } void testParseCommentsFrame() { ID3v2::CommentsFrame f( ByteVector("COMM" "\x00\x00\x00\x14" "\x00\x00" "\x03" "deu" "Description\x00" "Text", 30)); CPPUNIT_ASSERT_EQUAL(String::UTF8, f.textEncoding()); CPPUNIT_ASSERT_EQUAL(ByteVector("deu"), f.language()); CPPUNIT_ASSERT_EQUAL(String("Description"), f.description()); CPPUNIT_ASSERT_EQUAL(String("Text"), f.text()); } void testRenderCommentsFrame() { ID3v2::CommentsFrame f; f.setTextEncoding(String::UTF16); f.setLanguage("eng"); f.setDescription("Description"); f.setText("Text"); CPPUNIT_ASSERT_EQUAL( ByteVector("COMM" "\x00\x00\x00\x28" "\x00\x00" "\x01" "eng" "\xff\xfe" "D\0e\0s\0c\0r\0i\0p\0t\0i\0o\0n\0" "\x00\x00" "\xff\xfe" "T\0e\0x\0t\0", 50), f.render()); } void testParsePodcastFrame() { ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("PCST" "\x00\x00\x00\x04" "\x00\x00" "\x00\x00\x00\x00", 14); const ID3v2::Header header; ID3v2::Frame *frame = factory->createFrame(data, &header); CPPUNIT_ASSERT(dynamic_cast<ID3v2::PodcastFrame *>(frame)); delete frame; } void testRenderPodcastFrame() { ID3v2::PodcastFrame f; CPPUNIT_ASSERT_EQUAL( ByteVector("PCST" "\x00\x00\x00\x04" "\x00\x00" "\x00\x00\x00\x00", 14), f.render()); } void testParsePrivateFrame() { ID3v2::PrivateFrame f( ByteVector("PRIV" "\x00\x00\x00\x0e" "\x00\x00" "WM/Provider\x00" "TL", 24)); CPPUNIT_ASSERT_EQUAL(String("WM/Provider"), f.owner()); CPPUNIT_ASSERT_EQUAL(ByteVector("TL"), f.data()); } void testRenderPrivateFrame() { ID3v2::PrivateFrame f; f.setOwner("WM/Provider"); f.setData("TL"); CPPUNIT_ASSERT_EQUAL( ByteVector("PRIV" "\x00\x00\x00\x0e" "\x00\x00" "WM/Provider\x00" "TL", 24), f.render()); } void testParseUserTextIdentificationFrame() { ID3v2::UserTextIdentificationFrame frameWithoutDescription( ByteVector("TXXX" "\x00\x00\x00\x06" "\x00\x00\x00" "\x00" "Text", 16)); CPPUNIT_ASSERT_EQUAL(String(""), frameWithoutDescription.description()); CPPUNIT_ASSERT_EQUAL(String("Text"), frameWithoutDescription.fieldList()[1]); ID3v2::UserTextIdentificationFrame frameWithDescription( ByteVector("TXXX" "\x00\x00\x00\x11" "\x00\x00\x00" "Description\x00" "Text", 27)); CPPUNIT_ASSERT_EQUAL(String("Description"), frameWithDescription.description()); CPPUNIT_ASSERT_EQUAL(String("Text"), frameWithDescription.fieldList()[1]); } void testRenderUserTextIdentificationFrame() { ID3v2::UserTextIdentificationFrame f; f.setDescription(""); f.setText("Text"); CPPUNIT_ASSERT_EQUAL( ByteVector("TXXX" "\x00\x00\x00\x06" "\x00\x00\x00" "\x00" "Text", 16), f.render()); f.setDescription("Description"); f.setText("Text"); CPPUNIT_ASSERT_EQUAL( ByteVector("TXXX" "\x00\x00\x00\x11" "\x00\x00\x00" "Description\x00" "Text", 27), f.render()); } void testItunes24FrameSize() { MPEG::File f(TEST_FILE_PATH_C("005411.id3"), false); CPPUNIT_ASSERT(f.tag()); CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("TIT2")); CPPUNIT_ASSERT_EQUAL(String("Sunshine Superman"), f.ID3v2Tag()->frameListMap()["TIT2"].front()->toString()); } void testSaveUTF16Comment() { String::Type defaultEncoding = ID3v2::FrameFactory::instance()->defaultTextEncoding(); ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); ID3v2::FrameFactory::instance()->setDefaultTextEncoding(String::UTF16); { MPEG::File foo(newname.c_str()); foo.strip(); foo.tag()->setComment("Test comment!"); foo.save(); } { MPEG::File bar(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("Test comment!"), bar.tag()->comment()); ID3v2::FrameFactory::instance()->setDefaultTextEncoding(defaultEncoding); } } void testUpdateGenre23_1() { // "Refinement" is the same as the ID3v1 genre - duplicate ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("TCON" // Frame ID "\x00\x00\x00\x10" // Frame size "\x00\x00" // Frame flags "\x00" // Encoding "(22)Death Metal", 26); // Text ID3v2::Header header; header.setMajorVersion(3); auto frame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(factory->createFrame(data, &header)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), frame->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("Death Metal"), frame->fieldList()[0]); ID3v2::Tag tag; tag.addFrame(frame); CPPUNIT_ASSERT_EQUAL(String("Death Metal"), tag.genre()); } void testUpdateGenre23_2() { // "Refinement" is different from the ID3v1 genre ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("TCON" // Frame ID "\x00\x00\x00\x0d" // Frame size "\x00\x00" // Frame flags "\x00" // Encoding "(4)Eurodisco", 23); // Text ID3v2::Header header; header.setMajorVersion(3); auto frame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(factory->createFrame(data, &header)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), frame->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("4"), frame->fieldList()[0]); CPPUNIT_ASSERT_EQUAL(String("Eurodisco"), frame->fieldList()[1]); ID3v2::Tag tag; tag.addFrame(frame); CPPUNIT_ASSERT_EQUAL(String("Disco / Eurodisco"), tag.genre()); } void testUpdateGenre23_3() { // Multiple references and a refinement ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("TCON" // Frame ID "\x00\x00\x00\x15" // Frame size "\x00\x00" // Frame flags "\x00" // Encoding "(9)(138)Viking Metal", 31); // Text ID3v2::Header header; header.setMajorVersion(3); auto frame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(factory->createFrame(data, &header)); CPPUNIT_ASSERT_EQUAL(3U, frame->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("9"), frame->fieldList()[0]); CPPUNIT_ASSERT_EQUAL(String("138"), frame->fieldList()[1]); CPPUNIT_ASSERT_EQUAL(String("Viking Metal"), frame->fieldList()[2]); ID3v2::Tag tag; tag.addFrame(frame); CPPUNIT_ASSERT_EQUAL(String("Metal / Black Metal / Viking Metal"), tag.genre()); } void testUpdateGenre24() { ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); auto data = ByteVector("TCON" // Frame ID "\x00\x00\x00\x0D" // Frame size "\x00\x00" // Frame flags "\0" // Encoding "14\0Eurodisco", 23); // Text ID3v2::Header header; auto frame = dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(factory->createFrame(data, &header)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), frame->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("14"), frame->fieldList()[0]); CPPUNIT_ASSERT_EQUAL(String("Eurodisco"), frame->fieldList()[1]); ID3v2::Tag tag; tag.addFrame(frame); CPPUNIT_ASSERT_EQUAL(String("R&B / Eurodisco"), tag.genre()); } void testUpdateDate22() { MPEG::File f(TEST_FILE_PATH_C("id3v22-tda.mp3"), false); CPPUNIT_ASSERT(f.tag()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2010), f.tag()->year()); } void testUpdateFullDate22() { MPEG::File f(TEST_FILE_PATH_C("id3v22-tda.mp3"), false); CPPUNIT_ASSERT(f.tag()); CPPUNIT_ASSERT_EQUAL(String("2010-04-03"), f.ID3v2Tag()->frameListMap()["TDRC"].front()->toString()); } void testDowngradeTo23() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); ID3v2::TextIdentificationFrame *tf; { MPEG::File foo(newname.c_str()); tf = new ID3v2::TextIdentificationFrame("TDOR", String::Latin1); tf->setText("2011-03-16"); foo.ID3v2Tag()->addFrame(tf); tf = new ID3v2::TextIdentificationFrame("TDRC", String::Latin1); tf->setText("2012-04-17T12:01"); foo.ID3v2Tag()->addFrame(tf); tf = new ID3v2::TextIdentificationFrame("TMCL", String::Latin1); tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2")); foo.ID3v2Tag()->addFrame(tf); tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1); tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4")); foo.ID3v2Tag()->addFrame(tf); tf = new ID3v2::TextIdentificationFrame("TCON", String::Latin1); tf->setText(StringList().append("51").append("Noise").append("Power Noise")); foo.ID3v2Tag()->addFrame(tf); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDRL", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDTG", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TMOO", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TPRO", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOA", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOT", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSST", String::Latin1)); foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOP", String::Latin1)); foo.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v3); } { MPEG::File bar(newname.c_str()); tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDOR").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), tf->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("2011"), tf->fieldList().front()); tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDRC").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), tf->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("2012-04-17T12:01"), tf->fieldList().front()); tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TIPL").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8), tf->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]); CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]); CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]); CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]); CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]); CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]); CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]); CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]); tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TCON").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(3U, tf->fieldList().size()); CPPUNIT_ASSERT_EQUAL(String("51"), tf->fieldList()[0]); CPPUNIT_ASSERT_EQUAL(String("39"), tf->fieldList()[1]); CPPUNIT_ASSERT_EQUAL(String("Power Noise"), tf->fieldList()[2]); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDRL")); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDTG")); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TMOO")); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TPRO")); #ifdef NO_ITUNES_HACKS CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOA")); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOT")); CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOP")); #endif CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSST")); } { const ByteVector expectedId3v23Data( "ID3" "\x03\x00\x00\x00\x00\x09\x49" "TSOA" "\x00\x00\x00\x01\x00\x00\x00" "TSOT" "\x00\x00\x00\x01\x00\x00\x00" "TSOP" "\x00\x00\x00\x01\x00\x00\x00" "TORY" "\x00\x00\x00\x05\x00\x00\x00" "2011" "TYER" "\x00\x00\x00\x05\x00\x00\x00" "2012" "TDAT" "\x00\x00\x00\x05\x00\x00\x00" "1704" "TIME" "\x00\x00\x00\x05\x00\x00\x00" "1201" "IPLS" "\x00\x00\x00\x44\x00\x00\x00" "Guitar" "\x00" "Artist 1" "\x00" "Drums" "\x00" "Artist 2" "\x00" "Producer" "\x00" "Artist 3" "\x00" "Mastering" "\x00" "Artist 4" "TCON" "\x00\x00\x00\x14\x00\x00\x00" "(51)(39)Power Noise", 211); const ByteVector actualId3v23Data = PlainFile(newname.c_str()).readBlock(expectedId3v23Data.size()); CPPUNIT_ASSERT_EQUAL(expectedId3v23Data, actualId3v23Data); } { ScopedFileCopy rareFramesCopy("rare_frames", ".mp3"); MPEG::File f(rareFramesCopy.fileName().c_str()); f.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v3); f.seek(f.find("TCON") + 11); CPPUNIT_ASSERT_EQUAL(ByteVector("(13)"), f.readBlock(4)); } } void testCompressedFrameWithBrokenLength() { MPEG::File f(TEST_FILE_PATH_C("compressed_id3_frame.mp3"), false); CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("APIC")); if(zlib::isAvailable()) { auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(f.ID3v2Tag()->frameListMap()["APIC"].front()); CPPUNIT_ASSERT(frame); assert(frame != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(String("image/bmp"), frame->mimeType()); CPPUNIT_ASSERT_EQUAL(ID3v2::AttachedPictureFrame::Other, frame->type()); CPPUNIT_ASSERT_EQUAL(String(""), frame->description()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(86414), frame->picture().size()); } else { // Skip the test if ZLIB is not installed. // The message "Compressed frames are currently not supported." will be displayed. auto frame = dynamic_cast<TagLib::ID3v2::UnknownFrame*>(f.ID3v2Tag()->frameListMap()["APIC"].front()); CPPUNIT_ASSERT(frame); } } void testW000() { MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false); CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000")); auto frame = dynamic_cast<TagLib::ID3v2::UrlLinkFrame*>(f.ID3v2Tag()->frameListMap()["W000"].front()); CPPUNIT_ASSERT(frame); assert(frame != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url()); } void testPropertyInterface() { ScopedFileCopy copy("rare_frames", ".mp3"); string newname = copy.fileName(); MPEG::File f(newname.c_str()); PropertyMap dict = f.ID3v2Tag(false)->properties(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(6), dict.size()); CPPUNIT_ASSERT(dict.contains("USERTEXTDESCRIPTION1")); CPPUNIT_ASSERT(dict.contains("QuodLibet::USERTEXTDESCRIPTION2")); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), dict["USERTEXTDESCRIPTION1"].size()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), dict["QuodLibet::USERTEXTDESCRIPTION2"].size()); CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["USERTEXTDESCRIPTION1"][0]); CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["USERTEXTDESCRIPTION1"][1]); CPPUNIT_ASSERT_EQUAL(String("userTextData1"), dict["QuodLibet::USERTEXTDESCRIPTION2"][0]); CPPUNIT_ASSERT_EQUAL(String("userTextData2"), dict["QuodLibet::USERTEXTDESCRIPTION2"][1]); CPPUNIT_ASSERT_EQUAL(String("Pop"), dict["GENRE"].front()); CPPUNIT_ASSERT_EQUAL(String("http://a.user.url"), dict["URL:USERURL"].front()); CPPUNIT_ASSERT_EQUAL(String("http://a.user.url/with/empty/description"), dict["URL"].front()); CPPUNIT_ASSERT_EQUAL(String("A COMMENT"), dict["COMMENT"].front()); CPPUNIT_ASSERT_EQUAL(1u, dict.unsupportedData().size()); CPPUNIT_ASSERT_EQUAL(String("UFID/supermihi@web.de"), dict.unsupportedData().front()); } void testPropertyInterface2() { ID3v2::Tag tag; auto frame1 = new ID3v2::UnsynchronizedLyricsFrame(); frame1->setDescription("test"); frame1->setText("la-la-la test"); tag.addFrame(frame1); auto frame2 = new ID3v2::UnsynchronizedLyricsFrame(); frame2->setDescription(""); frame2->setText("la-la-la nodescription"); tag.addFrame(frame2); auto frame3 = new ID3v2::AttachedPictureFrame(); frame3->setDescription("test picture"); tag.addFrame(frame3); auto frame4 = new ID3v2::TextIdentificationFrame("TIPL"); frame4->setText("single value is invalid for TIPL"); tag.addFrame(frame4); auto frame5 = new ID3v2::TextIdentificationFrame("TMCL"); StringList tmclData; tmclData.append("VIOLIN"); tmclData.append("a violinist"); tmclData.append("PIANO"); tmclData.append("a pianist"); frame5->setText(tmclData); tag.addFrame(frame5); auto frame6 = new ID3v2::UniqueFileIdentifierFrame("http://musicbrainz.org", "152454b9-19ba-49f3-9fc9-8fc26545cf41"); tag.addFrame(frame6); auto frame7 = new ID3v2::UniqueFileIdentifierFrame("http://example.com", "123"); tag.addFrame(frame7); auto frame8 = new ID3v2::UserTextIdentificationFrame(); frame8->setDescription("MusicBrainz Album Id"); frame8->setText("95c454a5-d7e0-4d8f-9900-db04aca98ab3"); tag.addFrame(frame8); auto frame9 = new ID3v2::UnknownFrame("WXYZ"); tag.addFrame(frame9); auto frame10 = new ID3v2::CommentsFrame(); frame10->setDescription("iTunNORM"); frame10->setText("00002C3B"); frame10->setLanguage("eng"); tag.addFrame(frame10); PropertyMap properties = tag.properties(); CPPUNIT_ASSERT_EQUAL(4u, properties.unsupportedData().size()); CPPUNIT_ASSERT(properties.unsupportedData().contains("TIPL")); CPPUNIT_ASSERT(properties.unsupportedData().contains("APIC")); CPPUNIT_ASSERT(properties.unsupportedData().contains("UFID/http://example.com")); CPPUNIT_ASSERT(properties.unsupportedData().contains("UNKNOWN/WXYZ")); CPPUNIT_ASSERT(properties.contains("PERFORMER:VIOLIN")); CPPUNIT_ASSERT(properties.contains("PERFORMER:PIANO")); CPPUNIT_ASSERT_EQUAL(String("a violinist"), properties["PERFORMER:VIOLIN"].front()); CPPUNIT_ASSERT_EQUAL(String("a pianist"), properties["PERFORMER:PIANO"].front()); CPPUNIT_ASSERT(properties.contains("LYRICS")); CPPUNIT_ASSERT(properties.contains("LYRICS:TEST")); CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_TRACKID")); CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_TRACKID"].front()); CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_ALBUMID")); CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_ALBUMID"].front()); CPPUNIT_ASSERT(properties.contains("COMMENT:ITUNNORM")); CPPUNIT_ASSERT_EQUAL(String("00002C3B"), properties["COMMENT:ITUNNORM"].front()); tag.removeUnsupportedProperties(properties.unsupportedData()); CPPUNIT_ASSERT(tag.frameList("APIC").isEmpty()); CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty()); CPPUNIT_ASSERT(tag.frameList("WXYZ").isEmpty()); CPPUNIT_ASSERT_EQUAL(static_cast<ID3v2::UniqueFileIdentifierFrame *>(nullptr), ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://example.com")); CPPUNIT_ASSERT_EQUAL(frame1, ID3v2::UnsynchronizedLyricsFrame::findByDescription(&tag, "test")); CPPUNIT_ASSERT_EQUAL(frame6, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://musicbrainz.org")); CPPUNIT_ASSERT_EQUAL(frame8, ID3v2::UserTextIdentificationFrame::find(&tag, "MusicBrainz Album Id")); CPPUNIT_ASSERT_EQUAL(static_cast<ID3v2::UserTextIdentificationFrame *>(nullptr), ID3v2::UserTextIdentificationFrame::find(&tag, "non existing")); CPPUNIT_ASSERT_EQUAL(frame10, ID3v2::CommentsFrame::findByDescription(&tag, "iTunNORM")); CPPUNIT_ASSERT_EQUAL(static_cast<ID3v2::CommentsFrame *>(nullptr), ID3v2::CommentsFrame::findByDescription(&tag, "non existing")); } void testPropertiesMovement() { ID3v2::Tag tag; auto frameMvnm = new ID3v2::TextIdentificationFrame("MVNM"); frameMvnm->setText("Movement Name"); tag.addFrame(frameMvnm); auto frameMvin = new ID3v2::TextIdentificationFrame("MVIN"); frameMvin->setText("2/3"); tag.addFrame(frameMvin); PropertyMap properties = tag.properties(); CPPUNIT_ASSERT(properties.contains("MOVEMENTNAME")); CPPUNIT_ASSERT(properties.contains("MOVEMENTNUMBER")); CPPUNIT_ASSERT_EQUAL(String("Movement Name"), properties["MOVEMENTNAME"].front()); CPPUNIT_ASSERT_EQUAL(String("2/3"), properties["MOVEMENTNUMBER"].front()); ByteVector frameDataMvnm("MVNM" "\x00\x00\x00\x0e" "\x00\x00" "\x00" "Movement Name", 24); CPPUNIT_ASSERT_EQUAL(frameDataMvnm, frameMvnm->render()); ByteVector frameDataMvin("MVIN" "\x00\x00\x00\x04" "\x00\x00" "\x00" "2/3", 14); CPPUNIT_ASSERT_EQUAL(frameDataMvin, frameMvin->render()); ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); ID3v2::Header header; auto parsedFrameMvnm = dynamic_cast<ID3v2::TextIdentificationFrame *>( factory->createFrame(frameDataMvnm, &header)); auto parsedFrameMvin = dynamic_cast<ID3v2::TextIdentificationFrame *>( factory->createFrame(frameDataMvin, &header)); CPPUNIT_ASSERT(parsedFrameMvnm); CPPUNIT_ASSERT(parsedFrameMvin); CPPUNIT_ASSERT_EQUAL(String("Movement Name"), parsedFrameMvnm->toString()); CPPUNIT_ASSERT_EQUAL(String("2/3"), parsedFrameMvin->toString()); tag.addFrame(parsedFrameMvnm); tag.addFrame(parsedFrameMvin); } void testPropertyGrouping() { ID3v2::Tag tag; auto frameGrp1 = new ID3v2::TextIdentificationFrame("GRP1"); frameGrp1->setText("Grouping"); tag.addFrame(frameGrp1); PropertyMap properties = tag.properties(); CPPUNIT_ASSERT(properties.contains("GROUPING")); CPPUNIT_ASSERT_EQUAL(String("Grouping"), properties["GROUPING"].front()); ByteVector frameDataGrp1("GRP1" "\x00\x00\x00\x09" "\x00\x00" "\x00" "Grouping", 19); CPPUNIT_ASSERT_EQUAL(frameDataGrp1, frameGrp1->render()); ID3v2::FrameFactory *factory = ID3v2::FrameFactory::instance(); ID3v2::Header header; auto parsedFrameGrp1 = dynamic_cast<ID3v2::TextIdentificationFrame *>( factory->createFrame(frameDataGrp1, &header)); CPPUNIT_ASSERT(parsedFrameGrp1); CPPUNIT_ASSERT_EQUAL(String("Grouping"), parsedFrameGrp1->toString()); tag.addFrame(parsedFrameGrp1); } void testDeleteFrame() { ScopedFileCopy copy("rare_frames", ".mp3"); string newname = copy.fileName(); { MPEG::File f(newname.c_str()); ID3v2::Tag *t = f.ID3v2Tag(); ID3v2::Frame *frame = t->frameList("TCON")[0]; CPPUNIT_ASSERT_EQUAL(1u, t->frameList("TCON").size()); t->removeFrame(frame, true); f.save(MPEG::File::ID3v2); } { MPEG::File f2(newname.c_str()); ID3v2::Tag *t = f2.ID3v2Tag(); CPPUNIT_ASSERT(t->frameList("TCON").isEmpty()); } } void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); { MPEG::File foo(newname.c_str()); foo.tag()->setArtist("Artist"); foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2); } { MPEG::File bar(newname.c_str()); bar.ID3v2Tag()->removeFrames("TPE1"); // Should strip ID3v1 here and not add old values to ID3v2 again bar.save(MPEG::File::ID3v2, File::StripOthers); } MPEG::File f(newname.c_str()); CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1")); } void testParseChapterFrame() { ID3v2::Header header; auto chapterData = ByteVector("CHAP" // Frame ID "\x00\x00\x00\x20" // Frame size "\x00\x00" // Frame flags "\x43\x00" // Element ID ("C") "\x00\x00\x00\x03" // Start time "\x00\x00\x00\x05" // End time "\x00\x00\x00\x02" // Start offset "\x00\x00\x00\x03", 28); // End offset auto embeddedFrameData = ByteVector("TIT2" // Embedded frame ID "\x00\x00\x00\x04" // Embedded frame size "\x00\x00" // Embedded frame flags "\x00" // TIT2 frame text encoding "CH1", 14); // Chapter title ID3v2::ChapterFrame f1(&header, chapterData); CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f1.elementID()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x03) == f1.startTime()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x05) == f1.endTime()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x02) == f1.startOffset()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x03) == f1.endOffset()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x00) == f1.embeddedFrameList().size()); ID3v2::ChapterFrame f2(&header, chapterData + embeddedFrameData); CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f2.elementID()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x03) == f2.startTime()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x05) == f2.endTime()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x02) == f2.startOffset()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x03) == f2.endOffset()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x01) == f2.embeddedFrameList().size()); CPPUNIT_ASSERT(f2.embeddedFrameList("TIT2").size() == 1); CPPUNIT_ASSERT(f2.embeddedFrameList("TIT2")[0]->toString() == "CH1"); } void testRenderChapterFrame() { ID3v2::Header header; ID3v2::ChapterFrame f1(&header, "CHAP"); f1.setElementID(ByteVector("\x43\x00", 2)); f1.setStartTime(3); f1.setEndTime(5); f1.setStartOffset(2); f1.setEndOffset(3); auto eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("CH1"); f1.addEmbeddedFrame(eF); auto expected = ByteVector("CHAP" // Frame ID "\x00\x00\x00\x20" // Frame size "\x00\x00" // Frame flags "\x43\x00" // Element ID "\x00\x00\x00\x03" // Start time "\x00\x00\x00\x05" // End time "\x00\x00\x00\x02" // Start offset "\x00\x00\x00\x03" // End offset "TIT2" // Embedded frame ID "\x00\x00\x00\x04" // Embedded frame size "\x00\x00" // Embedded frame flags "\x00" // TIT2 frame text encoding "CH1", 42); // Chapter title CPPUNIT_ASSERT_EQUAL(expected, f1.render()); f1.setElementID("C"); CPPUNIT_ASSERT_EQUAL(expected, f1.render()); ID3v2::FrameList frames; eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("CH1"); frames.append(eF); ID3v2::ChapterFrame f2(ByteVector("\x43\x00", 2), 3, 5, 2, 3, frames); CPPUNIT_ASSERT_EQUAL(expected, f2.render()); frames.clear(); eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("CH1"); frames.append(eF); ID3v2::ChapterFrame f3(ByteVector("C\x00", 2), 3, 5, 2, 3, frames); CPPUNIT_ASSERT_EQUAL(expected, f3.render()); frames.clear(); eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("CH1"); frames.append(eF); ID3v2::ChapterFrame f4("C", 3, 5, 2, 3, frames); CPPUNIT_ASSERT_EQUAL(expected, f4.render()); CPPUNIT_ASSERT(!f4.toString().isEmpty()); ID3v2::ChapterFrame f5("C", 3, 5, 2, 3); eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("CH1"); f5.addEmbeddedFrame(eF); CPPUNIT_ASSERT_EQUAL(expected, f5.render()); } void testParseTableOfContentsFrame() { ID3v2::Header header; ID3v2::TableOfContentsFrame f( &header, ByteVector("CTOC" // Frame ID "\x00\x00\x00\x16" // Frame size "\x00\x00" // Frame flags "\x54\x00" // Element ID ("T") "\x01" // CTOC flags "\x02" // Entry count "\x43\x00" // First entry ("C") "\x44\x00" // Second entry ("D") "TIT2" // Embedded frame ID "\x00\x00\x00\x04" // Embedded frame size "\x00\x00" // Embedded frame flags "\x00" // TIT2 frame text encoding "TC1", 32)); // Table of contents title CPPUNIT_ASSERT_EQUAL(ByteVector("T"), f.elementID()); CPPUNIT_ASSERT(!f.isTopLevel()); CPPUNIT_ASSERT(f.isOrdered()); CPPUNIT_ASSERT(static_cast<unsigned int>(0x02) == f.entryCount()); CPPUNIT_ASSERT_EQUAL(ByteVector("C"), f.childElements()[0]); CPPUNIT_ASSERT_EQUAL(ByteVector("D"), f.childElements()[1]); CPPUNIT_ASSERT(static_cast<unsigned int>(0x01) == f.embeddedFrameList().size()); CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1); CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "TC1"); f.removeChildElement("E"); // not existing CPPUNIT_ASSERT_EQUAL(2U, f.entryCount()); f.removeChildElement("C"); CPPUNIT_ASSERT_EQUAL(1U, f.entryCount()); CPPUNIT_ASSERT_EQUAL(ByteVector("D"), f.childElements()[0]); ID3v2::Frame *frame = f.embeddedFrameList("TIT2")[0]; f.removeEmbeddedFrame(frame); CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").isEmpty()); } void testRenderTableOfContentsFrame() { ID3v2::Header header; ID3v2::TableOfContentsFrame f(&header, "CTOC"); f.setElementID("T"); f.setIsTopLevel(false); f.setIsOrdered(true); f.addChildElement("C"); f.addChildElement("D"); auto eF = new ID3v2::TextIdentificationFrame("TIT2"); eF->setText("TC1"); f.addEmbeddedFrame(eF); CPPUNIT_ASSERT_EQUAL( ByteVector("CTOC" // Frame ID "\x00\x00\x00\x16" // Frame size "\x00\x00" // Frame flags "\x54\x00" // Element ID "\x01" // CTOC flags "\x02" // Entry count "\x43\x00" // First entry "\x44\x00" // Second entry "TIT2" // Embedded frame ID "\x00\x00\x00\x04" // Embedded frame size "\x00\x00" // Embedded frame flags "\x00" // TIT2 frame text encoding "TC1", 32), // Table of contents title f.render()); } void testShrinkPadding() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); { MPEG::File f(newname.c_str()); f.ID3v2Tag()->setTitle(longText(64 * 1024)); f.save(MPEG::File::ID3v2, File::StripOthers); } { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(74789), f.length()); f.ID3v2Tag()->setTitle("ABCDEFGHIJ"); f.save(MPEG::File::ID3v2, File::StripOthers); } { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(9263), f.length()); } } void testEmptyFrame() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); { MPEG::File f(newname.c_str()); ID3v2::Tag *tag = f.ID3v2Tag(true); auto frame1 = new ID3v2::UrlLinkFrame( ByteVector("WOAF\x00\x00\x00\x01\x00\x00\x00", 11)); tag->addFrame(frame1); auto frame2 = new ID3v2::TextIdentificationFrame("TIT2"); frame2->setText("Title"); tag->addFrame(frame2); f.save(); } { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); ID3v2::Tag *tag = f.ID3v2Tag(); CPPUNIT_ASSERT_EQUAL(String("Title"), tag->title()); CPPUNIT_ASSERT_EQUAL(true, tag->frameListMap()["WOAF"].isEmpty()); } } void testDuplicateTags() { ScopedFileCopy copy("duplicate_id3v2", ".mp3"); ByteVector audioStream; { MPEG::File f(copy.fileName().c_str()); f.seek(f.ID3v2Tag()->header()->completeTagSize()); audioStream = f.readBlock(2089); // duplicate_id3v2.mp3 has duplicate ID3v2 tags. // Sample rate will be 32000 if we can't skip the second tag. CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8049), f.ID3v2Tag()->header()->completeTagSize()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); f.ID3v2Tag()->setArtist("Artist A"); f.save(MPEG::File::ID3v2, File::StripOthers); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(3594), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1505), f.ID3v2Tag()->header()->completeTagSize()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f.ID3v2Tag()->artist()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); f.seek(f.ID3v2Tag()->header()->completeTagSize()); CPPUNIT_ASSERT_EQUAL(f.readBlock(2089), audioStream); } } void testParseTOCFrameWithManyChildren() { MPEG::File f(TEST_FILE_PATH_C("toc_many_children.mp3")); CPPUNIT_ASSERT(f.isValid()); ID3v2::Tag *tag = f.ID3v2Tag(); CPPUNIT_ASSERT_EQUAL(130U, tag->frameList().size()); int i = 0; for(const auto &frame : std::as_const(tag->frameList())) { if(i > 0) { CPPUNIT_ASSERT_EQUAL(ByteVector("CHAP"), frame->frameID()); auto chapFrame = dynamic_cast<const ID3v2::ChapterFrame *>(frame); CPPUNIT_ASSERT_EQUAL(ByteVector("chapter") + ByteVector(String::number(i - 1).toCString()), chapFrame->elementID()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(100 * i), chapFrame->startTime()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(100 * i), chapFrame->endTime()); const ID3v2::FrameList &embeddedFrames = chapFrame->embeddedFrameList(); CPPUNIT_ASSERT_EQUAL(1U, embeddedFrames.size()); auto tit2Frame = dynamic_cast<const ID3v2::TextIdentificationFrame *>( embeddedFrames.front()); CPPUNIT_ASSERT(tit2Frame); CPPUNIT_ASSERT_EQUAL(String("Marker ") + String::number(i), tit2Frame->fieldList().front()); } else { CPPUNIT_ASSERT_EQUAL(ByteVector("CTOC"), frame->frameID()); auto ctocFrame = dynamic_cast<const ID3v2::TableOfContentsFrame *>(frame); CPPUNIT_ASSERT_EQUAL(ByteVector("toc"), ctocFrame->elementID()); CPPUNIT_ASSERT(!ctocFrame->isTopLevel()); CPPUNIT_ASSERT(!ctocFrame->isOrdered()); CPPUNIT_ASSERT_EQUAL(129U, ctocFrame->entryCount()); const ID3v2::FrameList &embeddedFrames = ctocFrame->embeddedFrameList(); CPPUNIT_ASSERT_EQUAL(1U, embeddedFrames.size()); auto tit2Frame = dynamic_cast<const ID3v2::TextIdentificationFrame *>( embeddedFrames.front()); CPPUNIT_ASSERT(tit2Frame); CPPUNIT_ASSERT_EQUAL(StringList("toplevel toc"), tit2Frame->fieldList()); } ++i; } CPPUNIT_ASSERT(!ID3v2::ChapterFrame::findByElementID(tag, "chap2")); CPPUNIT_ASSERT(ID3v2::ChapterFrame::findByElementID(tag, "chapter2")); CPPUNIT_ASSERT(!ID3v2::TableOfContentsFrame::findTopLevel(tag)); CPPUNIT_ASSERT(!ID3v2::TableOfContentsFrame::findByElementID(tag, "ctoc")); CPPUNIT_ASSERT(ID3v2::TableOfContentsFrame::findByElementID(tag, "toc")); auto tocFrame = ID3v2::TableOfContentsFrame::findByElementID(tag, "toc"); CPPUNIT_ASSERT_EQUAL(1U, tocFrame->embeddedFrameList().size()); tocFrame->removeEmbeddedFrames("TIT2"); CPPUNIT_ASSERT(tocFrame->embeddedFrameList().isEmpty()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2); ������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_id3v2framefactory.cpp�������������������������������������������������������0000664�0000000�0000000�00000031314�14662262111�0021217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <functional> #include <memory> #include "flacproperties.h" #include "mpegproperties.h" #include "tbytevector.h" #include "tpropertymap.h" #include "mpegfile.h" #include "flacfile.h" #include "trueaudiofile.h" #include "trueaudioproperties.h" #include "wavfile.h" #include "aifffile.h" #include "dsffile.h" #include "dsdifffile.h" #include "id3v2tag.h" #include "id3v2frame.h" #include "id3v2framefactory.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; namespace { class CustomFrameFactory; // Just a silly example of a custom frame holding a number. class CustomFrame : public ID3v2::Frame { friend class CustomFrameFactory; public: explicit CustomFrame(unsigned int value = 0) : Frame("CUST"), m_value(value) {} CustomFrame(const CustomFrame &) = delete; CustomFrame &operator=(const CustomFrame &) = delete; ~CustomFrame() override = default; String toString() const override { return String::number(m_value); } PropertyMap asProperties() const override { return SimplePropertyMap{{"CUSTOM", StringList(String::number(m_value))}}; } unsigned int value() const { return m_value; } protected: void parseFields(const ByteVector &data) override { m_value = data.toUInt(); } ByteVector renderFields() const override { return ByteVector::fromUInt(m_value); } private: CustomFrame(const ByteVector &data, Header *h) : Frame(h), m_value(fieldData(data).toUInt()) {} unsigned int m_value; }; // Example for frame factory with support for CustomFrame. class CustomFrameFactory : public ID3v2::FrameFactory { public: CustomFrameFactory(const CustomFrameFactory &) = delete; CustomFrameFactory &operator=(const CustomFrameFactory &) = delete; static CustomFrameFactory *instance() { return &factory; } ID3v2::Frame *createFrameForProperty( const String &key, const StringList &values) const override { if(key == "CUSTOM") { return new CustomFrame(!values.isEmpty() ? values.front().toInt() : 0); } return ID3v2::FrameFactory::createFrameForProperty(key, values); } protected: CustomFrameFactory() = default; ~CustomFrameFactory() = default; ID3v2::Frame *createFrame(const ByteVector &data, ID3v2::Frame::Header *header, const ID3v2::Header *tagHeader) const override { if(header->frameID() == "CUST") { return new CustomFrame(data, header); } return ID3v2::FrameFactory::createFrame(data, header, tagHeader); } private: static CustomFrameFactory factory; }; CustomFrameFactory CustomFrameFactory::factory; } // namespace class TestId3v2FrameFactory : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestId3v2FrameFactory); CPPUNIT_TEST(testMPEG); CPPUNIT_TEST(testFLAC); CPPUNIT_TEST(testTrueAudio); CPPUNIT_TEST(testWAV); CPPUNIT_TEST(testAIFF); CPPUNIT_TEST(testDSF); CPPUNIT_TEST(testDSDIFF); CPPUNIT_TEST_SUITE_END(); public: void testGenericFrameFactory( const char *fileName, function<File *(const char *)> createFileWithDefaultFactory, function<File *(const char *, ID3v2::FrameFactory *factory)> createFileWithFactory, function<bool(const File &)> hasID3v2Tag, function<ID3v2::Tag *(File &)> getID3v2Tag, function<bool(File &)> stripAllTags) { { auto f = std::unique_ptr<File>(createFileWithDefaultFactory(fileName)); CPPUNIT_ASSERT(f->isValid()); ID3v2::Tag *tag = getID3v2Tag(*f); ID3v2::FrameList frames = tag->frameList(); for(auto it = frames.begin(); it != frames.end(); it = frames.erase(it)) { tag->removeFrame(*it); } tag->setArtist("An artist"); tag->setTitle("A title"); f->save(); } { auto f = std::unique_ptr<File>(createFileWithDefaultFactory(fileName)); CPPUNIT_ASSERT(f->isValid()); CPPUNIT_ASSERT(hasID3v2Tag(*f)); ID3v2::Tag *tag = getID3v2Tag(*f); tag->addFrame(new CustomFrame(1234567890)); f->save(); } { auto f = std::unique_ptr<File>(createFileWithDefaultFactory(fileName)); CPPUNIT_ASSERT(f->isValid()); CPPUNIT_ASSERT(hasID3v2Tag(*f)); ID3v2::Tag *tag = getID3v2Tag(*f); const auto &frames = tag->frameList("CUST"); CPPUNIT_ASSERT(!frames.isEmpty()); // Without a specialized FrameFactory, you can add custom frames, // but your cannot parse them. CPPUNIT_ASSERT(!dynamic_cast<CustomFrame *>(frames.front())); } { auto f = std::unique_ptr<File>(createFileWithFactory(fileName, CustomFrameFactory::instance())); CPPUNIT_ASSERT(f->isValid()); CPPUNIT_ASSERT(hasID3v2Tag(*f)); ID3v2::Tag *tag = getID3v2Tag(*f); const auto &frames = tag->frameList("CUST"); CPPUNIT_ASSERT(!frames.isEmpty()); auto frame = dynamic_cast<CustomFrame *>(frames.front()); CPPUNIT_ASSERT(frame); CPPUNIT_ASSERT_EQUAL(1234567890U, frame->value()); PropertyMap properties = tag->properties(); CPPUNIT_ASSERT_EQUAL(StringList("1234567890"), properties.value("CUSTOM")); CPPUNIT_ASSERT_EQUAL(StringList("An artist"), properties.value("ARTIST")); CPPUNIT_ASSERT_EQUAL(StringList("A title"), properties.value("TITLE")); stripAllTags(*f); } { auto f = std::unique_ptr<File>(createFileWithFactory(fileName, CustomFrameFactory::instance())); CPPUNIT_ASSERT(f->isValid()); CPPUNIT_ASSERT(!hasID3v2Tag(*f)); ID3v2::Tag *tag = getID3v2Tag(*f); PropertyMap properties = tag->properties(); CPPUNIT_ASSERT(properties.isEmpty()); properties.insert("CUSTOM", StringList("305419896")); tag->setProperties(properties); f->save(); } { auto f = std::unique_ptr<File>(createFileWithFactory(fileName, CustomFrameFactory::instance())); CPPUNIT_ASSERT(f->isValid()); CPPUNIT_ASSERT(hasID3v2Tag(*f)); ID3v2::Tag *tag = getID3v2Tag(*f); PropertyMap properties = tag->properties(); CPPUNIT_ASSERT_EQUAL(StringList("305419896"), properties.value("CUSTOM")); const auto &frames = tag->frameList("CUST"); CPPUNIT_ASSERT(!frames.isEmpty()); auto frame = dynamic_cast<CustomFrame *>(frames.front()); CPPUNIT_ASSERT(frame); CPPUNIT_ASSERT_EQUAL(0x12345678U, frame->value()); } } void testMPEG() { ScopedFileCopy copy("lame_cbr", ".mp3"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new MPEG::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new MPEG::File(fileName, true, MPEG::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const MPEG::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<MPEG::File &>(f).ID3v2Tag(true); }, [](File &f) { return dynamic_cast<MPEG::File &>(f).strip(); } ); } void testFLAC() { ScopedFileCopy copy("no-tags", ".flac"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new FLAC::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new FLAC::File(fileName, true, FLAC::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const FLAC::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<FLAC::File &>(f).ID3v2Tag(true); }, [](File &f) { dynamic_cast<FLAC::File &>(f).strip(); return f.save(); } ); } void testTrueAudio() { ScopedFileCopy copy("empty", ".tta"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new TrueAudio::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new TrueAudio::File(fileName, true, TrueAudio::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const TrueAudio::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<TrueAudio::File &>(f).ID3v2Tag(true); }, [](File &f) { dynamic_cast<TrueAudio::File &>(f).strip(); return f.save(); } ); } void testWAV() { ScopedFileCopy copy("empty", ".wav"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new RIFF::WAV::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new RIFF::WAV::File( fileName, true, RIFF::WAV::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const RIFF::WAV::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<RIFF::WAV::File &>(f).ID3v2Tag(); }, [](File &f) { dynamic_cast<RIFF::WAV::File &>(f).strip(); return true; } ); } void testAIFF() { ScopedFileCopy copy("empty", ".aiff"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new RIFF::AIFF::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new RIFF::AIFF::File( fileName, true, RIFF::AIFF::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const RIFF::AIFF::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<RIFF::AIFF::File &>(f).tag(); }, [](File &f) { f.setProperties({}); return f.save(); } ); } void testDSF() { ScopedFileCopy copy("empty10ms", ".dsf"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new DSF::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new DSF::File( fileName, true, DSF::Properties::Average, factory); }, [](const File &f) { return !f.tag()->isEmpty(); }, [](File &f) { return dynamic_cast<DSF::File &>(f).tag(); }, [](File &f) { f.setProperties({}); return f.save(); } ); } void testDSDIFF() { ScopedFileCopy copy("empty10ms", ".dff"); testGenericFrameFactory( copy.fileName().c_str(), [](const char *fileName) { return new DSDIFF::File(fileName); }, [](const char *fileName, ID3v2::FrameFactory *factory) { return new DSDIFF::File( fileName, true, DSDIFF::Properties::Average, factory); }, [](const File &f) { return dynamic_cast<const DSDIFF::File &>(f).hasID3v2Tag(); }, [](File &f) { return dynamic_cast<DSDIFF::File &>(f).ID3v2Tag(true); }, [](File &f) { dynamic_cast<DSDIFF::File &>(f).strip(); return true; } ); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestId3v2FrameFactory); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_info.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000005776�14662262111�0016635�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "infotag.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestInfoTag : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestInfoTag); CPPUNIT_TEST(testTitle); CPPUNIT_TEST(testNumericFields); CPPUNIT_TEST_SUITE_END(); public: void testTitle() { RIFF::Info::Tag tag; CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); tag.setTitle("Test title 1"); tag.setFieldText("TEST", "Dummy Text"); CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title()); RIFF::Info::FieldListMap map = tag.fieldListMap(); CPPUNIT_ASSERT_EQUAL(String("Test title 1"), map["INAM"]); CPPUNIT_ASSERT_EQUAL(String("Dummy Text"), map["TEST"]); } void testNumericFields() { RIFF::Info::Tag tag; CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), tag.track()); tag.setTrack(1234); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1234), tag.track()); CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT")); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), tag.year()); tag.setYear(1234); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1234), tag.year()); CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD")); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag); ��taglib-2.0.2/tests/test_it.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000013224�14662262111�0016301�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cassert> #include "tstringlist.h" #include "itfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; static const String titleBefore("test song name"); static const String titleAfter("changed title"); static const String commentBefore( "This is a sample name.\n" "In module file formats\n" "sample names are abused\n" "as multiline comments.\n" " "); static const String newComment( "This is a sample name!\n" "In module file formats\n" "sample names are abused\n" "as multiline comments.\n" "-----------------------------------\n" "The previous line is truncated but starting with this line\n" "the comment is not limeted in the line length but to 8000\n" "additional characters (bytes).\n" "\n" "This is because it is saved in the 'message' proportion of\n" "IT files."); static const String commentAfter( "This is a sample name!\n" "In module file formats\n" "sample names are abused\n" "as multiline comments.\n" "-------------------------\n" "The previous line is truncated but starting with this line\n" "the comment is not limeted in the line length but to 8000\n" "additional characters (bytes).\n" "\n" "This is because it is saved in the 'message' proportion of\n" "IT files."); class TestIT : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestIT); CPPUNIT_TEST(testReadTags); CPPUNIT_TEST(testWriteTags); CPPUNIT_TEST_SUITE_END(); public: void testReadTags() { testRead(TEST_FILE_PATH_C("test.it"), titleBefore, commentBefore); } void testWriteTags() { ScopedFileCopy copy("test", ".it"); { IT::File file(copy.fileName().c_str()); CPPUNIT_ASSERT(file.tag() != nullptr); file.tag()->setTitle(titleAfter); file.tag()->setComment(newComment); file.tag()->setTrackerName("won't be saved"); CPPUNIT_ASSERT(file.save()); } testRead(copy.fileName().c_str(), titleAfter, commentAfter); } private: void testRead(FileName fileName, const String &title, const String &comment) { IT::File file(fileName); CPPUNIT_ASSERT(file.isValid()); IT::Properties *p = file.audioProperties(); Mod::Tag *t = file.tag(); CPPUNIT_ASSERT(nullptr != p); CPPUNIT_ASSERT(nullptr != t); assert(p != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL( 0, p->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL( 0, p->bitrate()); CPPUNIT_ASSERT_EQUAL( 0, p->sampleRate()); CPPUNIT_ASSERT_EQUAL(64, p->channels()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->lengthInPatterns()); CPPUNIT_ASSERT_EQUAL(true, p->stereo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->instrumentCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(5), p->sampleCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->patternCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(535), p->version()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(532), p->compatibleVersion()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(9), p->flags()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(128), p->globalVolume()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(48), p->mixVolume()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(125), p->tempo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(6), p->bpmSpeed()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(128), p->panningSeparation()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(0), p->pitchWheelDepth()); CPPUNIT_ASSERT_EQUAL(title, t->title()); CPPUNIT_ASSERT_EQUAL(String(), t->artist()); CPPUNIT_ASSERT_EQUAL(String(), t->album()); CPPUNIT_ASSERT_EQUAL(comment, t->comment()); CPPUNIT_ASSERT_EQUAL(String(), t->genre()); CPPUNIT_ASSERT_EQUAL(0U, t->year()); CPPUNIT_ASSERT_EQUAL(0U, t->track()); CPPUNIT_ASSERT_EQUAL(String("Impulse Tracker"), t->trackerName()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestIT); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_list.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000011157�14662262111�0016643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tlist.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestList : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestList); CPPUNIT_TEST(testAppend); CPPUNIT_TEST(testDetach); CPPUNIT_TEST(bracedInit); CPPUNIT_TEST(testSort); CPPUNIT_TEST_SUITE_END(); public: void testAppend() { List<int> l1; List<int> l2; List<int> l3; l1.append(2); l2.append(3); l2.append(4); l1.append(l2); l1.prepend(1); l3.append(1); l3.append(2); l3.append(3); l3.append(4); CPPUNIT_ASSERT_EQUAL(4U, l1.size()); CPPUNIT_ASSERT(l1 == l3); } void testDetach() { { List<int> l1; l1.append(1); l1.append(2); l1.append(3); l1.append(4); List<int> l2 = l1; auto it = l2.find(3); *it = 33; CPPUNIT_ASSERT_EQUAL(3, l1[2]); CPPUNIT_ASSERT_EQUAL(33, l2[2]); } { List<int *> l1; List<int *> l2 = l1; CPPUNIT_ASSERT(!l1.autoDelete()); CPPUNIT_ASSERT(!l2.autoDelete()); l2.setAutoDelete(true); CPPUNIT_ASSERT(!l1.autoDelete()); CPPUNIT_ASSERT(l2.autoDelete()); } { List<int> l1; List<int> l2 = l1; l1.insert(l1.begin(), 1); CPPUNIT_ASSERT(!l1.isEmpty()); CPPUNIT_ASSERT_EQUAL(1, l1.front()); CPPUNIT_ASSERT(l2.isEmpty()); } } void bracedInit() { List<int> l1 { 1, 2, 3 }; CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(l1.size())); CPPUNIT_ASSERT_EQUAL(1, l1[0]); CPPUNIT_ASSERT_EQUAL(2, l1[1]); CPPUNIT_ASSERT_EQUAL(3, l1[2]); List<int*> l2 { new int(1), new int(2), new int(3) }; l2.setAutoDelete(true); CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(l2.size())); CPPUNIT_ASSERT_EQUAL(1, *l2[0]); CPPUNIT_ASSERT_EQUAL(2, *l2[1]); CPPUNIT_ASSERT_EQUAL(3, *l2[2]); List<int> l3 = { 1, 2, 3 }; CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(l3.size())); CPPUNIT_ASSERT_EQUAL(1, l3[0]); CPPUNIT_ASSERT_EQUAL(2, l3[1]); CPPUNIT_ASSERT_EQUAL(3, l3[2]); List<int*> l4 = { new int(1), new int(2), new int(3) }; l4.setAutoDelete(true); l4 = { new int(4), new int(5), new int(6) }; CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(l4.size())); CPPUNIT_ASSERT_EQUAL(4, *l4[0]); CPPUNIT_ASSERT_EQUAL(5, *l4[1]); CPPUNIT_ASSERT_EQUAL(6, *l4[2]); } void testSort() { List<int> list1 { 3, 2, 1 }; list1.sort(); CPPUNIT_ASSERT_EQUAL(list1[0], 1); CPPUNIT_ASSERT_EQUAL(list1[1], 2); CPPUNIT_ASSERT_EQUAL(list1[2], 3); List<int> list2 { 1, 2, 3 }; list2.sort([](const auto &a, const auto &b) { return a > b; }); CPPUNIT_ASSERT_EQUAL(list2[0], 3); CPPUNIT_ASSERT_EQUAL(list2[1], 2); CPPUNIT_ASSERT_EQUAL(list2[2], 1); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestList); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_map.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000006572�14662262111�0016452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tstring.h" #include "tmap.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestMap : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMap); CPPUNIT_TEST(testInsert); CPPUNIT_TEST(testDetach); CPPUNIT_TEST(testBracedInit); CPPUNIT_TEST_SUITE_END(); public: void testInsert() { Map<String, int> m1; m1.insert("foo", 3); m1.insert("bar", 5); CPPUNIT_ASSERT_EQUAL(2U, m1.size()); CPPUNIT_ASSERT_EQUAL(3, m1["foo"]); CPPUNIT_ASSERT_EQUAL(5, m1["bar"]); m1.insert("foo", 7); CPPUNIT_ASSERT_EQUAL(2U, m1.size()); CPPUNIT_ASSERT_EQUAL(7, m1["foo"]); CPPUNIT_ASSERT_EQUAL(5, m1["bar"]); } void testDetach() { Map<String, int> m1; m1.insert("alice", 5); m1.insert("bob", 9); m1.insert("carol", 11); Map<String, int> m2 = m1; auto it = m2.find("bob"); it->second = 99; CPPUNIT_ASSERT_EQUAL(9, m1["bob"]); CPPUNIT_ASSERT_EQUAL(99, m2["bob"]); } void testBracedInit() { Map<String, int> m1 { {"ONE", 1}, {"TWO", 2}, {"THREE", 3} }; CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(m1.size())); CPPUNIT_ASSERT(m1.contains("ONE") && m1["ONE"] == 1); CPPUNIT_ASSERT(m1.contains("TWO") && m1["TWO"] == 2); CPPUNIT_ASSERT(m1.contains("THREE") && m1["THREE"] == 3); Map<String, int> m2 = { {"FOUR", 4}, {"FIVE", 5}, {"SIX", 6} }; CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(m2.size())); CPPUNIT_ASSERT(m2.contains("FOUR") && m2["FOUR"] == 4); CPPUNIT_ASSERT(m2.contains("FIVE") && m2["FIVE"] == 5); CPPUNIT_ASSERT(m2.contains("SIX") && m2["SIX"] == 6); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMap); ��������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_mod.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000011664�14662262111�0016452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cassert> #include "tpropertymap.h" #include "modfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; static const String titleBefore("title of song"); static const String titleAfter("changed title"); static const String commentBefore( "Instrument names\n" "are abused as\n" "comments in\n" "module file formats.\n" "-+-+-+-+-+-+-+-+-+-+-+\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); static const String newComment( "This line will be truncated because it is too long for a mod instrument name.\n" "This line is ok."); static const String commentAfter( "This line will be trun\n" "This line is ok.\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); class TestMod : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMod); CPPUNIT_TEST(testReadTags); CPPUNIT_TEST(testWriteTags); CPPUNIT_TEST(testPropertyInterface); CPPUNIT_TEST_SUITE_END(); public: void testReadTags() { testRead(TEST_FILE_PATH_C("test.mod"), titleBefore, commentBefore); } void testWriteTags() { ScopedFileCopy copy("test", ".mod"); { Mod::File file(copy.fileName().c_str()); CPPUNIT_ASSERT(file.tag() != nullptr); file.tag()->setTitle(titleAfter); file.tag()->setComment(newComment); CPPUNIT_ASSERT(file.save()); } testRead(copy.fileName().c_str(), titleAfter, commentAfter); CPPUNIT_ASSERT(fileEqual( copy.fileName(), testFilePath("changed.mod"))); } void testPropertyInterface() { Mod::Tag t; PropertyMap properties; properties["BLA"] = String("bla"); properties["ARTIST"] = String("artist1"); properties["ARTIST"].append("artist2"); properties["TITLE"] = String("title"); PropertyMap unsupported = t.setProperties(properties); CPPUNIT_ASSERT(unsupported.contains("BLA")); CPPUNIT_ASSERT(unsupported.contains("ARTIST")); CPPUNIT_ASSERT_EQUAL(properties["ARTIST"], unsupported["ARTIST"]); CPPUNIT_ASSERT(!unsupported.contains("TITLE")); properties = t.properties(); CPPUNIT_ASSERT_EQUAL(StringList("title"), properties["TITLE"]); } private: void testRead(FileName fileName, const String &title, const String &comment) { Mod::File file(fileName); CPPUNIT_ASSERT(file.isValid()); Mod::Properties *p = file.audioProperties(); Mod::Tag *t = file.tag(); CPPUNIT_ASSERT(nullptr != p); CPPUNIT_ASSERT(nullptr != t); assert(p != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); CPPUNIT_ASSERT_EQUAL(8, p->channels()); CPPUNIT_ASSERT_EQUAL(31U, p->instrumentCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(1), p->lengthInPatterns()); CPPUNIT_ASSERT_EQUAL(title, t->title()); CPPUNIT_ASSERT_EQUAL(String(), t->artist()); CPPUNIT_ASSERT_EQUAL(String(), t->album()); CPPUNIT_ASSERT_EQUAL(comment, t->comment()); CPPUNIT_ASSERT_EQUAL(String(), t->genre()); CPPUNIT_ASSERT_EQUAL(0U, t->year()); CPPUNIT_ASSERT_EQUAL(0U, t->track()); CPPUNIT_ASSERT_EQUAL(String("StarTrekker"), t->trackerName()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMod); ����������������������������������������������������������������������������taglib-2.0.2/tests/test_mp4.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000077532�14662262111�0016401�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2008 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tbytevectorstream.h" #include "tpropertymap.h" #include "tag.h" #include "mp4tag.h" #include "mp4atom.h" #include "mp4file.h" #include "mp4itemfactory.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; namespace { class CustomItemFactory : public MP4::ItemFactory { public: CustomItemFactory(const CustomItemFactory &) = delete; CustomItemFactory &operator=(const CustomItemFactory &) = delete; static CustomItemFactory *instance() { return &factory; } protected: CustomItemFactory() = default; ~CustomItemFactory() = default; NameHandlerMap nameHandlerMap() const override { return MP4::ItemFactory::nameHandlerMap() .insert("tsti", ItemHandlerType::Int) .insert("tstt", ItemHandlerType::Text); } Map<ByteVector, String> namePropertyMap() const override { return MP4::ItemFactory::namePropertyMap() .insert("tsti", "TESTINTEGER"); } private: static CustomItemFactory factory; }; CustomItemFactory CustomItemFactory::factory; } // namespace class TestMP4 : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMP4); CPPUNIT_TEST(testPropertiesAAC); CPPUNIT_TEST(testPropertiesAACWithoutBitrate); CPPUNIT_TEST(testPropertiesALAC); CPPUNIT_TEST(testPropertiesALACWithoutBitrate); CPPUNIT_TEST(testPropertiesAACWithoutLength); CPPUNIT_TEST(testPropertiesM4V); CPPUNIT_TEST(testFreeForm); CPPUNIT_TEST(testCheckValid); CPPUNIT_TEST(testHasTag); CPPUNIT_TEST(testIsEmpty); CPPUNIT_TEST(testUpdateStco); CPPUNIT_TEST(testSaveExisingWhenIlstIsLast); CPPUNIT_TEST(test64BitAtom); CPPUNIT_TEST(testGnre); CPPUNIT_TEST(testCovrRead); CPPUNIT_TEST(testCovrWrite); CPPUNIT_TEST(testCovrRead2); CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testPropertiesAllSupported); CPPUNIT_TEST(testPropertiesMovement); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST(testWithZeroLengthAtom); CPPUNIT_TEST(testEmptyValuesRemoveItems); CPPUNIT_TEST(testRemoveMetadata); CPPUNIT_TEST(testNonFullMetaAtom); CPPUNIT_TEST(testItemFactory); CPPUNIT_TEST(testNonPrintableAtom); CPPUNIT_TEST_SUITE_END(); public: void testPropertiesAAC() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3708, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec()); } void testPropertiesAACWithoutBitrate() { ByteVector aacData = PlainFile(TEST_FILE_PATH_C("has-tags.m4a")).readAll(); CPPUNIT_ASSERT_GREATER(1960U, aacData.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("mp4a"), aacData.mid(1890, 4)); // Set the bitrate to zero for (int offset = 1956; offset < 1960; ++offset) { aacData[offset] = 0; } ByteVectorStream aacStream(aacData); MP4::File f(&aacStream); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3708, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec()); } void testPropertiesALAC() { MP4::File f(TEST_FILE_PATH_C("empty_alac.m4a")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::ALAC, f.audioProperties()->codec()); } void testPropertiesALACWithoutBitrate() { ByteVector alacData = PlainFile(TEST_FILE_PATH_C("empty_alac.m4a")).readAll(); CPPUNIT_ASSERT_GREATER(474U, alacData.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("alac"), alacData.mid(446, 4)); // Set the bitrate to zero for (int offset = 470; offset < 474; ++offset) { alacData[offset] = 0; } ByteVectorStream alacStream(alacData); MP4::File f(&alacStream); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::ALAC, f.audioProperties()->codec()); } void testPropertiesAACWithoutLength() { ByteVector m4aData = PlainFile(TEST_FILE_PATH_C("no-tags.m4a")).readAll(); CPPUNIT_ASSERT_EQUAL(2898U, m4aData.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("mdhd"), m4aData.mid(1749, 4)); // Set the length to zero for (int offset = 1769; offset < 1773; ++offset) { m4aData[offset] = 0; } ByteVectorStream m4aStream(m4aData); MP4::File f(&m4aStream); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3707, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec()); } void testPropertiesM4V() { MP4::File f(TEST_FILE_PATH_C("blank_video.m4v")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(975, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(96, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isEncrypted()); CPPUNIT_ASSERT_EQUAL(MP4::Properties::AAC, f.audioProperties()->codec()); } void testCheckValid() { MP4::File f(TEST_FILE_PATH_C("empty.aiff")); CPPUNIT_ASSERT(!f.isValid()); } void testHasTag() { { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); } ScopedFileCopy copy("no-tags", ".m4a"); { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasMP4Tag()); f.tag()->setTitle("TITLE"); f.save(); } { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); } } void testIsEmpty() { MP4::Tag t1; CPPUNIT_ASSERT(t1.isEmpty()); t1.setArtist("Foo"); CPPUNIT_ASSERT(!t1.isEmpty()); MP4::Tag t2; t2.setItem("foo", "bar"); CPPUNIT_ASSERT(!t2.isEmpty()); } void testUpdateStco() { ScopedFileCopy copy("no-tags", ".3g2"); string filename = copy.fileName(); ByteVectorList data1; { MP4::File f(filename.c_str()); f.tag()->setArtist(ByteVector(3000, 'x')); MP4::Atoms a(&f); MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; f.seek(stco->offset() + 12); ByteVector data = f.readBlock(stco->length() - 12); unsigned int count = data.mid(0, 4).toUInt(); int pos = 4; while (count--) { unsigned int offset = data.mid(pos, 4).toUInt(); f.seek(offset); data1.append(f.readBlock(20)); pos += 4; } f.save(); } { MP4::File f(filename.c_str()); MP4::Atoms a(&f); MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; f.seek(stco->offset() + 12); ByteVector data = f.readBlock(stco->length() - 12); unsigned int count = data.mid(0, 4).toUInt(); int pos = 4, i = 0; while (count--) { unsigned int offset = data.mid(pos, 4).toUInt(); f.seek(offset); CPPUNIT_ASSERT_EQUAL(data1[i], f.readBlock(20)); pos += 4; i++; } } } void testFreeForm() { ScopedFileCopy copy("has-tags", ".m4a"); string filename = copy.fileName(); { MP4::File f(filename.c_str()); CPPUNIT_ASSERT(f.tag()->contains("----:com.apple.iTunes:iTunNORM")); f.tag()->setItem("----:org.kde.TagLib:Foo", StringList("Bar")); f.save(); } { MP4::File f(filename.c_str()); CPPUNIT_ASSERT(f.tag()->contains("----:org.kde.TagLib:Foo")); CPPUNIT_ASSERT_EQUAL(String("Bar"), f.tag()->item("----:org.kde.TagLib:Foo").toStringList().front()); f.save(); } } void testSaveExisingWhenIlstIsLast() { ScopedFileCopy copy("ilst-is-last", ".m4a"); string filename = copy.fileName(); { MP4::File f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(String("82,164"), f.tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front()); CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f.tag()->artist()); f.tag()->setComment("foo"); f.save(); } { MP4::File f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(String("82,164"), f.tag()->item("----:com.apple.iTunes:replaygain_track_minmax").toStringList().front()); CPPUNIT_ASSERT_EQUAL(String("Pearl Jam"), f.tag()->artist()); CPPUNIT_ASSERT_EQUAL(String("foo"), f.tag()->comment()); } } void test64BitAtom() { ScopedFileCopy copy("64bit", ".mp4"); string filename = copy.fileName(); { MP4::File f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemMap()["cpil"].toBool()); MP4::Atoms atoms(&f); MP4::Atom *moov = atoms.atoms()[0]; CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77), moov->length()); f.tag()->setItem("pgap", true); f.save(); } { MP4::File f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool()); CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("pgap").toBool()); MP4::Atoms atoms(&f); MP4::Atom *moov = atoms.atoms()[0]; // original size + 'pgap' size + padding CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(77 + 25 + 974), moov->length()); } } void testGnre() { MP4::File f(TEST_FILE_PATH_C("gnre.m4a")); CPPUNIT_ASSERT_EQUAL(TagLib::String("Ska"), f.tag()->genre()); } void testCovrRead() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); CPPUNIT_ASSERT(f.tag()->contains("covr")); MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), l.size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(79), l[0].data().size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(287), l[1].data().size()); } void testCovrWrite() { ScopedFileCopy copy("has-tags", ".m4a"); string filename = copy.fileName(); { MP4::File f(filename.c_str()); CPPUNIT_ASSERT(f.tag()->contains("covr")); MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); f.tag()->setItem("covr", l); f.save(); } { MP4::File f(filename.c_str()); CPPUNIT_ASSERT(f.tag()->contains("covr")); MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), l.size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(79), l[0].data().size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(287), l[1].data().size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[2].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), l[2].data().size()); } } void testCovrRead2() { MP4::File f(TEST_FILE_PATH_C("covr-junk.m4a")); CPPUNIT_ASSERT(f.tag()->contains("covr")); MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), l.size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(79), l[0].data().size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(287), l[1].data().size()); } void testProperties() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); PropertyMap tags = f.properties(); CPPUNIT_ASSERT_EQUAL(StringList("Test Artist"), tags["ARTIST"]); tags["TRACKNUMBER"] = StringList("2/4"); tags["DISCNUMBER"] = StringList("3/5"); tags["BPM"] = StringList("123"); tags["ARTIST"] = StringList("Foo Bar"); tags["COMPILATION"] = StringList("1"); tags["REMIXEDBY"] = StringList("Remixed by"); f.setProperties(tags); tags = f.properties(); CPPUNIT_ASSERT(f.tag()->contains("trkn")); CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("trkn").toIntPair().first); CPPUNIT_ASSERT_EQUAL(4, f.tag()->item("trkn").toIntPair().second); CPPUNIT_ASSERT_EQUAL(StringList("2/4"), tags["TRACKNUMBER"]); CPPUNIT_ASSERT(f.tag()->contains("disk")); CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("disk").toIntPair().first); CPPUNIT_ASSERT_EQUAL(5, f.tag()->item("disk").toIntPair().second); CPPUNIT_ASSERT_EQUAL(StringList("3/5"), tags["DISCNUMBER"]); CPPUNIT_ASSERT(f.tag()->contains("tmpo")); CPPUNIT_ASSERT_EQUAL(123, f.tag()->item("tmpo").toInt()); CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); CPPUNIT_ASSERT(f.tag()->contains("\251ART")); CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->item("\251ART").toStringList()); CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); CPPUNIT_ASSERT(f.tag()->contains("cpil")); CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("cpil").toBool()); CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]); CPPUNIT_ASSERT(f.tag()->contains("----:com.apple.iTunes:REMIXEDBY")); CPPUNIT_ASSERT_EQUAL(StringList("Remixed by"), f.tag()->item("----:com.apple.iTunes:REMIXEDBY").toStringList()); CPPUNIT_ASSERT_EQUAL(StringList("Remixed by"), tags["REMIXEDBY"]); tags["COMPILATION"] = StringList("0"); f.setProperties(tags); tags = f.properties(); CPPUNIT_ASSERT(f.tag()->contains("cpil")); CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("cpil").toBool()); CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]); // Empty properties do not result in access violations // when converting integers tags["TRACKNUMBER"] = StringList(); tags["DISCNUMBER"] = StringList(); tags["BPM"] = StringList(); tags["COMPILATION"] = StringList(); f.setProperties(tags); } void testPropertiesAllSupported() { PropertyMap tags; tags["ALBUM"] = StringList("Album"); tags["ALBUMARTIST"] = StringList("Album Artist"); tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); tags["ALBUMSORT"] = StringList("Album Sort"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTS"] = StringList("Artists"); tags["ARTISTSORT"] = StringList("Artist Sort"); tags["ASIN"] = StringList("ASIN"); tags["BARCODE"] = StringList("Barcode"); tags["BPM"] = StringList("123"); tags["CATALOGNUMBER"] = StringList("Catalog Number"); tags["COMMENT"] = StringList("Comment"); tags["COMPILATION"] = StringList("1"); tags["COMPOSER"] = StringList("Composer"); tags["COMPOSERSORT"] = StringList("Composer Sort"); tags["CONDUCTOR"] = StringList("Conductor"); tags["COPYRIGHT"] = StringList("2021 Copyright"); tags["DATE"] = StringList("2021-01-03 12:29:23"); tags["DISCNUMBER"] = StringList("3/5"); tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); tags["DJMIXER"] = StringList("DJ Mixer"); tags["ENCODEDBY"] = StringList("Encoded by"); tags["ENCODING"] = StringList("Encoding"); tags["ENGINEER"] = StringList("Engineer"); tags["GAPLESSPLAYBACK"] = StringList("1"); tags["GENRE"] = StringList("Genre"); tags["GROUPING"] = StringList("Grouping"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label"); tags["LANGUAGE"] = StringList("eng"); tags["LICENSE"] = StringList("License"); tags["LYRICIST"] = StringList("Lyricist"); tags["LYRICS"] = StringList("Lyrics"); tags["MEDIA"] = StringList("Media"); tags["MIXER"] = StringList("Mixer"); tags["MOOD"] = StringList("Mood"); tags["MOVEMENTCOUNT"] = StringList("3"); tags["MOVEMENTNAME"] = StringList("Movement Name"); tags["MOVEMENTNUMBER"] = StringList("2"); tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); tags["OWNER"] = StringList("Owner"); tags["PODCAST"] = StringList("1"); tags["PODCASTCATEGORY"] = StringList("Podcast Category"); tags["PODCASTDESC"] = StringList("Podcast Description"); tags["PODCASTID"] = StringList("Podcast ID"); tags["PODCASTURL"] = StringList("Podcast URL"); tags["PRODUCER"] = StringList("Producer"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["RELEASESTATUS"] = StringList("Release Status"); tags["RELEASETYPE"] = StringList("Release Type"); tags["REMIXER"] = StringList("Remixer"); tags["SCRIPT"] = StringList("Script"); tags["SHOWSORT"] = StringList("Show Sort"); tags["SHOWWORKMOVEMENT"] = StringList("1"); tags["SUBTITLE"] = StringList("Subtitle"); tags["TITLE"] = StringList("Title"); tags["TITLESORT"] = StringList("Title Sort"); tags["TRACKNUMBER"] = StringList("2/4"); tags["TVEPISODE"] = StringList("3"); tags["TVEPISODEID"] = StringList("TV Episode ID"); tags["TVNETWORK"] = StringList("TV Network"); tags["TVSEASON"] = StringList("2"); tags["TVSHOW"] = StringList("TV Show"); tags["WORK"] = StringList("Work"); ScopedFileCopy copy("no-tags", ".m4a"); { MP4::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT(properties.isEmpty()); f.setProperties(tags); f.save(); } { const MP4::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); } } void testPropertiesMovement() { MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); PropertyMap tags = f.properties(); tags["WORK"] = StringList("Foo"); tags["MOVEMENTNAME"] = StringList("Bar"); tags["MOVEMENTNUMBER"] = StringList("2"); tags["MOVEMENTCOUNT"] = StringList("3"); tags["SHOWWORKMOVEMENT"] = StringList("1"); f.setProperties(tags); tags = f.properties(); CPPUNIT_ASSERT(f.tag()->contains("\251wrk")); CPPUNIT_ASSERT_EQUAL(StringList("Foo"), f.tag()->item("\251wrk").toStringList()); CPPUNIT_ASSERT_EQUAL(StringList("Foo"), tags["WORK"]); CPPUNIT_ASSERT(f.tag()->contains("\251mvn")); CPPUNIT_ASSERT_EQUAL(StringList("Bar"), f.tag()->item("\251mvn").toStringList()); CPPUNIT_ASSERT_EQUAL(StringList("Bar"), tags["MOVEMENTNAME"]); CPPUNIT_ASSERT(f.tag()->contains("\251mvi")); CPPUNIT_ASSERT_EQUAL(2, f.tag()->item("\251mvi").toInt()); CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["MOVEMENTNUMBER"]); CPPUNIT_ASSERT(f.tag()->contains("\251mvc")); CPPUNIT_ASSERT_EQUAL(3, f.tag()->item("\251mvc").toInt()); CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["MOVEMENTCOUNT"]); CPPUNIT_ASSERT(f.tag()->contains("shwm")); CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("shwm").toBool()); CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["SHOWWORKMOVEMENT"]); tags["SHOWWORKMOVEMENT"] = StringList("0"); f.setProperties(tags); tags = f.properties(); CPPUNIT_ASSERT(f.tag()->contains("shwm")); CPPUNIT_ASSERT_EQUAL(false, f.tag()->item("shwm").toBool()); CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["SHOWWORKMOVEMENT"]); tags["WORK"] = StringList(); tags["MOVEMENTNAME"] = StringList(); tags["MOVEMENTNUMBER"] = StringList(); tags["MOVEMENTCOUNT"] = StringList(); tags["SHOWWORKMOVEMENT"] = StringList(); f.setProperties(tags); } void testFuzzedFile() { MP4::File f(TEST_FILE_PATH_C("infloop.m4a")); CPPUNIT_ASSERT(f.isValid()); } void testRepeatedSave() { ScopedFileCopy copy("no-tags", ".m4a"); MP4::File f(copy.fileName().c_str()); f.tag()->setTitle("0123456789"); f.save(); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2862), f.find("0123456789")); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(-1), f.find("0123456789", 2863)); } void testWithZeroLengthAtom() { MP4::File f(TEST_FILE_PATH_C("zero-length-mdat.m4a")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(1115, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(22050, f.audioProperties()->sampleRate()); } void testEmptyValuesRemoveItems() { const MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); MP4::Tag *tag = f.tag(); const String testTitle("Title"); const String testArtist("Artist"); const String testAlbum("Album"); const String testComment("Comment"); const String testGenre("Genre"); const String nullString; constexpr unsigned int testYear = 2020; constexpr unsigned int testTrack = 1; constexpr unsigned int zeroUInt = 0; tag->setTitle(testTitle); CPPUNIT_ASSERT_EQUAL(testTitle, tag->title()); CPPUNIT_ASSERT(tag->contains("\251nam")); tag->setArtist(testArtist); CPPUNIT_ASSERT_EQUAL(testArtist, tag->artist()); CPPUNIT_ASSERT(tag->contains("\251ART")); tag->setAlbum(testAlbum); CPPUNIT_ASSERT_EQUAL(testAlbum, tag->album()); CPPUNIT_ASSERT(tag->contains("\251alb")); tag->setComment(testComment); CPPUNIT_ASSERT_EQUAL(testComment, tag->comment()); CPPUNIT_ASSERT(tag->contains("\251cmt")); tag->setGenre(testGenre); CPPUNIT_ASSERT_EQUAL(testGenre, tag->genre()); CPPUNIT_ASSERT(tag->contains("\251gen")); tag->setYear(testYear); CPPUNIT_ASSERT_EQUAL(testYear, tag->year()); CPPUNIT_ASSERT(tag->contains("\251day")); tag->setTrack(testTrack); CPPUNIT_ASSERT_EQUAL(testTrack, tag->track()); CPPUNIT_ASSERT(tag->contains("trkn")); tag->setTitle(nullString); CPPUNIT_ASSERT_EQUAL(nullString, tag->title()); CPPUNIT_ASSERT(!tag->contains("\251nam")); tag->setArtist(nullString); CPPUNIT_ASSERT_EQUAL(nullString, tag->artist()); CPPUNIT_ASSERT(!tag->contains("\251ART")); tag->setAlbum(nullString); CPPUNIT_ASSERT_EQUAL(nullString, tag->album()); CPPUNIT_ASSERT(!tag->contains("\251alb")); tag->setComment(nullString); CPPUNIT_ASSERT_EQUAL(nullString, tag->comment()); CPPUNIT_ASSERT(!tag->contains("\251cmt")); tag->setGenre(nullString); CPPUNIT_ASSERT_EQUAL(nullString, tag->genre()); CPPUNIT_ASSERT(!tag->contains("\251gen")); tag->setYear(zeroUInt); CPPUNIT_ASSERT_EQUAL(zeroUInt, tag->year()); CPPUNIT_ASSERT(!tag->contains("\251day")); tag->setTrack(zeroUInt); CPPUNIT_ASSERT_EQUAL(zeroUInt, tag->track()); CPPUNIT_ASSERT(!tag->contains("trkn")); } void testRemoveMetadata() { ScopedFileCopy copy("no-tags", ".m4a"); { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasMP4Tag()); MP4::Tag *tag = f.tag(); CPPUNIT_ASSERT(tag->isEmpty()); tag->setTitle("TITLE"); f.save(); } { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); CPPUNIT_ASSERT(!f.tag()->isEmpty()); f.strip(); } { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasMP4Tag()); CPPUNIT_ASSERT(f.tag()->isEmpty()); CPPUNIT_ASSERT(fileEqual( copy.fileName(), testFilePath("no-tags.m4a"))); } } void testNonFullMetaAtom() { { MP4::File f(TEST_FILE_PATH_C("non-full-meta.m4a")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); CPPUNIT_ASSERT(f.tag()->contains("covr")); MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), l.size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(79), l[0].data().size()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(287), l[1].data().size()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT_EQUAL(StringList("Test Artist!!!!"), properties["ARTIST"]); CPPUNIT_ASSERT_EQUAL(StringList("FAAC 1.24"), properties["ENCODING"]); } } void testItemFactory() { ScopedFileCopy copy("no-tags", ".m4a"); { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasMP4Tag()); MP4::Tag *tag = f.tag(); tag->setItem("tsti", MP4::Item(123)); tag->setItem("tstt", MP4::Item(StringList("Test text"))); f.save(); } { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); MP4::Tag *tag = f.tag(); // Without a custom item factory, only custom text atoms with four // letter names are possible. MP4::Item item = tag->item("tsti"); CPPUNIT_ASSERT(!item.isValid()); CPPUNIT_ASSERT(item.toInt() != 123); item = tag->item("tstt"); CPPUNIT_ASSERT(item.isValid()); CPPUNIT_ASSERT_EQUAL(StringList("Test text"), item.toStringList()); f.strip(); } { MP4::File f(copy.fileName().c_str(), true, MP4::Properties::Average, CustomItemFactory::instance()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasMP4Tag()); MP4::Tag *tag = f.tag(); tag->setItem("tsti", MP4::Item(123)); tag->setItem("tstt", MP4::Item(StringList("Test text"))); tag->setItem("trkn", MP4::Item(2, 10)); tag->setItem("rate", MP4::Item(80)); tag->setItem("plID", MP4::Item(1540934238LL)); tag->setItem("rtng", MP4::Item(static_cast<unsigned char>(2))); f.save(); } { MP4::File f(copy.fileName().c_str(), true, MP4::Properties::Average, CustomItemFactory::instance()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); MP4::Tag *tag = f.tag(); MP4::Item item = tag->item("tsti"); CPPUNIT_ASSERT(item.isValid()); CPPUNIT_ASSERT_EQUAL(123, item.toInt()); item = tag->item("tstt"); CPPUNIT_ASSERT(item.isValid()); CPPUNIT_ASSERT_EQUAL(StringList("Test text"), item.toStringList()); item = tag->item("trkn"); CPPUNIT_ASSERT(item.isValid()); CPPUNIT_ASSERT_EQUAL(2, item.toIntPair().first); CPPUNIT_ASSERT_EQUAL(10, item.toIntPair().second); CPPUNIT_ASSERT_EQUAL(80, tag->item("rate").toInt()); CPPUNIT_ASSERT_EQUAL(1540934238LL, tag->item("plID").toLongLong()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(2), tag->item("rtng").toByte()); PropertyMap properties = tag->properties(); CPPUNIT_ASSERT_EQUAL(StringList("123"), properties.value("TESTINTEGER")); CPPUNIT_ASSERT_EQUAL(StringList("2/10"), properties.value("TRACKNUMBER")); properties["TESTINTEGER"] = StringList("456"); tag->setProperties(properties); f.save(); } { MP4::File f(copy.fileName().c_str(), true, MP4::Properties::Average, CustomItemFactory::instance()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); MP4::Tag *tag = f.tag(); MP4::Item item = tag->item("tsti"); CPPUNIT_ASSERT(item.isValid()); CPPUNIT_ASSERT_EQUAL(456, item.toInt()); PropertyMap properties = tag->properties(); CPPUNIT_ASSERT_EQUAL(StringList("456"), properties.value("TESTINTEGER")); } } void testNonPrintableAtom() { ScopedFileCopy copy("nonprintable-atom-type", ".m4a"); { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(32000, f.audioProperties()->sampleRate()); f.tag()->setTitle("TITLE"); f.save(); } { MP4::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasMP4Tag()); CPPUNIT_ASSERT_EQUAL(String("TITLE"), f.tag()->title()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_mp4coverart.cpp�������������������������������������������������������������0000664�0000000�0000000�00000005720�14662262111�0020135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tag.h" #include "mp4coverart.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestMP4CoverArt : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMP4CoverArt); CPPUNIT_TEST(testSimple); CPPUNIT_TEST(testList); CPPUNIT_TEST_SUITE_END(); public: void testSimple() { MP4::CoverArt c(MP4::CoverArt::PNG, "foo"); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c.format()); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c.data()); MP4::CoverArt c2(c); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c2.format()); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c2.data()); MP4::CoverArt c3 = c; CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, c3.format()); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), c3.data()); } void testList() { MP4::CoverArtList l; l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar")); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4CoverArt); ������������������������������������������������taglib-2.0.2/tests/test_mp4item.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000014632�14662262111�0017250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tag.h" #include "mp4coverart.h" #include "mp4item.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestMP4Item : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMP4Item); CPPUNIT_TEST(testCoverArtList); CPPUNIT_TEST(testItemOperations); CPPUNIT_TEST_SUITE_END(); public: void testCoverArtList() { MP4::CoverArtList l; l.append(MP4::CoverArt(MP4::CoverArt::PNG, "foo")); l.append(MP4::CoverArt(MP4::CoverArt::JPEG, "bar")); MP4::Item i(l); MP4::CoverArtList l2 = i.toCoverArtList(); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format()); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), l[0].data()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format()); CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data()); } void testItemOperations() { MP4::Item e; MP4::Item i1(1); MP4::Item i2(1); MP4::Item i3(-1); MP4::Item c1(static_cast<unsigned char>('A')); MP4::Item c2(static_cast<unsigned char>('A')); MP4::Item c3(static_cast<unsigned char>('Z')); MP4::Item u1(2U); MP4::Item u2(2U); MP4::Item u3(0U); MP4::Item l1(3LL); MP4::Item l2(3LL); MP4::Item l3(-7LL); MP4::Item b1(true); MP4::Item b2(true); MP4::Item b3(false); MP4::Item p1(4, 5); MP4::Item p2(4, 5); MP4::Item p3(-4, -5); MP4::Item s1(StringList{"abc", "de"}); MP4::Item s2(StringList{"abc", "de"}); MP4::Item s3(StringList{"abc"}); MP4::Item v1(ByteVectorList{"f", "gh"}); MP4::Item v2(ByteVectorList{"f", "gh"}); MP4::Item v3(ByteVectorList{}); MP4::Item a1(MP4::CoverArtList{ MP4::CoverArt(MP4::CoverArt::PNG, "foo"), MP4::CoverArt(MP4::CoverArt::JPEG, "bar") }); MP4::Item a2(MP4::CoverArtList{ MP4::CoverArt(MP4::CoverArt::PNG, "foo"), MP4::CoverArt(MP4::CoverArt::JPEG, "bar") }); MP4::Item a3(MP4::CoverArtList{ MP4::CoverArt(MP4::CoverArt::JPEG, "bar") }); CPPUNIT_ASSERT(i1 == i2); CPPUNIT_ASSERT(i2 != i3); CPPUNIT_ASSERT(i3 != c1); CPPUNIT_ASSERT(c1 == c1); CPPUNIT_ASSERT(c1 == c2); CPPUNIT_ASSERT(c2 != c3); CPPUNIT_ASSERT(c3 != u1); CPPUNIT_ASSERT(u1 == u2); CPPUNIT_ASSERT(u2 != u3); CPPUNIT_ASSERT(u3 != l1); CPPUNIT_ASSERT(l1 == l2); CPPUNIT_ASSERT(l2 != l3); CPPUNIT_ASSERT(l3 != b1); CPPUNIT_ASSERT(b1 == b2); CPPUNIT_ASSERT(b2 != b3); CPPUNIT_ASSERT(b3 != p1); CPPUNIT_ASSERT(p1 == p2); CPPUNIT_ASSERT(p2 != p3); CPPUNIT_ASSERT(p3 != s1); CPPUNIT_ASSERT(s1 == s2); CPPUNIT_ASSERT(s2 != s3); CPPUNIT_ASSERT(s3 != v1); CPPUNIT_ASSERT(v1 == v2); CPPUNIT_ASSERT(v2 != v3); CPPUNIT_ASSERT(v3 != a1); CPPUNIT_ASSERT(a1 == a2); CPPUNIT_ASSERT(a2 != a3); CPPUNIT_ASSERT(a3 != e); CPPUNIT_ASSERT(!e.isValid()); CPPUNIT_ASSERT(i1.isValid()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Void, e.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Int, i1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Byte, c1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::UInt, u1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::LongLong, l1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Bool, b1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::IntPair, p1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::StringList, s1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::ByteVectorList, v1.type()); CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::CoverArtList, a1.type()); CPPUNIT_ASSERT_EQUAL(1, i1.toInt()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>('A'), c1.toByte()); CPPUNIT_ASSERT_EQUAL(2U, u1.toUInt()); CPPUNIT_ASSERT_EQUAL(3LL, l1.toLongLong()); CPPUNIT_ASSERT_EQUAL(true, b1.toBool()); CPPUNIT_ASSERT_EQUAL(4, p1.toIntPair().first); CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s1.toStringList()); CPPUNIT_ASSERT_EQUAL((ByteVectorList{"f", "gh"}), v1.toByteVectorList()); CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, a1.toCoverArtList().front().format()); s3.swap(s1); CPPUNIT_ASSERT_EQUAL((StringList{"abc"}), s1.toStringList()); CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s3.toStringList()); CPPUNIT_ASSERT_EQUAL(MP4::AtomDataType::TypeUndefined, s1.atomDataType()); s1.setAtomDataType(MP4::AtomDataType::TypeUTF8); CPPUNIT_ASSERT_EQUAL(MP4::AtomDataType::TypeUTF8, s1.atomDataType()); s1 = s3; CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s1.toStringList()); MP4::ItemMap m1{{"key1", i1}, {"key2", p1}}; MP4::ItemMap m2{{"key1", i2}, {"key2", p2}}; MP4::ItemMap m3{{"key1", i2}, {"key2", p3}}; CPPUNIT_ASSERT(m1 == m2); CPPUNIT_ASSERT(m1 != m3); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4Item); ������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_mpc.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000015722�14662262111�0016451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tbytevectorlist.h" #include "tpropertymap.h" #include "apetag.h" #include "id3v1tag.h" #include "mpcfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestMPC : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMPC); CPPUNIT_TEST(testPropertiesSV8); CPPUNIT_TEST(testPropertiesSV7); CPPUNIT_TEST(testPropertiesSV5); CPPUNIT_TEST(testPropertiesSV4); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST(testFuzzedFile3); CPPUNIT_TEST(testFuzzedFile4); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: void testPropertiesSV8() { MPC::File f(TEST_FILE_PATH_C("sv8_header.mpc")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->mpcVersion()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(1497, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(66014UL, f.audioProperties()->sampleFrames()); } void testPropertiesSV7() { MPC::File f(TEST_FILE_PATH_C("click.mpc")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->mpcVersion()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(40, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(318, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1760UL, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(14221, f.audioProperties()->trackGain()); CPPUNIT_ASSERT_EQUAL(19848, f.audioProperties()->trackPeak()); CPPUNIT_ASSERT_EQUAL(14221, f.audioProperties()->albumGain()); CPPUNIT_ASSERT_EQUAL(19848, f.audioProperties()->albumPeak()); } void testPropertiesSV5() { MPC::File f(TEST_FILE_PATH_C("sv5_header.mpc")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(5, f.audioProperties()->mpcVersion()); CPPUNIT_ASSERT_EQUAL(26, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(26371, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1162944UL, f.audioProperties()->sampleFrames()); } void testPropertiesSV4() { MPC::File f(TEST_FILE_PATH_C("sv4_header.mpc")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->mpcVersion()); CPPUNIT_ASSERT_EQUAL(26, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(26371, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(1162944UL, f.audioProperties()->sampleFrames()); } void testFuzzedFile1() { MPC::File f(TEST_FILE_PATH_C("zerodiv.mpc")); CPPUNIT_ASSERT(f.isValid()); } void testFuzzedFile2() { MPC::File f(TEST_FILE_PATH_C("infloop.mpc")); CPPUNIT_ASSERT(f.isValid()); } void testFuzzedFile3() { MPC::File f(TEST_FILE_PATH_C("segfault.mpc")); CPPUNIT_ASSERT(f.isValid()); } void testFuzzedFile4() { MPC::File f(TEST_FILE_PATH_C("segfault2.mpc")); CPPUNIT_ASSERT(f.isValid()); } void testStripAndProperties() { ScopedFileCopy copy("click", ".mpc"); { MPC::File f(copy.fileName().c_str()); f.APETag(true)->setTitle("APE"); f.ID3v1Tag(true)->setTitle("ID3v1"); f.save(); } { MPC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); f.strip(MPC::File::APE); CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); f.strip(MPC::File::ID3v1); CPPUNIT_ASSERT(f.properties().isEmpty()); f.save(); } { MPC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(f.properties()["TITLE"].isEmpty()); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testRepeatedSave() { ScopedFileCopy copy("click", ".mpc"); { MPC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); f.APETag()->setTitle("0"); f.save(); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); f.save(); } { MPC::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasAPETag()); CPPUNIT_ASSERT(f.hasID3v1Tag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC); ����������������������������������������������taglib-2.0.2/tests/test_mpeg.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000062056�14662262111�0016624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include <array> #include "tstring.h" #include "tpropertymap.h" #include "mpegfile.h" #include "id3v2tag.h" #include "id3v1tag.h" #include "apetag.h" #include "mpegproperties.h" #include "xingheader.h" #include "mpegheader.h" #include "id3v2extendedheader.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestMPEG : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMPEG); CPPUNIT_TEST(testAudioPropertiesXingHeaderCBR); CPPUNIT_TEST(testAudioPropertiesXingHeaderVBR); CPPUNIT_TEST(testAudioPropertiesVBRIHeader); CPPUNIT_TEST(testAudioPropertiesNoVBRHeaders); CPPUNIT_TEST(testAudioPropertiesADTS); CPPUNIT_TEST(testSkipInvalidFrames1); CPPUNIT_TEST(testSkipInvalidFrames2); CPPUNIT_TEST(testSkipInvalidFrames3); CPPUNIT_TEST(testVersion2DurationWithXingHeader); CPPUNIT_TEST(testSaveID3v24); CPPUNIT_TEST(testSaveID3v23); CPPUNIT_TEST(testDuplicateID3v2); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testFrameOffset); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testProperties); CPPUNIT_TEST(testRepeatedSave1); CPPUNIT_TEST(testRepeatedSave2); CPPUNIT_TEST(testRepeatedSave3); CPPUNIT_TEST(testEmptyID3v2); CPPUNIT_TEST(testEmptyID3v1); CPPUNIT_TEST(testEmptyAPE); CPPUNIT_TEST(testIgnoreGarbage); CPPUNIT_TEST(testExtendedHeader); CPPUNIT_TEST(testReadStyleFast); CPPUNIT_TEST(testID3v22Properties); CPPUNIT_TEST_SUITE_END(); public: void testAudioPropertiesXingHeaderCBR() { MPEG::File f(TEST_FILE_PATH_C("lame_cbr.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(1887, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(1887164, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesXingHeaderVBR() { MPEG::File f(TEST_FILE_PATH_C("lame_vbr.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(1887, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(1887164, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(70, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesVBRIHeader() { MPEG::File f(TEST_FILE_PATH_C("rare_frames.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(222, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(222198, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(233, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::VBRI, f.audioProperties()->xingHeader()->type()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testAudioPropertiesNoVBRHeaders() { MPEG::File f(TEST_FILE_PATH_C("bladeenc.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3553, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); const offset_t last = f.lastFrameOffset(); const MPEG::Header lastHeader(&f, last, false); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(28213), last); CPPUNIT_ASSERT_EQUAL(209, lastHeader.frameLength()); } void testAudioPropertiesADTS() { constexpr std::array readStyles = { MPEG::Properties::Fast, MPEG::Properties::Average, MPEG::Properties::Accurate }; for(auto readStyle : readStyles) { MPEG::File f(TEST_FILE_PATH_C("empty1s.aac"), true, readStyle); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1176, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(readStyle == MPEG::Properties::Fast ? 0 : 1, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(MPEG::Header::FrontCenter, f.audioProperties()->channelConfiguration()); CPPUNIT_ASSERT_EQUAL(11025, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); CPPUNIT_ASSERT(f.audioProperties()->isADTS()); const offset_t last = f.lastFrameOffset(); const MPEG::Header lastHeader(&f, last, false); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(136), last); CPPUNIT_ASSERT_EQUAL(11, lastHeader.frameLength()); } } void testSkipInvalidFrames1() { MPEG::File f(TEST_FILE_PATH_C("invalid-frames1.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(392, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(160, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testSkipInvalidFrames2() { MPEG::File f(TEST_FILE_PATH_C("invalid-frames2.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(314, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(192, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testSkipInvalidFrames3() { MPEG::File f(TEST_FILE_PATH_C("invalid-frames3.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(183, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(320, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); CPPUNIT_ASSERT(!f.audioProperties()->isADTS()); } void testVersion2DurationWithXingHeader() { MPEG::File f(TEST_FILE_PATH_C("mpeg2.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(5387, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(5387285, f.audioProperties()->lengthInMilliseconds()); } void testSaveID3v24() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); String xxx = ByteVector(254, 'X'); { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v4); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); } { MPEG::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), f2.ID3v2Tag()->header()->majorVersion()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); } } void testSaveID3v23() { ScopedFileCopy copy("xing", ".mp3"); string newname = copy.fileName(); String xxx = ByteVector(254, 'X'); { MPEG::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(MPEG::File::AllTags, File::StripOthers, ID3v2::v3); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); } { MPEG::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f2.ID3v2Tag()->header()->majorVersion()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); } } void testDuplicateID3v2() { MPEG::File f(TEST_FILE_PATH_C("duplicate_id3v2.mp3")); // duplicate_id3v2.mp3 has duplicate ID3v2 tags. // Sample rate will be 32000 if can't skip the second tag. CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); } void testFuzzedFile() { MPEG::File f(TEST_FILE_PATH_C("excessive_alloc.mp3")); CPPUNIT_ASSERT(f.isValid()); } void testFrameOffset() { { MPEG::File f(TEST_FILE_PATH_C("ape.mp3")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0000), f.firstFrameOffset()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x1FD6), f.lastFrameOffset()); } { MPEG::File f(TEST_FILE_PATH_C("ape-id3v1.mp3")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0000), f.firstFrameOffset()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x1FD6), f.lastFrameOffset()); } { MPEG::File f(TEST_FILE_PATH_C("ape-id3v2.mp3")); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x041A), f.firstFrameOffset()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x23F0), f.lastFrameOffset()); } } void testStripAndProperties() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("ID3v2"); f.APETag(true)->setTitle("APE"); f.ID3v1Tag(true)->setTitle("ID3v1"); f.save(); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); f.strip(MPEG::File::ID3v2); CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); f.strip(MPEG::File::APE); CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); f.strip(MPEG::File::ID3v1); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testProperties() { PropertyMap tags; tags["ALBUM"] = StringList("Album"); tags["ALBUMARTIST"] = StringList("Album Artist"); tags["ALBUMARTISTSORT"] = StringList("Album Artist Sort"); tags["ALBUMSORT"] = StringList("Album Sort"); tags["ARRANGER"] = StringList("Arranger"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTSORT"] = StringList("Artist Sort"); tags["ARTISTWEBPAGE"] = StringList("Artist Web Page"); tags["ASIN"] = StringList("ASIN"); tags["AUDIOSOURCEWEBPAGE"] = StringList("Audio Source Web Page"); tags["BARCODE"] = StringList("Barcode"); tags["BPM"] = StringList("123"); tags["CATALOGNUMBER"] = StringList("Catalog Number"); tags["COMMENT"] = StringList("Comment"); tags["COMMENT:CDESC"] = StringList("Comment with Description"); tags["COMPILATION"] = StringList("1"); tags["COMPOSER"] = StringList("Composer"); tags["COMPOSERSORT"] = StringList("Composer Sort"); tags["CONDUCTOR"] = StringList("Conductor"); tags["WORK"] = StringList("Content Group"); tags["COPYRIGHT"] = StringList("2021 Copyright"); tags["COPYRIGHTURL"] = StringList("Copyright URL"); tags["DATE"] = StringList("2021-01-03 12:29:23"); tags["DISCNUMBER"] = StringList("3/5"); tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); tags["DJMIXER"] = StringList("DJ Mixer"); tags["ENCODEDBY"] = StringList("Encoded by"); tags["ENCODING"] = StringList("Encoding"); tags["ENCODINGTIME"] = StringList("2021-01-03 13:48:44"); tags["ENGINEER"] = StringList("Engineer"); tags["FILETYPE"] = StringList("File Type"); tags["FILEWEBPAGE"] = StringList("File Web Page"); tags["GENRE"] = StringList("Genre"); tags["GROUPING"] = StringList("Grouping"); tags["INITIALKEY"] = StringList("Dbm"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label"); tags["LANGUAGE"] = StringList("eng"); tags["LENGTH"] = StringList("1234"); tags["LYRICIST"] = StringList("Lyricist"); tags["LYRICS:LDESC"] = StringList("Lyrics"); tags["MEDIA"] = StringList("Media"); tags["MIXER"] = StringList("Mixer"); tags["MOOD"] = StringList("Mood"); tags["MOVEMENTNAME"] = StringList("Movement Name"); tags["MOVEMENTNUMBER"] = StringList("2"); tags["MUSICBRAINZ_ALBUMID"] = StringList("MusicBrainz_AlbumID"); tags["MUSICBRAINZ_ALBUMARTISTID"] = StringList("MusicBrainz_AlbumartistID"); tags["MUSICBRAINZ_ARTISTID"] = StringList("MusicBrainz_ArtistID"); tags["MUSICBRAINZ_RELEASEGROUPID"] = StringList("MusicBrainz_ReleasegroupID"); tags["MUSICBRAINZ_RELEASETRACKID"] = StringList("MusicBrainz_ReleasetrackID"); tags["MUSICBRAINZ_TRACKID"] = StringList("MusicBrainz_TrackID"); tags["MUSICBRAINZ_WORKID"] = StringList("MusicBrainz_WorkID"); tags["ORIGINALALBUM"] = StringList("Original Album"); tags["ORIGINALARTIST"] = StringList("Original Artist"); tags["ORIGINALDATE"] = StringList("2021-01-03 13:52:19"); tags["ORIGINALFILENAME"] = StringList("Original Filename"); tags["ORIGINALLYRICIST"] = StringList("Original Lyricist"); tags["OWNER"] = StringList("Owner"); tags["PAYMENTWEBPAGE"] = StringList("Payment Web Page"); tags["PERFORMER:DRUMS"] = StringList("Drummer"); tags["PERFORMER:GUITAR"] = StringList("Guitarist"); tags["PLAYLISTDELAY"] = StringList("10"); tags["PODCAST"] = StringList(); tags["PODCASTCATEGORY"] = StringList("Podcast Category"); tags["PODCASTDESC"] = StringList("Podcast Description"); tags["PODCASTID"] = StringList("Podcast ID"); tags["PODCASTURL"] = StringList("Podcast URL"); tags["PRODUCEDNOTICE"] = StringList("2021 Produced Notice"); tags["PRODUCER"] = StringList("Producer"); tags["PUBLISHERWEBPAGE"] = StringList("Publisher Web Page"); tags["RADIOSTATION"] = StringList("Radio Station"); tags["RADIOSTATIONOWNER"] = StringList("Radio Station Owner"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["RELEASESTATUS"] = StringList("Release Status"); tags["RELEASETYPE"] = StringList("Release Type"); tags["REMIXER"] = StringList("Remixer"); tags["SCRIPT"] = StringList("Script"); tags["SUBTITLE"] = StringList("Subtitle"); tags["TITLE"] = StringList("Title"); tags["TITLESORT"] = StringList("Title Sort"); tags["TRACKNUMBER"] = StringList("2/4"); tags["URL:UDESC"] = StringList("URL"); ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT(properties.isEmpty()); f.setProperties(tags); f.save(); } { const MPEG::File f(copy.fileName().c_str()); PropertyMap properties = f.properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); } } void testRepeatedSave1() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle(std::string(4096, 'X').c_str()); f.save(); } { MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle(""); f.save(); f.ID3v2Tag(true)->setTitle(std::string(4096, 'X').c_str()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(5141), f.firstFrameOffset()); } } void testRepeatedSave2() { ScopedFileCopy copy("xing", ".mp3"); MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("0123456789"); f.save(); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(-1), f.find("ID3", 3)); } void testRepeatedSave3() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); f.APETag()->setTitle("0"); f.save(); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); f.save(); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasAPETag()); CPPUNIT_ASSERT(f.hasID3v1Tag()); } } void testEmptyID3v2() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("0123456789"); f.save(MPEG::File::ID3v2); } { MPEG::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle(""); f.save(MPEG::File::ID3v2, File::StripNone); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } } void testEmptyID3v1() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); f.ID3v1Tag(true)->setTitle("0123456789"); f.save(MPEG::File::ID3v1); } { MPEG::File f(copy.fileName().c_str()); f.ID3v1Tag(true)->setTitle(""); f.save(MPEG::File::ID3v1, File::StripNone); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); } } void testEmptyAPE() { ScopedFileCopy copy("xing", ".mp3"); { MPEG::File f(copy.fileName().c_str()); f.APETag(true)->setTitle("0123456789"); f.save(MPEG::File::APE); } { MPEG::File f(copy.fileName().c_str()); f.APETag(true)->setTitle(""); f.save(MPEG::File::APE, File::StripNone); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); } } void testIgnoreGarbage() { const ScopedFileCopy copy("garbage", ".mp3"); { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2255), f.firstFrameOffset()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(6015), f.lastFrameOffset()); CPPUNIT_ASSERT_EQUAL(String("Title A"), f.ID3v2Tag()->title()); f.ID3v2Tag()->setTitle("Title B"); f.save(); } { MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("Title B"), f.ID3v2Tag()->title()); } } void testExtendedHeader() { const ScopedFileCopy copy("extended-header", ".mp3"); MPEG::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); ID3v2::Tag *tag = f.ID3v2Tag(); ID3v2::ExtendedHeader *ext = tag->extendedHeader(); CPPUNIT_ASSERT(ext); CPPUNIT_ASSERT_EQUAL(12U, ext->size()); CPPUNIT_ASSERT_EQUAL(String("Druids"), tag->title()); CPPUNIT_ASSERT_EQUAL(String("Excelsis"), tag->artist()); CPPUNIT_ASSERT_EQUAL(String("Vo Chrieger U Drache"), tag->album()); CPPUNIT_ASSERT_EQUAL(2013U, tag->year()); CPPUNIT_ASSERT_EQUAL(String("Folk/Power Metal"), tag->genre()); CPPUNIT_ASSERT_EQUAL(3U, tag->track()); CPPUNIT_ASSERT_EQUAL(String("2013"), f.properties().value("ORIGINALDATE").front()); } void testReadStyleFast() { const ScopedFileCopy copy("lame_cbr", ".mp3"); { MPEG::File f(copy.fileName().c_str(), true, MPEG::Properties::Fast); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(1887, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(1887164, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(""), f.ID3v2Tag()->title()); PropertyMap properties = f.properties(); CPPUNIT_ASSERT_EQUAL(String("-1.020000 dB"), properties.value("REPLAYGAIN_TRACK_GAIN").front()); CPPUNIT_ASSERT_EQUAL(String("0.920032"), properties.value("REPLAYGAIN_TRACK_PEAK").front()); properties["TITLE"] = String("A Title"); properties["Artist"] = String("An Artist"); f.setProperties(properties); f.save(); } { MPEG::File f(copy.fileName().c_str(), true, MPEG::Properties::Fast); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("A Title"), f.ID3v2Tag()->title()); CPPUNIT_ASSERT_EQUAL(String("An Artist"), f.ID3v2Tag()->artist()); } { MPEG::File f(TEST_FILE_PATH_C("garbage.mp3"), true, MPEG::Properties::Fast); CPPUNIT_ASSERT(f.isValid()); // Garbage prevents detection of ID3v2 with fast read style CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2255), f.firstFrameOffset()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(6015), f.lastFrameOffset()); } } void testID3v22Properties() { ScopedFileCopy copy("itunes10", ".mp3"); MPEG::File f(copy.fileName().c_str()); PropertyMap expectedProperties(SimplePropertyMap{ {"ALBUM", {"Album"}}, {"ALBUMARTIST", {"Album Artist"}}, {"ALBUMARTISTSORT", {"Sort Album Artist"}}, {"ALBUMSORT", {"Sort Album"}}, {"ARTIST", {"Artist"}}, {"ARTISTSORT", {"Sort Artist"}}, {"BPM", {"180"}}, {"COMMENT", {"Comments"}}, {"COMMENT:ITUNPGAP", {"1"}}, {"COMPILATION", {"1"}}, {"COMPOSER", {"Composer"}}, {"COMPOSERSORT", {"Sort Composer"}}, {"DATE", {"2011"}}, {"DISCNUMBER", {"1/2"}}, {"GENRE", {"Heavy Metal"}}, {"LYRICS", {"Lyrics"}}, {"SUBTITLE", {"Description"}}, {"TITLE", {"iTunes10MP3"}}, {"TITLESORT", {"Sort Name"}}, {"TRACKNUMBER", {"1/10"}}, {"WORK", {"Grouping"}} }); expectedProperties.addUnsupportedData("APIC"); expectedProperties.addUnsupportedData("UNKNOWN/RVA"); PropertyMap properties = f.properties(); if (expectedProperties != properties) { CPPUNIT_ASSERT_EQUAL(expectedProperties.toString(), properties.toString()); } CPPUNIT_ASSERT(expectedProperties == properties); const String PICTURE_KEY("PICTURE"); CPPUNIT_ASSERT_EQUAL(StringList(PICTURE_KEY), f.complexPropertyKeys()); auto pictures = f.complexProperties(PICTURE_KEY); CPPUNIT_ASSERT_EQUAL(1U, pictures.size()); auto picture = pictures.front(); CPPUNIT_ASSERT_EQUAL(String("image/png"), picture.value("mimeType").toString()); CPPUNIT_ASSERT(picture.value("description").toString().isEmpty()); CPPUNIT_ASSERT_EQUAL(String("Other"), picture.value("pictureType").toString()); auto data = picture.value("data").toByteVector(); CPPUNIT_ASSERT(data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")); CPPUNIT_ASSERT_EQUAL(2315U, data.size()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_ogg.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000021146�14662262111�0016443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tag.h" #include "tstringlist.h" #include "tpropertymap.h" #include "oggfile.h" #include "vorbisfile.h" #include "oggpageheader.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestOGG : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestOGG); CPPUNIT_TEST(testSimple); CPPUNIT_TEST(testSplitPackets1); CPPUNIT_TEST(testSplitPackets2); CPPUNIT_TEST(testDictInterface1); CPPUNIT_TEST(testDictInterface2); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testPageChecksum); CPPUNIT_TEST(testPageGranulePosition); CPPUNIT_TEST_SUITE_END(); public: void testSimple() { ScopedFileCopy copy("empty", ".ogg"); string newname = copy.fileName(); { Vorbis::File f(newname.c_str()); f.tag()->setArtist("The Artist"); f.save(); } { Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); } } void testSplitPackets1() { ScopedFileCopy copy("empty", ".ogg"); string newname = copy.fileName(); const String text = longText(128 * 1024, true); { Vorbis::File f(newname.c_str()); f.tag()->setTitle(text); f.save(); } { Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(136383), f.length()); CPPUNIT_ASSERT_EQUAL(19, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(30U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(131127U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(3832U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); f.tag()->setTitle("ABCDE"); f.save(); } { Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4370), f.length()); CPPUNIT_ASSERT_EQUAL(3, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(30U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(60U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(3832U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); } } void testSplitPackets2() { ScopedFileCopy copy("empty", ".ogg"); string newname = copy.fileName(); const String text = longText(60890, true); { Vorbis::File f(newname.c_str()); f.tag()->setTitle(text); f.save(); } { Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); f.tag()->setTitle("ABCDE"); f.save(); } { Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); } } void testDictInterface1() { ScopedFileCopy copy("empty", ".ogg"); string newname = copy.fileName(); Vorbis::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.tag()->properties().size()); PropertyMap newTags; StringList values("value 1"); values.append("value 2"); newTags["ARTIST"] = values; f.tag()->setProperties(newTags); PropertyMap map = f.tag()->properties(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), map.size()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), map["ARTIST"].size()); CPPUNIT_ASSERT_EQUAL(String("value 1"), map["ARTIST"][0]); } void testDictInterface2() { ScopedFileCopy copy("test", ".ogg"); string newname = copy.fileName(); Vorbis::File f(newname.c_str()); PropertyMap tags = f.tag()->properties(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), tags["UNUSUALTAG"].size()); CPPUNIT_ASSERT_EQUAL(String("usual value"), tags["UNUSUALTAG"][0]); CPPUNIT_ASSERT_EQUAL(String("another value"), tags["UNUSUALTAG"][1]); CPPUNIT_ASSERT_EQUAL( String("\xC3\xB6\xC3\xA4\xC3\xBC\x6F\xCE\xA3\xC3\xB8", String::UTF8), tags["UNICODETAG"][0]); tags["UNICODETAG"][0] = String( "\xCE\xBD\xCE\xB5\xCF\x89\x20\xCE\xBD\xCE\xB1\xCE\xBB\xCF\x85\xCE\xB5", String::UTF8); tags.erase("UNUSUALTAG"); f.tag()->setProperties(tags); CPPUNIT_ASSERT_EQUAL( String("\xCE\xBD\xCE\xB5\xCF\x89\x20\xCE\xBD\xCE\xB1\xCE\xBB\xCF\x85\xCE\xB5", String::UTF8), f.tag()->properties()["UNICODETAG"][0]); CPPUNIT_ASSERT_EQUAL(false, f.tag()->properties().contains("UNUSUALTAG")); } void testAudioProperties() { Ogg::Vorbis::File f(TEST_FILE_PATH_C("empty.ogg")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->vorbisVersion()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMaximum()); CPPUNIT_ASSERT_EQUAL(112000, f.audioProperties()->bitrateNominal()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMinimum()); } void testPageChecksum() { ScopedFileCopy copy("empty", ".ogg"); { Vorbis::File f(copy.fileName().c_str()); f.tag()->setArtist("The Artist"); f.save(); f.seek(0x50); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0x3d3bd92d), f.readBlock(4).toUInt(0, true)); } { Vorbis::File f(copy.fileName().c_str()); f.tag()->setArtist("The Artist 2"); f.save(); f.seek(0x50); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0xd985291c), f.readBlock(4).toUInt(0, true)); } } void testPageGranulePosition() { ScopedFileCopy copy("empty", ".ogg"); { Vorbis::File f(copy.fileName().c_str()); // Force the Vorbis comment packet to span more than one page and // check if the granule position is -1 indicating that no packets // finish on this page. f.tag()->setComment(String(ByteVector(70000, 'A'))); f.save(); f.seek(0x3a); CPPUNIT_ASSERT_EQUAL(ByteVector("OggS\0\0", 6), f.readBlock(6)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(-1), f.readBlock(8).toLongLong()); } { Vorbis::File f(copy.fileName().c_str()); // Use a small Vorbis comment package which ends on the seconds page and // check if the granule position is zero. f.tag()->setComment("A small comment"); f.save(); f.seek(0x3a); CPPUNIT_ASSERT_EQUAL(ByteVector("OggS\0\0", 6), f.readBlock(6)); CPPUNIT_ASSERT_EQUAL(static_cast<long long>(0), f.readBlock(8).toLongLong()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestOGG); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_oggflac.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000010467�14662262111�0017275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tstringlist.h" #include "tag.h" #include "oggfile.h" #include "oggflacfile.h" #include "oggpageheader.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestOggFLAC : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestOggFLAC); CPPUNIT_TEST(testFramingBit); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testSplitPackets); CPPUNIT_TEST_SUITE_END(); public: void testFramingBit() { ScopedFileCopy copy("empty_flac", ".oga"); string newname = copy.fileName(); { Ogg::FLAC::File f(newname.c_str()); f.tag()->setArtist("The Artist"); f.save(); } { Ogg::FLAC::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); f.seek(0, File::End); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(9134), f.tell()); } } void testFuzzedFile() { Ogg::FLAC::File f(TEST_FILE_PATH_C("segfault.oga")); CPPUNIT_ASSERT(!f.isValid()); } void testSplitPackets() { ScopedFileCopy copy("empty_flac", ".oga"); string newname = copy.fileName(); const String text = longText(128 * 1024, true); { Ogg::FLAC::File f(newname.c_str()); f.tag()->setTitle(text); f.save(); } { Ogg::FLAC::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(141141), f.length()); CPPUNIT_ASSERT_EQUAL(21, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(51U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(131126U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(22U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(8196U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); f.tag()->setTitle("ABCDE"); f.save(); } { Ogg::FLAC::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(9128), f.length()); CPPUNIT_ASSERT_EQUAL(5, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(51U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(59U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(22U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(8196U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestOggFLAC); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_opus.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000012760�14662262111�0016657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tag.h" #include "opusfile.h" #include "oggpageheader.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestOpus : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestOpus); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testReadComments); CPPUNIT_TEST(testWriteComments); CPPUNIT_TEST(testSplitPackets); CPPUNIT_TEST_SUITE_END(); public: void testAudioProperties() { Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(36, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->inputSampleRate()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->opusVersion()); } void testReadComments() { Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]); CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION")); CPPUNIT_ASSERT(!f.tag()->fieldListMap().contains("ARTIST")); CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID()); } void testWriteComments() { ScopedFileCopy copy("correctness_gain_silent_output", ".opus"); string filename = copy.fileName(); { Ogg::Opus::File f(filename.c_str()); f.tag()->setArtist("Your Tester"); f.save(); } { Ogg::Opus::File f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]); CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION")); CPPUNIT_ASSERT_EQUAL(StringList("Your Tester"), f.tag()->fieldListMap()["ARTIST"]); CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID()); } } void testSplitPackets() { ScopedFileCopy copy("correctness_gain_silent_output", ".opus"); string newname = copy.fileName(); const String text = longText(128 * 1024, true); { Ogg::Opus::File f(newname.c_str()); f.tag()->setTitle(text); f.save(); } { Ogg::Opus::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(167534), f.length()); CPPUNIT_ASSERT_EQUAL(27, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(19U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(131380U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(5U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(5U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); f.tag()->setTitle("ABCDE"); f.save(); } { Ogg::Opus::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(35521), f.length()); CPPUNIT_ASSERT_EQUAL(11, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(19U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(313U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(5U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(5U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(7737, f.audioProperties()->lengthInMilliseconds()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestOpus); ����������������taglib-2.0.2/tests/test_propertymap.cpp�������������������������������������������������������������0000664�0000000�0000000�00000015575�14662262111�0020262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2012 by Michael Helmling email : helmling@mathematik.uni-kl.de ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tpropertymap.h" #include "tag.h" #include "apetag.h" #include "asftag.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "infotag.h" #include "mp4tag.h" #include "xiphcomment.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace TagLib; class TestPropertyMap : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestPropertyMap); CPPUNIT_TEST(testInvalidKeys); CPPUNIT_TEST(testGetSetApe); CPPUNIT_TEST(testGetSetAsf); CPPUNIT_TEST(testGetSetId3v1); CPPUNIT_TEST(testGetSetId3v2); CPPUNIT_TEST(testGetSetInfo); CPPUNIT_TEST(testGetSetMp4); CPPUNIT_TEST(testGetSetXiphComment); CPPUNIT_TEST(testGetSet); CPPUNIT_TEST_SUITE_END(); public: void testInvalidKeys() { PropertyMap map1; CPPUNIT_ASSERT(map1.isEmpty()); map1[L"\x00c4\x00d6\x00dc"].append("test"); CPPUNIT_ASSERT_EQUAL(map1.size(), 1u); PropertyMap map2; map2[L"\x00c4\x00d6\x00dc"].append("test"); CPPUNIT_ASSERT(map1 == map2); CPPUNIT_ASSERT(map1.contains(map2)); map2["ARTIST"] = String("Test Artist"); CPPUNIT_ASSERT(map1 != map2); CPPUNIT_ASSERT(map2.contains(map1)); map2[L"\x00c4\x00d6\x00dc"].append("test 2"); CPPUNIT_ASSERT(!map2.contains(map1)); } template <typename T> void tagGetSet() { T tag; tag.setTitle("Test Title"); tag.setArtist("Test Artist"); tag.setAlbum("Test Album"); tag.setYear(2015); tag.setTrack(10); { PropertyMap prop = tag.properties(); CPPUNIT_ASSERT_EQUAL(String("Test Title"), prop["TITLE" ].front()); CPPUNIT_ASSERT_EQUAL(String("Test Artist"), prop["ARTIST" ].front()); CPPUNIT_ASSERT_EQUAL(String("Test Album"), prop["ALBUM" ].front()); CPPUNIT_ASSERT_EQUAL(String("2015"), prop["DATE" ].front()); CPPUNIT_ASSERT_EQUAL(String("10"), prop["TRACKNUMBER"].front()); prop["TITLE" ].front() = "Test Title 2"; prop["ARTIST" ].front() = "Test Artist 2"; prop["TRACKNUMBER"].front() = "5"; tag.setProperties(prop); } CPPUNIT_ASSERT_EQUAL(String("Test Title 2"), tag.title()); CPPUNIT_ASSERT_EQUAL(String("Test Artist 2"), tag.artist()); CPPUNIT_ASSERT_EQUAL(5U, tag.track()); CPPUNIT_ASSERT(!tag.isEmpty()); PropertyMap props = tag.properties(); CPPUNIT_ASSERT_EQUAL(StringList("Test Artist 2"), props.find("ARTIST")->second); CPPUNIT_ASSERT(props.find("COMMENT") == props.end()); props.replace("ARTIST", StringList("Test Artist 3")); CPPUNIT_ASSERT_EQUAL(StringList("Test Artist 3"), props["ARTIST"]); PropertyMap eraseMap; eraseMap.insert("ARTIST", StringList()); eraseMap.insert("ALBUM", StringList()); eraseMap.insert("TITLE", StringList()); props.erase(eraseMap); CPPUNIT_ASSERT_EQUAL(String("DATE=2015\nTRACKNUMBER=5\n"), props.toString()); tag.setProperties(PropertyMap()); CPPUNIT_ASSERT(tag.isEmpty()); CPPUNIT_ASSERT(tag.properties().isEmpty()); CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); CPPUNIT_ASSERT_EQUAL(String(""), tag.artist()); CPPUNIT_ASSERT_EQUAL(String(""), tag.album()); CPPUNIT_ASSERT_EQUAL(String(""), tag.comment()); CPPUNIT_ASSERT_EQUAL(String(""), tag.genre()); CPPUNIT_ASSERT_EQUAL(0U, tag.track()); CPPUNIT_ASSERT(tag.isEmpty()); CPPUNIT_ASSERT(tag.properties().isEmpty()); } void testGetSetId3v1() { tagGetSet<ID3v1::Tag>(); } void testGetSetId3v2() { tagGetSet<ID3v2::Tag>(); } void testGetSetXiphComment() { tagGetSet<Ogg::XiphComment>(); } void testGetSetApe() { tagGetSet<APE::Tag>(); } void testGetSetAsf() { tagGetSet<ASF::Tag>(); } void testGetSetMp4() { tagGetSet<MP4::Tag>(); } void testGetSetInfo() { tagGetSet<RIFF::Info::Tag>(); } void testGetSet() { PropertyMap props; props["Title"] = String("Test Title"); StringList artists("Artist 1"); artists.append("Artist 2"); props.insert("Artist", artists); CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.value("TITLE")); CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.value("Title")); CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props["TITLE"]); CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props["Title"]); CPPUNIT_ASSERT(props.contains("title")); CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.find("TITLE")->second); CPPUNIT_ASSERT_EQUAL(2U, props.size()); CPPUNIT_ASSERT(!props.isEmpty()); props.clear(); CPPUNIT_ASSERT(props.isEmpty()); CPPUNIT_ASSERT_EQUAL(StringList(), props.value("TITLE")); CPPUNIT_ASSERT_EQUAL(StringList(), props.value("Title")); CPPUNIT_ASSERT_EQUAL(artists, props.value("Title", artists)); CPPUNIT_ASSERT(!props.contains("title")); CPPUNIT_ASSERT(props.find("TITLE") == props.end()); CPPUNIT_ASSERT_EQUAL(0U, props.size()); CPPUNIT_ASSERT(props.isEmpty()); CPPUNIT_ASSERT_EQUAL(StringList(), props["TITLE"]); CPPUNIT_ASSERT_EQUAL(StringList(), props["Title"]); CPPUNIT_ASSERT(props.contains("title")); CPPUNIT_ASSERT(props.find("TITLE") != props.end()); CPPUNIT_ASSERT_EQUAL(1U, props.size()); CPPUNIT_ASSERT(!props.isEmpty()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestPropertyMap); �����������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_riff.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000033412�14662262111�0016614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tag.h" #include "rifffile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class PublicRIFF : public RIFF::File { public: PublicRIFF(FileName file) : RIFF::File(file, BigEndian) { } unsigned int riffSize() const { return RIFF::File::riffSize(); } unsigned int chunkCount() const { return RIFF::File::chunkCount(); } offset_t chunkOffset(unsigned int i) const { return RIFF::File::chunkOffset(i); } unsigned int chunkPadding(unsigned int i) const { return RIFF::File::chunkPadding(i); } unsigned int chunkDataSize(unsigned int i) const { return RIFF::File::chunkDataSize(i); } ByteVector chunkName(unsigned int i) const { return RIFF::File::chunkName(i); } ByteVector chunkData(unsigned int i) { return RIFF::File::chunkData(i); } void setChunkData(unsigned int i, const ByteVector &data) { RIFF::File::setChunkData(i, data); } void setChunkData(const ByteVector &name, const ByteVector &data) { RIFF::File::setChunkData(name, data); } TagLib::Tag *tag() const override { return nullptr; } TagLib::AudioProperties *audioProperties() const override { return nullptr; } bool save() override { return false; } void removeChunk(unsigned int i) { RIFF::File::removeChunk(i); } void removeChunk(const ByteVector &name) { RIFF::File::removeChunk(name); } }; class TestRIFF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestRIFF); CPPUNIT_TEST(testPadding); CPPUNIT_TEST(testLastChunkAtEvenPosition); CPPUNIT_TEST(testLastChunkAtEvenPosition2); CPPUNIT_TEST(testLastChunkAtEvenPosition3); CPPUNIT_TEST(testChunkOffset); CPPUNIT_TEST_SUITE_END(); public: void testPadding() { ScopedFileCopy copy("empty", ".aiff"); string filename = copy.fileName(); { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x1728 + 8), f.chunkOffset(2)); f.setChunkData("TEST", "foo"); } { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x1728 + 8), f.chunkOffset(2)); f.setChunkData("SSND", "abcd"); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.chunkData(1)); f.seek(f.chunkOffset(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.readBlock(4)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); f.seek(f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.readBlock(3)); } { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f.chunkData(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f.chunkData(2)); } } void testLastChunkAtEvenPosition() { ScopedFileCopy copy("noise", ".aif"); string filename = copy.fileName(); { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0xff0 + 8), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4400), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4399 - 8), f.riffSize()); f.setChunkData("TEST", "abcd"); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4412 - 8), f.riffSize()); } { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4412), f.length()); } } void testLastChunkAtEvenPosition2() { ScopedFileCopy copy("noise_odd", ".aif"); string filename = copy.fileName(); { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0xff0 + 8), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4399), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4399 - 8), f.riffSize()); f.setChunkData("TEST", "abcd"); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4412 - 8), f.riffSize()); } { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4412), f.length()); } } void testLastChunkAtEvenPosition3() { ScopedFileCopy copy("noise_odd", ".aif"); string filename = copy.fileName(); { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0xff0 + 8), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4399), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4399 - 8), f.riffSize()); f.setChunkData("TEST", "abc"); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4412 - 8), f.riffSize()); } { PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4088), f.chunkOffset(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(311), f.chunkDataSize(2)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4408), f.chunkOffset(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f.chunkDataSize(3)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(3)); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), f.chunkPadding(3)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(4412), f.length()); } } void testChunkOffset() { ScopedFileCopy copy("empty", ".aiff"); string filename = copy.fileName(); PublicRIFF f(filename.c_str()); CPPUNIT_ASSERT_EQUAL(5928U, f.riffSize()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(5936), f.length()); CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.chunkName(0)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x000C + 8), f.chunkOffset(0)); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.chunkName(1)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0026 + 8), f.chunkOffset(1)); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.chunkName(2)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x1728 + 8), f.chunkOffset(2)); const ByteVector data(0x400, ' '); f.setChunkData("SSND", data); CPPUNIT_ASSERT_EQUAL(1070U, f.riffSize()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(1078), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x000C + 8), f.chunkOffset(0)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0026 + 8), f.chunkOffset(1)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x042E + 8), f.chunkOffset(2)); f.seek(f.chunkOffset(0) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); f.seek(f.chunkOffset(1) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.readBlock(4)); f.seek(f.chunkOffset(2) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); f.setChunkData(0, data); CPPUNIT_ASSERT_EQUAL(2076U, f.riffSize()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(2084), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x000C + 8), f.chunkOffset(0)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0414 + 8), f.chunkOffset(1)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x081C + 8), f.chunkOffset(2)); f.seek(f.chunkOffset(0) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); f.seek(f.chunkOffset(1) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f.readBlock(4)); f.seek(f.chunkOffset(2) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); f.removeChunk("SSND"); CPPUNIT_ASSERT_EQUAL(1044U, f.riffSize()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(1052), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x000C + 8), f.chunkOffset(0)); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x0414 + 8), f.chunkOffset(1)); f.seek(f.chunkOffset(0) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("COMM"), f.readBlock(4)); f.seek(f.chunkOffset(1) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); f.removeChunk(0); CPPUNIT_ASSERT_EQUAL(12U, f.riffSize()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(20), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(0x000C + 8), f.chunkOffset(0)); f.seek(f.chunkOffset(0) - 8); CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f.readBlock(4)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestRIFF); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_s3m.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000012077�14662262111�0016374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cassert> #include "s3mfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; static const String titleBefore("test song name"); static const String titleAfter("changed title"); static const String commentBefore( "This is an instrument name.\n" "Module file formats\n" "abuse instrument names\n" "as multiline comments.\n" " "); static const String newComment( "This is an instrument name!\n" "Module file formats\n" "abuse instrument names\n" "as multiline comments.\n" "-----------------------------------\n" "This line will be dropped and the previous is truncated."); static const String commentAfter( "This is an instrument name!\n" "Module file formats\n" "abuse instrument names\n" "as multiline comments.\n" "---------------------------"); class TestS3M : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestS3M); CPPUNIT_TEST(testReadTags); CPPUNIT_TEST(testWriteTags); CPPUNIT_TEST_SUITE_END(); public: void testReadTags() { testRead(TEST_FILE_PATH_C("test.s3m"), titleBefore, commentBefore); } void testWriteTags() { ScopedFileCopy copy("test", ".s3m"); { S3M::File file(copy.fileName().c_str()); CPPUNIT_ASSERT(file.tag() != nullptr); file.tag()->setTitle(titleAfter); file.tag()->setComment(newComment); file.tag()->setTrackerName("won't be saved"); CPPUNIT_ASSERT(file.save()); } testRead(copy.fileName().c_str(), titleAfter, commentAfter); CPPUNIT_ASSERT(fileEqual( copy.fileName(), testFilePath("changed.s3m"))); } private: void testRead(FileName fileName, const String &title, const String &comment) { S3M::File file(fileName); CPPUNIT_ASSERT(file.isValid()); S3M::Properties *p = file.audioProperties(); Mod::Tag *t = file.tag(); CPPUNIT_ASSERT(nullptr != p); CPPUNIT_ASSERT(nullptr != t); assert(p != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL( 0, p->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL( 0, p->bitrate()); CPPUNIT_ASSERT_EQUAL( 0, p->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, p->channels()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->lengthInPatterns()); CPPUNIT_ASSERT_EQUAL(false, p->stereo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(5), p->sampleCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->patternCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->flags()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(4896), p->trackerVersion()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(2), p->fileFormatVersion()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(64), p->globalVolume()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(48), p->masterVolume()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(125), p->tempo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned char>(6), p->bpmSpeed()); CPPUNIT_ASSERT_EQUAL(title, t->title()); CPPUNIT_ASSERT_EQUAL(String(), t->artist()); CPPUNIT_ASSERT_EQUAL(String(), t->album()); CPPUNIT_ASSERT_EQUAL(comment, t->comment()); CPPUNIT_ASSERT_EQUAL(String(), t->genre()); CPPUNIT_ASSERT_EQUAL(0U, t->year()); CPPUNIT_ASSERT_EQUAL(0U, t->track()); CPPUNIT_ASSERT_EQUAL(String("ScreamTracker III"), t->trackerName()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestS3M); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_sizes.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000034012�14662262111�0017020�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 Scott Wheeler email : wheeler@kde.org ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cstring> #include "aifffile.h" #include "aiffproperties.h" #include "apefile.h" #include "apefooter.h" #include "apeitem.h" #include "apeproperties.h" #include "apetag.h" #include "asfattribute.h" #include "asffile.h" #include "asfpicture.h" #include "asfproperties.h" #include "asftag.h" #include "attachedpictureframe.h" #include "audioproperties.h" #include "chapterframe.h" #include "commentsframe.h" #include "dsffile.h" #include "dsfproperties.h" #include "dsdifffile.h" #include "dsdiffproperties.h" #include "eventtimingcodesframe.h" #include "fileref.h" #include "flacfile.h" #include "flacmetadatablock.h" #include "flacunknownmetadatablock.h" #include "flacpicture.h" #include "flacproperties.h" #include "generalencapsulatedobjectframe.h" #include "id3v1tag.h" #include "id3v2extendedheader.h" #include "id3v2footer.h" #include "id3v2frame.h" #include "id3v2framefactory.h" #include "id3v2header.h" #include "id3v2tag.h" #include "infotag.h" #include "itfile.h" #include "itproperties.h" #include "modfile.h" #include "modfilebase.h" #include "modproperties.h" #include "modtag.h" #include "mp4coverart.h" #include "mp4file.h" #include "mp4item.h" #include "mp4itemfactory.h" #include "mp4properties.h" #include "mp4tag.h" #include "mpcfile.h" #include "mpcproperties.h" #include "mpegfile.h" #include "mpegheader.h" #include "mpegproperties.h" #include "oggfile.h" #include "oggflacfile.h" #include "oggpage.h" #include "oggpageheader.h" #include "opusfile.h" #include "opusproperties.h" #include "ownershipframe.h" #include "podcastframe.h" #include "popularimeterframe.h" #include "privateframe.h" #include "relativevolumeframe.h" #include "rifffile.h" #include "s3mfile.h" #include "s3mproperties.h" #include "speexfile.h" #include "speexproperties.h" #include "synchronizedlyricsframe.h" #include "tableofcontentsframe.h" #include "tag.h" #include "tbytevector.h" #include "tbytevectorlist.h" #include "tbytevectorstream.h" #include "tdebuglistener.h" #include "textidentificationframe.h" #include "tfile.h" #include "tfilestream.h" #include "tiostream.h" #include "tlist.h" #include "tmap.h" #include "tpropertymap.h" #include "trueaudiofile.h" #include "trueaudioproperties.h" #include "tstring.h" #include "tstringlist.h" #include "uniquefileidentifierframe.h" #include "unknownframe.h" #include "unsynchronizedlyricsframe.h" #include "urllinkframe.h" #include "vorbisfile.h" #include "vorbisproperties.h" #include "wavfile.h" #include "wavpackfile.h" #include "wavpackproperties.h" #include "wavproperties.h" #include "xingheader.h" #include "xiphcomment.h" #include "xmfile.h" #include "xmproperties.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestSizes : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestSizes); CPPUNIT_TEST(testSizes); CPPUNIT_TEST_SUITE_END(); public: void testSizes() { // Class list was built by generating XML docs with Doxygen, and then running: // $ grep kind=\"class\" index.xml | sed -E -e 's/(.*<name>|<\/name>.*)//g' CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::APE::File)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::APE::Footer)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::APE::Item)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::APE::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::APE::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::ASF::Attribute)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ASF::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::ASF::Picture)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ASF::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ASF::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::AudioProperties)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::ByteVector)); CPPUNIT_ASSERT_EQUAL(classSize(2, false), sizeof(TagLib::ByteVectorList)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ByteVectorStream)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::DebugListener)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::DSDIFF::DIIN::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::DSDIFF::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::DSDIFF::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FLAC::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::DSF::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::DSF::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::FLAC::MetadataBlock)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FLAC::Picture)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FLAC::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FLAC::UnknownMetadataBlock)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::FileRef)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::FileRef::FileTypeResolver)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FileRef::StreamTypeResolver)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::FileStream)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::ID3v1::StringHandler)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v1::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::AttachedPictureFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::AttachedPictureFrameV22)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::ChapterFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::CommentsFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::EventTimingCodesFrame)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::ID3v2::ExtendedHeader)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::ID3v2::Footer)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::ID3v2::Frame)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::ID3v2::FrameFactory)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::GeneralEncapsulatedObjectFrame)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::ID3v2::Header)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::ID3v2::Latin1StringHandler)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::OwnershipFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::PodcastFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::PopularimeterFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::PrivateFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::RelativeVolumeFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::SynchronizedLyricsFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::TableOfContentsFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::TextIdentificationFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::UniqueFileIdentifierFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::UnknownFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::UnsynchronizedLyricsFrame)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::ID3v2::UrlLinkFrame)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::ID3v2::UserTextIdentificationFrame)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::ID3v2::UserUrlLinkFrame)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::IOStream)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::IT::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::IT::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::List<int>)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::MP4::CoverArt)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MP4::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::MP4::Item)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::MP4::ItemFactory)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MP4::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MP4::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MPC::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MPC::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MPEG::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::MPEG::Header)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::MPEG::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::MPEG::XingHeader)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::Map<int, int>)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::Mod::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Mod::FileBase)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Mod::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Mod::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::Ogg::FLAC::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Ogg::File)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::Ogg::Opus::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Ogg::Opus::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::Ogg::Page)); CPPUNIT_ASSERT_EQUAL(classSize(0, false), sizeof(TagLib::Ogg::PageHeader)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::Ogg::Speex::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Ogg::Speex::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::Ogg::Vorbis::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Ogg::Vorbis::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Ogg::XiphComment)); CPPUNIT_ASSERT_EQUAL(classSize(2, false), sizeof(TagLib::PropertyMap)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::RIFF::AIFF::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::RIFF::AIFF::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::RIFF::File)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::RIFF::Info::StringHandler)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::RIFF::Info::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::RIFF::WAV::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::RIFF::WAV::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::S3M::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::S3M::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::String)); CPPUNIT_ASSERT_EQUAL(classSize(2, false), sizeof(TagLib::StringList)); CPPUNIT_ASSERT_EQUAL(classSize(0, true), sizeof(TagLib::Tag)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::TrueAudio::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::TrueAudio::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::WavPack::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::WavPack::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(2, true), sizeof(TagLib::XM::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::XM::Properties)); CPPUNIT_ASSERT_EQUAL(classSize(1, false), sizeof(TagLib::Variant)); } private: constexpr size_t classSize(int baseClasses, bool isVirtual) { return sizeof(void *) * (baseClasses + static_cast<int>(isVirtual) + 1); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestSizes); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_speex.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000010220�14662262111�0017002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2015 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "speexfile.h" #include "oggpageheader.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestSpeex : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestSpeex); CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testSplitPackets); CPPUNIT_TEST_SUITE_END(); public: void testAudioProperties() { Ogg::Speex::File f(TEST_FILE_PATH_C("empty.spx")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(53, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(-1, f.audioProperties()->bitrateNominal()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); } void testSplitPackets() { ScopedFileCopy copy("empty", ".spx"); string newname = copy.fileName(); const String text = longText(128 * 1024, true); { Ogg::Speex::File f(newname.c_str()); f.tag()->setTitle(text); f.save(); } { Ogg::Speex::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(156330), f.length()); CPPUNIT_ASSERT_EQUAL(23, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(80U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(131116U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(93U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(93U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(text, f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); f.tag()->setTitle("ABCDE"); f.save(); } { Ogg::Speex::File f(newname.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(24317), f.length()); CPPUNIT_ASSERT_EQUAL(7, f.lastPageHeader()->pageSequenceNumber()); CPPUNIT_ASSERT_EQUAL(80U, f.packet(0).size()); CPPUNIT_ASSERT_EQUAL(49U, f.packet(1).size()); CPPUNIT_ASSERT_EQUAL(93U, f.packet(2).size()); CPPUNIT_ASSERT_EQUAL(93U, f.packet(3).size()); CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title()); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestSpeex); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_string.cpp������������������������������������������������������������������0000664�0000000�0000000�00000033244�14662262111�0017177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cstring> #include "tstring.h" #include "tutils.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestString : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestString); CPPUNIT_TEST(testString); CPPUNIT_TEST(testRfind); CPPUNIT_TEST(testUTF16Encode); CPPUNIT_TEST(testUTF16Decode); CPPUNIT_TEST(testUTF16DecodeInvalidBOM); CPPUNIT_TEST(testUTF16DecodeEmptyWithBOM); CPPUNIT_TEST(testSurrogatePair); CPPUNIT_TEST(testAppendCharDetach); CPPUNIT_TEST(testAppendStringDetach); CPPUNIT_TEST(testToInt); CPPUNIT_TEST(testFromInt); CPPUNIT_TEST(testSubstr); CPPUNIT_TEST(testNewline); CPPUNIT_TEST(testUpper); CPPUNIT_TEST(testEncodeNonLatin1); CPPUNIT_TEST(testEncodeEmpty); CPPUNIT_TEST(testEncodeNonBMP); CPPUNIT_TEST(testIterator); CPPUNIT_TEST(testInvalidUTF8); CPPUNIT_TEST_SUITE_END(); public: void testString() { String s = "taglib string"; ByteVector v = "taglib string"; CPPUNIT_ASSERT(v == s.data(String::Latin1)); char str[] = "taglib string"; CPPUNIT_ASSERT(strcmp(s.toCString(), str) == 0); CPPUNIT_ASSERT(s == "taglib string"); CPPUNIT_ASSERT(s != "taglib STRING"); CPPUNIT_ASSERT(s != "taglib"); CPPUNIT_ASSERT(s != "taglib string taglib"); CPPUNIT_ASSERT(s == L"taglib string"); CPPUNIT_ASSERT(s != L"taglib STRING"); CPPUNIT_ASSERT(s != L"taglib"); CPPUNIT_ASSERT(s != L"taglib string taglib"); s.clear(); CPPUNIT_ASSERT(s.isEmpty()); String unicode("José Carlos", String::UTF8); CPPUNIT_ASSERT(strcmp(unicode.toCString(), "Jos\xe9 Carlos") == 0); String latin = "Jos\xe9 Carlos"; CPPUNIT_ASSERT(strcmp(latin.toCString(true), "José Carlos") == 0); String c = "1"; CPPUNIT_ASSERT(c == L"1"); c = L'\u4E00'; CPPUNIT_ASSERT(c == L"\u4E00"); String unicode2(unicode.to8Bit(true), String::UTF8); CPPUNIT_ASSERT(unicode == unicode2); String unicode3(L"\u65E5\u672C\u8A9E"); CPPUNIT_ASSERT(*(unicode3.toCWString() + 1) == L'\u672C'); constexpr wchar_t wcSystemOrder[] = {L'\u65E5', L'\u672C', L'\u8A9E', 0}; constexpr wchar_t wcSwappedOrder[] = {L'\uE565', L'\u2C67', L'\u9E8A', 0}; const std::wstring wsSystemOrder = L"\u65e5\u672c\u8a9e"; const std::wstring wsSwappedOrder = L"\ue565\u2c67\u9e8a"; const bool isLe = Utils::systemByteOrder() == Utils::LittleEndian; String unicode4(isLe ? wcSwappedOrder : wcSystemOrder, String::UTF16BE); CPPUNIT_ASSERT(unicode4[1] == L'\u672c'); String unicode5(isLe ? wcSystemOrder : wcSwappedOrder, String::UTF16LE); CPPUNIT_ASSERT(unicode5[1] == L'\u672c'); String unicode6(isLe ? wsSwappedOrder : wsSystemOrder, String::UTF16BE); CPPUNIT_ASSERT(unicode6[1] == L'\u672c'); String unicode7(isLe ? wsSystemOrder : wsSwappedOrder, String::UTF16LE); CPPUNIT_ASSERT(unicode7[1] == L'\u672c'); CPPUNIT_ASSERT(String(" foo ").stripWhiteSpace() == String("foo")); CPPUNIT_ASSERT(String("foo ").stripWhiteSpace() == String("foo")); CPPUNIT_ASSERT(String(" foo").stripWhiteSpace() == String("foo")); CPPUNIT_ASSERT(String("foo").stripWhiteSpace() == String("foo")); CPPUNIT_ASSERT(String("f o o").stripWhiteSpace() == String("f o o")); CPPUNIT_ASSERT(String(" f o o ").stripWhiteSpace() == String("f o o")); CPPUNIT_ASSERT(memcmp(String("foo").data(String::Latin1).data(), "foo", 3) == 0); CPPUNIT_ASSERT(memcmp(String("f").data(String::Latin1).data(), "f", 1) == 0); } void testUTF16Encode() { String a("foo"); ByteVector b("\0f\0o\0o", 6); ByteVector c("f\0o\0o\0", 6); ByteVector d("\377\376f\0o\0o\0", 8); CPPUNIT_ASSERT(a.data(String::UTF16BE) != a.data(String::UTF16LE)); CPPUNIT_ASSERT(b == a.data(String::UTF16BE)); CPPUNIT_ASSERT(c == a.data(String::UTF16LE)); CPPUNIT_ASSERT_EQUAL(d, a.data(String::UTF16)); } void testUTF16Decode() { String a("foo"); ByteVector b("\0f\0o\0o", 6); ByteVector c("f\0o\0o\0", 6); ByteVector d("\377\376f\0o\0o\0", 8); CPPUNIT_ASSERT_EQUAL(a, String(b, String::UTF16BE)); CPPUNIT_ASSERT_EQUAL(a, String(c, String::UTF16LE)); CPPUNIT_ASSERT_EQUAL(a, String(d, String::UTF16)); } // this test is expected to print "TagLib: String::prepare() - // Invalid UTF16 string." on the console 3 times void testUTF16DecodeInvalidBOM() { ByteVector b(" ", 1); ByteVector c(" ", 2); ByteVector d(" \0f\0o\0o", 8); CPPUNIT_ASSERT_EQUAL(String(), String(b, String::UTF16)); CPPUNIT_ASSERT_EQUAL(String(), String(c, String::UTF16)); CPPUNIT_ASSERT_EQUAL(String(), String(d, String::UTF16)); } void testUTF16DecodeEmptyWithBOM() { ByteVector a("\377\376", 2); ByteVector b("\376\377", 2); CPPUNIT_ASSERT_EQUAL(String(), String(a, String::UTF16)); CPPUNIT_ASSERT_EQUAL(String(), String(b, String::UTF16)); } void testSurrogatePair() { // Make sure that a surrogate pair is converted into single UTF-8 char // and vice versa. const ByteVector v1("\xff\xfe\x42\xd8\xb7\xdf\xce\x91\x4b\x5c"); const ByteVector v2("\xf0\xa0\xae\xb7\xe9\x87\x8e\xe5\xb1\x8b"); const String s1(v1, String::UTF16); CPPUNIT_ASSERT_EQUAL(s1.data(String::UTF8), v2); const String s2(v2, String::UTF8); CPPUNIT_ASSERT_EQUAL(s2.data(String::UTF16), v1); const ByteVector v3("\xfe\xff\xd8\x01\x30\x42"); CPPUNIT_ASSERT(String(v3, String::UTF16).data(String::UTF8).isEmpty()); const ByteVector v4("\xfe\xff\x30\x42\xdc\x01"); CPPUNIT_ASSERT(String(v4, String::UTF16).data(String::UTF8).isEmpty()); const ByteVector v5("\xfe\xff\xdc\x01\xd8\x01"); CPPUNIT_ASSERT(String(v5, String::UTF16).data(String::UTF8).isEmpty()); } void testAppendStringDetach() { String a("a"); String b = a; a += "b"; CPPUNIT_ASSERT_EQUAL(String("ab"), a); CPPUNIT_ASSERT_EQUAL(String("a"), b); } void testAppendCharDetach() { String a("a"); String b = a; a += 'b'; CPPUNIT_ASSERT_EQUAL(String("ab"), a); CPPUNIT_ASSERT_EQUAL(String("a"), b); } void testRfind() { CPPUNIT_ASSERT_EQUAL(-1, String("foo.bar").rfind(".", 0)); CPPUNIT_ASSERT_EQUAL(-1, String("foo.bar").rfind(".", 1)); CPPUNIT_ASSERT_EQUAL(-1, String("foo.bar").rfind(".", 2)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".", 3)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".", 4)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".", 5)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".", 6)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".", 7)); CPPUNIT_ASSERT_EQUAL(3, String("foo.bar").rfind(".")); } void testToInt() { bool ok; CPPUNIT_ASSERT_EQUAL(String("123").toInt(&ok), 123); CPPUNIT_ASSERT_EQUAL(ok, true); CPPUNIT_ASSERT_EQUAL(String("-123").toInt(&ok), -123); CPPUNIT_ASSERT_EQUAL(ok, true); CPPUNIT_ASSERT_EQUAL(String("abc").toInt(&ok), 0); CPPUNIT_ASSERT_EQUAL(ok, false); CPPUNIT_ASSERT_EQUAL(String("1x").toInt(&ok), 1); CPPUNIT_ASSERT_EQUAL(ok, false); CPPUNIT_ASSERT_EQUAL(String("").toInt(&ok), 0); CPPUNIT_ASSERT_EQUAL(ok, false); CPPUNIT_ASSERT_EQUAL(String("-").toInt(&ok), 0); CPPUNIT_ASSERT_EQUAL(ok, false); CPPUNIT_ASSERT_EQUAL(String("123").toInt(), 123); CPPUNIT_ASSERT_EQUAL(String("-123").toInt(), -123); CPPUNIT_ASSERT_EQUAL(String("123aa").toInt(), 123); CPPUNIT_ASSERT_EQUAL(String("-123aa").toInt(), -123); CPPUNIT_ASSERT_EQUAL(String("0000").toInt(), 0); CPPUNIT_ASSERT_EQUAL(String("0001").toInt(), 1); String("2147483648").toInt(&ok); CPPUNIT_ASSERT_EQUAL(ok, false); String("-2147483649").toInt(&ok); CPPUNIT_ASSERT_EQUAL(ok, false); } void testFromInt() { CPPUNIT_ASSERT_EQUAL(String::number(0), String("0")); CPPUNIT_ASSERT_EQUAL(String::number(12345678), String("12345678")); CPPUNIT_ASSERT_EQUAL(String::number(-12345678), String("-12345678")); } void testSubstr() { CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2)); CPPUNIT_ASSERT_EQUAL(String("12"), String("0123456").substr(1, 2)); CPPUNIT_ASSERT_EQUAL(String("123456"), String("0123456").substr(1, 200)); CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 7)); CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 200)); } void testNewline() { ByteVector cr("abc\x0dxyz", 7); ByteVector lf("abc\x0axyz", 7); ByteVector crlf("abc\x0d\x0axyz", 8); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(7), String(cr).size()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(7), String(lf).size()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8), String(crlf).size()); CPPUNIT_ASSERT(L'\x0d' == String(cr)[3]); CPPUNIT_ASSERT(L'\x0a' == String(lf)[3]); CPPUNIT_ASSERT(L'\x0d' == String(crlf)[3]); CPPUNIT_ASSERT(L'\x0a' == String(crlf)[4]); } void testUpper() { String s1 = "tagLIB 012 strING"; String s2 = s1.upper(); CPPUNIT_ASSERT_EQUAL(String("tagLIB 012 strING"), s1); CPPUNIT_ASSERT_EQUAL(String("TAGLIB 012 STRING"), s2); } void testEncodeNonLatin1() { const String jpn(L"\u65E5\u672C\u8A9E"); CPPUNIT_ASSERT_EQUAL(ByteVector("\xE5\x2C\x9E"), jpn.data(String::Latin1)); CPPUNIT_ASSERT_EQUAL(ByteVector("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"), jpn.data(String::UTF8)); CPPUNIT_ASSERT_EQUAL(ByteVector("\xFF\xFE\xE5\x65\x2C\x67\x9E\x8A"), jpn.data(String::UTF16)); CPPUNIT_ASSERT_EQUAL(ByteVector("\xE5\x65\x2C\x67\x9E\x8A"), jpn.data(String::UTF16LE)); CPPUNIT_ASSERT_EQUAL(ByteVector("\x65\xE5\x67\x2C\x8A\x9E"), jpn.data(String::UTF16BE)); CPPUNIT_ASSERT_EQUAL(std::string("\xE5\x2C\x9E"), jpn.to8Bit(false)); CPPUNIT_ASSERT_EQUAL(std::string("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"), jpn.to8Bit(true)); } void testEncodeEmpty() { const String empty; CPPUNIT_ASSERT(empty.data(String::Latin1).isEmpty()); CPPUNIT_ASSERT(empty.data(String::UTF8).isEmpty()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xFF\xFE"), empty.data(String::UTF16)); CPPUNIT_ASSERT(empty.data(String::UTF16LE).isEmpty()); CPPUNIT_ASSERT(empty.data(String::UTF16BE).isEmpty()); CPPUNIT_ASSERT(empty.to8Bit(false).empty()); CPPUNIT_ASSERT(empty.to8Bit(true).empty()); } void testEncodeNonBMP() { const ByteVector a("\xFF\xFE\x3C\xD8\x50\xDD\x40\xD8\xF5\xDC\x3C\xD8\x00\xDE", 14); const ByteVector b("\xF0\x9F\x85\x90\xF0\xA0\x83\xB5\xF0\x9F\x88\x80"); CPPUNIT_ASSERT_EQUAL(b, String(a, String::UTF16).data(String::UTF8)); } void testIterator() { String s1 = "taglib string"; String s2 = s1; String::Iterator it1 = s1.begin(); String::Iterator it2 = s2.begin(); CPPUNIT_ASSERT(L't' == *it1); CPPUNIT_ASSERT(L't' == *it2); std::advance(it1, 4); std::advance(it2, 4); *it2 = L'I'; CPPUNIT_ASSERT(L'i' == *it1); CPPUNIT_ASSERT(L'I' == *it2); } void testInvalidUTF8() { CPPUNIT_ASSERT_EQUAL(String("/"), String(ByteVector("\x2F"), String::UTF8)); CPPUNIT_ASSERT(String(ByteVector("\xC0\xAF"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xE0\x80\xAF"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80\xAF"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xC2"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xE0\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String('\x80', String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xED\xA0\x80\xED\xB0\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestString); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_synchdata.cpp���������������������������������������������������������������0000664�0000000�0000000�00000010626�14662262111�0017646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "id3v2synchdata.h" #include <cppunit/extensions/HelperMacros.h> using namespace std; using namespace TagLib; class TestID3v2SynchData : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestID3v2SynchData); CPPUNIT_TEST(test1); CPPUNIT_TEST(test2); CPPUNIT_TEST(test3); CPPUNIT_TEST(testToUIntBroken); CPPUNIT_TEST(testToUIntBrokenAndTooLarge); CPPUNIT_TEST(testDecode1); CPPUNIT_TEST(testDecode2); CPPUNIT_TEST(testDecode3); CPPUNIT_TEST(testDecode4); CPPUNIT_TEST_SUITE_END(); public: void test1() { char data[] = { 0, 0, 0, 127 }; ByteVector v(data, 4); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), static_cast<unsigned int>(127)); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(127), v); } void test2() { char data[] = { 0, 0, 1, 0 }; ByteVector v(data, 4); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), static_cast<unsigned int>(128)); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(128), v); } void test3() { char data[] = { 0, 0, 1, 1 }; ByteVector v(data, 4); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::toUInt(v), static_cast<unsigned int>(129)); CPPUNIT_ASSERT_EQUAL(ID3v2::SynchData::fromUInt(129), v); } void testToUIntBroken() { char data[] = { 0, 0, 0, static_cast<char>(-1) }; char data2[] = { 0, 0, static_cast<char>(-1), static_cast<char>(-1) }; CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(255), ID3v2::SynchData::toUInt(ByteVector(data, 4))); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(65535), ID3v2::SynchData::toUInt(ByteVector(data2, 4))); } void testToUIntBrokenAndTooLarge() { char data[] = { 0, 0, 0, static_cast<char>(-1), 0 }; ByteVector v(data, 5); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(255), ID3v2::SynchData::toUInt(v)); } void testDecode1() { ByteVector a("\xff\x00\x00", 3); a = ID3v2::SynchData::decode(a); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), a.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\x00", 2), a); } void testDecode2() { ByteVector a("\xff\x44", 2); a = ID3v2::SynchData::decode(a); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), a.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\x44", 2), a); } void testDecode3() { ByteVector a("\xff\xff\x00", 3); a = ID3v2::SynchData::decode(a); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2), a.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xff", 2), a); } void testDecode4() { ByteVector a("\xff\xff\xff", 3); a = ID3v2::SynchData::decode(a); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), a.size()); CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xff\xff", 3), a); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2SynchData); ����������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_tag_c.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000023304�14662262111�0016742�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <unordered_map> #include <list> #include "tag_c.h" #include "tbytevector.h" #include "tstring.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace TagLib; namespace { void propertiesToMap( const TagLib_File *file, std::unordered_map<std::string, std::list<std::string>> &propertyMap) { if(char **keys = taglib_property_keys(file)) { char **keyPtr = keys; while(*keyPtr) { char **values = taglib_property_get(file, *keyPtr); char **valuePtr = values; std::list<std::string> valueList; while(*valuePtr) { valueList.push_back(*valuePtr++); } taglib_property_free(values); propertyMap[*keyPtr++] = valueList; } taglib_property_free(keys); } } void complexPropertyKeysToList( const TagLib_File *file, std::list<std::string> &keyList) { if(char **complexKeys = taglib_complex_property_keys(file)) { char **complexKeyPtr = complexKeys; while(*complexKeyPtr) { keyList.push_back(*complexKeyPtr++); } taglib_complex_property_free_keys(complexKeys); } } } // namespace class TestTagC : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestTagC); CPPUNIT_TEST(testMp3); CPPUNIT_TEST(testStream); CPPUNIT_TEST_SUITE_END(); public: void testMp3() { ScopedFileCopy copy("xing", ".mp3"); { TagLib_File *file = taglib_file_new(copy.fileName().c_str()); CPPUNIT_ASSERT(taglib_file_is_valid(file)); const TagLib_AudioProperties *audioProperties = taglib_file_audioproperties(file); CPPUNIT_ASSERT_EQUAL(32, taglib_audioproperties_bitrate(audioProperties)); CPPUNIT_ASSERT_EQUAL(2, taglib_audioproperties_channels(audioProperties)); CPPUNIT_ASSERT_EQUAL(2, taglib_audioproperties_length(audioProperties)); CPPUNIT_ASSERT_EQUAL(44100, taglib_audioproperties_samplerate(audioProperties)); TagLib_Tag *tag = taglib_file_tag(file); CPPUNIT_ASSERT_EQUAL(""s, std::string(taglib_tag_album(tag))); taglib_tag_set_album(tag, "Album"); taglib_tag_set_artist(tag, "Artist"); taglib_tag_set_comment(tag, "Comment"); taglib_tag_set_genre(tag, "Genre"); taglib_tag_set_title(tag, "Title"); taglib_tag_set_track(tag, 2); taglib_tag_set_year(tag, 2023); taglib_property_set(file, "COMPOSER", "Composer 1"); taglib_property_set_append(file, "COMPOSER", "Composer 2"); taglib_property_set(file, "ALBUMARTIST", "Album Artist"); // cppcheck-suppress cstyleCast TAGLIB_COMPLEX_PROPERTY_PICTURE(props, "JPEG Data", 9, "Written by TagLib", "image/jpeg", "Front Cover"); taglib_complex_property_set(file, "PICTURE", props); taglib_file_save(file); taglib_file_free(file); } { TagLib_File *file = taglib_file_new(copy.fileName().c_str()); CPPUNIT_ASSERT(taglib_file_is_valid(file)); TagLib_Tag *tag = taglib_file_tag(file); CPPUNIT_ASSERT_EQUAL("Album"s, std::string(taglib_tag_album(tag))); CPPUNIT_ASSERT_EQUAL("Artist"s, std::string(taglib_tag_artist(tag))); CPPUNIT_ASSERT_EQUAL("Comment"s, std::string(taglib_tag_comment(tag))); CPPUNIT_ASSERT_EQUAL("Genre"s, std::string(taglib_tag_genre(tag))); CPPUNIT_ASSERT_EQUAL("Title"s, std::string(taglib_tag_title(tag))); CPPUNIT_ASSERT_EQUAL(2U, taglib_tag_track(tag)); CPPUNIT_ASSERT_EQUAL(2023U, taglib_tag_year(tag)); std::unordered_map<std::string, std::list<std::string>> propertyMap; propertiesToMap(file, propertyMap); const std::unordered_map<std::string, std::list<std::string>> expected { {"TRACKNUMBER"s, {"2"s}}, {"TITLE"s, {"Title"s}}, {"GENRE"s, {"Genre"s}}, {"DATE"s, {"2023"s}}, {"COMPOSER"s, {"Composer 1"s, "Composer 2"s}}, {"COMMENT"s, {"Comment"s}}, {"ARTIST"s, {"Artist"s}}, {"ALBUMARTIST"s, {"Album Artist"s}}, {"ALBUM"s, {"Album"s}} }; CPPUNIT_ASSERT(expected == propertyMap); std::list<std::string> keyList; complexPropertyKeysToList(file, keyList); CPPUNIT_ASSERT(std::list{"PICTURE"s} == keyList); TagLib_Complex_Property_Attribute*** properties = taglib_complex_property_get(file, "PICTURE"); TagLib_Complex_Property_Picture_Data picture; taglib_picture_from_complex_property(properties, &picture); CPPUNIT_ASSERT_EQUAL("image/jpeg"s, std::string(picture.mimeType)); CPPUNIT_ASSERT_EQUAL("Written by TagLib"s, std::string(picture.description)); CPPUNIT_ASSERT_EQUAL("Front Cover"s, std::string(picture.pictureType)); CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG Data"), ByteVector(picture.data, 9)); CPPUNIT_ASSERT_EQUAL(9U, picture.size); taglib_complex_property_free(properties); taglib_file_free(file); } taglib_tag_free_strings(); } void testStream() { // Only fetch the beginning of a FLAC file const ByteVector data = PlainFile(TEST_FILE_PATH_C("silence-44-s.flac")) .readBlock(4200); { TagLib_IOStream *stream = taglib_memory_iostream_new(data.data(), data.size()); TagLib_File *file = taglib_file_new_iostream(stream); CPPUNIT_ASSERT(taglib_file_is_valid(file)); const TagLib_AudioProperties *audioProperties = taglib_file_audioproperties(file); CPPUNIT_ASSERT_EQUAL(2, taglib_audioproperties_channels(audioProperties)); CPPUNIT_ASSERT_EQUAL(3, taglib_audioproperties_length(audioProperties)); CPPUNIT_ASSERT_EQUAL(44100, taglib_audioproperties_samplerate(audioProperties)); TagLib_Tag *tag = taglib_file_tag(file); CPPUNIT_ASSERT_EQUAL("Quod Libet Test Data"s, std::string(taglib_tag_album(tag))); CPPUNIT_ASSERT_EQUAL("piman / jzig"s, std::string(taglib_tag_artist(tag))); CPPUNIT_ASSERT_EQUAL("Silence"s, std::string(taglib_tag_genre(tag))); CPPUNIT_ASSERT_EQUAL(""s, std::string(taglib_tag_comment(tag))); CPPUNIT_ASSERT_EQUAL("Silence"s, std::string(taglib_tag_title(tag))); CPPUNIT_ASSERT_EQUAL(2U, taglib_tag_track(tag)); CPPUNIT_ASSERT_EQUAL(2004U, taglib_tag_year(tag)); std::unordered_map<std::string, std::list<std::string>> propertyMap; propertiesToMap(file, propertyMap); const std::unordered_map<std::string, std::list<std::string>> expected { {"TRACKNUMBER"s, {"02/10"s}}, {"TITLE"s, {"Silence"s}}, {"GENRE"s, {"Silence"s}}, {"DATE"s, {"2004"s}}, {"ARTIST"s, {"piman"s, "jzig"s}}, {"ALBUM"s, {"Quod Libet Test Data"s}} }; CPPUNIT_ASSERT(expected == propertyMap); std::list<std::string> keyList; complexPropertyKeysToList(file, keyList); CPPUNIT_ASSERT(std::list{"PICTURE"s} == keyList); TagLib_Complex_Property_Attribute*** properties = taglib_complex_property_get(file, "PICTURE"); TagLib_Complex_Property_Picture_Data picture; taglib_picture_from_complex_property(properties, &picture); ByteVector expectedData( "\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR" "\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90\x77\x53" "\xde\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b" "\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd6" "\x0b\x1c\x0a\x36\x06\x08\x44\x3d\x32\x00\x00\x00\x1dtEXt" "Comment\0Created" " with The GIMP\xef\x64" "\x25\x6e\x00\x00\x00\x0cIDAT\x08\xd7\x63\xf8\xff\xff" "\x3f\x00\x05\xfe\x02\xfe\xdc\xcc\x59\xe7\x00\x00\x00\x00IEND" "\xae\x42\x60\x82", 150); CPPUNIT_ASSERT_EQUAL("image/png"s, std::string(picture.mimeType)); CPPUNIT_ASSERT_EQUAL("A pixel."s, std::string(picture.description)); CPPUNIT_ASSERT_EQUAL("Front Cover"s, std::string(picture.pictureType)); CPPUNIT_ASSERT_EQUAL(expectedData, ByteVector(picture.data, 150)); CPPUNIT_ASSERT_EQUAL(150U, picture.size); taglib_complex_property_free(properties); taglib_file_free(file); taglib_iostream_free(stream); } taglib_tag_free_strings(); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestTagC); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_trueaudio.cpp���������������������������������������������������������������0000664�0000000�0000000�00000012230�14662262111�0017662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tpropertymap.h" #include "id3v1tag.h" #include "id3v2tag.h" #include "trueaudiofile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestTrueAudio : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestTrueAudio); CPPUNIT_TEST(testReadPropertiesWithoutID3v2); CPPUNIT_TEST(testReadPropertiesWithTags); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: void testReadPropertiesWithoutID3v2() { TrueAudio::File f(TEST_FILE_PATH_C("empty.tta")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(173, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->ttaVersion()); } void testReadPropertiesWithTags() { TrueAudio::File f(TEST_FILE_PATH_C("tagged.tta")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(173, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(162496U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->ttaVersion()); } void testStripAndProperties() { ScopedFileCopy copy("empty", ".tta"); { TrueAudio::File f(copy.fileName().c_str()); f.ID3v2Tag(true)->setTitle("ID3v2"); f.ID3v1Tag(true)->setTitle("ID3v1"); f.save(); } { TrueAudio::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v1Tag()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); f.strip(TrueAudio::File::ID3v2); CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); f.strip(TrueAudio::File::ID3v1); CPPUNIT_ASSERT(f.properties().isEmpty()); f.save(); } { TrueAudio::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.properties()["TITLE"].isEmpty()); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testRepeatedSave() { ScopedFileCopy copy("empty", ".tta"); { TrueAudio::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); f.ID3v2Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); f.ID3v2Tag()->setTitle("0"); f.save(); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.ID3v2Tag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); f.save(); } { TrueAudio::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasID3v1Tag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestTrueAudio); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_variant.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000017743�14662262111�0017343�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <array> #include "tbytevector.h" #include "tvariant.h" #include "tstringlist.h" #include "tbytevectorlist.h" #include <cppunit/extensions/HelperMacros.h> #include <sstream> #include "utils.h" using namespace TagLib; class TestVariant : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestVariant); CPPUNIT_TEST(testVariantTypes); CPPUNIT_TEST(testVariantToOStream); CPPUNIT_TEST_SUITE_END(); public: void testVariantTypes() { ByteVectorList bvl {"first", "second"}; StringList sl {"el0", "el"}; VariantList vl {"1st", "2nd"}; VariantMap vm {{"key1", "value1"}, {"key2", "value2"}}; Variant varVoid; Variant varBool(true); Variant varInt(-4); Variant varUInt(5U); Variant varLongLong(-6LL); Variant varULongLong(7ULL); Variant varDouble(1.23); Variant varString(String("test")); Variant varString2("charp"); Variant varStringList(sl); Variant varByteVector(ByteVector("data")); Variant varByteVectorList(bvl); Variant varVariantList(vl); Variant varVariantMap(vm); static const std::array<std::tuple<Variant, Variant::Type>, 14> varTypes { std::tuple{varVoid, Variant::Void}, std::tuple{varBool, Variant::Bool}, std::tuple{varInt, Variant::Int}, std::tuple{varUInt, Variant::UInt}, std::tuple{varLongLong, Variant::LongLong}, std::tuple{varULongLong, Variant::ULongLong}, std::tuple{varDouble, Variant::Double}, std::tuple{varString, Variant::String}, std::tuple{varString2, Variant::String}, std::tuple{varStringList, Variant::StringList}, std::tuple{varByteVector, Variant::ByteVector}, std::tuple{varByteVectorList, Variant::ByteVectorList}, std::tuple{varVariantList, Variant::VariantList}, std::tuple{varVariantMap, Variant::VariantMap} }; for(const auto &t : varTypes) { CPPUNIT_ASSERT_EQUAL(std::get<1>(t), std::get<0>(t).type()); if(std::get<0>(t).type() == Variant::Void) { CPPUNIT_ASSERT(std::get<0>(t).isEmpty()); } else { CPPUNIT_ASSERT(!std::get<0>(t).isEmpty()); } } bool ok; CPPUNIT_ASSERT_EQUAL(true, varBool.toBool(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(true, varBool.value<bool>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(-4, varInt.toInt(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(-4, varInt.value<int>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(5U, varUInt.toUInt(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(5U, varUInt.value<unsigned int>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(-6LL, varLongLong.toLongLong(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(-6LL, varLongLong.value<long long>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(7ULL, varULongLong.toULongLong(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(7ULL, varULongLong.value<unsigned long long>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(1.23, varDouble.toDouble(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(1.23, varDouble.value<double>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(String("test"), varString.toString(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(String("test"), varString.value<String>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(String("charp"), varString2.toString(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(String("charp"), varString2.value<String>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(sl, varStringList.toStringList(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(sl, varStringList.value<StringList>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(ByteVector("data"), varByteVector.toByteVector(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(ByteVector("data"), varByteVector.value<ByteVector>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(bvl, varByteVectorList.toByteVectorList(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(bvl, varByteVectorList.value<ByteVectorList>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(vl, varVariantList.toList(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(vl, varVariantList.value<VariantList>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(vm, varVariantMap.toMap(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(vm, varVariantMap.value<VariantMap>(&ok)); CPPUNIT_ASSERT(ok); CPPUNIT_ASSERT_EQUAL(0, varBool.toInt(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(0U, varInt.toUInt(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(0LL, varUInt.toLongLong(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(0ULL, varLongLong.toULongLong(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(0.0, varULongLong.toDouble(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(String(), varDouble.toString(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(StringList(), varString.toStringList(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(ByteVector(), varStringList.toByteVector(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT(varByteVector.toByteVectorList(&ok).isEmpty()); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(VariantList(), varByteVectorList.toList(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(VariantMap(), varVariantList.toMap(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT_EQUAL(false, varVariantMap.toBool(&ok)); CPPUNIT_ASSERT(!ok); CPPUNIT_ASSERT(varUInt == varUInt); CPPUNIT_ASSERT(varUInt == Variant(5U)); CPPUNIT_ASSERT(varUInt != Variant(6U)); CPPUNIT_ASSERT(varUInt != Variant(5)); CPPUNIT_ASSERT(varUInt != varInt); Variant varUInt2(varUInt); CPPUNIT_ASSERT(varUInt == varUInt2); varUInt2 = 6U; CPPUNIT_ASSERT(varUInt != varUInt2); CPPUNIT_ASSERT_EQUAL(5U, varUInt.toUInt()); CPPUNIT_ASSERT_EQUAL(6U, varUInt2.toUInt()); } void testVariantToOStream() { std::stringstream ss; VariantMap vm { {"strlist", StringList {"first", "second"}}, {"varlist", VariantList {Variant(), 1U, -10LL, 4.32, false}}, {"data", ByteVector("\xa9\x01\x7f", 3)} }; ss << vm; CPPUNIT_ASSERT_EQUAL( R"({"data": "\xa9\x01\x7f", "strlist": ["first", "second"],)" R"( "varlist": [null, 1, -10, 4.32, false]})"s, ss.str()); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestVariant); �����������������������������taglib-2.0.2/tests/test_versionnumber.cpp�����������������������������������������������������������0000664�0000000�0000000�00000006747�14662262111�0020577�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2020 by Kevin Andre email : hyperquantum@gmail.com copyright : (C) 2023 by Urs Fleisch email : ufleisch@users.sourceforge.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include "tversionnumber.h" #include "tstring.h" #include "taglib.h" #include <cppunit/extensions/HelperMacros.h> using namespace TagLib; class TestTagLib : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestTagLib); CPPUNIT_TEST(testVersionNumber); CPPUNIT_TEST(testRuntimeVersion); CPPUNIT_TEST_SUITE_END(); public: void testVersionNumber() { VersionNumber v210(2, 1, 0); VersionNumber v211(2, 1, 1); VersionNumber v220(2, 2, 0); VersionNumber v300(3, 0, 0); CPPUNIT_ASSERT_EQUAL(0x020100U, v210.combinedVersion()); CPPUNIT_ASSERT_EQUAL(2U, v210.majorVersion()); CPPUNIT_ASSERT_EQUAL(1U, v210.minorVersion()); CPPUNIT_ASSERT_EQUAL(0U, v210.patchVersion()); CPPUNIT_ASSERT(v210 == VersionNumber(2, 1)); CPPUNIT_ASSERT(!(v210 == v211)); CPPUNIT_ASSERT(v210 != v211); CPPUNIT_ASSERT(!(v210 != v210)); CPPUNIT_ASSERT(v210 < v211); CPPUNIT_ASSERT(!(v220 < v210)); CPPUNIT_ASSERT(v220 > v211); CPPUNIT_ASSERT(!(v210 > v211)); CPPUNIT_ASSERT(v210 <= v210); CPPUNIT_ASSERT(v210 <= v300); CPPUNIT_ASSERT(!(v300 <= v220)); CPPUNIT_ASSERT(v210 >= v210); CPPUNIT_ASSERT(v220 >= v210); CPPUNIT_ASSERT(!(v210 >= v300)); CPPUNIT_ASSERT_EQUAL(String("2.1.0"), v210.toString()); } void testRuntimeVersion() { CPPUNIT_ASSERT(runtimeVersion() >= VersionNumber( TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION)); CPPUNIT_ASSERT(runtimeVersion() >= VersionNumber( TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION)); CPPUNIT_ASSERT(runtimeVersion() == VersionNumber( TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION)); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestTagLib); �������������������������taglib-2.0.2/tests/test_wav.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000041552�14662262111�0016467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "id3v2tag.h" #include "infotag.h" #include "tbytevectorlist.h" #include "tbytevectorstream.h" #include "tfilestream.h" #include "tpropertymap.h" #include "wavfile.h" #include "plainfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestWAV : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestWAV); CPPUNIT_TEST(testPCMProperties); CPPUNIT_TEST(testALAWProperties); CPPUNIT_TEST(testFloatProperties); CPPUNIT_TEST(testFloatWithoutFactChunkProperties); CPPUNIT_TEST(testZeroSizeDataChunk); CPPUNIT_TEST(testID3v2Tag); CPPUNIT_TEST(testSaveID3v23); CPPUNIT_TEST(testInfoTag); CPPUNIT_TEST(testStripTags); CPPUNIT_TEST(testDuplicateTags); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST(testFileWithGarbageAppended); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testPCMWithFactChunk); CPPUNIT_TEST(testWaveFormatExtensible); CPPUNIT_TEST(testInvalidChunk); CPPUNIT_TEST(testRIFFInfoProperties); CPPUNIT_TEST_SUITE_END(); public: void testPCMProperties() { RIFF::WAV::File f(TEST_FILE_PATH_C("empty.wav")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(32, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->format()); } void testALAWProperties() { RIFF::WAV::File f(TEST_FILE_PATH_C("alaw.wav")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(128, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(8000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(28400U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(6, f.audioProperties()->format()); } void testFloatProperties() { RIFF::WAV::File f(TEST_FILE_PATH_C("float64.wav")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(97, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(5645, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(4281U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->format()); } void testFloatWithoutFactChunkProperties() { ByteVector wavData = PlainFile(TEST_FILE_PATH_C("float64.wav")).readAll(); CPPUNIT_ASSERT_EQUAL(ByteVector("fact"), wavData.mid(36, 4)); // Remove the fact chunk by renaming it to fakt wavData[38] = 'k'; ByteVectorStream wavStream(wavData); RIFF::WAV::File f(&wavStream); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(97, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(5645, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(4281U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->format()); } void testZeroSizeDataChunk() { RIFF::WAV::File f(TEST_FILE_PATH_C("zero-size-chunk.wav")); CPPUNIT_ASSERT(f.isValid()); } void testID3v2Tag() { ScopedFileCopy copy("empty", ".wav"); string filename = copy.fileName(); { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); f.ID3v2Tag()->setTitle(L"Title"); f.ID3v2Tag()->setArtist(L"Artist"); f.save(); CPPUNIT_ASSERT(f.hasID3v2Tag()); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.ID3v2Tag()->title()); CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.ID3v2Tag()->artist()); f.ID3v2Tag()->setTitle(L""); f.ID3v2Tag()->setArtist(L""); f.save(); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->title()); CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->artist()); } } void testSaveID3v23() { ScopedFileCopy copy("empty", ".wav"); string newname = copy.fileName(); String xxx = ByteVector(254, 'X'); { RIFF::WAV::File f(newname.c_str()); CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(RIFF::WAV::File::AllTags, File::StripOthers, ID3v2::v3); CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); } { RIFF::WAV::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(3), f2.ID3v2Tag()->header()->majorVersion()); CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist()); CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title()); } } void testInfoTag() { ScopedFileCopy copy("empty", ".wav"); string filename = copy.fileName(); { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasInfoTag()); f.InfoTag()->setTitle(L"Title"); f.InfoTag()->setArtist(L"Artist"); f.save(); CPPUNIT_ASSERT(f.hasInfoTag()); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(f.hasInfoTag()); CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.InfoTag()->title()); CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.InfoTag()->artist()); f.InfoTag()->setTitle(L""); f.InfoTag()->setArtist(L""); f.save(); CPPUNIT_ASSERT(!f.hasInfoTag()); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT(!f.hasInfoTag()); CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->title()); CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->artist()); } } void testStripTags() { ScopedFileCopy copy("empty", ".wav"); string filename = copy.fileName(); { RIFF::WAV::File f(filename.c_str()); f.ID3v2Tag()->setTitle("test title"); f.InfoTag()->setTitle("test title"); f.save(); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasInfoTag()); f.save(RIFF::WAV::File::ID3v2, File::StripOthers); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(!f.hasInfoTag()); f.ID3v2Tag()->setTitle("test title"); f.InfoTag()->setTitle("test title"); f.save(); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasInfoTag()); f.save(RIFF::WAV::File::Info, File::StripOthers); } { RIFF::WAV::File f(filename.c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); CPPUNIT_ASSERT(f.hasInfoTag()); } } void testDuplicateTags() { ScopedFileCopy copy("duplicate_tags", ".wav"); RIFF::WAV::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(17052), f.length()); // duplicate_tags.wav has duplicate ID3v2/INFO tags. // title() returns "Title2" if can't skip the second tag. CPPUNIT_ASSERT(f.hasID3v2Tag()); CPPUNIT_ASSERT_EQUAL(String("Title1"), f.ID3v2Tag()->title()); CPPUNIT_ASSERT(f.hasInfoTag()); CPPUNIT_ASSERT_EQUAL(String("Title1"), f.InfoTag()->title()); f.save(); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(15898), f.length()); CPPUNIT_ASSERT_EQUAL(static_cast<offset_t>(-1), f.find("Title2")); } void testFuzzedFile1() { RIFF::WAV::File f1(TEST_FILE_PATH_C("infloop.wav")); CPPUNIT_ASSERT(f1.isValid()); // The file has problems: // Chunk 'ISTt' has invalid size (larger than the file size). // Its properties can nevertheless be read. RIFF::WAV::Properties* properties = f1.audioProperties(); CPPUNIT_ASSERT_EQUAL(1, properties->channels()); CPPUNIT_ASSERT_EQUAL(88, properties->bitrate()); CPPUNIT_ASSERT_EQUAL(8, properties->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(11025, properties->sampleRate()); CPPUNIT_ASSERT(!f1.hasInfoTag()); CPPUNIT_ASSERT(!f1.hasID3v2Tag()); } void testFuzzedFile2() { RIFF::WAV::File f2(TEST_FILE_PATH_C("segfault.wav")); CPPUNIT_ASSERT(f2.isValid()); } void testFileWithGarbageAppended() { ScopedFileCopy copy("empty", ".wav"); ByteVector contentsBeforeModification; { FileStream stream(copy.fileName().c_str()); stream.seek(0, IOStream::End); constexpr char garbage[] = "12345678"; stream.writeBlock(ByteVector(garbage, sizeof(garbage) - 1)); stream.seek(0); contentsBeforeModification = stream.readBlock(stream.length()); } { RIFF::WAV::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.isValid()); f.ID3v2Tag()->setTitle("ID3v2 Title"); f.InfoTag()->setTitle("INFO Title"); CPPUNIT_ASSERT(f.save()); } { RIFF::WAV::File f(copy.fileName().c_str()); f.strip(); } { FileStream stream(copy.fileName().c_str()); ByteVector contentsAfterModification = stream.readBlock(stream.length()); CPPUNIT_ASSERT_EQUAL(contentsBeforeModification, contentsAfterModification); } } void testStripAndProperties() { ScopedFileCopy copy("empty", ".wav"); { RIFF::WAV::File f(copy.fileName().c_str()); f.ID3v2Tag()->setTitle("ID3v2"); f.InfoTag()->setTitle("INFO"); f.save(); } { RIFF::WAV::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("ID3v2"), f.properties()["TITLE"].front()); f.strip(RIFF::WAV::File::ID3v2); CPPUNIT_ASSERT_EQUAL(String("INFO"), f.properties()["TITLE"].front()); f.strip(RIFF::WAV::File::Info); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testPCMWithFactChunk() { RIFF::WAV::File f(TEST_FILE_PATH_C("pcm_with_fact_chunk.wav")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(32, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->format()); } void testWaveFormatExtensible() { RIFF::WAV::File f(TEST_FILE_PATH_C("uint8we.wav")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(2937, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(128, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(8000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(23493U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->format()); } void testInvalidChunk() { ScopedFileCopy copy("invalid-chunk", ".wav"); { RIFF::WAV::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT(f.hasID3v2Tag()); f.ID3v2Tag()->setTitle("Title"); f.save(); } { RIFF::WAV::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasID3v2Tag()); } } void testRIFFInfoProperties() { PropertyMap tags; tags["ALBUM"] = StringList("Album"); tags["ARRANGER"] = StringList("Arranger"); tags["ARTIST"] = StringList("Artist"); tags["ARTISTWEBPAGE"] = StringList("Artist Webpage"); tags["BPM"] = StringList("123"); tags["COMMENT"] = StringList("Comment"); tags["COMPOSER"] = StringList("Composer"); tags["COPYRIGHT"] = StringList("2023 Copyright"); tags["DATE"] = StringList("2023"); tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); tags["ENCODEDBY"] = StringList("Encoded by"); tags["ENCODING"] = StringList("Encoding"); tags["ENCODINGTIME"] = StringList("2023-11-25 15:42:39"); tags["GENRE"] = StringList("Genre"); tags["ISRC"] = StringList("UKAAA0500001"); tags["LABEL"] = StringList("Label"); tags["LANGUAGE"] = StringList("eng"); tags["LYRICIST"] = StringList("Lyricist"); tags["MEDIA"] = StringList("Media"); tags["PERFORMER"] = StringList("Performer"); tags["RELEASECOUNTRY"] = StringList("Release Country"); tags["REMIXER"] = StringList("Remixer"); tags["TITLE"] = StringList("Title"); tags["TRACKNUMBER"] = StringList("2/4"); ScopedFileCopy copy("empty", ".wav"); { RIFF::WAV::File f(copy.fileName().c_str()); RIFF::Info::Tag *infoTag = f.InfoTag(); CPPUNIT_ASSERT(infoTag->isEmpty()); PropertyMap properties = infoTag->properties(); CPPUNIT_ASSERT(properties.isEmpty()); infoTag->setProperties(tags); f.save(); } { const RIFF::WAV::File f(copy.fileName().c_str()); RIFF::Info::Tag *infoTag = f.InfoTag(); CPPUNIT_ASSERT(!infoTag->isEmpty()); PropertyMap properties = infoTag->properties(); if (tags != properties) { CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); } CPPUNIT_ASSERT(tags == properties); const RIFF::Info::FieldListMap expectedFields = { {"IPRD", "Album"}, {"IENG", "Arranger"}, {"IART", "Artist"}, {"IBSU", "Artist Webpage"}, {"IBPM", "123"}, {"ICMT", "Comment"}, {"IMUS", "Composer"}, {"ICOP", "2023 Copyright"}, {"ICRD", "2023"}, {"PRT1", "Disc Subtitle"}, {"ITCH", "Encoded by"}, {"ISFT", "Encoding"}, {"IDIT", "2023-11-25 15:42:39"}, {"IGNR", "Genre"}, {"ISRC", "UKAAA0500001"}, {"IPUB", "Label"}, {"ILNG", "eng"}, {"IWRI", "Lyricist"}, {"IMED", "Media"}, {"ISTR", "Performer"}, {"ICNT", "Release Country"}, {"IEDT", "Remixer"}, {"INAM", "Title"}, {"IPRT", "2/4"} }; CPPUNIT_ASSERT(expectedFields == infoTag->fieldListMap()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestWAV); ������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_wavpack.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000016615�14662262111�0017330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2010 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tbytevectorlist.h" #include "tpropertymap.h" #include "apetag.h" #include "id3v1tag.h" #include "wavpackfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestWavPack : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestWavPack); CPPUNIT_TEST(testNoLengthProperties); CPPUNIT_TEST(testMultiChannelProperties); CPPUNIT_TEST(testDsdStereoProperties); CPPUNIT_TEST(testNonStandardRateProperties); CPPUNIT_TEST(testTaggedProperties); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testStripAndProperties); CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: void testNoLengthProperties() { WavPack::File f(TEST_FILE_PATH_C("no_length.wv")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(163392U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); } void testMultiChannelProperties() { WavPack::File f(TEST_FILE_PATH_C("four_channels.wv")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3833, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(112, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isLossless()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(169031U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); } void testDsdStereoProperties() { WavPack::File f(TEST_FILE_PATH_C("dsd_stereo.wv")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(200, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(2096, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); CPPUNIT_ASSERT_EQUAL(352800, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(70560U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1040, f.audioProperties()->version()); } void testNonStandardRateProperties() { WavPack::File f(TEST_FILE_PATH_C("non_standard_rate.wv")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1040, f.audioProperties()->version()); } void testTaggedProperties() { WavPack::File f(TEST_FILE_PATH_C("tagged.wv")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(172, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isLossless()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); } void testFuzzedFile() { WavPack::File f(TEST_FILE_PATH_C("infloop.wv")); CPPUNIT_ASSERT(f.isValid()); } void testStripAndProperties() { ScopedFileCopy copy("click", ".wv"); { WavPack::File f(copy.fileName().c_str()); f.APETag(true)->setTitle("APE"); f.ID3v1Tag(true)->setTitle("ID3v1"); f.save(); } { WavPack::File f(copy.fileName().c_str()); CPPUNIT_ASSERT_EQUAL(String("APE"), f.properties()["TITLE"].front()); f.strip(WavPack::File::APE); CPPUNIT_ASSERT_EQUAL(String("ID3v1"), f.properties()["TITLE"].front()); f.strip(WavPack::File::ID3v1); CPPUNIT_ASSERT(f.properties().isEmpty()); } } void testRepeatedSave() { ScopedFileCopy copy("click", ".wv"); { WavPack::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(!f.hasAPETag()); CPPUNIT_ASSERT(!f.hasID3v1Tag()); f.APETag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.save(); f.APETag()->setTitle("0"); f.save(); f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); f.APETag()->setTitle("01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789"); f.save(); } { WavPack::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.hasAPETag()); CPPUNIT_ASSERT(f.hasID3v1Tag()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestWavPack); �������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_xiphcomment.cpp�������������������������������������������������������������0000664�0000000�0000000�00000017135�14662262111�0020225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2009 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <string> #include <cstdio> #include "tpropertymap.h" #include "xiphcomment.h" #include "vorbisfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; class TestXiphComment : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestXiphComment); CPPUNIT_TEST(testYear); CPPUNIT_TEST(testSetYear); CPPUNIT_TEST(testTrack); CPPUNIT_TEST(testSetTrack); CPPUNIT_TEST(testInvalidKeys1); CPPUNIT_TEST(testInvalidKeys2); CPPUNIT_TEST(testClearComment); CPPUNIT_TEST(testRemoveFields); CPPUNIT_TEST(testPicture); CPPUNIT_TEST(testLowercaseFields); CPPUNIT_TEST_SUITE_END(); public: void testYear() { Ogg::XiphComment cmt; CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), cmt.year()); cmt.addField("YEAR", "2009"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2009), cmt.year()); cmt.addField("DATE", "2008"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(2008), cmt.year()); } void testSetYear() { Ogg::XiphComment cmt; cmt.addField("YEAR", "2009"); cmt.addField("DATE", "2008"); cmt.setYear(1995); CPPUNIT_ASSERT(cmt.fieldListMap()["YEAR"].isEmpty()); CPPUNIT_ASSERT_EQUAL(String("1995"), cmt.fieldListMap()["DATE"].front()); } void testTrack() { Ogg::XiphComment cmt; CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0), cmt.track()); cmt.addField("TRACKNUM", "7"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(7), cmt.track()); cmt.addField("TRACKNUMBER", "8"); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8), cmt.track()); } void testSetTrack() { Ogg::XiphComment cmt; cmt.addField("TRACKNUM", "7"); cmt.addField("TRACKNUMBER", "8"); cmt.setTrack(3); CPPUNIT_ASSERT(cmt.fieldListMap()["TRACKNUM"].isEmpty()); CPPUNIT_ASSERT_EQUAL(String("3"), cmt.fieldListMap()["TRACKNUMBER"].front()); } void testInvalidKeys1() { PropertyMap map; map[""] = String("invalid key: empty string"); map["A=B"] = String("invalid key: contains '='"); map["A~B"] = String("invalid key: contains '~'"); map["A\x7F" "B"] = String("invalid key: contains '\x7F'"); map[L"A\x3456" "B"] = String("invalid key: Unicode"); Ogg::XiphComment cmt; PropertyMap unsuccessful = cmt.setProperties(map); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(5), unsuccessful.size()); CPPUNIT_ASSERT(cmt.properties().isEmpty()); } void testInvalidKeys2() { Ogg::XiphComment cmt; cmt.addField("", "invalid key: empty string"); cmt.addField("A=B", "invalid key: contains '='"); cmt.addField("A~B", "invalid key: contains '~'"); cmt.addField("A\x7F" "B", "invalid key: contains '\x7F'"); cmt.addField(L"A\x3456" "B", "invalid key: Unicode"); CPPUNIT_ASSERT_EQUAL(0U, cmt.fieldCount()); } void testClearComment() { ScopedFileCopy copy("empty", ".ogg"); { Ogg::Vorbis::File f(copy.fileName().c_str()); f.tag()->addField("COMMENT", "Comment1"); f.save(); } { Ogg::Vorbis::File f(copy.fileName().c_str()); f.tag()->setComment(""); CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->comment()); } } void testRemoveFields() { Ogg::Vorbis::File f(TEST_FILE_PATH_C("empty.ogg")); f.tag()->addField("title", "Title1"); f.tag()->addField("Title", "Title1", false); f.tag()->addField("titlE", "Title2", false); f.tag()->addField("TITLE", "Title3", false); f.tag()->addField("artist", "Artist1"); f.tag()->addField("ARTIST", "Artist2", false); CPPUNIT_ASSERT_EQUAL(String("Title1 / Title1 / Title2 / Title3"), f.tag()->title()); CPPUNIT_ASSERT_EQUAL(String("Artist1 / Artist2"), f.tag()->artist()); f.tag()->removeFields("title", "Title1"); CPPUNIT_ASSERT_EQUAL(String("Title2 / Title3"), f.tag()->title()); CPPUNIT_ASSERT_EQUAL(String("Artist1 / Artist2"), f.tag()->artist()); f.tag()->removeFields("Artist"); CPPUNIT_ASSERT_EQUAL(String("Title2 / Title3"), f.tag()->title()); CPPUNIT_ASSERT(f.tag()->artist().isEmpty()); f.tag()->removeAllFields(); CPPUNIT_ASSERT(f.tag()->title().isEmpty()); CPPUNIT_ASSERT(f.tag()->artist().isEmpty()); CPPUNIT_ASSERT_EQUAL(String("Xiph.Org libVorbis I 20050304"), f.tag()->vendorID()); } void testPicture() { ScopedFileCopy copy("empty", ".ogg"); string newname = copy.fileName(); { Vorbis::File f(newname.c_str()); auto newpic = new FLAC::Picture(); newpic->setType(FLAC::Picture::BackCover); newpic->setWidth(5); newpic->setHeight(6); newpic->setColorDepth(16); newpic->setNumColors(7); newpic->setMimeType("image/jpeg"); newpic->setDescription("new image"); newpic->setData("JPEG data"); f.tag()->addPicture(newpic); f.save(); } { Vorbis::File f(newname.c_str()); List<FLAC::Picture *> lst = f.tag()->pictureList(); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); CPPUNIT_ASSERT_EQUAL(5, lst[0]->width()); CPPUNIT_ASSERT_EQUAL(6, lst[0]->height()); CPPUNIT_ASSERT_EQUAL(16, lst[0]->colorDepth()); CPPUNIT_ASSERT_EQUAL(7, lst[0]->numColors()); CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), lst[0]->mimeType()); CPPUNIT_ASSERT_EQUAL(String("new image"), lst[0]->description()); CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), lst[0]->data()); } } void testLowercaseFields() { const ScopedFileCopy copy("lowercase-fields", ".ogg"); { Vorbis::File f(copy.fileName().c_str()); List<FLAC::Picture *> lst = f.tag()->pictureList(); CPPUNIT_ASSERT_EQUAL(String("TEST TITLE"), f.tag()->title()); CPPUNIT_ASSERT_EQUAL(String("TEST ARTIST"), f.tag()->artist()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1), lst.size()); f.save(); } { Vorbis::File f(copy.fileName().c_str()); CPPUNIT_ASSERT(f.find("METADATA_BLOCK_PICTURE") > 0); } } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestXiphComment); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/test_xm.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000020237�14662262111�0016313�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #include <cassert> #include "xmfile.h" #include <cppunit/extensions/HelperMacros.h> #include "utils.h" using namespace std; using namespace TagLib; static const String titleBefore("title of song"); static const String titleAfter("changed title"); static const String trackerNameBefore("MilkyTracker "); static const String trackerNameAfter("TagLib"); static const String commentBefore( "Instrument names\n" "are abused as\n" "comments in\n" "module file formats.\n" "-+-+-+-+-+-+-+-+-+-+-+\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n" "Sample\n" "names\n" "are sometimes\n" "also abused as\n" "comments."); static const String newCommentShort( "Instrument names\n" "are abused as\n" "comments in\n" "module file formats.\n" "======================\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n" "Sample names\n" "are sometimes\n" "also abused as\n" "comments."); static const String newCommentLong( "Instrument names\n" "are abused as\n" "comments in\n" "module file formats.\n" "======================\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n" "Sample names\n" "are sometimes\n" "also abused as\n" "comments.\n" "\n\n\n\n\n\n\n" "TEST"); static const String commentAfter( "Instrument names\n" "are abused as\n" "comments in\n" "module file formats.\n" "======================\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n" "Sample names\n" "are sometimes\n" "also abused as\n" "comments.\n"); class TestXM : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestXM); CPPUNIT_TEST(testReadTags); CPPUNIT_TEST(testReadStrippedTags); CPPUNIT_TEST(testWriteTagsShort); CPPUNIT_TEST(testWriteTagsLong); CPPUNIT_TEST_SUITE_END(); public: void testReadTags() { testRead(TEST_FILE_PATH_C("test.xm"), titleBefore, commentBefore, trackerNameBefore); } void testReadStrippedTags() { XM::File file(TEST_FILE_PATH_C("stripped.xm")); CPPUNIT_ASSERT(file.isValid()); XM::Properties *p = file.audioProperties(); Mod::Tag *t = file.tag(); CPPUNIT_ASSERT(nullptr != p); CPPUNIT_ASSERT(nullptr != t); assert(p != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); CPPUNIT_ASSERT_EQUAL(8, p->channels()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->lengthInPatterns()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->version()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0) , p->restartPosition()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->patternCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->instrumentCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->flags()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(6), p->tempo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(125), p->bpmSpeed()); CPPUNIT_ASSERT_EQUAL(titleBefore, t->title()); CPPUNIT_ASSERT_EQUAL(String(), t->artist()); CPPUNIT_ASSERT_EQUAL(String(), t->album()); CPPUNIT_ASSERT_EQUAL(String(), t->comment()); CPPUNIT_ASSERT_EQUAL(String(), t->genre()); CPPUNIT_ASSERT_EQUAL(0U, t->year()); CPPUNIT_ASSERT_EQUAL(0U, t->track()); CPPUNIT_ASSERT_EQUAL(String(), t->trackerName()); } void testWriteTagsShort() { testWriteTags(newCommentShort); } void testWriteTagsLong() { testWriteTags(newCommentLong); } private: void testRead(FileName fileName, const String &title, const String &comment, const String &trackerName) { XM::File file(fileName); CPPUNIT_ASSERT(file.isValid()); XM::Properties *p = file.audioProperties(); Mod::Tag *t = file.tag(); CPPUNIT_ASSERT(nullptr != p); CPPUNIT_ASSERT(nullptr != t); assert(p != nullptr); // to silence the clang analyzer CPPUNIT_ASSERT_EQUAL(0, p->lengthInSeconds()); CPPUNIT_ASSERT_EQUAL(0, p->bitrate()); CPPUNIT_ASSERT_EQUAL(0, p->sampleRate()); CPPUNIT_ASSERT_EQUAL(8, p->channels()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->lengthInPatterns()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(260), p->version()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(0), p->restartPosition()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->patternCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(128), p->instrumentCount()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(1), p->flags()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(6), p->tempo()); CPPUNIT_ASSERT_EQUAL(static_cast<unsigned short>(125), p->bpmSpeed()); CPPUNIT_ASSERT_EQUAL(title, t->title()); CPPUNIT_ASSERT_EQUAL(String(), t->artist()); CPPUNIT_ASSERT_EQUAL(String(), t->album()); CPPUNIT_ASSERT_EQUAL(comment, t->comment()); CPPUNIT_ASSERT_EQUAL(String(), t->genre()); CPPUNIT_ASSERT_EQUAL(0U, t->year()); CPPUNIT_ASSERT_EQUAL(0U, t->track()); CPPUNIT_ASSERT_EQUAL(trackerName, t->trackerName()); } void testWriteTags(const String &comment) { ScopedFileCopy copy("test", ".xm"); { XM::File file(copy.fileName().c_str()); CPPUNIT_ASSERT(file.tag() != nullptr); file.tag()->setTitle(titleAfter); file.tag()->setComment(comment); file.tag()->setTrackerName(trackerNameAfter); CPPUNIT_ASSERT(file.save()); } testRead(copy.fileName().c_str(), titleAfter, commentAfter, trackerNameAfter); CPPUNIT_ASSERT(fileEqual( copy.fileName(), testFilePath("changed.xm"))); } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestXM); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������taglib-2.0.2/tests/utils.h��������������������������������������������������������������������������0000664�0000000�0000000�00000010523�14662262111�0015432�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * * 2.1 as published by the Free Software Foundation. * * * * This library is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * * 02110-1301 USA * * * * Alternatively, this file is available under the Mozilla Public * * License Version 1.1. You may obtain a copy of the License at * * http://www.mozilla.org/MPL/ * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #include <fcntl.h> #include <sys/fcntl.h> #include <sys/stat.h> #endif #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <fstream> using namespace std; inline string testFilePath(const string &filename) { return string(TESTS_DIR "data/") + filename; } #define TEST_FILE_PATH_C(f) testFilePath(f).c_str() inline string copyFile(const string &filename, const string &ext) { char testFileName[1024]; #ifdef _WIN32 char tempDir[MAX_PATH + 1]; GetTempPathA(sizeof(tempDir), tempDir); wsprintfA(testFileName, "%s\\taglib-test%s", tempDir, ext.c_str()); #else snprintf(testFileName, sizeof(testFileName), "/%s/taglib-test%s", P_tmpdir, ext.c_str()); #endif string sourceFileName = testFilePath(filename) + ext; ifstream source(sourceFileName.c_str(), std::ios::binary); ofstream destination(testFileName, std::ios::binary); destination << source.rdbuf(); return string(testFileName); } inline void deleteFile(const string &filename) { remove(filename.c_str()); } inline bool fileEqual(const string &filename1, const string &filename2) { char buf1[BUFSIZ]; char buf2[BUFSIZ]; ifstream stream1(filename1.c_str(), ios_base::in | ios_base::binary); ifstream stream2(filename2.c_str(), ios_base::in | ios_base::binary); if(!stream1 && !stream2) return true; if(!stream1 || !stream2) return false; for(;;) { stream1.read(buf1, BUFSIZ); stream2.read(buf2, BUFSIZ); streamsize n1 = stream1.gcount(); streamsize n2 = stream2.gcount(); if(n1 != n2) return false; if(n1 == 0) break; if(memcmp(buf1, buf2, static_cast<size_t>(n1)) != 0) return false; } return stream1.good() == stream2.good(); } #ifdef TAGLIB_STRING_H namespace TagLib { inline String longText(size_t length, bool random = false) { const wchar_t chars[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; std::wstring text(length, L'X'); if(random) { for(size_t i = 0; i < length; ++i) text[i] = chars[rand() % 53]; } return String(text); } } // namespace TagLib #endif class ScopedFileCopy { public: ScopedFileCopy(const string &filename, const string &ext, bool deleteFile=true) : m_deleteFile(deleteFile), m_filename(copyFile(filename, ext)) { } ~ScopedFileCopy() { if(m_deleteFile) deleteFile(m_filename); } ScopedFileCopy(const ScopedFileCopy &) = delete; ScopedFileCopy &operator=(const ScopedFileCopy &) = delete; string fileName() const { return m_filename; } private: const bool m_deleteFile; const string m_filename; }; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������