From cea85dd0f0c8f29c6c45b9405fe9e3b0d1528156 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Sat, 19 Oct 2013 21:12:01 +0300 Subject: [PATCH] NowPlaying: Update TagLib to 1.9.1 --- Plugins/PluginNowPlaying/Cover.cpp | 33 - Plugins/PluginNowPlaying/Cover.h | 3 - .../PluginNowPlaying/PluginNowPlaying.vcxproj | 226 +++-- .../PluginNowPlaying.vcxproj.filters | 600 ++++++++----- .../PluginNowPlaying/taglib/ape/apefile.cpp | 60 +- Plugins/PluginNowPlaying/taglib/ape/apefile.h | 80 +- .../PluginNowPlaying/taglib/ape/apefooter.cpp | 50 +- .../PluginNowPlaying/taglib/ape/apeitem.cpp | 82 +- Plugins/PluginNowPlaying/taglib/ape/apeitem.h | 49 +- .../taglib/ape/apeproperties.cpp | 44 +- .../taglib/ape/apeproperties.h | 1 + .../PluginNowPlaying/taglib/ape/apetag.cpp | 124 ++- Plugins/PluginNowPlaying/taglib/ape/apetag.h | 42 +- .../taglib/asf/asfattribute.cpp | 8 +- .../taglib/asf/asfattribute.h | 2 +- .../PluginNowPlaying/taglib/asf/asffile.cpp | 57 +- Plugins/PluginNowPlaying/taglib/asf/asffile.h | 40 +- .../taglib/asf/asfpicture.cpp | 14 +- .../PluginNowPlaying/taglib/asf/asfpicture.h | 4 +- .../taglib/asf/asfproperties.cpp | 24 +- .../taglib/asf/asfproperties.h | 4 +- .../PluginNowPlaying/taglib/asf/asftag.cpp | 167 +++- Plugins/PluginNowPlaying/taglib/asf/asftag.h | 10 +- Plugins/PluginNowPlaying/taglib/config.h | 2 - Plugins/PluginNowPlaying/taglib/fileref.cpp | 115 +-- .../PluginNowPlaying/taglib/flac/flacfile.cpp | 105 ++- .../PluginNowPlaying/taglib/flac/flacfile.h | 115 ++- .../taglib/flac/flacmetadatablock.cpp | 6 +- .../taglib/flac/flacpicture.cpp | 26 +- .../taglib/flac/flacproperties.cpp | 25 +- .../taglib/flac/flacproperties.h | 7 +- .../taglib/flac/flacunknownmetadatablock.cpp | 6 +- .../PluginNowPlaying/taglib/mp4/mp4atom.cpp | 197 ----- Plugins/PluginNowPlaying/taglib/mp4/mp4atom.h | 77 -- .../PluginNowPlaying/taglib/mp4/mp4file.cpp | 145 ---- Plugins/PluginNowPlaying/taglib/mp4/mp4file.h | 103 --- .../PluginNowPlaying/taglib/mp4/mp4item.cpp | 149 ---- Plugins/PluginNowPlaying/taglib/mp4/mp4item.h | 72 -- .../taglib/mp4/mp4properties.cpp | 178 ---- .../taglib/mp4/mp4properties.h | 61 -- .../PluginNowPlaying/taglib/mp4/mp4tag.cpp | 636 -------------- Plugins/PluginNowPlaying/taglib/mp4/mp4tag.h | 104 --- .../PluginNowPlaying/taglib/mpc/mpcfile.cpp | 62 +- Plugins/PluginNowPlaying/taglib/mpc/mpcfile.h | 80 +- .../taglib/mpc/mpcproperties.cpp | 216 ++++- .../taglib/mpc/mpcproperties.h | 41 +- .../taglib/mpeg/id3v1/id3v1genres.cpp | 2 +- .../taglib/mpeg/id3v1/id3v1tag.cpp | 26 +- .../taglib/mpeg/id3v1/id3v1tag.h | 29 +- .../id3v2/frames/attachedpictureframe.cpp | 4 +- .../mpeg/id3v2/frames/commentsframe.cpp | 27 +- .../taglib/mpeg/id3v2/frames/commentsframe.h | 11 + .../mpeg/id3v2/frames/ownershipframe.cpp | 162 ++++ .../taglib/mpeg/id3v2/frames/ownershipframe.h | 151 ++++ .../mpeg/id3v2/frames/popularimeterframe.cpp | 2 +- .../taglib/mpeg/id3v2/frames/privateframe.cpp | 2 +- .../mpeg/id3v2/frames/relativevolumeframe.cpp | 2 +- .../id3v2/frames/textidentificationframe.cpp | 160 +++- .../id3v2/frames/textidentificationframe.h | 55 ++ .../frames/uniquefileidentifierframe.cpp | 30 + .../id3v2/frames/uniquefileidentifierframe.h | 12 +- .../frames/unsynchronizedlyricsframe.cpp | 35 +- .../id3v2/frames/unsynchronizedlyricsframe.h | 22 + .../taglib/mpeg/id3v2/frames/urllinkframe.cpp | 40 +- .../taglib/mpeg/id3v2/frames/urllinkframe.h | 17 + .../taglib/mpeg/id3v2/id3v2frame.cpp | 313 ++++++- .../taglib/mpeg/id3v2/id3v2frame.h | 108 ++- .../taglib/mpeg/id3v2/id3v2framefactory.cpp | 42 +- .../taglib/mpeg/id3v2/id3v2framefactory.h | 5 +- .../taglib/mpeg/id3v2/id3v2header.cpp | 2 +- .../taglib/mpeg/id3v2/id3v2synchdata.cpp | 9 +- .../taglib/mpeg/id3v2/id3v2tag.cpp | 273 +++++- .../taglib/mpeg/id3v2/id3v2tag.h | 112 +++ .../PluginNowPlaying/taglib/mpeg/mpegfile.cpp | 100 ++- .../PluginNowPlaying/taglib/mpeg/mpegfile.h | 137 ++- .../taglib/mpeg/mpegheader.cpp | 1 + .../taglib/mpeg/mpegproperties.cpp | 2 +- .../taglib/mpeg/xingheader.cpp | 4 +- .../taglib/ogg/flac/oggflacfile.cpp | 37 +- .../taglib/ogg/flac/oggflacfile.h | 50 +- .../PluginNowPlaying/taglib/ogg/oggfile.cpp | 11 +- Plugins/PluginNowPlaying/taglib/ogg/oggfile.h | 16 +- .../PluginNowPlaying/taglib/ogg/oggpage.cpp | 8 +- Plugins/PluginNowPlaying/taglib/ogg/oggpage.h | 4 +- .../taglib/ogg/oggpageheader.cpp | 6 +- .../taglib/ogg/speex/speexfile.cpp | 22 +- .../taglib/ogg/speex/speexfile.h | 33 +- .../taglib/ogg/speex/speexproperties.cpp | 14 +- .../taglib/ogg/vorbis/vorbisfile.cpp | 23 +- .../taglib/ogg/vorbis/vorbisfile.h | 32 +- .../taglib/ogg/vorbis/vorbisproperties.cpp | 14 +- .../taglib/ogg/xiphcomment.cpp | 61 +- .../PluginNowPlaying/taglib/ogg/xiphcomment.h | 23 + .../taglib/riff/aiff/aifffile.cpp | 117 --- .../taglib/riff/aiff/aifffile.h | 102 --- .../taglib/riff/aiff/aiffproperties.cpp | 153 ---- .../taglib/riff/aiff/aiffproperties.h | 82 -- .../PluginNowPlaying/taglib/riff/rifffile.cpp | 274 ------ .../PluginNowPlaying/taglib/riff/rifffile.h | 121 --- .../taglib/riff/wav/wavfile.cpp | 124 --- .../taglib/riff/wav/wavfile.h | 102 --- .../taglib/riff/wav/wavproperties.cpp | 120 --- .../taglib/riff/wav/wavproperties.h | 91 -- Plugins/PluginNowPlaying/taglib/tag.cpp | 97 +++ Plugins/PluginNowPlaying/taglib/tag.h | 28 + .../PluginNowPlaying/taglib/taglib_config.h | 6 +- .../PluginNowPlaying/taglib/taglib_export.h | 8 +- Plugins/PluginNowPlaying/taglib/tagunion.cpp | 1 + .../PluginNowPlaying/taglib/toolkit/taglib.h | 44 +- .../taglib/toolkit/tbytevector.cpp | 803 +++++++++++------- .../taglib/toolkit/tbytevector.h | 105 ++- .../taglib/toolkit/tbytevectorstream.cpp | 167 ++++ .../taglib/toolkit/tbytevectorstream.h | 145 ++++ .../taglib/toolkit/tdebug.cpp | 74 +- .../PluginNowPlaying/taglib/toolkit/tdebug.h | 19 +- .../tdebuglistener.cpp} | 102 ++- .../tdebuglistener.h} | 83 +- .../PluginNowPlaying/taglib/toolkit/tfile.cpp | 485 +++++------ .../PluginNowPlaying/taglib/toolkit/tfile.h | 70 +- .../taglib/toolkit/tfilestream.cpp | 492 +++++++++++ .../taglib/toolkit/tfilestream.h | 154 ++++ .../taglib/toolkit/tiostream.cpp | 150 ++++ .../taglib/toolkit/tiostream.h | 169 ++++ .../PluginNowPlaying/taglib/toolkit/tlist.h | 5 + .../PluginNowPlaying/taglib/toolkit/tlist.tcc | 10 +- .../PluginNowPlaying/taglib/toolkit/tmap.h | 1 - .../PluginNowPlaying/taglib/toolkit/tmap.tcc | 11 +- .../taglib/toolkit/tpropertymap.cpp | 173 ++++ .../taglib/toolkit/tpropertymap.h | 233 +++++ .../taglib/toolkit/trefcounter.cpp | 108 +++ .../taglib/toolkit/trefcounter.h | 111 +++ .../taglib/toolkit/tstring.cpp | 679 ++++++++------- .../PluginNowPlaying/taglib/toolkit/tstring.h | 112 ++- .../PluginNowPlaying/taglib/toolkit/tutils.h | 190 +++++ .../taglib/toolkit/unicode.cpp | 6 +- .../PluginNowPlaying/taglib/toolkit/unicode.h | 13 +- .../taglib/trueaudio/trueaudiofile.cpp | 272 ------ .../taglib/trueaudio/trueaudiofile.h | 180 ---- .../taglib/trueaudio/trueaudioproperties.cpp | 143 ---- .../taglib/trueaudio/trueaudioproperties.h | 93 -- .../taglib/wavpack/wavpackfile.cpp | 267 ------ .../taglib/wavpack/wavpackfile.h | 164 ---- .../taglib/wavpack/wavpackproperties.cpp | 194 ----- .../taglib/wavpack/wavpackproperties.h | 104 --- 144 files changed, 7731 insertions(+), 6329 deletions(-) delete mode 100644 Plugins/PluginNowPlaying/taglib/config.h delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4atom.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4atom.h delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4file.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4file.h delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4item.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4item.h delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4properties.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4properties.h delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4tag.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/mp4/mp4tag.h create mode 100644 Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.h delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.h delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.h delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/rifffile.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/rifffile.h delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.h delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.h create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.h rename Plugins/PluginNowPlaying/taglib/{mp4/mp4coverart.cpp => toolkit/tdebuglistener.cpp} (60%) rename Plugins/PluginNowPlaying/taglib/{mp4/mp4coverart.h => toolkit/tdebuglistener.h} (53%) create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.h create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tiostream.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tiostream.h create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.h create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.cpp create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.h create mode 100644 Plugins/PluginNowPlaying/taglib/toolkit/tutils.h delete mode 100644 Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.h delete mode 100644 Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.h delete mode 100644 Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.h delete mode 100644 Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.cpp delete mode 100644 Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.h diff --git a/Plugins/PluginNowPlaying/Cover.cpp b/Plugins/PluginNowPlaying/Cover.cpp index 5e0557b6..cc9c2b84 100644 --- a/Plugins/PluginNowPlaying/Cover.cpp +++ b/Plugins/PluginNowPlaying/Cover.cpp @@ -79,13 +79,6 @@ bool CCover::GetEmbedded(const TagLib::FileRef& fr, const std::wstring& target) found = ExtractAPE(file->APETag(), target); } } - else if (TagLib::MP4::File* file = dynamic_cast(fr.file())) - { - if (file->tag()) - { - found = ExtractMP4(file, target); - } - } else if (TagLib::FLAC::File* file = dynamic_cast(fr.file())) { found = ExtractFLAC(file, target); @@ -113,13 +106,6 @@ bool CCover::GetEmbedded(const TagLib::FileRef& fr, const std::wstring& target) found = ExtractAPE(file->APETag(), target); } } - else if (TagLib::WavPack::File* file = dynamic_cast(fr.file())) - { - if (file->APETag()) - { - found = ExtractAPE(file->APETag(), target); - } - } return found; } @@ -222,25 +208,6 @@ bool CCover::ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target) return false; } -/* -** Extracts cover art embedded in MP4-like files. -** -*/ -bool CCover::ExtractMP4(TagLib::MP4::File* file, const std::wstring& target) -{ - TagLib::MP4::Tag* tag = file->tag(); - if (tag->itemListMap().contains("covr")) - { - TagLib::MP4::CoverArtList coverList = tag->itemListMap()["covr"].toCoverArtList(); - if (coverList[0].data().size() > 0) - { - return WriteCover(coverList[0].data(), target); - } - } - - return false; -} - /* ** Write cover data to file. ** diff --git a/Plugins/PluginNowPlaying/Cover.h b/Plugins/PluginNowPlaying/Cover.h index 95d80359..d7670de7 100644 --- a/Plugins/PluginNowPlaying/Cover.h +++ b/Plugins/PluginNowPlaying/Cover.h @@ -30,14 +30,12 @@ #include "id3v1genres.h" #include "id3v2tag.h" #include "mpcfile.h" -#include "mp4file.h" #include "mpegfile.h" #include "tag.h" #include "taglib.h" #include "textidentificationframe.h" #include "tstring.h" #include "vorbisfile.h" -#include "wavpackfile.h" class CCover { @@ -52,7 +50,6 @@ private: static bool ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target); static bool ExtractASF(TagLib::ASF::File* file, const std::wstring& target); static bool ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target); - static bool ExtractMP4(TagLib::MP4::File* file, const std::wstring& target); static bool WriteCover(const TagLib::ByteVector& data, const std::wstring& target); }; diff --git a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj index 4cfb4fd2..312a0053 100644 --- a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj +++ b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj @@ -18,20 +18,19 @@ - _USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) - 4018;4090;4114;4351;4786;4800;4996 - .\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories) + _USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) + ./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories) + 4351;4800;4273;4251 - Psapi.lib;WinInet.lib;comsuppwd.lib;%(AdditionalDependencies) + Psapi.lib;WinInet.lib;comsuppwd.lib;D:\Biru\Downloads\taglib-1.9.1.tar\taglib-1.9.1\taglib-1.9.1\taglib\Debug\tag.lib;%(AdditionalDependencies) true - _USRDLL;HAVE_CONFIG_H;NowPlaying_EXPORTS;%(PreprocessorDefinitions) - 4018;4090;4114;4351;4786;4800;4996 - .\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories) + _USRDLL;NowPlaying_EXPORTS;%(PreprocessorDefinitions) + ./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories) Psapi.lib;WinInet.lib;comsuppwd.lib;%(AdditionalDependencies) @@ -40,9 +39,8 @@ - _USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) - 4018;4090;4099;4114;4244;4267;4309;4351;4390;4530;4786;4800;4996 - .\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories) + _USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) + ./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories) false true @@ -53,11 +51,11 @@ - _USRDLL;HAVE_CONFIG_H;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) - 4018;4090;4099;4114;4244;4267;4309;4351;4390;4530;4786;4800;4996 - .\sha2;.\taglib;.\taglib\toolkit;.\taglib\mpeg\id3v2\frames;.\taglib\ogg;.\taglib\asf;.\taglib\mp4;.\taglib\ogg\vorbis;.\taglib\ogg\flac;.\taglib\ogg\speex;.\taglib\riff;.\taglib\riff\wav;.\taglib\riff\aiff;.\taglib\flac;.\taglib\mpeg;.\taglib\mpeg\id3v1;.\taglib\mpeg\id3v2;.\taglib\mpc;.\taglib\ape;.\taglib\trueaudio;.\taglib\wavpack;.\SDKs\;%(AdditionalIncludeDirectories) + _USRDLL;PluginNowPlaying_EXPORTS;%(PreprocessorDefinitions) + ./taglib;./taglib/toolkit;./taglib/asf;./taglib/mpeg;./taglib/ogg;./taglib/ogg/flac;./taglib/flac;./taglib/mpc;./taglib/mp4;./taglib/ogg/vorbis;./taglib/mpeg/id3v2;./taglib/mpeg/id3v2/frames;./taglib/mpeg/id3v1;./taglib/ape;./taglib/wavpack;.\SDKs\;%(AdditionalIncludeDirectories) false true + 4351;4800;4267 Psapi.lib;WinInet.lib;comsuppw.lib;%(AdditionalDependencies) @@ -65,6 +63,7 @@ + @@ -79,75 +78,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -169,6 +102,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters index 7eda38c5..22688cc7 100644 --- a/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters +++ b/Plugins/PluginNowPlaying/PluginNowPlaying.vcxproj.filters @@ -30,207 +30,6 @@ SDKs\iTunes - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - - - taglib - Source Files @@ -270,6 +69,204 @@ Source Files + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + @@ -332,6 +329,207 @@ Header Files + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + + + taglib + diff --git a/Plugins/PluginNowPlaying/taglib/ape/apefile.cpp b/Plugins/PluginNowPlaying/taglib/ape/apefile.cpp index 5d914756..3cb9d9a7 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apefile.cpp +++ b/Plugins/PluginNowPlaying/taglib/ape/apefile.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "apefile.h" @@ -46,7 +47,7 @@ using namespace TagLib; namespace { - enum { APEIndex, ID3v1Index }; + enum { ApeAPEIndex = 0, ApeID3v1Index = 1 }; } class APE::File::FilePrivate @@ -89,7 +90,16 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +APE::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } APE::File::~File() @@ -102,6 +112,30 @@ TagLib::Tag *APE::File::tag() const return &d->tag; } +PropertyMap APE::File::properties() const +{ + if(d->hasAPE) + return d->tag.access(ApeAPEIndex, false)->properties(); + if(d->hasID3v1) + return d->tag.access(ApeID3v1Index, false)->properties(); + return PropertyMap(); +} + +void APE::File::removeUnsupportedProperties(const StringList &properties) +{ + if(d->hasAPE) + d->tag.access(ApeAPEIndex, false)->removeUnsupportedProperties(properties); + if(d->hasID3v1) + d->tag.access(ApeID3v1Index, false)->removeUnsupportedProperties(properties); +} + +PropertyMap APE::File::setProperties(const PropertyMap &properties) +{ + if(d->hasID3v1) + d->tag.access(ApeID3v1Index, false)->setProperties(properties); + return d->tag.access(ApeAPEIndex, true)->setProperties(properties); +} + APE::Properties *APE::File::audioProperties() const { return d->properties; @@ -178,29 +212,39 @@ bool APE::File::save() ID3v1::Tag *APE::File::ID3v1Tag(bool create) { - return d->tag.access(ID3v1Index, create); + return d->tag.access(ApeID3v1Index, create); } APE::Tag *APE::File::APETag(bool create) { - return d->tag.access(APEIndex, create); + return d->tag.access(ApeAPEIndex, create); } void APE::File::strip(int tags) { if(tags & ID3v1) { - d->tag.set(ID3v1Index, 0); + d->tag.set(ApeID3v1Index, 0); APETag(true); } if(tags & APE) { - d->tag.set(APEIndex, 0); + d->tag.set(ApeAPEIndex, 0); if(!ID3v1Tag()) APETag(true); } } +bool APE::File::hasAPETag() const +{ + return d->hasAPE; +} + +bool APE::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -212,7 +256,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty d->ID3v1Location = findID3v1(); if(d->ID3v1Location >= 0) { - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); + d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->hasID3v1 = true; } @@ -221,7 +265,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty d->APELocation = findAPE(); if(d->APELocation >= 0) { - d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); + d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation)); d->APESize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize; d->hasAPE = true; diff --git a/Plugins/PluginNowPlaying/taglib/ape/apefile.h b/Plugins/PluginNowPlaying/taglib/ape/apefile.h index a4bc80d9..f7b509f4 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apefile.h +++ b/Plugins/PluginNowPlaying/taglib/ape/apefile.h @@ -59,7 +59,7 @@ namespace TagLib { //! An implementation of TagLib::File with APE specific methods /*! - * This implements and provides an interface APE WavPack files to the + * 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. @@ -84,13 +84,26 @@ namespace TagLib { }; /*! - * Contructs an WavPack file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs an APE file from \a file. If \a readProperties is 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 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. */ @@ -102,6 +115,26 @@ namespace TagLib { */ virtual TagLib::Tag *tag() const; + /*! + * 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; + + /*! + * Removes unsupported properties. Forwards to the actual Tag's + * removeUnsupportedProperties() function. + */ + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + * Creates an APEv2 tag if necessary. A pontentially existing ID3v1 + * tag will be updated as well. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the APE::Properties for this file. If no audio properties * were read then this will return a null pointer. @@ -119,27 +152,38 @@ namespace TagLib { /*! * Returns a pointer to the ID3v1 tag of the file. * - * If \a create is false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. If there is already an APE tag, the - * new ID3v1 tag will be placed after it. + * an ID3v1 tag if one does not exist and returns a valid pointer. * - * \note The Tag is still owned by the APE::File and should not be + * \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 is still 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 false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is true it will create - * a APE tag if one does not exist. + * an APE tag if one does not exist and returns a valid pointer. * - * \note The Tag is still owned by the APE::File and should not be + * \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 is still 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); @@ -153,6 +197,20 @@ namespace TagLib { */ 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; + private: File(const File &); File &operator=(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/ape/apefooter.cpp b/Plugins/PluginNowPlaying/taglib/ape/apefooter.cpp index 1eaab254..539832ba 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apefooter.cpp +++ b/Plugins/PluginNowPlaying/taglib/ape/apefooter.cpp @@ -35,7 +35,7 @@ using namespace TagLib; using namespace APE; -class Footer::FooterPrivate +class APE::Footer::FooterPrivate { public: FooterPrivate() : version(0), @@ -64,12 +64,12 @@ public: // static members //////////////////////////////////////////////////////////////////////////////// -TagLib::uint Footer::size() +TagLib::uint APE::Footer::size() { return FooterPrivate::size; } -ByteVector Footer::fileIdentifier() +ByteVector APE::Footer::fileIdentifier() { return ByteVector::fromCString("APETAGEX"); } @@ -78,63 +78,63 @@ ByteVector Footer::fileIdentifier() // public members //////////////////////////////////////////////////////////////////////////////// -Footer::Footer() +APE::Footer::Footer() { d = new FooterPrivate; } -Footer::Footer(const ByteVector &data) +APE::Footer::Footer(const ByteVector &data) { d = new FooterPrivate; parse(data); } -Footer::~Footer() +APE::Footer::~Footer() { delete d; } -TagLib::uint Footer::version() const +TagLib::uint APE::Footer::version() const { return d->version; } -bool Footer::headerPresent() const +bool APE::Footer::headerPresent() const { return d->headerPresent; } -bool Footer::footerPresent() const +bool APE::Footer::footerPresent() const { return d->footerPresent; } -bool Footer::isHeader() const +bool APE::Footer::isHeader() const { return d->isHeader; } -void Footer::setHeaderPresent(bool b) const +void APE::Footer::setHeaderPresent(bool b) const { d->headerPresent = b; } -TagLib::uint Footer::itemCount() const +TagLib::uint APE::Footer::itemCount() const { return d->itemCount; } -void Footer::setItemCount(uint s) +void APE::Footer::setItemCount(uint s) { d->itemCount = s; } -TagLib::uint Footer::tagSize() const +TagLib::uint APE::Footer::tagSize() const { return d->tagSize; } -TagLib::uint Footer::completeTagSize() const +TagLib::uint APE::Footer::completeTagSize() const { if(d->headerPresent) return d->tagSize + d->size; @@ -142,22 +142,22 @@ TagLib::uint Footer::completeTagSize() const return d->tagSize; } -void Footer::setTagSize(uint s) +void APE::Footer::setTagSize(uint s) { d->tagSize = s; } -void Footer::setData(const ByteVector &data) +void APE::Footer::setData(const ByteVector &data) { parse(data); } -ByteVector Footer::renderFooter() const +ByteVector APE::Footer::renderFooter() const { return render(false); } -ByteVector Footer::renderHeader() const +ByteVector APE::Footer::renderHeader() const { if (!d->headerPresent) return ByteVector(); @@ -168,7 +168,7 @@ ByteVector Footer::renderHeader() const // protected members //////////////////////////////////////////////////////////////////////////////// -void Footer::parse(const ByteVector &data) +void APE::Footer::parse(const ByteVector &data) { if(data.size() < size()) return; @@ -177,19 +177,19 @@ void Footer::parse(const ByteVector &data) // Read the version number - d->version = data.mid(8, 4).toUInt(false); + d->version = data.toUInt(8, false); // Read the tag size - d->tagSize = data.mid(12, 4).toUInt(false); + d->tagSize = data.toUInt(12, false); // Read the item count - d->itemCount = data.mid(16, 4).toUInt(false); + d->itemCount = data.toUInt(16, false); // Read the flags - std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false))); + std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false))); d->headerPresent = flags[31]; d->footerPresent = !flags[30]; @@ -197,7 +197,7 @@ void Footer::parse(const ByteVector &data) } -ByteVector Footer::render(bool isHeader) const +ByteVector APE::Footer::render(bool isHeader) const { ByteVector v; diff --git a/Plugins/PluginNowPlaying/taglib/ape/apeitem.cpp b/Plugins/PluginNowPlaying/taglib/ape/apeitem.cpp index 350eac0b..3490173a 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apeitem.cpp +++ b/Plugins/PluginNowPlaying/taglib/ape/apeitem.cpp @@ -62,6 +62,18 @@ APE::Item::Item(const String &key, const StringList &values) d->text = values; } +APE::Item::Item(const String &key, const ByteVector &value, bool binary) +{ + d = new ItemPrivate; + d->key = key; + if(binary) { + d->type = Binary; + d->value = value; + } + else + d->text.append(value); +} + APE::Item::Item(const Item &item) { d = new ItemPrivate(*item.d); @@ -104,6 +116,18 @@ 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(); +} + ByteVector APE::Item::value() const { // This seems incorrect as it won't be actually rendering the value to keep it @@ -114,32 +138,59 @@ ByteVector APE::Item::value() const void APE::Item::setKey(const String &key) { - d->key = key; + d->key = key; } void APE::Item::setValue(const String &value) { - d->text = value; + d->type = Text; + d->text = value; + d->value.clear(); } void APE::Item::setValues(const StringList &value) { - d->text = value; + d->type = Text; + d->text = value; + d->value.clear(); } void APE::Item::appendValue(const String &value) { - d->text.append(value); + d->type = Text; + d->text.append(value); + d->value.clear(); } void APE::Item::appendValues(const StringList &values) { - d->text.append(values); + d->type = Text; + d->text.append(values); + d->value.clear(); } int APE::Item::size() const { - return 8 + d->key.size() + 1 + d->value.size(); + // SFB: Why is d->key.size() used when size() returns the length in UniChars and not UTF-8? + int result = 8 + d->key.size() /* d->key.data(String::UTF8).size() */ + 1; + switch (d->type) { + case Text: + if(d->text.size()) { + StringList::ConstIterator it = d->text.begin(); + + result += it->data(String::UTF8).size(); + it++; + for(; it != d->text.end(); ++it) + result += 1 + it->data(String::UTF8).size(); + } + break; + + case Binary: + case Locator: + result += d->value.size(); + break; + } + return result; } StringList APE::Item::toStringList() const @@ -154,19 +205,22 @@ StringList APE::Item::values() const String APE::Item::toString() const { - return isEmpty() ? String::null : d->text.front(); + if(d->type == Text && !isEmpty()) + return d->text.front(); + else + return String::null; } bool APE::Item::isEmpty() const { switch(d->type) { case Text: - case Binary: if(d->text.isEmpty()) return true; if(d->text.size() == 1 && d->text.front().isEmpty()) return true; return false; + case Binary: case Locator: return d->value.isEmpty(); default: @@ -183,18 +237,20 @@ void APE::Item::parse(const ByteVector &data) return; } - uint valueLength = data.mid(0, 4).toUInt(false); - uint flags = data.mid(4, 4).toUInt(false); + const uint valueLength = data.toUInt(0, false); + const uint flags = data.toUInt(4, false); d->key = String(data.mid(8), String::UTF8); - d->value = data.mid(8 + d->key.size() + 1, valueLength); + const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength); setReadOnly(flags & 1); setType(ItemTypes((flags >> 1) & 3)); - if(int(d->type) < 2) - d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8); + if(Text == d->type) + d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8); + else + d->value = value; } ByteVector APE::Item::render() const diff --git a/Plugins/PluginNowPlaying/taglib/ape/apeitem.h b/Plugins/PluginNowPlaying/taglib/ape/apeitem.h index 01fcc764..0588d185 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apeitem.h +++ b/Plugins/PluginNowPlaying/taglib/ape/apeitem.h @@ -59,16 +59,22 @@ namespace TagLib { Item(); /*! - * Constructs an item with \a key and \a value. + * Constructs a text item with \a key and \a value. */ // BIC: Remove this, StringList has a constructor from a single string Item(const String &key, const String &value); /*! - * Constructs an item with \a key and \a values. + * 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 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. */ @@ -91,12 +97,20 @@ namespace TagLib { /*! * Returns the binary value. - * - * \deprecated This will be removed in the next binary incompatible version - * as it is not kept in sync with the things that are set using setValue() - * and friends. + * 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); + +#ifndef DO_NOT_DOCUMENT + /* Remove in next binary incompatible release */ ByteVector value() const; +#endif /*! * Sets the key for the item to \a key. @@ -104,14 +118,14 @@ namespace TagLib { void setKey(const String &key); /*! - * Sets the value of the item to \a value and clears any previous contents. + * Sets the text value of the item to \a value and clears any previous contents. * * \see toString() */ void setValue(const String &value); /*! - * Sets the value of the item to the list of values in \a value and clears + * Sets the text value of the item to the list of values in \a value and clears * any previous contents. * * \see toStringList() @@ -119,14 +133,14 @@ namespace TagLib { void setValues(const StringList &values); /*! - * Appends \a value to create (or extend) the current list of 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 values. + * Appends \a values to extend the current list of text values. * * \see toStringList() */ @@ -138,19 +152,20 @@ namespace TagLib { int size() const; /*! - * Returns the value as a single string. In case of multiple strings, - * the first is returned. + * 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; - /*! - * \deprecated - * \see values - */ +#ifndef DO_NOT_DOCUMENT + /* Remove in next binary incompatible release */ StringList toStringList() const; +#endif /*! - * Returns the list of values. + * Returns the list of text values. If the data type is not \a Text, always + * returns an empty StringList. */ StringList values() const; diff --git a/Plugins/PluginNowPlaying/taglib/ape/apeproperties.cpp b/Plugins/PluginNowPlaying/taglib/ape/apeproperties.cpp index 7f9cdb5a..4940edea 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apeproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/ape/apeproperties.cpp @@ -46,6 +46,7 @@ public: channels(0), version(0), bitsPerSample(0), + sampleFrames(0), file(file), streamLength(streamLength) {} @@ -55,6 +56,7 @@ public: int channels; int version; int bitsPerSample; + uint sampleFrames; File *file; long streamLength; }; @@ -104,6 +106,11 @@ int APE::Properties::bitsPerSample() const return d->bitsPerSample; } +TagLib::uint APE::Properties::sampleFrames() const +{ + return d->sampleFrames; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -118,10 +125,10 @@ void APE::Properties::read() // Then we read the header common for all versions of APE d->file->seek(offset); - ByteVector commonHeader=d->file->readBlock(6); + ByteVector commonHeader = d->file->readBlock(6); if(!commonHeader.startsWith("MAC ")) return; - d->version = commonHeader.mid(4).toUInt(false); + d->version = commonHeader.toUShort(4, false); if(d->version >= 3980) { analyzeCurrent(); @@ -137,7 +144,7 @@ long APE::Properties::findDescriptor() long ID3v2OriginalSize = 0; bool hasID3v2 = false; if(ID3v2Location >= 0) { - ID3v2::Tag tag(d->file, ID3v2Location, 0); + ID3v2::Tag tag(d->file, ID3v2Location); ID3v2OriginalSize = tag.header()->completeTagSize(); if(tag.header()->tagSize() > 0) hasID3v2 = true; @@ -175,7 +182,7 @@ void APE::Properties::analyzeCurrent() // Read the descriptor d->file->seek(2, File::Current); ByteVector descriptor = d->file->readBlock(44); - uint descriptorBytes = descriptor.mid(0,4).toUInt(false); + const uint descriptorBytes = descriptor.toUInt(0, false); if ((descriptorBytes - 52) > 0) d->file->seek(descriptorBytes - 52, File::Current); @@ -184,29 +191,29 @@ void APE::Properties::analyzeCurrent() ByteVector header = d->file->readBlock(24); // Get the APE info - d->channels = header.mid(18, 2).toShort(false); - d->sampleRate = header.mid(20, 4).toUInt(false); - d->bitsPerSample = header.mid(16, 2).toShort(false); + d->channels = header.toShort(18, false); + d->sampleRate = header.toUInt(20, false); + d->bitsPerSample = header.toShort(16, false); //d->compressionLevel = - uint totalFrames = header.mid(12, 4).toUInt(false); - uint blocksPerFrame = header.mid(4, 4).toUInt(false); - uint finalFrameBlocks = header.mid(8, 4).toUInt(false); - uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; - d->length = d->sampleRate > 0 ? totalBlocks / d->sampleRate : 0; + const uint totalFrames = header.toUInt(12, false); + const uint blocksPerFrame = header.toUInt(4, false); + const uint finalFrameBlocks = header.toUInt(8, false); + d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; + d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; } void APE::Properties::analyzeOld() { ByteVector header = d->file->readBlock(26); - uint totalFrames = header.mid(18, 4).toUInt(false); + const uint totalFrames = header.toUInt(18, false); // Fail on 0 length APE files (catches non-finalized APE files) if(totalFrames == 0) return; - short compressionLevel = header.mid(0, 2).toShort(false); + const short compressionLevel = header.toShort(0, false); uint blocksPerFrame; if(d->version >= 3950) blocksPerFrame = 73728 * 4; @@ -214,10 +221,11 @@ void APE::Properties::analyzeOld() blocksPerFrame = 73728; else blocksPerFrame = 9216; - d->channels = header.mid(4, 2).toShort(false); - d->sampleRate = header.mid(6, 4).toUInt(false); - uint finalFrameBlocks = header.mid(22, 4).toUInt(false); - uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; + d->channels = header.toShort(4, false); + d->sampleRate = header.toUInt(6, false); + const uint finalFrameBlocks = header.toUInt(22, false); + const uint totalBlocks + = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; d->length = totalBlocks / d->sampleRate; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; } diff --git a/Plugins/PluginNowPlaying/taglib/ape/apeproperties.h b/Plugins/PluginNowPlaying/taglib/ape/apeproperties.h index 8b543a57..f154ec34 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apeproperties.h +++ b/Plugins/PluginNowPlaying/taglib/ape/apeproperties.h @@ -71,6 +71,7 @@ namespace TagLib { * Returns number of bits per sample. */ int bitsPerSample() const; + uint sampleFrames() const; /*! * Returns APE version. diff --git a/Plugins/PluginNowPlaying/taglib/ape/apetag.cpp b/Plugins/PluginNowPlaying/taglib/ape/apetag.cpp index 082fd038..664ee3a8 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apetag.cpp +++ b/Plugins/PluginNowPlaying/taglib/ape/apetag.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "apetag.h" #include "apefooter.h" @@ -47,7 +48,7 @@ class APE::Tag::TagPrivate public: TagPrivate() : file(0), footerLocation(-1), tagLength(0) {} - File *file; + TagLib::File *file; long footerLocation; long tagLength; @@ -65,7 +66,7 @@ APE::Tag::Tag() : TagLib::Tag() d = new TagPrivate; } -APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag() +APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag() { d = new TagPrivate; d->file = file; @@ -174,6 +175,103 @@ void APE::Tag::setTrack(uint i) addValue("TRACK", String::number(i), true); } +// conversions of tag keys between what we use in PropertyMap and what's usual +// for APE tags +static const TagLib::uint keyConversionsSize = 5; //usual, APE +static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" }, + {"DATE", "YEAR" }, + {"ALBUMARTIST", "ALBUM ARTIST"}, + {"DISCNUMBER", "DISC" }, + {"REMIXER", "MIXARTIST" }}; + +PropertyMap APE::Tag::properties() const +{ + PropertyMap properties; + ItemListMap::ConstIterator it = itemListMap().begin(); + for(; it != itemListMap().end(); ++it) { + String tagName = it->first.upper(); + // if the item is Binary or Locator, or if the key is an invalid string, + // add to unsupportedData + if(it->second.type() != Item::Text || tagName.isNull()) + properties.unsupportedData().append(it->first); + else { + // Some tags need to be handled specially + for(uint i = 0; i < keyConversionsSize; ++i) + if(tagName == keyConversions[i][1]) + tagName = keyConversions[i][0]; + properties[tagName].append(it->second.toStringList()); + } + } + return properties; +} + +void APE::Tag::removeUnsupportedProperties(const StringList &properties) +{ + StringList::ConstIterator it = properties.begin(); + for(; it != properties.end(); ++it) + removeItem(*it); +} + +PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) +{ + PropertyMap properties(origProps); // make a local copy that can be modified + + // see comment in properties() + for(uint i = 0; i < keyConversionsSize; ++i) + if(properties.contains(keyConversions[i][0])) { + properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]); + properties.erase(keyConversions[i][0]); + } + + // first check if tags need to be removed completely + StringList toRemove; + ItemListMap::ConstIterator remIt = itemListMap().begin(); + for(; remIt != itemListMap().end(); ++remIt) { + String key = remIt->first.upper(); + // only remove if a) key is valid, b) type is text, c) key not contained in new properties + if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key)) + toRemove.append(remIt->first); + } + + for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++) + removeItem(*removeIt); + + // now sync in the "forward direction" + PropertyMap::ConstIterator it = properties.begin(); + PropertyMap invalid; + for(; it != properties.end(); ++it) { + const String &tagName = it->first; + if(!checkKey(tagName)) + invalid.insert(it->first, it->second); + else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) { + if(it->second.size() == 0) + removeItem(tagName); + else { + StringList::ConstIterator valueIt = it->second.begin(); + addValue(tagName, *valueIt, true); + ++valueIt; + for(; valueIt != it->second.end(); ++valueIt) + addValue(tagName, *valueIt, false); + } + } + } + return invalid; +} + +bool APE::Tag::checkKey(const String &key) +{ + if(key.size() < 2 || key.size() > 16) + return false; + for(String::ConstIterator it = key.begin(); it != key.end(); it++) + // only allow printable ASCII including space (32..127) + if (*it < 32 || *it >= 128) + return false; + String upperKey = key.upper(); + if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+") + return false; + return true; +} + APE::Footer *APE::Tag::footer() const { return &d->footer; @@ -195,17 +293,31 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace) { if(replace) removeItem(key); - if(!value.isEmpty()) { - if(d->itemListMap.contains(key) || !replace) - d->itemListMap[key.upper()].appendValue(value); + if(!key.isEmpty() && !value.isEmpty()) { + if(!replace && d->itemListMap.contains(key)) { + // Text items may contain more than one value + if(APE::Item::Text == d->itemListMap.begin()->second.type()) + d->itemListMap[key.upper()].appendValue(value); + // Binary or locator items may have only one value + else + setItem(key, Item(key, value)); + } else setItem(key, Item(key, value)); } } +void APE::Tag::setData(const String &key, const ByteVector &value) +{ + removeItem(key); + if(!key.isEmpty() && !value.isEmpty()) + setItem(key, Item(key, value, true)); +} + void APE::Tag::setItem(const String &key, const Item &item) { - d->itemListMap.insert(key.upper(), item); + if(!key.isEmpty()) + d->itemListMap.insert(key.upper(), item); } bool APE::Tag::isEmpty() const diff --git a/Plugins/PluginNowPlaying/taglib/ape/apetag.h b/Plugins/PluginNowPlaying/taglib/ape/apetag.h index 13efd5e0..c1f12e29 100644 --- a/Plugins/PluginNowPlaying/taglib/ape/apetag.h +++ b/Plugins/PluginNowPlaying/taglib/ape/apetag.h @@ -103,6 +103,37 @@ namespace TagLib { virtual void setYear(uint i); virtual void setTrack(uint i); + /*! + * 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, + * in order to be compliant with the names used in other formats. + */ + PropertyMap properties() const; + + void removeUnsupportedProperties(const StringList &properties); + + /*! + * 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 &); + + /*! + * Check if the given String is a valid APE tag key. + */ + static bool checkKey(const String&); + /*! * Returns a pointer to the tag's footer. */ @@ -128,12 +159,19 @@ namespace TagLib { void removeItem(const String &key); /*! - * Adds to the item specified by \a key the data \a value. If \a replace + * Adds to the text item specified by \a key the data \a value. If \a replace * is true, then all of the other values on the same key will be removed - * first. + * 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. diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfattribute.cpp b/Plugins/PluginNowPlaying/taglib/asf/asfattribute.cpp index ecdf1f61..4ee2d0a1 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfattribute.cpp +++ b/Plugins/PluginNowPlaying/taglib/asf/asfattribute.cpp @@ -23,14 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_ASF - #include #include +#include "trefcounter.h" #include "asfattribute.h" #include "asffile.h" @@ -353,4 +348,3 @@ void ASF::Attribute::setStream(int value) d->stream = value; } -#endif diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfattribute.h b/Plugins/PluginNowPlaying/taglib/asf/asfattribute.h index 56186999..54eb0c7d 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfattribute.h +++ b/Plugins/PluginNowPlaying/taglib/asf/asfattribute.h @@ -70,7 +70,7 @@ namespace TagLib /*! * Constructs an attribute with \a key and a BytesType \a value. */ - Attribute(const ByteVector &value); + Attribute(const ByteVector &value); /*! * Constructs an attribute with \a key and a Picture \a value. diff --git a/Plugins/PluginNowPlaying/taglib/asf/asffile.cpp b/Plugins/PluginNowPlaying/taglib/asf/asffile.cpp index fbde6fa1..241998ca 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asffile.cpp +++ b/Plugins/PluginNowPlaying/taglib/asf/asffile.cpp @@ -23,14 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_ASF - #include #include +#include #include #include "asffile.h" #include "asftag.h" @@ -69,6 +64,9 @@ static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); +static ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16); +static ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16); +static ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16); class ASF::File::BaseObject { @@ -185,7 +183,8 @@ ByteVector ASF::File::FilePropertiesObject::guid() void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size) { BaseObject::parse(file, size); - file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L)); + file->d->properties->setLength( + (int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L)); } ByteVector ASF::File::StreamPropertiesObject::guid() @@ -196,9 +195,9 @@ ByteVector ASF::File::StreamPropertiesObject::guid() void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size) { BaseObject::parse(file, size); - file->d->properties->setChannels(data.mid(56, 2).toShort(false)); - file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false)); - file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000); + file->d->properties->setChannels(data.toShort(56, false)); + file->d->properties->setSampleRate(data.toUInt(58, false)); + file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000); } ByteVector ASF::File::ContentDescriptionObject::guid() @@ -347,7 +346,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) else { obj = new UnknownObject(guid); } - obj->parse(file, size); + obj->parse(file, (unsigned int)size); objects.append(obj); dataPos += size; } @@ -367,11 +366,20 @@ ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file) // public members //////////////////////////////////////////////////////////////////////////////// -ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) +ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) + : TagLib::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } ASF::File::~File() @@ -393,6 +401,21 @@ ASF::Tag *ASF::File::tag() const return d->tag; } +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; @@ -454,6 +477,11 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties obj = new HeaderExtensionObject(); } else { + if(guid == contentEncryptionGuid || + guid == extendedContentEncryptionGuid || + guid == advancedContentEncryptionGuid) { + d->properties->setEncrypted(true); + } obj = new UnknownObject(guid); } obj->parse(this, size); @@ -522,7 +550,7 @@ bool ASF::File::save() data.append(d->objects[i]->render(this)); } data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; - insert(data, 0, d->size); + insert(data, 0, (TagLib::ulong)d->size); return true; } @@ -600,4 +628,3 @@ ByteVector ASF::File::renderString(const String &str, bool includeLength) return data; } -#endif diff --git a/Plugins/PluginNowPlaying/taglib/asf/asffile.h b/Plugins/PluginNowPlaying/taglib/asf/asffile.h index 45e603dc..5ccf2fde 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asffile.h +++ b/Plugins/PluginNowPlaying/taglib/asf/asffile.h @@ -48,14 +48,27 @@ namespace TagLib { public: /*! - * Contructs an ASF file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs an ASF file from \a file. * * \note In the current implementation, both \a readProperties and - * \a propertiesStyle are ignored. + * \a propertiesStyle are ignored. The audio properties are always + * read. */ - File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); + 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. @@ -74,6 +87,22 @@ namespace TagLib { */ virtual Tag *tag() const; + /*! + * Implements the unified property interface -- export function. + */ + PropertyMap properties() const; + + /*! + * Removes unsupported properties. Forwards to the actual Tag's + * removeUnsupportedProperties() function. + */ + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the ASF audio properties for this file. */ @@ -87,7 +116,6 @@ namespace TagLib { virtual bool save(); private: - int readBYTE(bool *ok = 0); int readWORD(bool *ok = 0); unsigned int readDWORD(bool *ok = 0); diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfpicture.cpp b/Plugins/PluginNowPlaying/taglib/asf/asfpicture.cpp index 551229b5..999f9204 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfpicture.cpp +++ b/Plugins/PluginNowPlaying/taglib/asf/asfpicture.cpp @@ -23,21 +23,16 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_ASF - #include #include +#include "trefcounter.h" #include "asfattribute.h" #include "asffile.h" #include "asfpicture.h" using namespace TagLib; -class ASF::Picture::PicturePriavte : public RefCounter +class ASF::Picture::PicturePrivate : public RefCounter { public: bool valid; @@ -53,7 +48,7 @@ public: ASF::Picture::Picture() { - d = new PicturePriavte(); + d = new PicturePrivate(); d->valid = true; } @@ -151,7 +146,7 @@ void ASF::Picture::parse(const ByteVector& bytes) return; int pos = 0; d->type = (Type)bytes[0]; ++pos; - uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4; + const uint dataLen = bytes.toUInt(pos, false); pos+=4; const ByteVector nullStringTerminator(2, 0); @@ -182,4 +177,3 @@ ASF::Picture ASF::Picture::fromInvalid() return ret; } -#endif diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfpicture.h b/Plugins/PluginNowPlaying/taglib/asf/asfpicture.h index 5c1bfbfd..aa0a060c 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfpicture.h +++ b/Plugins/PluginNowPlaying/taglib/asf/asfpicture.h @@ -208,8 +208,8 @@ namespace TagLib friend class Attribute; #endif private: - struct PicturePriavte; - PicturePriavte *d; + class PicturePrivate; + PicturePrivate *d; }; } } diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfproperties.cpp b/Plugins/PluginNowPlaying/taglib/asf/asfproperties.cpp index 02d2d942..b82c131a 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/asf/asfproperties.cpp @@ -23,12 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_ASF - #include #include #include "asfproperties.h" @@ -38,11 +32,12 @@ using namespace TagLib; class ASF::Properties::PropertiesPrivate { public: - PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0) {} + PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {} int length; int bitrate; int sampleRate; int channels; + bool encrypted; }; //////////////////////////////////////////////////////////////////////////////// @@ -57,7 +52,7 @@ ASF::Properties::Properties() : AudioProperties(AudioProperties::Average) ASF::Properties::~Properties() { if(d) - delete d; + delete d; } int ASF::Properties::length() const @@ -78,7 +73,12 @@ int ASF::Properties::sampleRate() const int ASF::Properties::channels() const { return d->channels; -} +} + +bool ASF::Properties::isEncrypted() const +{ + return d->encrypted; +} //////////////////////////////////////////////////////////////////////////////// // private members @@ -104,4 +104,8 @@ void ASF::Properties::setChannels(int length) d->channels = length; } -#endif +void ASF::Properties::setEncrypted(bool encrypted) +{ + d->encrypted = encrypted; +} + diff --git a/Plugins/PluginNowPlaying/taglib/asf/asfproperties.h b/Plugins/PluginNowPlaying/taglib/asf/asfproperties.h index 290eac77..95730d8e 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asfproperties.h +++ b/Plugins/PluginNowPlaying/taglib/asf/asfproperties.h @@ -54,12 +54,14 @@ namespace TagLib { virtual int bitrate() const; virtual int sampleRate() const; virtual int channels() const; + bool isEncrypted() const; #ifndef DO_NOT_DOCUMENT void setLength(int value); void setBitrate(int value); void setSampleRate(int value); void setChannels(int value); + void setEncrypted(bool value); #endif private: @@ -71,4 +73,4 @@ namespace TagLib { } -#endif +#endif diff --git a/Plugins/PluginNowPlaying/taglib/asf/asftag.cpp b/Plugins/PluginNowPlaying/taglib/asf/asftag.cpp index c77646e1..c07abe94 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asftag.cpp +++ b/Plugins/PluginNowPlaying/taglib/asf/asftag.cpp @@ -23,12 +23,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_ASF - +#include #include "asftag.h" using namespace TagLib; @@ -198,4 +193,162 @@ bool ASF::Tag::isEmpty() const d->attributeListMap.isEmpty(); } -#endif +static const char *keyTranslation[][2] = { + { "WM/AlbumTitle", "ALBUM" }, + { "WM/Composer", "COMPOSER" }, + { "WM/Writer", "WRITER" }, + { "WM/Conductor", "CONDUCTOR" }, + { "WM/ModifiedBy", "REMIXER" }, + { "WM/Year", "DATE" }, + { "WM/OriginalReleaseYear", "ORIGINALDATE" }, + { "WM/Producer", "PRODUCER" }, + { "WM/ContentGroupDescription", "GROUPING" }, + { "WM/SubTitle", "SUBTITLE" }, + { "WM/SetSubTitle", "DISCSUBTITLE" }, + { "WM/TrackNumber", "TRACKNUMBER" }, + { "WM/PartOfSet", "DISCNUMBER" }, + { "WM/Genre", "GENRE" }, + { "WM/BeatsPerMinute", "BPM" }, + { "WM/Mood", "MOOD" }, + { "WM/ISRC", "ISRC" }, + { "WM/Lyrics", "LYRICS" }, + { "WM/Media", "MEDIA" }, + { "WM/Publisher", "LABEL" }, + { "WM/CatalogNo", "CATALOGNUMBER" }, + { "WM/Barcode", "BARCODE" }, + { "WM/EncodedBy", "ENCODEDBY" }, + { "WM/AlbumSortOrder", "ALBUMSORT" }, + { "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" }, + { "WM/ArtistSortOrder", "ARTISTSORT" }, + { "WM/TitleSortOrder", "TITLESORT" }, + { "WM/Script", "SCRIPT" }, + { "WM/Language", "LANGUAGE" }, + { "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" }, + { "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" }, + { "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" }, + { "MusicIP/PUID", "MUSICIP_PUID" }, + { "Acoustid/Id", "ACOUSTID_ID" }, + { "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" }, +}; + +PropertyMap ASF::Tag::properties() const +{ + static Map keyMap; + if(keyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + keyMap[keyTranslation[i][0]] = keyTranslation[i][1]; + } + } + + 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; + } + + ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin(); + for(; it != d->attributeListMap.end(); ++it) { + if(keyMap.contains(it->first)) { + String key = keyMap[it->first]; + AttributeList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + if(key == "TRACKNUMBER") { + if(it2->type() == ASF::Attribute::DWordType) + props.insert(key, String::number(it2->toUInt())); + else + props.insert(key, it2->toString()); + } + else { + props.insert(key, it2->toString()); + } + } + } + else { + props.unsupportedData().append(it->first); + } + } + return props; +} + +void ASF::Tag::removeUnsupportedProperties(const StringList &props) +{ + StringList::ConstIterator it = props.begin(); + for(; it != props.end(); ++it) + d->attributeListMap.erase(*it); +} + +PropertyMap ASF::Tag::setProperties(const PropertyMap &props) +{ + static Map reverseKeyMap; + if(reverseKeyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; + } + } + + PropertyMap origProps = properties(); + PropertyMap::ConstIterator it = origProps.begin(); + for(; it != origProps.end(); ++it) { + if(!props.contains(it->first) || props[it->first].isEmpty()) { + if(it->first == "TITLE") { + d->title = String::null; + } + else if(it->first == "ARTIST") { + d->artist = String::null; + } + else if(it->first == "COMMENT") { + d->comment = String::null; + } + else if(it->first == "COPYRIGHT") { + d->copyright = String::null; + } + else { + d->attributeListMap.erase(reverseKeyMap[it->first]); + } + } + } + + PropertyMap ignoredProps; + it = props.begin(); + for(; it != props.end(); ++it) { + if(reverseKeyMap.contains(it->first)) { + String name = reverseKeyMap[it->first]; + removeItem(name); + StringList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + addAttribute(name, *it2); + } + } + else if(it->first == "TITLE") { + d->title = it->second.toString(); + } + else if(it->first == "ARTIST") { + d->artist = it->second.toString(); + } + else if(it->first == "COMMENT") { + d->comment = it->second.toString(); + } + else if(it->first == "COPYRIGHT") { + d->copyright = it->second.toString(); + } + else { + ignoredProps.insert(it->first, it->second); + } + } + + return ignoredProps; +} diff --git a/Plugins/PluginNowPlaying/taglib/asf/asftag.h b/Plugins/PluginNowPlaying/taglib/asf/asftag.h index a6c49df5..f68579d8 100644 --- a/Plugins/PluginNowPlaying/taglib/asf/asftag.h +++ b/Plugins/PluginNowPlaying/taglib/asf/asftag.h @@ -120,17 +120,17 @@ namespace TagLib { virtual void setComment(const String &s); /*! - * Sets the rating to \a s. + * Sets the rating to \a s. */ virtual void setRating(const String &s); /*! - * Sets the copyright to \a s. + * Sets the copyright to \a s. */ virtual void setCopyright(const String &s); /*! - * Sets the genre to \a s. + * Sets the genre to \a s. */ virtual void setGenre(const String &s); @@ -176,6 +176,10 @@ namespace TagLib { */ void addAttribute(const String &name, const Attribute &attribute); + PropertyMap properties() const; + void removeUnsupportedProperties(const StringList& properties); + PropertyMap setProperties(const PropertyMap &properties); + private: class TagPrivate; diff --git a/Plugins/PluginNowPlaying/taglib/config.h b/Plugins/PluginNowPlaying/taglib/config.h deleted file mode 100644 index a1cd1701..00000000 --- a/Plugins/PluginNowPlaying/taglib/config.h +++ /dev/null @@ -1,2 +0,0 @@ -#define WITH_ASF 1 -#define WITH_MP4 1 diff --git a/Plugins/PluginNowPlaying/taglib/fileref.cpp b/Plugins/PluginNowPlaying/taglib/fileref.cpp index 595e0d60..01d3bec0 100644 --- a/Plugins/PluginNowPlaying/taglib/fileref.cpp +++ b/Plugins/PluginNowPlaying/taglib/fileref.cpp @@ -1,7 +1,7 @@ /*************************************************************************** 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) @@ -27,13 +27,10 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include +#include "trefcounter.h" #include "fileref.h" #include "asffile.h" @@ -42,13 +39,17 @@ #include "flacfile.h" #include "oggflacfile.h" #include "mpcfile.h" -#include "mp4file.h" -#include "wavpackfile.h" +//#include "mp4file.h" +//#include "wavpackfile.h" //#include "speexfile.h" //#include "trueaudiofile.h" //#include "aifffile.h" //#include "wavfile.h" #include "apefile.h" +//#include "modfile.h" +//#include "s3mfile.h" +//#include "itfile.h" +//#include "xmfile.h" using namespace TagLib; @@ -144,24 +145,28 @@ StringList FileRef::defaultFileExtensions() l.append("oga"); l.append("mp3"); l.append("mpc"); - l.append("wv"); -//l.append("spx"); -//l.append("tta"); -#ifdef TAGLIB_WITH_MP4 - l.append("m4a"); - l.append("m4b"); - l.append("m4p"); - l.append("3g2"); - l.append("mp4"); -#endif -#ifdef TAGLIB_WITH_ASF +// l.append("wv"); +// l.append("spx"); +// l.append("tta"); +// l.append("m4a"); +// l.append("m4r"); +// l.append("m4b"); +// l.append("m4p"); +// l.append("3g2"); +// l.append("mp4"); l.append("wma"); l.append("asf"); -#endif -//l.append("aif"); -//l.append("aiff"); -//l.append("wav"); +// l.append("aif"); +// l.append("aiff"); +// 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"); return l; } @@ -209,21 +214,28 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, // Ok, this is really dumb for now, but it works for testing. - String s; - + String ext; + { #ifdef _WIN32 - s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName); + + String s = fileName.toString(); + #else - s = fileName; -#endif + + String s = fileName; + + #endif + + const int pos = s.rfind("."); + if(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. - int pos = s.rfind("."); - if(pos != -1) { - String ext = s.substr(pos + 1).upper(); + if(!ext.isEmpty()) { if(ext == "MP3") return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "OGG") @@ -238,28 +250,35 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, } if(ext == "FLAC") return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle); - if(ext == "MPC") - return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle); - if(ext == "WV") - return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle); -// if(ext == "SPX") -// return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle); -// if(ext == "TTA") -// return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle); -#ifdef TAGLIB_WITH_MP4 - if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2") - return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle); -#endif -#ifdef TAGLIB_WITH_ASF +// if(ext == "MPC") +// return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "WV") +// return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "SPX") +// return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "OPUS") +// return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "TTA") +// return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2") +// return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WMA" || ext == "ASF") return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle); -#endif -// if(ext == "AIF" || ext == "AIFF") -// return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); -// if(ext == "WAV") -// return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "AIF" || ext == "AIFF") +// return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "WAV") +// return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "APE") return new APE::File(fileName, readAudioProperties, audioPropertiesStyle); + // module, nst and wow are possible but uncommon extensions +// if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW") +// return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "S3M") +// return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "IT") +// return new IT::File(fileName, readAudioProperties, audioPropertiesStyle); +// if(ext == "XM") +// return new XM::File(fileName, readAudioProperties, audioPropertiesStyle); } return 0; diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacfile.cpp b/Plugins/PluginNowPlaying/taglib/flac/flacfile.cpp index 4e3d2b36..823170aa 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/flac/flacfile.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,7 @@ using namespace TagLib; namespace { - enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 }; + enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 }; enum { MinPaddingLength = 4096 }; enum { LastBlockFlag = 0x80 }; } @@ -69,7 +70,8 @@ public: ~FilePrivate() { - for(uint i = 0; i < blocks.size(); i++) { + uint size = blocks.size(); + for(uint i = 0; i < size; i++) { delete blocks[i]; } delete properties; @@ -107,7 +109,8 @@ FLAC::File::File(FileName file, bool readProperties, TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, @@ -116,7 +119,18 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, { d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, + bool readProperties, Properties::ReadStyle propertiesStyle) : + TagLib::File(stream) +{ + d = new FilePrivate; + d->ID3v2FrameFactory = frameFactory; + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::~File() @@ -129,6 +143,34 @@ TagLib::Tag *FLAC::File::tag() const return &d->tag; } +PropertyMap FLAC::File::properties() const +{ + // once Tag::properties() is virtual, this case distinction could actually be done + // within TagUnion. + if(d->hasXiphComment) + return d->tag.access(FlacXiphIndex, false)->properties(); + if(d->hasID3v2) + return d->tag.access(FlacID3v2Index, false)->properties(); + if(d->hasID3v1) + return d->tag.access(FlacID3v1Index, false)->properties(); + return PropertyMap(); +} + +void FLAC::File::removeUnsupportedProperties(const StringList &unsupported) +{ + if(d->hasXiphComment) + d->tag.access(FlacXiphIndex, false)->removeUnsupportedProperties(unsupported); + if(d->hasID3v2) + d->tag.access(FlacID3v2Index, false)->removeUnsupportedProperties(unsupported); + if(d->hasID3v1) + d->tag.access(FlacID3v1Index, false)->removeUnsupportedProperties(unsupported); +} + +PropertyMap FLAC::File::setProperties(const PropertyMap &properties) +{ + return d->tag.access(FlacXiphIndex, true)->setProperties(properties); +} + FLAC::Properties *FLAC::File::audioProperties() const { return d->properties; @@ -198,7 +240,7 @@ bool FLAC::File::save() } ByteVector padding = ByteVector::fromUInt(paddingLength); padding.resize(paddingLength + 4); - padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag; + padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag); data.append(padding); // Write the data to the file @@ -230,21 +272,21 @@ bool FLAC::File::save() ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) { - if(!create || d->tag[ID3v2Index]) - return static_cast(d->tag[ID3v2Index]); + if(!create || d->tag[FlacID3v2Index]) + return static_cast(d->tag[FlacID3v2Index]); - d->tag.set(ID3v2Index, new ID3v2::Tag); - return static_cast(d->tag[ID3v2Index]); + d->tag.set(FlacID3v2Index, new ID3v2::Tag); + return static_cast(d->tag[FlacID3v2Index]); } ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) { - return d->tag.access(ID3v1Index, create); + return d->tag.access(FlacID3v1Index, create); } Ogg::XiphComment *FLAC::File::xiphComment(bool create) { - return d->tag.access(XiphIndex, create); + return d->tag.access(FlacXiphIndex, create); } void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) @@ -265,12 +307,12 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle if(d->ID3v2Location >= 0) { - d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); + d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); if(ID3v2Tag()->header()->tagSize() <= 0) - d->tag.set(ID3v2Index, 0); + d->tag.set(FlacID3v2Index, 0); else d->hasID3v2 = true; } @@ -280,7 +322,7 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle d->ID3v1Location = findID3v1(); if(d->ID3v1Location >= 0) { - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); + d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->hasID3v1 = true; } @@ -292,9 +334,9 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle return; if(d->hasXiphComment) - d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData())); + d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData())); else - d->tag.set(XiphIndex, new Ogg::XiphComment); + d->tag.set(FlacXiphIndex, new Ogg::XiphComment); if(readProperties) d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle); @@ -357,7 +399,7 @@ void FLAC::File::scan() char blockType = header[0] & 0x7f; bool isLastBlock = (header[0] & 0x80) != 0; - uint length = header.mid(1, 3).toUInt(); + uint length = header.toUInt(1U, 3U); // First block should be the stream_info metadata @@ -377,10 +419,10 @@ void FLAC::File::scan() header = readBlock(4); blockType = header[0] & 0x7f; isLastBlock = (header[0] & 0x80) != 0; - length = header.mid(1, 3).toUInt(); + length = header.toUInt(1U, 3U); ByteVector data = readBlock(length); - if(data.size() != length) { + if(data.size() != length || length == 0) { debug("FLAC::File::scan() -- FLAC stream corrupted"); setValid(false); return; @@ -484,6 +526,17 @@ void FLAC::File::addPicture(Picture *picture) d->blocks.append(picture); } +void FLAC::File::removePicture(Picture *picture, bool del) +{ + MetadataBlock *block = picture; + List::Iterator it = d->blocks.find(block); + if(it != d->blocks.end()) + d->blocks.erase(it); + + if(del) + delete picture; +} + void FLAC::File::removePictures() { List newBlocks; @@ -499,3 +552,17 @@ void FLAC::File::removePictures() d->blocks = newBlocks; } +bool FLAC::File::hasXiphComment() const +{ + return d->hasXiphComment; +} + +bool FLAC::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool FLAC::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacfile.h b/Plugins/PluginNowPlaying/taglib/flac/flacfile.h index 64e67bc0..0963f4af 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacfile.h +++ b/Plugins/PluginNowPlaying/taglib/flac/flacfile.h @@ -29,6 +29,7 @@ #include "taglib_export.h" #include "tfile.h" #include "tlist.h" +#include "tag.h" #include "flacpicture.h" #include "flacproperties.h" @@ -36,7 +37,6 @@ namespace TagLib { class Tag; - namespace ID3v2 { class FrameFactory; class Tag; } namespace ID3v1 { class Tag; } namespace Ogg { class XiphComment; } @@ -67,9 +67,10 @@ namespace TagLib { { public: /*! - * Contructs a FLAC file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs a FLAC file from \a file. If \a readProperties is true the + * file's audio properties will also be read. + * + * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated This constructor will be dropped in favor of the one below * in a future version. @@ -78,18 +79,36 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Contructs a FLAC file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs an APE file from \a file. If \a readProperties is true the + * file's audio properties will also be read. * * If this file contains and ID3v2 tag the frames will be created using * \a frameFactory. + * + * \note In the current implementation, \a propertiesStyle is ignored. */ // BIC: merge with the above constructor 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 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 and ID3v2 tag the frames will be created using + * \a frameFactory. + * + * \note In the current implementation, \a propertiesStyle is ignored. + */ + // BIC: merge with the above constructor + File(IOStream *stream, ID3v2::FrameFactory *frameFactory, + bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + /*! * Destroys this instance of the File. */ @@ -105,6 +124,25 @@ namespace TagLib { */ virtual TagLib::Tag *tag() const; + /*! + * 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; + + void removeUnsupportedProperties(const StringList &); + + /*! + * 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 &); + /*! * Returns the FLAC::Properties for this file. If no audio properties * were read then this will return a null pointer. @@ -123,39 +161,57 @@ namespace TagLib { /*! * Returns a pointer to the ID3v2 tag of the file. * - * If \a create is false (the default) this will return a null pointer + * If \a create is false (the default) this returns a null pointer * if there is no valid ID3v2 tag. If \a create is true it will create - * an ID3v2 tag if one does not exist. + * an ID3v2 tag if one does not exist and returns a valid pointer. * - * \note The Tag is still owned by the FLAC::File and should not be + * \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 is still 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 false (the default) this will return a null pointer - * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. + * If \a create is false (the default) this returns a null pointer + * if there is no valid APE tag. If \a create is true it will create + * an APE tag if one does not exist and returns a valid pointer. * - * \note The Tag is still owned by the FLAC::File and should not be + * \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 is still 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 XiphComment for the file. * - * If \a create is false (the default) this will return a null pointer + * If \a create is false (the default) this returns a null pointer * if there is no valid XiphComment. If \a create is true it will create - * a XiphComment if one does not exist. + * 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 is still 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); @@ -189,6 +245,12 @@ namespace TagLib { */ List pictureList(); + /*! + * Removes an attached picture. If \a del is true the picture's memory + * will be freed; if it is false, it must be deleted by the user. + */ + void removePicture(Picture *picture, bool del = true); + /*! * Remove all attached images. */ @@ -202,6 +264,27 @@ namespace TagLib { */ void addPicture(Picture *picture); + /*! + * 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; + private: File(const File &); File &operator=(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacmetadatablock.cpp b/Plugins/PluginNowPlaying/taglib/flac/flacmetadatablock.cpp index 0671fc3a..17ab05f3 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacmetadatablock.cpp +++ b/Plugins/PluginNowPlaying/taglib/flac/flacmetadatablock.cpp @@ -23,17 +23,13 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "flacmetadatablock.h" using namespace TagLib; -class FLAC::MetadataBlock::MetadataBlockPrivate +class FLAC::MetadataBlock::MetadataBlockPrivate { public: MetadataBlockPrivate() {} diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacpicture.cpp b/Plugins/PluginNowPlaying/taglib/flac/flacpicture.cpp index 38967199..a2a9000b 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacpicture.cpp +++ b/Plugins/PluginNowPlaying/taglib/flac/flacpicture.cpp @@ -23,17 +23,13 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "flacpicture.h" using namespace TagLib; -class FLAC::Picture::PicturePrivate +class FLAC::Picture::PicturePrivate { public: PicturePrivate() : @@ -82,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data) return false; } - int pos = 0; - d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt()); + uint pos = 0; + d->type = FLAC::Picture::Type(data.toUInt(pos)); pos += 4; - uint mimeTypeLength = data.mid(pos, 4).toUInt(); + uint mimeTypeLength = data.toUInt(pos); pos += 4; if(pos + mimeTypeLength + 24 > data.size()) { debug("Invalid picture block."); @@ -93,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data) } d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8); pos += mimeTypeLength; - uint descriptionLength = data.mid(pos, 4).toUInt(); + uint descriptionLength = data.toUInt(pos); pos += 4; if(pos + descriptionLength + 20 > data.size()) { debug("Invalid picture block."); @@ -101,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data) } d->description = String(data.mid(pos, descriptionLength), String::UTF8); pos += descriptionLength; - d->width = data.mid(pos, 4).toUInt(); + d->width = data.toUInt(pos); pos += 4; - d->height = data.mid(pos, 4).toUInt(); + d->height = data.toUInt(pos); pos += 4; - d->colorDepth = data.mid(pos, 4).toUInt(); + d->colorDepth = data.toUInt(pos); pos += 4; - d->numColors = data.mid(pos, 4).toUInt(); + d->numColors = data.toUInt(pos); pos += 4; - uint dataLength = data.mid(pos, 4).toUInt(); + uint dataLength = data.toUInt(pos); pos += 4; if(pos + dataLength > data.size()) { debug("Invalid picture block."); @@ -117,7 +113,7 @@ bool FLAC::Picture::parse(const ByteVector &data) } d->data = data.mid(pos, dataLength); - return true; + return true; } ByteVector FLAC::Picture::render() const diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacproperties.cpp b/Plugins/PluginNowPlaying/taglib/flac/flacproperties.cpp index d36d26ec..e591193e 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/flac/flacproperties.cpp @@ -42,7 +42,8 @@ public: bitrate(0), sampleRate(0), sampleWidth(0), - channels(0) {} + channels(0), + sampleFrames(0) {} ByteVector data; long streamLength; @@ -52,6 +53,7 @@ public: int sampleRate; int sampleWidth; int channels; + unsigned long long sampleFrames; ByteVector signature; }; @@ -101,6 +103,11 @@ 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; @@ -117,7 +124,7 @@ void FLAC::Properties::read() return; } - int pos = 0; + uint pos = 0; // Minimum block size (in samples) pos += 2; @@ -131,7 +138,9 @@ void FLAC::Properties::read() // Maximum frame size (in bytes) pos += 3; - uint flags = d->data.mid(pos, 4).toUInt(true); + uint flags = d->data.toUInt(pos, true); + pos += 4; + d->sampleRate = flags >> 12; d->channels = ((flags >> 9) & 7) + 1; d->sampleWidth = ((flags >> 4) & 31) + 1; @@ -139,12 +148,14 @@ void FLAC::Properties::read() // The last 4 bits are the most significant 4 bits for the 36 bit // stream length in samples. (Audio files measured in days) - uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0; + unsigned long long hi = flags & 0xf; + unsigned long long lo = d->data.toUInt(pos, true); pos += 4; - d->length = d->sampleRate > 0 ? - (d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0; - pos += 4; + d->sampleFrames = (hi << 32) | lo; + + if(d->sampleRate > 0) + d->length = int(d->sampleFrames / d->sampleRate); // Uncompressed bitrate: diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacproperties.h b/Plugins/PluginNowPlaying/taglib/flac/flacproperties.h index 5bba6417..c1458981 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacproperties.h +++ b/Plugins/PluginNowPlaying/taglib/flac/flacproperties.h @@ -77,9 +77,14 @@ namespace TagLib { */ int sampleWidth() 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 header. + * from the stream info header header. */ ByteVector signature() const; diff --git a/Plugins/PluginNowPlaying/taglib/flac/flacunknownmetadatablock.cpp b/Plugins/PluginNowPlaying/taglib/flac/flacunknownmetadatablock.cpp index 96e3447f..dcd5d651 100644 --- a/Plugins/PluginNowPlaying/taglib/flac/flacunknownmetadatablock.cpp +++ b/Plugins/PluginNowPlaying/taglib/flac/flacunknownmetadatablock.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include @@ -34,7 +30,7 @@ using namespace TagLib; -class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate +class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate { public: UnknownMetadataBlockPrivate() : code(0) {} diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.cpp b/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.cpp deleted file mode 100644 index 26d6d25a..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_MP4 - -#include -#include -#include "mp4atom.h" - -using namespace TagLib; - -const char *MP4::Atom::containers[10] = { - "moov", "udta", "mdia", "meta", "ilst", - "stbl", "minf", "moof", "traf", "trak", -}; - -MP4::Atom::Atom(File *file) -{ - offset = file->tell(); - ByteVector header = file->readBlock(8); - if (header.size() != 8) { - // The atom header must be 8 bytes long, otherwise there is either - // trailing garbage or the file is truncated - debug("MP4: Couldn't read 8 bytes of data for atom header"); - length = 0; - file->seek(0, File::End); - return; - } - - length = header.mid(0, 4).toUInt(); - - if (length == 1) { - long long longLength = file->readBlock(8).toLongLong(); - if (longLength >= 8 && longLength <= 0xFFFFFFFF) { - // The atom has a 64-bit length, but it's actually a 32-bit value - length = (long)longLength; - } - else { - debug("MP4: 64-bit atoms are not supported"); - length = 0; - file->seek(0, File::End); - return; - } - } - if (length < 8) { - debug("MP4: Invalid atom size"); - length = 0; - file->seek(0, File::End); - return; - } - - name = header.mid(4, 4); - - for(int i = 0; i < numContainers; i++) { - if(name == containers[i]) { - if(name == "meta") { - file->seek(4, File::Current); - } - while(file->tell() < offset + length) { - MP4::Atom *child = new MP4::Atom(file); - children.append(child); - if (child->length == 0) - return; - } - return; - } - } - - file->seek(offset + length); -} - -MP4::Atom::~Atom() -{ - for(unsigned int i = 0; i < children.size(); i++) { - delete children[i]; - } - children.clear(); -} - -MP4::Atom * -MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4) -{ - if(name1 == 0) { - return this; - } - for(unsigned int i = 0; i < children.size(); i++) { - if(children[i]->name == name1) { - return children[i]->find(name2, name3, name4); - } - } - return 0; -} - -MP4::AtomList -MP4::Atom::findall(const char *name, bool recursive) -{ - MP4::AtomList result; - for(unsigned int i = 0; i < children.size(); i++) { - if(children[i]->name == name) { - result.append(children[i]); - } - if(recursive) { - result.append(children[i]->findall(name, recursive)); - } - } - return result; -} - -bool -MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3) -{ - path.append(this); - if(name1 == 0) { - return true; - } - for(unsigned int i = 0; i < children.size(); i++) { - if(children[i]->name == name1) { - return children[i]->path(path, name2, name3); - } - } - return false; -} - -MP4::Atoms::Atoms(File *file) -{ - file->seek(0, File::End); - long end = file->tell(); - file->seek(0); - while(file->tell() + 8 <= end) { - MP4::Atom *atom = new MP4::Atom(file); - atoms.append(atom); - if (atom->length == 0) - break; - } -} - -MP4::Atoms::~Atoms() -{ - for(unsigned int i = 0; i < atoms.size(); i++) { - delete atoms[i]; - } - atoms.clear(); -} - -MP4::Atom * -MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) -{ - for(unsigned int i = 0; i < atoms.size(); i++) { - if(atoms[i]->name == name1) { - return atoms[i]->find(name2, name3, name4); - } - } - return 0; -} - -MP4::AtomList -MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) -{ - MP4::AtomList path; - for(unsigned int i = 0; i < atoms.size(); i++) { - if(atoms[i]->name == name1) { - if(!atoms[i]->path(path, name2, name3, name4)) { - path.clear(); - } - return path; - } - } - return path; -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.h b/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.h deleted file mode 100644 index 7d9dac28..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4atom.h +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -// This file is not part of the public API! - -#ifndef DO_NOT_DOCUMENT - -#ifndef TAGLIB_MP4ATOM_H -#define TAGLIB_MP4ATOM_H - -#include "tfile.h" -#include "tlist.h" - -namespace TagLib { - - namespace MP4 { - - class Atom; - typedef TagLib::List AtomList; - - class Atom - { - public: - Atom(File *file); - ~Atom(); - Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); - bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0); - AtomList findall(const char *name, bool recursive = false); - long offset; - long length; - TagLib::ByteVector name; - AtomList children; - private: - static const int numContainers = 10; - static const char *containers[10]; - }; - - //! Root-level atoms - class Atoms - { - public: - Atoms(File *file); - ~Atoms(); - Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); - AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0); - AtomList atoms; - }; - - } - -} - -#endif - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4file.cpp b/Plugins/PluginNowPlaying/taglib/mp4/mp4file.cpp deleted file mode 100644 index 2d59a8e5..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4file.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_MP4 - -#include -#include -#include "mp4atom.h" -#include "mp4tag.h" -#include "mp4file.h" - -using namespace TagLib; - -class MP4::File::FilePrivate -{ -public: - FilePrivate() : tag(0), atoms(0), properties(0) - { - } - - ~FilePrivate() - { - if(atoms) { - delete atoms; - atoms = 0; - } - if(tag) { - delete tag; - tag = 0; - } - if(properties) { - delete properties; - properties = 0; - } - } - - MP4::Tag *tag; - MP4::Atoms *atoms; - MP4::Properties *properties; -}; - -MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) - : TagLib::File(file) -{ - d = new FilePrivate; - read(readProperties, audioPropertiesStyle); -} - -MP4::File::~File() -{ - delete d; -} - -MP4::Tag * -MP4::File::tag() const -{ - return d->tag; -} - -MP4::Properties * -MP4::File::audioProperties() const -{ - return d->properties; -} - -bool -MP4::File::checkValid(const MP4::AtomList &list) -{ - for(uint i = 0; i < list.size(); i++) { - if(list[i]->length == 0) - return false; - if(!checkValid(list[i]->children)) - return false; - } - return true; -} - -void -MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle) -{ - if(!isValid()) - return; - - d->atoms = new Atoms(this); - if (!checkValid(d->atoms->atoms)) { - setValid(false); - return; - } - - // must have a moov atom, otherwise consider it invalid - MP4::Atom *moov = d->atoms->find("moov"); - if(!moov) { - setValid(false); - return; - } - - d->tag = new Tag(this, d->atoms); - if(readProperties) { - d->properties = new Properties(this, d->atoms, audioPropertiesStyle); - } -} - -bool -MP4::File::save() -{ - if(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(); -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4file.h b/Plugins/PluginNowPlaying/taglib/mp4/mp4file.h deleted file mode 100644 index 5c28d774..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4file.h +++ /dev/null @@ -1,103 +0,0 @@ -/************************************************************************** - 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 "tag.h" -#include "tfile.h" -#include "taglib_export.h" -#include "mp4properties.h" -#include "mp4tag.h" - -namespace TagLib { - - //! An implementation of MP4 (AAC, ALAC, ...) metadata - namespace MP4 { - - class Atoms; - - /*! - * This implements and provides an interface for MP4 files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to MP4 files. - */ - class TAGLIB_EXPORT File : public TagLib::File - { - public: - /*! - * Contructs a MP4 file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. - * - * \note In the current implementation, both \a readProperties and - * \a propertiesStyle are ignored. - */ - File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average); - - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - /*! - * Returns a pointer to the MP4 tag of the file. - * - * MP4::Tag implements the tag interface, so this serves as the - * reimplementation of TagLib::File::tag(). - * - * \note The Tag is still owned by the MP4::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - Tag *tag() const; - - /*! - * Returns the MP4 audio properties for this file. - */ - Properties *audioProperties() const; - - /*! - * Save the file. - * - * This returns true if the save was successful. - */ - bool save(); - - private: - - void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle); - bool checkValid(const MP4::AtomList &list); - - class FilePrivate; - FilePrivate *d; - }; - - } - -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4item.cpp b/Plugins/PluginNowPlaying/taglib/mp4/mp4item.cpp deleted file mode 100644 index 7dcf5946..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4item.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_MP4 - -#include -#include -#include "mp4item.h" - -using namespace TagLib; - -class MP4::Item::ItemPrivate : public RefCounter -{ -public: - ItemPrivate() : RefCounter(), valid(true) {} - - bool valid; - union { - bool m_bool; - int m_int; - IntPair m_intPair; - }; - StringList m_stringList; - MP4::CoverArtList m_coverArtList; -}; - -MP4::Item::Item() -{ - d = new ItemPrivate; - d->valid = false; -} - -MP4::Item::Item(const Item &item) : d(item.d) -{ - d->ref(); -} - -MP4::Item & -MP4::Item::operator=(const Item &item) -{ - if(d->deref()) { - delete d; - } - d = item.d; - d->ref(); - return *this; -} - -MP4::Item::~Item() -{ - if(d->deref()) { - delete d; - } -} - -MP4::Item::Item(bool value) -{ - d = new ItemPrivate; - d->m_bool = value; -} - -MP4::Item::Item(int value) -{ - d = new ItemPrivate; - d->m_int = value; -} - -MP4::Item::Item(int value1, int value2) -{ - d = new ItemPrivate; - d->m_intPair.first = value1; - d->m_intPair.second = value2; -} - -MP4::Item::Item(const StringList &value) -{ - d = new ItemPrivate; - d->m_stringList = value; -} - -MP4::Item::Item(const MP4::CoverArtList &value) -{ - d = new ItemPrivate; - d->m_coverArtList = value; -} - -bool -MP4::Item::toBool() const -{ - return d->m_bool; -} - -int -MP4::Item::toInt() const -{ - return d->m_int; -} - -MP4::Item::IntPair -MP4::Item::toIntPair() const -{ - return d->m_intPair; -} - -StringList -MP4::Item::toStringList() const -{ - return d->m_stringList; -} - -MP4::CoverArtList -MP4::Item::toCoverArtList() const -{ - return d->m_coverArtList; -} - -bool -MP4::Item::isValid() const -{ - return d->valid; -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4item.h b/Plugins/PluginNowPlaying/taglib/mp4/mp4item.h deleted file mode 100644 index 3158b4dd..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4item.h +++ /dev/null @@ -1,72 +0,0 @@ -/************************************************************************** - 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 "mp4coverart.h" -#include "taglib_export.h" - -namespace TagLib { - - namespace MP4 { - - class TAGLIB_EXPORT Item - { - public: - struct IntPair { - int first, second; - }; - - Item(); - Item(const Item &item); - Item &operator=(const Item &item); - ~Item(); - - Item(int value); - Item(bool value); - Item(int first, int second); - Item(const StringList &value); - Item(const CoverArtList &value); - - int toInt() const; - bool toBool() const; - IntPair toIntPair() const; - StringList toStringList() const; - CoverArtList toCoverArtList() const; - - bool isValid() const; - - private: - class ItemPrivate; - ItemPrivate *d; - }; - - } - -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.cpp b/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.cpp deleted file mode 100644 index c1f8d47c..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_MP4 - -#include -#include -#include "mp4file.h" -#include "mp4atom.h" -#include "mp4properties.h" - -using namespace TagLib; - -class MP4::Properties::PropertiesPrivate -{ -public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0) {} - - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; -}; - -MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) - : AudioProperties(style) -{ - d = new PropertiesPrivate; - - MP4::Atom *moov = atoms->find("moov"); - if(!moov) { - debug("MP4: Atom 'moov' not found"); - return; - } - - MP4::Atom *trak = 0; - ByteVector data; - - MP4::AtomList trakList = moov->findall("trak"); - for (unsigned int i = 0; i < trakList.size(); i++) { - trak = trakList[i]; - MP4::Atom *hdlr = trak->find("mdia", "hdlr"); - if(!hdlr) { - debug("MP4: Atom 'trak.mdia.hdlr' not found"); - return; - } - file->seek(hdlr->offset); - data = file->readBlock(hdlr->length); - if(data.mid(16, 4) == "soun") { - break; - } - trak = 0; - } - if (!trak) { - debug("MP4: No audio tracks"); - return; - } - - MP4::Atom *mdhd = trak->find("mdia", "mdhd"); - if(!mdhd) { - debug("MP4: Atom 'trak.mdia.mdhd' not found"); - return; - } - - file->seek(mdhd->offset); - data = file->readBlock(mdhd->length); - uint version = data[8]; - if(version == 1) { - if (data.size() < 36 + 8) { - debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); - return; - } - long long unit = data.mid(28, 8).toLongLong(); - long long length = data.mid(36, 8).toLongLong(); - d->length = unit ? int(length / unit) : 0; - } - else { - if (data.size() < 24 + 4) { - debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); - return; - } - unsigned int unit = data.mid(20, 4).toUInt(); - unsigned int length = data.mid(24, 4).toUInt(); - d->length = unit ? length / unit : 0; - } - - MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); - if(!atom) { - return; - } - - file->seek(atom->offset); - data = file->readBlock(atom->length); - if(data.mid(20, 4) == "mp4a") { - d->channels = data.mid(40, 2).toShort(); - d->bitsPerSample = data.mid(42, 2).toShort(); - d->sampleRate = data.mid(46, 4).toUInt(); - if(data.mid(56, 4) == "esds" && data[64] == 0x03) { - long pos = 65; - if(data.mid(pos, 3) == "\x80\x80\x80") { - pos += 3; - } - pos += 4; - if(data[pos] == 0x04) { - pos += 1; - if(data.mid(pos, 3) == "\x80\x80\x80") { - pos += 3; - } - pos += 10; - d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000; - } - } - } -} - -MP4::Properties::~Properties() -{ - delete d; -} - -int -MP4::Properties::channels() const -{ - return d->channels; -} - -int -MP4::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int -MP4::Properties::length() const -{ - return d->length; -} - -int -MP4::Properties::bitrate() const -{ - return d->bitrate; -} - -int -MP4::Properties::bitsPerSample() const -{ - return d->bitsPerSample; -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.h b/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.h deleted file mode 100644 index ef813850..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4properties.h +++ /dev/null @@ -1,61 +0,0 @@ -/************************************************************************** - 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: - Properties(File *file, Atoms *atoms, ReadStyle style = Average); - virtual ~Properties(); - - virtual int length() const; - virtual int bitrate() const; - virtual int sampleRate() const; - virtual int channels() const; - virtual int bitsPerSample() const; - - private: - class PropertiesPrivate; - PropertiesPrivate *d; - }; - - } - -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.cpp b/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.cpp deleted file mode 100644 index d7933dbb..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.cpp +++ /dev/null @@ -1,636 +0,0 @@ -/************************************************************************** - 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/ * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef WITH_MP4 - -#include -#include -#include "mp4atom.h" -#include "mp4tag.h" -#include "id3v1genres.h" - -using namespace TagLib; - -class MP4::Tag::TagPrivate -{ -public: - TagPrivate() : file(0), atoms(0) {} - ~TagPrivate() {} - TagLib::File *file; - Atoms *atoms; - ItemListMap items; -}; - -MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) -{ - d = new TagPrivate; - d->file = file; - d->atoms = atoms; - - MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst"); - if(!ilst) { - //debug("Atom moov.udta.meta.ilst not found."); - return; - } - - for(unsigned int i = 0; i < ilst->children.size(); i++) { - MP4::Atom *atom = ilst->children[i]; - file->seek(atom->offset + 8); - if(atom->name == "----") { - parseFreeForm(atom, file); - } - else if(atom->name == "trkn" || atom->name == "disk") { - parseIntPair(atom, file); - } - else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst") { - parseBool(atom, file); - } - else if(atom->name == "tmpo") { - parseInt(atom, file); - } - else if(atom->name == "gnre") { - parseGnre(atom, file); - } - else if(atom->name == "covr") { - parseCovr(atom, file); - } - else { - parseText(atom, file); - } - } -} - -MP4::Tag::~Tag() -{ - delete d; -} - -ByteVectorList -MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) -{ - ByteVectorList result; - ByteVector data = file->readBlock(atom->length - 8); - int i = 0; - unsigned int pos = 0; - while(pos < data.size()) { - int length = data.mid(pos, 4).toUInt(); - ByteVector name = data.mid(pos + 4, 4); - int flags = data.mid(pos + 8, 4).toUInt(); - if(freeForm && i < 2) { - if(i == 0 && name != "mean") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\""); - return result; - } - else if(i == 1 && name != "name") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\""); - return result; - } - result.append(data.mid(pos + 12, length - 12)); - } - else { - if(name != "data") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); - return result; - } - if(expectedFlags == -1 || flags == expectedFlags) { - result.append(data.mid(pos + 16, length - 16)); - } - } - pos += length; - i++; - } - return result; -} - -void -MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file) -{ - ByteVectorList data = parseData(atom, file); - if(data.size()) { - d->items.insert(atom->name, (int)data[0].toShort()); - } -} - -void -MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file) -{ - ByteVectorList data = parseData(atom, file); - if(data.size()) { - int idx = (int)data[0].toShort(); - if(!d->items.contains("\251gen") && idx > 0) { - d->items.insert("\251gen", StringList(ID3v1::genre(idx - 1))); - } - } -} - -void -MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file) -{ - ByteVectorList data = parseData(atom, file); - if(data.size()) { - int a = data[0].mid(2, 2).toShort(); - int b = data[0].mid(4, 2).toShort(); - d->items.insert(atom->name, MP4::Item(a, b)); - } -} - -void -MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file) -{ - ByteVectorList data = parseData(atom, file); - if(data.size()) { - bool value = data[0].size() ? data[0][0] != '\0' : false; - d->items.insert(atom->name, value); - } -} - -void -MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags) -{ - ByteVectorList data = parseData(atom, file, expectedFlags); - if(data.size()) { - StringList value; - for(unsigned int i = 0; i < data.size(); i++) { - value.append(String(data[i], String::UTF8)); - } - d->items.insert(atom->name, value); - } -} - -void -MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) -{ - ByteVectorList data = parseData(atom, file, 1, true); - if(data.size() > 2) { - StringList value; - for(unsigned int i = 2; i < data.size(); i++) { - value.append(String(data[i], String::UTF8)); - } - String name = "----:" + data[0] + ':' + data[1]; - d->items.insert(name, value); - } -} - -void -MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) -{ - MP4::CoverArtList value; - ByteVector data = file->readBlock(atom->length - 8); - unsigned int pos = 0; - while(pos < data.size()) { - int length = data.mid(pos, 4).toUInt(); - ByteVector name = data.mid(pos + 4, 4); - int flags = data.mid(pos + 8, 4).toUInt(); - if(name != "data") { - debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); - break; - } - if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) { - value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), - data.mid(pos + 16, length - 16))); - } - pos += length; - } - if(value.size() > 0) - d->items.insert(atom->name, value); -} - -ByteVector -MP4::Tag::padIlst(const ByteVector &data, int length) -{ - if (length == -1) { - length = ((data.size() + 1023) & ~1023) - data.size(); - } - return renderAtom("free", ByteVector(length, '\1')); -} - -ByteVector -MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) -{ - return ByteVector::fromUInt(data.size() + 8) + name + data; -} - -ByteVector -MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) -{ - ByteVector result; - for(unsigned int i = 0; i < data.size(); i++) { - result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i])); - } - return renderAtom(name, result); -} - -ByteVector -MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item) -{ - ByteVectorList data; - data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); - return renderData(name, 0x15, data); -} - -ByteVector -MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item) -{ - ByteVectorList data; - data.append(ByteVector::fromShort(item.toInt())); - return renderData(name, 0x15, data); -} - -ByteVector -MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item) -{ - ByteVectorList data; - data.append(ByteVector(2, '\0') + - ByteVector::fromShort(item.toIntPair().first) + - ByteVector::fromShort(item.toIntPair().second) + - ByteVector(2, '\0')); - return renderData(name, 0x00, data); -} - -ByteVector -MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item) -{ - ByteVectorList data; - data.append(ByteVector(2, '\0') + - ByteVector::fromShort(item.toIntPair().first) + - ByteVector::fromShort(item.toIntPair().second)); - return renderData(name, 0x00, data); -} - -ByteVector -MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags) -{ - ByteVectorList data; - StringList value = item.toStringList(); - for(unsigned int i = 0; i < value.size(); i++) { - data.append(value[i].data(String::UTF8)); - } - return renderData(name, flags, data); -} - -ByteVector -MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item) -{ - ByteVector data; - MP4::CoverArtList value = item.toCoverArtList(); - for(unsigned int i = 0; i < value.size(); i++) { - data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) + - ByteVector(4, '\0') + value[i].data())); - } - return renderAtom(name, data); -} - -ByteVector -MP4::Tag::renderFreeForm(const String &name, MP4::Item &item) -{ - StringList header = StringList::split(name, ":"); - if (header.size() != 3) { - debug("MP4: Invalid free-form item name \"" + name + "\""); - return ByteVector::null; - } - ByteVector data; - data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8))); - data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8))); - StringList value = item.toStringList(); - for(unsigned int i = 0; i < value.size(); i++) { - data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8))); - } - return renderAtom("----", data); -} - -bool -MP4::Tag::save() -{ - ByteVector data; - for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) { - const String name = i->first; - if(name.startsWith("----")) { - data.append(renderFreeForm(name, i->second)); - } - else if(name == "trkn") { - data.append(renderIntPair(name.data(String::Latin1), i->second)); - } - else if(name == "disk") { - data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second)); - } - else if(name == "cpil" || name == "pgap" || name == "pcst") { - data.append(renderBool(name.data(String::Latin1), i->second)); - } - else if(name == "tmpo") { - data.append(renderInt(name.data(String::Latin1), i->second)); - } - else if(name == "covr") { - data.append(renderCovr(name.data(String::Latin1), i->second)); - } - else if(name.size() == 4){ - data.append(renderText(name.data(String::Latin1), i->second)); - } - else { - debug("MP4: Unknown item name \"" + name + "\""); - } - } - data = renderAtom("ilst", data); - - AtomList path = d->atoms->path("moov", "udta", "meta", "ilst"); - if(path.size() == 4) { - saveExisting(data, path); - } - else { - saveNew(data); - } - - return true; -} - -void -MP4::Tag::updateParents(AtomList &path, long delta, int ignore) -{ - for(unsigned int i = 0; i < path.size() - ignore; i++) { - d->file->seek(path[i]->offset); - long size = d->file->readBlock(4).toUInt(); - // 64-bit - if (size == 1) { - d->file->seek(4, File::Current); // Skip name - long long longSize = d->file->readBlock(8).toLongLong(); - // Seek the offset of the 64-bit size - d->file->seek(path[i]->offset + 8); - d->file->writeBlock(ByteVector::fromLongLong(longSize + delta)); - } - // 32-bit - else { - d->file->seek(path[i]->offset); - d->file->writeBlock(ByteVector::fromUInt(size + delta)); - } - } -} - -void -MP4::Tag::updateOffsets(long delta, long offset) -{ - MP4::Atom *moov = d->atoms->find("moov"); - if(moov) { - MP4::AtomList stco = moov->findall("stco", true); - for(unsigned int i = 0; i < stco.size(); i++) { - MP4::Atom *atom = stco[i]; - if(atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.mid(0, 4).toUInt(); - d->file->seek(atom->offset + 16); - int pos = 4; - while(count--) { - long o = data.mid(pos, 4).toUInt(); - if(o > offset) { - o += delta; - } - d->file->writeBlock(ByteVector::fromUInt(o)); - pos += 4; - } - } - - MP4::AtomList co64 = moov->findall("co64", true); - for(unsigned int i = 0; i < co64.size(); i++) { - MP4::Atom *atom = co64[i]; - if(atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.mid(0, 4).toUInt(); - d->file->seek(atom->offset + 16); - int pos = 4; - while(count--) { - long long o = data.mid(pos, 8).toLongLong(); - if(o > offset) { - o += delta; - } - d->file->writeBlock(ByteVector::fromLongLong(o)); - pos += 8; - } - } - } - - MP4::Atom *moof = d->atoms->find("moof"); - if(moof) { - MP4::AtomList tfhd = moof->findall("tfhd", true); - for(unsigned int i = 0; i < tfhd.size(); i++) { - MP4::Atom *atom = tfhd[i]; - if(atom->offset > offset) { - atom->offset += delta; - } - d->file->seek(atom->offset + 9); - ByteVector data = d->file->readBlock(atom->offset - 9); - unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt(); - if(flags & 1) { - long long o = data.mid(7, 8).toLongLong(); - if(o > offset) { - o += delta; - } - d->file->seek(atom->offset + 16); - d->file->writeBlock(ByteVector::fromLongLong(o)); - } - } - } -} - -void -MP4::Tag::saveNew(ByteVector &data) -{ - data = renderAtom("meta", TagLib::ByteVector(4, '\0') + - renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) + - data + padIlst(data)); - - AtomList path = d->atoms->path("moov", "udta"); - if(path.size() != 2) { - path = d->atoms->path("moov"); - data = renderAtom("udta", data); - } - - long offset = path[path.size() - 1]->offset + 8; - d->file->insert(data, offset, 0); - - updateParents(path, data.size()); - updateOffsets(data.size(), offset); -} - -void -MP4::Tag::saveExisting(ByteVector &data, AtomList &path) -{ - MP4::Atom *ilst = path[path.size() - 1]; - long offset = ilst->offset; - long length = ilst->length; - - MP4::Atom *meta = path[path.size() - 2]; - AtomList::Iterator index = meta->children.find(ilst); - - // check if there is an atom before 'ilst', and possibly use it as padding - if(index != meta->children.begin()) { - AtomList::Iterator prevIndex = index; - prevIndex--; - MP4::Atom *prev = *prevIndex; - if(prev->name == "free") { - offset = prev->offset; - length += prev->length; - } - } - // check if there is an atom after 'ilst', and possibly use it as padding - AtomList::Iterator nextIndex = index; - nextIndex++; - if(nextIndex != meta->children.end()) { - MP4::Atom *next = *nextIndex; - if(next->name == "free") { - length += next->length; - } - } - - long delta = data.size() - length; - if(delta > 0 || (delta < 0 && delta > -8)) { - data.append(padIlst(data)); - delta = data.size() - length; - } - else if(delta < 0) { - data.append(padIlst(data, -delta - 8)); - delta = 0; - } - - d->file->insert(data, offset, length); - - if(delta) { - updateParents(path, delta, 1); - updateOffsets(delta, offset); - } -} - -String -MP4::Tag::title() const -{ - if(d->items.contains("\251nam")) - return d->items["\251nam"].toStringList().toString(", "); - return String::null; -} - -String -MP4::Tag::artist() const -{ - if(d->items.contains("\251ART")) - return d->items["\251ART"].toStringList().toString(", "); - return String::null; -} - -String -MP4::Tag::album() const -{ - if(d->items.contains("\251alb")) - return d->items["\251alb"].toStringList().toString(", "); - return String::null; -} - -String -MP4::Tag::comment() const -{ - if(d->items.contains("\251cmt")) - return d->items["\251cmt"].toStringList().toString(", "); - return String::null; -} - -String -MP4::Tag::genre() const -{ - if(d->items.contains("\251gen")) - return d->items["\251gen"].toStringList().toString(", "); - return String::null; -} - -unsigned int -MP4::Tag::year() const -{ - if(d->items.contains("\251day")) - return d->items["\251day"].toStringList().toString().toInt(); - return 0; -} - -unsigned int -MP4::Tag::track() const -{ - if(d->items.contains("trkn")) - return d->items["trkn"].toIntPair().first; - return 0; -} - -void -MP4::Tag::setTitle(const String &value) -{ - d->items["\251nam"] = StringList(value); -} - -void -MP4::Tag::setArtist(const String &value) -{ - d->items["\251ART"] = StringList(value); -} - -void -MP4::Tag::setAlbum(const String &value) -{ - d->items["\251alb"] = StringList(value); -} - -void -MP4::Tag::setComment(const String &value) -{ - d->items["\251cmt"] = StringList(value); -} - -void -MP4::Tag::setGenre(const String &value) -{ - d->items["\251gen"] = StringList(value); -} - -void -MP4::Tag::setYear(uint value) -{ - d->items["\251day"] = StringList(String::number(value)); -} - -void -MP4::Tag::setTrack(uint value) -{ - d->items["trkn"] = MP4::Item(value, 0); -} - -MP4::ItemListMap & -MP4::Tag::itemListMap() -{ - return d->items; -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.h b/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.h deleted file mode 100644 index 3e6d667f..00000000 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4tag.h +++ /dev/null @@ -1,104 +0,0 @@ -/************************************************************************** - 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_MP4TAG_H -#define TAGLIB_MP4TAG_H - -#include "tag.h" -#include "tbytevectorlist.h" -#include "tfile.h" -#include "tmap.h" -#include "tstringlist.h" -#include "taglib_export.h" -#include "mp4atom.h" -#include "mp4item.h" - -namespace TagLib { - - namespace MP4 { - - typedef TagLib::Map ItemListMap; - - class TAGLIB_EXPORT Tag: public TagLib::Tag - { - public: - Tag(TagLib::File *file, Atoms *atoms); - ~Tag(); - bool save(); - - String title() const; - String artist() const; - String album() const; - String comment() const; - String genre() const; - uint year() const; - uint track() const; - - void setTitle(const String &value); - void setArtist(const String &value); - void setAlbum(const String &value); - void setComment(const String &value); - void setGenre(const String &value); - void setYear(uint value); - void setTrack(uint value); - - ItemListMap &itemListMap(); - - private: - TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); - void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1); - void parseFreeForm(Atom *atom, TagLib::File *file); - void parseInt(Atom *atom, TagLib::File *file); - void parseGnre(Atom *atom, TagLib::File *file); - void parseIntPair(Atom *atom, TagLib::File *file); - void parseBool(Atom *atom, TagLib::File *file); - void parseCovr(Atom *atom, TagLib::File *file); - - TagLib::ByteVector padIlst(const ByteVector &data, int length = -1); - TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data); - TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data); - TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1); - TagLib::ByteVector renderFreeForm(const String &name, Item &item); - TagLib::ByteVector renderBool(const ByteVector &name, Item &item); - TagLib::ByteVector renderInt(const ByteVector &name, Item &item); - TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item); - TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item); - TagLib::ByteVector renderCovr(const ByteVector &name, Item &item); - - void updateParents(AtomList &path, long delta, int ignore = 0); - void updateOffsets(long delta, long offset); - - void saveNew(TagLib::ByteVector &data); - void saveExisting(TagLib::ByteVector &data, AtomList &path); - - class TagPrivate; - TagPrivate *d; - }; - - } - -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.cpp b/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.cpp index 9e9d6b88..979c035e 100644 --- a/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "mpcfile.h" #include "id3v1tag.h" @@ -38,7 +39,7 @@ using namespace TagLib; namespace { - enum { APEIndex, ID3v1Index }; + enum { MPCAPEIndex = 0, MPCID3v1Index = 1 }; } class MPC::File::FilePrivate @@ -93,7 +94,16 @@ MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +MPC::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } MPC::File::~File() @@ -106,6 +116,30 @@ TagLib::Tag *MPC::File::tag() const return &d->tag; } +PropertyMap MPC::File::properties() const +{ + if(d->hasAPE) + return d->tag.access(MPCAPEIndex, false)->properties(); + if(d->hasID3v1) + return d->tag.access(MPCID3v1Index, false)->properties(); + return PropertyMap(); +} + +void MPC::File::removeUnsupportedProperties(const StringList &properties) +{ + if(d->hasAPE) + d->tag.access(MPCAPEIndex, false)->removeUnsupportedProperties(properties); + if(d->hasID3v1) + d->tag.access(MPCID3v1Index, false)->removeUnsupportedProperties(properties); +} + +PropertyMap MPC::File::setProperties(const PropertyMap &properties) +{ + if(d->hasID3v1) + d->tag.access(MPCID3v1Index, false)->setProperties(properties); + return d->tag.access(MPCAPEIndex, true)->setProperties(properties); +} + MPC::Properties *MPC::File::audioProperties() const { return d->properties; @@ -189,18 +223,18 @@ bool MPC::File::save() ID3v1::Tag *MPC::File::ID3v1Tag(bool create) { - return d->tag.access(ID3v1Index, create); + return d->tag.access(MPCID3v1Index, create); } APE::Tag *MPC::File::APETag(bool create) { - return d->tag.access(APEIndex, create); + return d->tag.access(MPCAPEIndex, create); } void MPC::File::strip(int tags) { if(tags & ID3v1) { - d->tag.set(ID3v1Index, 0); + d->tag.set(MPCID3v1Index, 0); APETag(true); } @@ -210,7 +244,7 @@ void MPC::File::strip(int tags) } if(tags & APE) { - d->tag.set(APEIndex, 0); + d->tag.set(MPCAPEIndex, 0); if(!ID3v1Tag()) APETag(true); @@ -222,6 +256,15 @@ void MPC::File::remove(int tags) strip(tags); } +bool MPC::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool MPC::File::hasAPETag() const +{ + return d->hasAPE; +} //////////////////////////////////////////////////////////////////////////////// // private members @@ -234,7 +277,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty d->ID3v1Location = findID3v1(); if(d->ID3v1Location >= 0) { - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); + d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); d->hasID3v1 = true; } @@ -245,7 +288,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty d->APELocation = findAPE(); if(d->APELocation >= 0) { - d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); + d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation)); d->APESize = APETag()->footer()->completeTagSize(); d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize; @@ -274,8 +317,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty // Look for MPC metadata if(readProperties) { - d->properties = new Properties(readBlock(MPC::HeaderSize), - length() - d->ID3v2Size - d->APESize); + d->properties = new Properties(this, length() - d->ID3v2Size - d->APESize); } } diff --git a/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.h b/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.h index 6adc0ffb..1eef8103 100644 --- a/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.h +++ b/Plugins/PluginNowPlaying/taglib/mpc/mpcfile.h @@ -28,9 +28,12 @@ #include "taglib_export.h" #include "tfile.h" +#include "tag.h" #include "mpcproperties.h" +#include "tlist.h" + namespace TagLib { class Tag; @@ -81,13 +84,26 @@ namespace TagLib { }; /*! - * Contructs an MPC file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs an MPC file from \a file. If \a readProperties is 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 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. */ @@ -99,6 +115,22 @@ namespace TagLib { */ virtual TagLib::Tag *tag() const; + /*! + * 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; + + void removeUnsupportedProperties(const StringList &properties); + + /*! + * 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 &); + /*! * Returns the MPC::Properties for this file. If no audio properties * were read then this will return a null pointer. @@ -113,28 +145,39 @@ namespace TagLib { /*! * Returns a pointer to the ID3v1 tag of the file. * - * If \a create is false (the default) this will return a null pointer - * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. If there is already an APE tag, the - * new ID3v1 tag will be placed after it. + * If \a create is false (the default) this returns a null pointer + * if there is no valid APE tag. If \a create is true it will create + * an APE tag if one does not exist and returns a valid pointer. * - * \note The Tag is still owned by the APE::File and should not be + * \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 is still 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 false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is true it will create - * a APE tag if one does not exist. If there is already an ID3v1 tag, thes - * new APE tag will be placed before it. + * an APE tag if one does not exist and returns a valid pointer. If + * there already be an ID3v1 tag, the new APE tag will be placed before it. * - * \note The Tag is still owned by the APE::File and should not be + * \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 is still 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); @@ -155,6 +198,19 @@ namespace TagLib { */ void remove(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; private: File(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.cpp b/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.cpp index 9adc6924..d406f8d7 100644 --- a/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "mpcproperties.h" #include "mpcfile.h" @@ -35,17 +36,21 @@ using namespace TagLib; class MPC::Properties::PropertiesPrivate { public: - PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : - data(d), + PropertiesPrivate(long length, ReadStyle s) : streamLength(length), style(s), version(0), length(0), bitrate(0), sampleRate(0), - channels(0) {} + channels(0), + totalFrames(0), + sampleFrames(0), + trackGain(0), + trackPeak(0), + albumGain(0), + albumPeak(0) {} - ByteVector data; long streamLength; ReadStyle style; int version; @@ -53,6 +58,13 @@ public: int bitrate; int sampleRate; int channels; + uint totalFrames; + uint sampleFrames; + uint trackGain; + uint trackPeak; + uint albumGain; + uint albumPeak; + String flags; }; //////////////////////////////////////////////////////////////////////////////// @@ -61,8 +73,22 @@ public: MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) { - d = new PropertiesPrivate(data, streamLength, style); - read(); + d = new PropertiesPrivate(streamLength, style); + readSV7(data); +} + +MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style) +{ + d = new PropertiesPrivate(streamLength, style); + ByteVector magic = file->readBlock(4); + if(magic == "MPCK") { + // Musepack version 8 + readSV8(file); + } + else { + // Musepack version 7 or older, fixed size header + readSV7(magic + file->readBlock(MPC::HeaderSize - 4)); + } } MPC::Properties::~Properties() @@ -95,30 +121,179 @@ int MPC::Properties::mpcVersion() const return d->version; } +TagLib::uint MPC::Properties::totalFrames() const +{ + return d->totalFrames; +} + +TagLib::uint 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 //////////////////////////////////////////////////////////////////////////////// +unsigned long readSize(File *file, TagLib::uint &sizelength) +{ + unsigned char tmp; + unsigned long size = 0; + + do { + ByteVector b = file->readBlock(1); + tmp = b[0]; + size = (size << 7) | (tmp & 0x7F); + sizelength++; + } while((tmp & 0x80)); + return size; +} + +unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength) +{ + unsigned char tmp; + unsigned long size = 0; + unsigned long pos = 0; + + do { + tmp = data[pos++]; + size = (size << 7) | (tmp & 0x7F); + sizelength++; + } while((tmp & 0x80) && (pos < data.size())); + return size; +} + static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 }; -void MPC::Properties::read() +void MPC::Properties::readSV8(File *file) { - if(!d->data.startsWith("MP+")) - return; + bool readSH = false, readRG = false; - d->version = d->data[3] & 15; + while(!readSH && !readRG) { + ByteVector packetType = file->readBlock(2); + uint packetSizeLength = 0; + unsigned long packetSize = readSize(file, packetSizeLength); + unsigned long dataSize = packetSize - 2 - packetSizeLength; - unsigned int frames; + if(packetType == "SH") { + // Stream Header + // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket + ByteVector data = file->readBlock(dataSize); + readSH = true; - if(d->version >= 7) { - frames = d->data.mid(4, 4).toUInt(false); + TagLib::uint pos = 4; + d->version = data[pos]; + pos += 1; + d->sampleFrames = readSize(data.mid(pos), pos); + ulong begSilence = readSize(data.mid(pos), pos); - std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(d->data.mid(8, 4).toUInt(false))); + std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true))); + pos += 2; + + d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]]; + d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1; + + if((d->sampleFrames - begSilence) != 0) + d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence)); + d->bitrate = d->bitrate / 1000; + + d->length = (d->sampleFrames - begSilence) / d->sampleRate; + } + + else if (packetType == "RG") { + // Replay Gain + // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket + ByteVector data = file->readBlock(dataSize); + readRG = true; + + int replayGainVersion = data[0]; + if(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) +{ + if(data.startsWith("MP+")) { + d->version = data[3] & 15; + if(d->version < 7) + return; + + d->totalFrames = data.toUInt(4, false); + + std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false))); d->sampleRate = sftable[flags[17] * 2 + flags[16]]; d->channels = 2; + + uint gapless = data.toUInt(5, false); + + d->trackGain = data.toShort(14, false); + d->trackPeak = data.toShort(12, false); + d->albumGain = data.toShort(18, false); + d->albumPeak = data.toShort(16, false); + + // convert gain info + if(d->trackGain != 0) { + int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5); + if(tmp >= (1 << 16) || tmp < 0) tmp = 0; + d->trackGain = tmp; + } + + if(d->albumGain != 0) { + int tmp = (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 = (int)(log10((double)d->trackPeak) * 20 * 256 + .5); + + if (d->albumPeak != 0) + d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5); + + bool trueGapless = (gapless >> 31) & 0x0001; + if(trueGapless) { + uint lastFrameSamples = (gapless >> 20) & 0x07FF; + d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples; + } + else + d->sampleFrames = d->totalFrames * 1152 - 576; } else { - uint headerData = d->data.mid(0, 4).toUInt(false); + uint headerData = data.toUInt(0, false); d->bitrate = (headerData >> 23) & 0x01ff; d->version = (headerData >> 11) & 0x03ff; @@ -126,15 +301,16 @@ void MPC::Properties::read() d->channels = 2; if(d->version >= 5) - frames = d->data.mid(4, 4).toUInt(false); + d->totalFrames = data.toUInt(4, false); else - frames = d->data.mid(6, 2).toUInt(false); + d->totalFrames = data.toUShort(6, false); + + d->sampleFrames = d->totalFrames * 1152 - 576; } - uint samples = frames * 1152 - 576; - - d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0; + d->length = d->sampleRate > 0 ? (d->sampleFrames + (d->sampleRate / 2)) / d->sampleRate : 0; if(!d->bitrate) d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; } + diff --git a/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.h b/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.h index d1593458..adf40d83 100644 --- a/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.h +++ b/Plugins/PluginNowPlaying/taglib/mpc/mpcproperties.h @@ -50,9 +50,17 @@ namespace TagLib { /*! * Create an instance of MPC::Properties with the data read from the * ByteVector \a data. + * + * This constructor is deprecated. It only works for MPC version up to 7. */ Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); + /*! + * Create an instance of MPC::Properties with the data read directly + * from a MPC::File. + */ + Properties(File *file, long streamLength, ReadStyle style = Average); + /*! * Destroys this MPC::Properties instance. */ @@ -66,15 +74,44 @@ namespace TagLib { virtual int channels() const; /*! - * Returns the version of the bitstream (SV4-SV7) + * Returns the version of the bitstream (SV4-SV8) */ int mpcVersion() const; + uint totalFrames() const; + uint 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: Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void readSV7(const ByteVector &data); + void readSV8(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1genres.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1genres.cpp index 7893c72c..eba4c526 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1genres.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1genres.cpp @@ -207,7 +207,7 @@ ID3v1::GenreMap ID3v1::genreMap() String ID3v1::genre(int i) { if(i >= 0 && i < genresSize) - return genres[i]; + return genres[i] + String::null; // always make a copy return String::null; } diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.cpp index 1457c81b..9fc8cfd7 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.cpp @@ -58,6 +58,10 @@ const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = &defaultStri // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// +StringHandler::StringHandler() +{ +} + String ID3v1::StringHandler::parse(const ByteVector &data) const { return String(data, String::Latin1).stripWhiteSpace(); @@ -178,22 +182,32 @@ void ID3v1::Tag::setGenre(const String &s) d->genre = ID3v1::genreIndex(s); } -void ID3v1::Tag::setYear(uint i) +void ID3v1::Tag::setYear(TagLib::uint i) { d->year = i > 0 ? String::number(i) : String::null; } -void ID3v1::Tag::setTrack(uint i) +void ID3v1::Tag::setTrack(TagLib::uint i) { d->track = i < 256 ? i : 0; } +TagLib::uint ID3v1::Tag::genreNumber() const +{ + return d->genre; +} + +void ID3v1::Tag::setGenreNumber(TagLib::uint i) +{ + d->genre = i < 256 ? i : 255; +} + void ID3v1::Tag::setStringHandler(const StringHandler *handler) { - if(TagPrivate::stringHandler != &defaultStringHandler) - delete TagPrivate::stringHandler; - - TagPrivate::stringHandler = handler; + if (handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; } //////////////////////////////////////////////////////////////////////////////// diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.h index fad485e5..7b26d023 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v1/id3v1tag.h @@ -62,6 +62,7 @@ namespace TagLib { TAGLIB_IGNORE_MISSING_DESTRUCTOR public: // BIC: Add virtual destructor. + StringHandler(); /*! * Decode a string from \a data. The default implementation assumes that @@ -139,20 +140,40 @@ namespace TagLib { virtual String album() const; virtual String comment() const; virtual String genre() const; - virtual uint year() const; - virtual uint track() const; + virtual TagLib::uint year() const; + virtual TagLib::uint track() const; virtual void setTitle(const String &s); virtual void setArtist(const String &s); virtual void setAlbum(const String &s); virtual void setComment(const String &s); virtual void setGenre(const String &s); - virtual void setYear(uint i); - virtual void setTrack(uint i); + virtual void setYear(TagLib::uint i); + virtual void setTrack(TagLib::uint i); + + /*! + * Returns the genre in number. + * + * /note Normally 255 indicates that this tag contains no genre. + */ + TagLib::uint 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(TagLib::uint 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 */ diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp index e4e97d01..86f8e355 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp @@ -136,7 +136,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data) int pos = 1; d->mimeType = readStringField(data, String::Latin1, &pos); - /* Now we need at least two more bytes available */ + /* Now we need at least two more bytes available */ if (uint(pos) + 1 >= data.size()) { debug("Truncated picture frame."); return; @@ -152,7 +152,7 @@ ByteVector AttachedPictureFrame::renderFields() const { ByteVector data; - String::Type encoding = checkEncoding(d->description, d->textEncoding); + String::Type encoding = checkTextEncoding(d->description, d->textEncoding); data.append(char(encoding)); data.append(d->mimeType.data(String::Latin1)); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.cpp index 406598d9..deaa9d9a 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -29,6 +29,7 @@ #include #include "commentsframe.h" +#include "tpropertymap.h" using namespace TagLib; using namespace ID3v2; @@ -109,6 +110,19 @@ 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 if(key.isNull()) + map.unsupportedData().append(L"COMM/" + description()); + else + map.insert("COMMENT:" + key, text()); + return map; +} + CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static { ID3v2::FrameList comments = tag->frameList("COMM"); @@ -144,8 +158,13 @@ void CommentsFrame::parseFields(const ByteVector &data) ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); if(l.size() == 2) { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); + 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); + } } } @@ -155,8 +174,8 @@ ByteVector CommentsFrame::renderFields() const String::Type encoding = d->textEncoding; - encoding = checkEncoding(d->description, encoding); - encoding = checkEncoding(d->text, encoding); + encoding = checkTextEncoding(d->description, encoding); + encoding = checkTextEncoding(d->text, encoding); v.append(char(encoding)); v.append(d->language.size() == 3 ? d->language : "XXX"); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.h index def01dcf..f65f6f01 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/commentsframe.h @@ -136,6 +136,17 @@ namespace TagLib { */ 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/" in the unsupportedData() + * attribute of the returned map. + * - otherwise, the key will be "COMMENT:" + * - The single value will be the frame's text(). + */ + PropertyMap asProperties() const; + /*! * Comments each have a unique description. This searches for a comment * frame with the decription \a d and returns a pointer to it. If no diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.cpp new file mode 100644 index 00000000..9451c4c4 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + 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 + +#include "ownershipframe.h" +#include + +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 = new OwnershipFramePrivate; + d->textEncoding = encoding; +} + +OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data) +{ + d = new OwnershipFramePrivate; + setData(data); +} + +OwnershipFrame::~OwnershipFrame() +{ + delete d; +} + +String OwnershipFrame::toString() const +{ + return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + 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 &s) +{ + d->seller = s; +} + +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; + + // Get the text encoding + d->textEncoding = String::Type(data[0]); + pos += 1; + + // Read the price paid this is a null terminate 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 +{ + ByteVector v; + + v.append(char(d->textEncoding)); + v.append(d->pricePaid.data(String::Latin1)); + v.append(textDelimiter(String::Latin1)); + v.append(d->datePurchased.data(String::Latin1)); + v.append(d->seller.data(d->textEncoding)); + + return v; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h) +{ + d = new OwnershipFramePrivate; + parseFields(fieldData(data)); +} diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.h new file mode 100644 index 00000000..34fc9129 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/ownershipframe.h @@ -0,0 +1,151 @@ +/*************************************************************************** + 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 "id3v2frame.h" +#include "taglib_export.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 a ownership based on the data in \a data. + */ + explicit OwnershipFrame(const ByteVector &data); + + /*! + * Destroys this OwnershipFrame instance. + */ + virtual ~OwnershipFrame(); + + /*! + * Returns the text of this popularimeter. + * + * \see text() + */ + virtual String toString() const; + + /*! + * Returns the date purchased. + * + * \see setDatePurchased() + */ + String datePurchased() const; + + /*! + * Set the date purchased. + * + * \see datePurchased() + */ + void setDatePurchased(const String &datePurchased); + + /*! + * Returns the price paid. + * + * \see setPricePaid() + */ + String pricePaid() const; + + /*! + * Set the price paid. + * + * \see pricePaid() + */ + void setPricePaid(const String &pricePaid); + + /*! + * 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. + + virtual void parseFields(const ByteVector &data); + virtual ByteVector renderFields() const; + + private: + /*! + * The constructor used by the FrameFactory. + */ + OwnershipFrame(const ByteVector &data, Header *h); + OwnershipFrame(const OwnershipFrame &); + OwnershipFrame &operator=(const OwnershipFrame &); + + class OwnershipFramePrivate; + OwnershipFramePrivate *d; + }; + + } +} +#endif diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/popularimeterframe.cpp index cfe8c9f4..3d4429f7 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/popularimeterframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/popularimeterframe.cpp @@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data) if(pos < size) { d->rating = (unsigned char)(data[pos++]); if(pos < size) { - d->counter = data.mid(pos, 4).toUInt(); + d->counter = data.toUInt(static_cast(pos)); } } } diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/privateframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/privateframe.cpp index f2d2a03c..24ee0f35 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/privateframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/privateframe.cpp @@ -98,7 +98,7 @@ void PrivateFrame::parseFields(const ByteVector &data) } // Owner identifier is assumed to be Latin1 - + const int byteAlign = 1; const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp index 955b3ad0..e3efbc38 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp @@ -191,7 +191,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data) ChannelData &channel = d->channels[type]; - channel.volumeAdjustment = data.mid(pos, 2).toShort(); + channel.volumeAdjustment = data.toShort(static_cast(pos)); pos += 2; channel.peakVolume.bitsRepresentingPeak = data[pos]; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 7c2ab909..70ea50f8 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -25,8 +25,9 @@ #include #include - #include "textidentificationframe.h" +#include "tpropertymap.h" +#include "id3v1genres.h" using namespace TagLib; using namespace ID3v2; @@ -57,6 +58,32 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : setData(data); } +TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) // static +{ + TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL"); + StringList l; + for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){ + l.append(it->first); + l.append(it->second.toString(",")); // comma-separated list of names + } + frame->setText(l); + return frame; +} + +TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) // static +{ + TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL"); + StringList l; + for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){ + if(!it->first.startsWith(instrumentPrefix)) // should not happen + continue; + l.append(it->first.substr(instrumentPrefix.size())); + l.append(it->second.toString(",")); + } + frame->setText(l); + return frame; +} + TextIdentificationFrame::~TextIdentificationFrame() { delete d; @@ -92,6 +119,61 @@ void TextIdentificationFrame::setTextEncoding(String::Type encoding) d->textEncoding = encoding; } +// array of allowed TIPL prefixes and their corresponding key value +static const TagLib::uint involvedPeopleSize = 5; +static const char* involvedPeople[][2] = { + {"ARRANGER", "ARRANGER"}, + {"ENGINEER", "ENGINEER"}, + {"PRODUCER", "PRODUCER"}, + {"DJ-MIX", "DJMIXER"}, + {"MIX", "MIXER"}, +}; + +const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static +{ + static KeyConversionMap m; + if(m.isEmpty()) + for(uint i = 0; i < involvedPeopleSize; ++i) + m.insert(involvedPeople[i][1], involvedPeople[i][0]); + return m; +} + +PropertyMap TextIdentificationFrame::asProperties() const +{ + if(frameID() == "TIPL") + return makeTIPLProperties(); + if(frameID() == "TMCL") + return makeTMCLProperties(); + PropertyMap map; + String tagName = frameIDToKey(frameID()); + if(tagName.isNull()) { + map.unsupportedData().append(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(StringList::Iterator it = values.begin(); it != values.end(); ++it) { + bool ok = false; + int test = it->toInt(&ok); // test if the genre value is an integer + if(ok) + *it = ID3v1::genre(test); + } + } else if(tagName == "DATE") { + for(StringList::Iterator it = values.begin(); it != values.end(); ++it) { + // 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 = it->find("T"); + if(tpos != -1) + (*it)[tpos] = ' '; + } + } + PropertyMap ret; + ret.insert(tagName, values); + return ret; +} + //////////////////////////////////////////////////////////////////////////////// // TextIdentificationFrame protected members //////////////////////////////////////////////////////////////////////////////// @@ -131,15 +213,17 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) { if(!(*it).isEmpty()) { - String s(*it, d->textEncoding); - d->fieldList.append(s); + if(d->textEncoding == String::Latin1) + d->fieldList.append(Tag::latin1StringHandler()->parse(*it)); + else + d->fieldList.append(String(*it, d->textEncoding)); } } } ByteVector TextIdentificationFrame::renderFields() const { - String::Type encoding = checkEncoding(d->fieldList, d->textEncoding); + String::Type encoding = checkTextEncoding(d->fieldList, d->textEncoding); ByteVector v; @@ -170,6 +254,55 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header 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.unsupportedData().append(frameID()); + return map; + } + StringList l = fieldList(); + for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + bool found = false; + for(uint i = 0; i < involvedPeopleSize; ++i) + if(*it == involvedPeople[i][0]) { + map.insert(involvedPeople[i][1], (++it)->split(",")); + found = true; + break; + } + if(!found){ + // invalid involved role -> mark whole frame as unsupported in order to be consisten with writing + map.clear(); + map.unsupportedData().append(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.unsupportedData().append(frameID()); + return map; + } + StringList l = fieldList(); + for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + String instrument = it->upper(); + if(instrument.isNull()) { + // instrument is not a valid key -> frame unsupported + map.clear(); + map.unsupportedData().append(frameID()); + return map; + } + map.insert(L"PERFORMER:" + instrument, (++it)->split(",")); + } + return map; +} + //////////////////////////////////////////////////////////////////////////////// // UserTextIdentificationFrame public members //////////////////////////////////////////////////////////////////////////////// @@ -191,6 +324,14 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data) checkFields(); } +UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) : + TextIdentificationFrame("TXXX", encoding), + d(0) +{ + setDescription(description); + setText(values); +} + String UserTextIdentificationFrame::toString() const { return "[" + description() + "] " + fieldList().toString(); @@ -238,6 +379,17 @@ void UserTextIdentificationFrame::setDescription(const String &s) TextIdentificationFrame::setText(l); } +PropertyMap UserTextIdentificationFrame::asProperties() const +{ + PropertyMap map; + String tagName = txxxToKey(description()); + StringList v = fieldList(); + for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it) + if(it != v.begin()) + map.insert(tagName, *it); + return map; +} + UserTextIdentificationFrame *UserTextIdentificationFrame::find( ID3v2::Tag *tag, const String &description) // static { diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.h index 418ef970..58ed63e6 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/textidentificationframe.h @@ -27,6 +27,7 @@ #define TAGLIB_TEXTIDENTIFICATIONFRAME_H #include "tstringlist.h" +#include "tmap.h" #include "taglib_export.h" #include "id3v2frame.h" @@ -36,6 +37,7 @@ namespace TagLib { namespace ID3v2 { class Tag; + typedef Map KeyConversionMap; //! An ID3v2 text identification frame implementation @@ -123,6 +125,20 @@ namespace TagLib { */ 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. */ @@ -173,6 +189,14 @@ namespace TagLib { */ 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; + protected: // Reimplementations. @@ -188,6 +212,16 @@ namespace TagLib { TextIdentificationFrame(const TextIdentificationFrame &); TextIdentificationFrame &operator=(const TextIdentificationFrame &); + /*! + * 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; TextIdentificationFramePrivate *d; }; @@ -218,6 +252,12 @@ namespace TagLib { */ 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); + virtual String toString() const; /*! @@ -236,6 +276,21 @@ namespace TagLib { void setText(const String &text); 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/" + * 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; + /*! * Searches for the user defined text frame with the description \a description * in \a tag. This returns null if no matching frames were found. diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp index e12583ad..a0e842e0 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp @@ -24,8 +24,10 @@ ***************************************************************************/ #include +#include #include +#include "id3v2tag.h" #include "uniquefileidentifierframe.h" using namespace TagLib; @@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const return String::null; } +PropertyMap UniqueFileIdentifierFrame::asProperties() const +{ + PropertyMap map; + if(d->owner == "http://musicbrainz.org") { + map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); + } + else { + map.unsupportedData().append(frameID() + String("/") + d->owner); + } + return map; +} + +UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static +{ + ID3v2::FrameList comments = tag->frameList("UFID"); + + for(ID3v2::FrameList::ConstIterator it = comments.begin(); + it != comments.end(); + ++it) + { + UniqueFileIdentifierFrame *frame = dynamic_cast(*it); + if(frame && frame->owner() == o) + return frame; + } + + return 0; +} + void UniqueFileIdentifierFrame::parseFields(const ByteVector &data) { if(data.size() < 1) { diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h index 1292b392..add5a2e0 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h @@ -94,13 +94,23 @@ namespace TagLib { virtual String toString() const; + PropertyMap asProperties() const; + + /*! + * 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: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; private: UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &); - UniqueFileIdentifierFrame &operator=(UniqueFileIdentifierFrame &); + UniqueFileIdentifierFrame &operator=(const UniqueFileIdentifierFrame &); UniqueFileIdentifierFrame(const ByteVector &data, Header *h); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 0a8927e7..8f96cb8d 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -27,7 +27,9 @@ #include "unsynchronizedlyricsframe.h" #include +#include #include +#include using namespace TagLib; using namespace ID3v2; @@ -111,6 +113,30 @@ void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding) d->textEncoding = encoding; } +PropertyMap UnsynchronizedLyricsFrame::asProperties() const +{ + PropertyMap map; + String key = description().upper(); + if(key.isEmpty() || key.upper() == "LYRICS") + map.insert("LYRICS", text()); + else if(key.isNull()) + map.unsupportedData().append(L"USLT/" + description()); + else + map.insert("LYRICS:" + key, text()); + return map; +} + +UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static +{ + ID3v2::FrameList lyrics = tag->frameList("USLT"); + + for(ID3v2::FrameList::ConstIterator it = lyrics.begin(); it != lyrics.end(); ++it){ + UnsynchronizedLyricsFrame *frame = dynamic_cast(*it); + if(frame && frame->description() == d) + return frame; + } + return 0; +} //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// @@ -132,8 +158,13 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); if(l.size() == 2) { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); + 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); + } } } diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h index 0f8260e4..3af354fc 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h @@ -134,6 +134,28 @@ namespace TagLib { */ 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/" in the unsupportedData() + * attribute of the returned map. + * - otherwise, the key will be "LYRICS:" + * - 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; + + /*! + * LyricsFrames each have a unique description. This searches for a lyrics + * frame with the decription \a d and returns a pointer to it. If no + * frame is found that matches the given description null is returned. + * + * \see description() + */ + static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d); + protected: // Reimplementations. diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 7756c4ad..6bcbbda4 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -26,8 +26,10 @@ ***************************************************************************/ #include "urllinkframe.h" +#include "id3v2tag.h" #include #include +#include using namespace TagLib; using namespace ID3v2; @@ -78,6 +80,18 @@ String UrlLinkFrame::toString() const return url(); } +PropertyMap UrlLinkFrame::asProperties() const +{ + String key = frameIDToKey(frameID()); + PropertyMap map; + if(key.isNull()) + // unknown W*** frame - this normally shouldn't happen + map.unsupportedData().append(frameID()); + else + map.insert(key, url()); + return map; +} + void UrlLinkFrame::parseFields(const ByteVector &data) { d->url = String(data); @@ -139,6 +153,30 @@ void UserUrlLinkFrame::setDescription(const String &s) d->description = s; } +PropertyMap UserUrlLinkFrame::asProperties() const +{ + PropertyMap map; + String key = description().upper(); + if(key.isEmpty() || key.upper() == "URL") + map.insert("URL", url()); + else if(key.isNull()) + map.unsupportedData().append(L"WXXX/" + description()); + else + map.insert("URL:" + key, url()); + return map; +} + +UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &description) // static +{ + FrameList l = tag->frameList("WXXX"); + for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) { + UserUrlLinkFrame *f = dynamic_cast(*it); + if(f && f->description() == description) + return f; + } + return 0; +} + void UserUrlLinkFrame::parseFields(const ByteVector &data) { if(data.size() < 2) { @@ -175,7 +213,7 @@ ByteVector UserUrlLinkFrame::renderFields() const { ByteVector v; - String::Type encoding = checkEncoding(d->description, d->textEncoding); + String::Type encoding = checkTextEncoding(d->description, d->textEncoding); v.append(char(encoding)); v.append(d->description.data(encoding)); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.h index f89faad0..7ac966b2 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/frames/urllinkframe.h @@ -68,6 +68,7 @@ namespace TagLib { virtual void setText(const String &s); virtual String toString() const; + PropertyMap asProperties() const; protected: virtual void parseFields(const ByteVector &data); @@ -150,6 +151,22 @@ namespace TagLib { */ void setDescription(const String &s); + /*! + * Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key, + * and the URL as 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/" + * in its unsupportedData() list. + */ + PropertyMap asProperties() const; + + /*! + * 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(Tag *tag, const String &description); + protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.cpp index 786336b9..3cafcff9 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.cpp @@ -23,7 +23,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifndef HAVE_ZLIB +#ifdef HAVE_CONFIG_H #include #endif @@ -36,8 +36,16 @@ #include #include +#include "id3v2tag.h" #include "id3v2frame.h" #include "id3v2synchdata.h" +#include "tpropertymap.h" +#include "frames/textidentificationframe.h" +#include "frames/urllinkframe.h" +#include "frames/unsynchronizedlyricsframe.h" +#include "frames/commentsframe.h" +#include "frames/uniquefileidentifierframe.h" +#include "frames/unknownframe.h" using namespace TagLib; using namespace ID3v2; @@ -65,7 +73,7 @@ namespace return false; for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { + if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { return false; } } @@ -95,10 +103,62 @@ ByteVector Frame::textDelimiter(String::Type t) return d; } +const String Frame::instrumentPrefix("PERFORMER:"); +const String Frame::commentPrefix("COMMENT:"); +const String Frame::lyricsPrefix("LYRICS:"); +const String Frame::urlPrefix("URL:"); + //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// +Frame *Frame::createTextualFrame(const String &key, const StringList &values) //static +{ + // check if the key is contained in the key<=>frameID mapping + ByteVector frameID = keyToFrameID(key); + if(!frameID.isNull()) { + if(frameID[0] == 'T'){ // text frame + TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8); + frame->setText(values); + return frame; + } else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value + UrlLinkFrame* frame = new UrlLinkFrame(frameID); + frame->setUrl(values.front()); + return frame; + } + } + if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) { + UniqueFileIdentifierFrame *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(lyricsPrefix)) && values.size() == 1){ + UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8); + frame->setDescription(key == "LYRICS" ? key : key.substr(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(urlPrefix)) && values.size() == 1){ + UserUrlLinkFrame *frame = new UserUrlLinkFrame(String::UTF8); + frame->setDescription(key == "URL" ? key : key.substr(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(commentPrefix)) && values.size() == 1){ + CommentsFrame *frame = new CommentsFrame(String::UTF8); + if (key != "COMMENT"){ + frame->setDescription(key.substr(commentPrefix.size())); + } + frame->setText(values.front()); + return frame; + } + // if non of the above cases apply, we use a TXXX frame with the key as description + return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8); +} + Frame::~Frame() { delete d; @@ -221,7 +281,11 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int if(end < *position) return String::null; - String str = String(data.mid(*position, end - *position), encoding); + 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(); @@ -230,19 +294,241 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static { + return checkEncoding(fields, encoding, 4); +} + +String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding, uint version) // static +{ + if((encoding == String::UTF8 || encoding == String::UTF16BE) && version != 4) + return String::UTF16; + if(encoding != String::Latin1) return encoding; for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { if(!(*it).isLatin1()) { - debug("Frame::checkEncoding() -- Rendering using UTF8."); - return String::UTF8; + if(version == 4) { + debug("Frame::checkEncoding() -- Rendering using UTF8."); + return String::UTF8; + } + else { + debug("Frame::checkEncoding() -- Rendering using UTF16."); + return String::UTF16; + } } } return String::Latin1; } +String::Type Frame::checkTextEncoding(const StringList &fields, String::Type encoding) const +{ + return checkEncoding(fields, encoding, header()->version()); +} + +static const TagLib::uint frameTranslationSize = 51; +static const char *frameTranslation[][2] = { + // Text information frames + { "TALB", "ALBUM"}, + { "TBPM", "BPM" }, + { "TCOM", "COMPOSER" }, + { "TCON", "GENRE" }, + { "TCOP", "COPYRIGHT" }, + { "TDEN", "ENCODINGTIME" }, + { "TDLY", "PLAYLISTDELAY" }, + { "TDOR", "ORIGINALDATE" }, + { "TDRC", "DATE" }, + // { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 + // { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 + // { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 + // { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4 + { "TDRL", "RELEASEDATE" }, + { "TDTG", "TAGGINGDATE" }, + { "TENC", "ENCODEDBY" }, + { "TEXT", "LYRICIST" }, + { "TFLT", "FILETYPE" }, + //{ "TIPL", "INVOLVEDPEOPLE" }, handled separately + { "TIT1", "CONTENTGROUP" }, + { "TIT2", "TITLE"}, + { "TIT3", "SUBTITLE" }, + { "TKEY", "INITIALKEY" }, + { "TLAN", "LANGUAGE" }, + { "TLEN", "LENGTH" }, + //{ "TMCL", "MUSICIANCREDITS" }, handled separately + { "TMED", "MEDIA" }, + { "TMOO", "MOOD" }, + { "TOAL", "ORIGINALALBUM" }, + { "TOFN", "ORIGINALFILENAME" }, + { "TOLY", "ORIGINALLYRICIST" }, + { "TOPE", "ORIGINALARTIST" }, + { "TOWN", "OWNER" }, + { "TPE1", "ARTIST"}, + { "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST' + { "TPE3", "CONDUCTOR" }, + { "TPE4", "REMIXER" }, // could also be ARRANGER + { "TPOS", "DISCNUMBER" }, + { "TPRO", "PRODUCEDNOTICE" }, + { "TPUB", "LABEL" }, + { "TRCK", "TRACKNUMBER" }, + { "TRSN", "RADIOSTATION" }, + { "TRSO", "RADIOSTATIONOWNER" }, + { "TSOA", "ALBUMSORT" }, + { "TSOP", "ARTISTSORT" }, + { "TSOT", "TITLESORT" }, + { "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes + { "TSRC", "ISRC" }, + { "TSSE", "ENCODING" }, + // URL frames + { "WCOP", "COPYRIGHTURL" }, + { "WOAF", "FILEWEBPAGE" }, + { "WOAR", "ARTISTWEBPAGE" }, + { "WOAS", "AUDIOSOURCEWEBPAGE" }, + { "WORS", "RADIOSTATIONWEBPAGE" }, + { "WPAY", "PAYMENTWEBPAGE" }, + { "WPUB", "PUBLISHERWEBPAGE" }, + //{ "WXXX", "URL"}, handled specially + // Other frames + { "COMM", "COMMENT" }, + //{ "USLT", "LYRICS" }, handled specially +}; + +static const TagLib::uint txxxFrameTranslationSize = 8; +static const char *txxxFrameTranslation[][2] = { + { "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, + { "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, + { "Acoustid Id", "ACOUSTID_ID" }, + { "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" }, + { "MusicIP PUID", "MUSICIP_PUID" }, +}; + +Map &idMap() +{ + static Map m; + if(m.isEmpty()) + for(size_t i = 0; i < frameTranslationSize; ++i) + m[frameTranslation[i][0]] = frameTranslation[i][1]; + return m; +} + +Map &txxxMap() +{ + static Map m; + if(m.isEmpty()) { + for(size_t i = 0; i < txxxFrameTranslationSize; ++i) { + String key = String(txxxFrameTranslation[i][0]).upper(); + m[key] = txxxFrameTranslation[i][1]; + } + } + return m; +} + +// list of deprecated frames and their successors +static const TagLib::uint deprecatedFramesSize = 4; +static const char *deprecatedFrames[][2] = { + {"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3) + {"TDAT", "TDRC"}, // 2.3 -> 2.4 + {"TYER", "TDRC"}, // 2.3 -> 2.4 + {"TIME", "TDRC"}, // 2.3 -> 2.4 +}; + +Map &deprecationMap() +{ + static Map depMap; + if(depMap.isEmpty()) + for(TagLib::uint i = 0; i < deprecatedFramesSize; ++i) + depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1]; + return depMap; +} + +String Frame::frameIDToKey(const ByteVector &id) +{ + Map &m = idMap(); + if(m.contains(id)) + return m[id]; + if(deprecationMap().contains(id)) + return m[deprecationMap()[id]]; + return String::null; +} + +ByteVector Frame::keyToFrameID(const String &s) +{ + static Map m; + if(m.isEmpty()) + for(size_t i = 0; i < frameTranslationSize; ++i) + m[frameTranslation[i][1]] = frameTranslation[i][0]; + if(m.contains(s.upper())) + return m[s]; + return ByteVector::null; +} + +String Frame::txxxToKey(const String &description) +{ + Map &m = txxxMap(); + String d = description.upper(); + if(m.contains(d)) + return m[d]; + return d; +} + +String Frame::keyToTXXX(const String &s) +{ + static Map m; + if(m.isEmpty()) + for(size_t i = 0; i < txxxFrameTranslationSize; ++i) + m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0]; + if(m.contains(s.upper())) + return m[s]; + return s; +} + +PropertyMap Frame::asProperties() const +{ + if(dynamic_cast< const UnknownFrame *>(this)) { + PropertyMap m; + m.unsupportedData().append("UNKNOWN/" + frameID()); + return m; + } + const ByteVector &id = frameID(); + // workaround until this function is virtual + if(id == "TXXX") + return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties(); + else if(id[0] == 'T') + return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties(); + else if(id == "WXXX") + return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties(); + else if(id[0] == 'W') + return dynamic_cast< const UrlLinkFrame* >(this)->asProperties(); + else if(id == "COMM") + return dynamic_cast< const CommentsFrame* >(this)->asProperties(); + else if(id == "USLT") + return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties(); + else if(id == "UFID") + return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties(); + PropertyMap m; + m.unsupportedData().append(id); + return m; +} + +void Frame::splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties, + PropertyMap &tiplProperties, PropertyMap &tmclProperties) +{ + + singleFrameProperties.clear(); + tiplProperties.clear(); + tmclProperties.clear(); + for(PropertyMap::ConstIterator it = original.begin(); it != original.end(); ++it) { + if(TextIdentificationFrame::involvedPeopleMap().contains(it->first)) + tiplProperties.insert(it->first, it->second); + else if(it->first.startsWith(TextIdentificationFrame::instrumentPrefix)) + tmclProperties.insert(it->first, it->second); + else + singleFrameProperties.insert(it->first, it->second); + } +} + //////////////////////////////////////////////////////////////////////////////// // Frame::Header class //////////////////////////////////////////////////////////////////////////////// @@ -356,7 +642,7 @@ void Frame::Header::setData(const ByteVector &data, uint version) return; } - d->frameSize = data.mid(3, 3).toUInt(); + d->frameSize = data.toUInt(3, 3, true); break; } @@ -384,7 +670,7 @@ void Frame::Header::setData(const ByteVector &data, uint version) // Set the size -- the frame size is the four bytes starting at byte four in // the frame header (structure 4) - d->frameSize = data.mid(4, 4).toUInt(); + d->frameSize = data.toUInt(4U); { // read the first byte of flags std::bitset<8> flags(data[8]); @@ -431,7 +717,7 @@ void Frame::Header::setData(const ByteVector &data, uint version) // iTunes writes v2.4 tags with v2.3-like frame sizes if(d->frameSize > 127) { if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) { - unsigned int uintSize = data.mid(4, 4).toUInt(); + unsigned int uintSize = data.toUInt(4U); if(isValidFrameID(data.mid(uintSize + 10, 4))) { d->frameSize = uintSize; } @@ -484,6 +770,11 @@ TagLib::uint Frame::Header::version() const return d->version; } +void Frame::Header::setVersion(TagLib::uint version) +{ + d->version = version; +} + bool Frame::Header::tagAlterPreservation() const { return d->tagAlterPreservation; @@ -538,7 +829,11 @@ ByteVector Frame::Header::render() const { ByteVector flags(2, char(0)); // just blank for the moment - ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags; + ByteVector v = d->frameID + + (d->version == 3 + ? ByteVector::fromUInt(d->frameSize) + : SynchData::fromUInt(d->frameSize)) + + flags; return v; } diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.h index 661be3d2..3e257d4f 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2frame.h @@ -33,6 +33,7 @@ namespace TagLib { class StringList; + class PropertyMap; namespace ID3v2 { @@ -56,6 +57,14 @@ namespace TagLib { friend class FrameFactory; public: + + /*! + * Creates a textual frame which corresponds to a single key in the PropertyMap + * interface. These are all (User)TextIdentificationFrames except TIPL and TMCL, + * all (User)URLLinkFrames, CommentsFrames, and UnsynchronizedLyricsFrame. + */ + static Frame *createTextualFrame(const String &key, const StringList &values); + /*! * Destroys this Frame instance. */ @@ -126,6 +135,28 @@ namespace TagLib { */ static ByteVector textDelimiter(String::Type t); + /*! + * 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: class Header; @@ -199,9 +230,76 @@ namespace TagLib { * Checks a the list of string values to see if they can be used with the * specified encoding and returns the recommended encoding. */ + // BIC: remove and make non-static static String::Type checkEncoding(const StringList &fields, String::Type encoding); + /*! + * Checks a 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 specified version. + */ + // BIC: remove and make non-static + static String::Type checkEncoding(const StringList &fields, + String::Type encoding, uint version); + + /*! + * Checks a 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 returend + * PropertyMap will be empty, and its unsupportedData() will contain this frame's + * ID. + * BIC: Will be a virtual function in future releases. + */ + PropertyMap asProperties() const; + + /*! + * Returns an appropriate ID3 frame ID for the given free-form tag key. This method + * will return ByteVector::null 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 String::null is returned. + */ + static String frameIDToKey(const ByteVector &); + + /*! + * 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 &); + + /*! + * 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: Frame(const Frame &); Frame &operator=(const Frame &); @@ -294,11 +392,17 @@ namespace TagLib { void setFrameSize(uint size); /*! - * Returns the ID3v2 version of the header (as passed in from the - * construction of the header). + * Returns the ID3v2 version of the header, as passed in from the + * construction of the header or set via setVersion(). */ uint version() const; + /*! + * Sets the ID3v2 version of the header, changing has impact on the + * correct parsing/rendering of frame data. + */ + void setVersion(uint version); + /*! * Returns the size of the frame header in bytes. * diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.cpp index a1067acc..3371ca7d 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -23,7 +23,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifndef HAVE_ZLIB +#ifdef HAVE_CONFIG_H #include #endif @@ -44,6 +44,7 @@ #include "frames/unsynchronizedlyricsframe.h" #include "frames/popularimeterframe.h" #include "frames/privateframe.h" +#include "frames/ownershipframe.h" using namespace TagLib; using namespace ID3v2; @@ -65,7 +66,7 @@ public: } }; -FrameFactory *FrameFactory::factory = 0; +FrameFactory FrameFactory::factory; //////////////////////////////////////////////////////////////////////////////// // public members @@ -73,7 +74,6 @@ FrameFactory *FrameFactory::factory = 0; FrameFactory *FrameFactory::instance() { - static FrameFactory factory; return &factory; } @@ -99,7 +99,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1 // characters. Also make sure that there is data in the frame. - if(!frameID.size() == (version < 3 ? 3 : 4) || + if(frameID.size() != (version < 3 ? 3 : 4) || header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) || header->frameSize() > data.size()) { @@ -107,8 +107,19 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) return 0; } +#ifndef NO_ITUNES_HACKS + if(version == 3 && frameID.size() == 4 && 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 + for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { + if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { delete header; return 0; } @@ -125,7 +136,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) // TagLib doesn't mess with encrypted frames, so just treat them // as unknown frames. -#if HAVE_ZLIB == 0 +#if !defined(HAVE_ZLIB) || HAVE_ZLIB == 0 if(header->compression()) { debug("Compressed frames are currently not supported."); return new UnknownFrame(data, header); @@ -184,13 +195,13 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) // ID3v2.2 Attached Picture - if(frameID == "PIC") { + if(frameID == "PIC") { AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header); d->setTextEncoding(f); return f; } - // Relative Volume Adjustment (frames 4.11) + // Relative Volume Adjustment (frames 4.11) if(frameID == "RVA2") return new RelativeVolumeFrame(data, header); @@ -239,6 +250,14 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) if(frameID == "PRIV") return new PrivateFrame(data, header); + + // Ownership (frames 4.22) + + if(frameID == "OWNE") { + OwnershipFrame *f = new OwnershipFrame(data, header); + d->setTextEncoding(f); + return f; + } return new UnknownFrame(data, header); } @@ -309,6 +328,7 @@ bool FrameFactory::updateFrame(Frame::Header *header) const convertFrame("TBP", "TBPM", header); convertFrame("TCM", "TCOM", header); convertFrame("TCO", "TCON", header); + convertFrame("TCP", "TCMP", header); convertFrame("TCR", "TCOP", header); convertFrame("TDY", "TDLY", header); convertFrame("TEN", "TENC", header); @@ -331,7 +351,12 @@ bool FrameFactory::updateFrame(Frame::Header *header) const convertFrame("TRC", "TSRC", header); convertFrame("TRD", "TDRC", header); convertFrame("TRK", "TRCK", header); + convertFrame("TS2", "TSO2", header); + convertFrame("TSA", "TSOA", header); + convertFrame("TSC", "TSOC", header); + convertFrame("TSP", "TSOP", header); convertFrame("TSS", "TSSE", header); + convertFrame("TST", "TSOT", header); convertFrame("TT1", "TIT1", header); convertFrame("TT2", "TIT2", header); convertFrame("TT3", "TIT3", header); @@ -367,6 +392,7 @@ bool FrameFactory::updateFrame(Frame::Header *header) const convertFrame("TORY", "TDOR", header); convertFrame("TYER", "TDRC", header); + convertFrame("IPLS", "TIPL", header); break; } diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.h index 34b704bf..762d7eb3 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2framefactory.h @@ -123,8 +123,7 @@ namespace TagLib { FrameFactory(); /*! - * Destroys the frame factory. In most cases this will never be called (as - * is typical of singletons). + * Destroys the frame factory. */ virtual ~FrameFactory(); @@ -155,7 +154,7 @@ namespace TagLib { void updateGenre(TextIdentificationFrame *frame) const; - static FrameFactory *factory; + static FrameFactory factory; class FrameFactoryPrivate; FrameFactoryPrivate *d; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2header.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2header.cpp index ef33af95..ad6b3aa7 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2header.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2header.cpp @@ -164,7 +164,7 @@ ByteVector Header::render() const // add the version number -- we always render a 2.4.0 tag regardless of what // the tag originally was. - v.append(char(4)); + v.append(char(majorVersion())); v.append(char(0)); // Currently we don't actually support writing extended headers, footers or diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2synchdata.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2synchdata.cpp index d4bab679..4acfd914 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2synchdata.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2synchdata.cpp @@ -49,7 +49,14 @@ TagLib::uint SynchData::toUInt(const ByteVector &data) // Invalid data; assume this was created by some buggy software that just // put normal integers here rather than syncsafe ones, and try it that // way. - sum = (data.size() > 4) ? data.mid(0, 4).toUInt() : data.toUInt(); + if(data.size() >= 4) { + sum = data.toUInt(0, true); + } + else { + ByteVector tmp(data); + tmp.resize(4); + sum = tmp.toUInt(0, true); + } } return sum; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.cpp index 7e8012b2..57637a36 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.cpp @@ -23,19 +23,28 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + #include -#include #include "id3v2tag.h" #include "id3v2header.h" #include "id3v2extendedheader.h" #include "id3v2footer.h" #include "id3v2synchdata.h" - +#include "tbytevector.h" #include "id3v1genres.h" +#include "tpropertymap.h" +#include #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; @@ -65,8 +74,30 @@ public: FrameListMap frameListMap; FrameList frameList; + + static const Latin1StringHandler *stringHandler; }; +static const Latin1StringHandler defaultStringHandler; +const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler; + +//////////////////////////////////////////////////////////////////////////////// +// StringHandler implementation +//////////////////////////////////////////////////////////////////////////////// + +Latin1StringHandler::Latin1StringHandler() +{ +} + +Latin1StringHandler::~Latin1StringHandler() +{ +} + +String Latin1StringHandler::parse(const ByteVector &data) const +{ + return String(data, String::Latin1); +} + //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// @@ -324,12 +355,206 @@ void ID3v2::Tag::removeFrame(Frame *frame, bool del) void ID3v2::Tag::removeFrames(const ByteVector &id) { - FrameList l = d->frameListMap[id]; - for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) - removeFrame(*it, true); + FrameList l = d->frameListMap[id]; + for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) + removeFrame(*it, true); +} + +PropertyMap ID3v2::Tag::properties() const +{ + PropertyMap properties; + for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) { + PropertyMap props = (*it)->asProperties(); + properties.merge(props); + } + return properties; +} + +void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) +{ + for(StringList::ConstIterator it = properties.begin(); it != properties.end(); ++it){ + if(it->startsWith("UNKNOWN/")) { + String frameID = it->substr(String("UNKNOWN/").size()); + if(frameID.size() != 4) + continue; // invalid specification + ByteVector id = frameID.data(String::Latin1); + // delete all unknown frames of given type + FrameList l = frameList(id); + for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++) + if (dynamic_cast(*fit) != 0) + removeFrame(*fit); + } + else if(it->size() == 4){ + ByteVector id = it->data(String::Latin1); + removeFrames(id); + } + else { + ByteVector id = it->substr(0,4).data(String::Latin1); + if(it->size() <= 5) + continue; // invalid specification + String description = it->substr(5); + Frame *frame = 0; + 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 properties; + PropertyMap tiplProperties; + PropertyMap tmclProperties; + Frame::splitProperties(origProps, properties, tiplProperties, tmclProperties); + for(FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it){ + for(FrameList::ConstIterator lit = it->second.begin(); lit != it->second.end(); ++lit){ + PropertyMap frameProperties = (*lit)->asProperties(); + if(it->first == "TIPL") { + if (tiplProperties != frameProperties) + framesToDelete.append(*lit); + else + tiplProperties.erase(frameProperties); + } else if(it->first == "TMCL") { + if (tmclProperties != frameProperties) + framesToDelete.append(*lit); + else + tmclProperties.erase(frameProperties); + } else if(!properties.contains(frameProperties)) + framesToDelete.append(*lit); + else + properties.erase(frameProperties); + } + } + for(FrameList::ConstIterator it = framesToDelete.begin(); it != framesToDelete.end(); ++it) + removeFrame(*it); + + // 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(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it) + addFrame(Frame::createTextualFrame(it->first, it->second)); + return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned } ByteVector ID3v2::Tag::render() const +{ + return render(4); +} + +void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const +{ + const char *unsupportedFrames[] = { + "ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG", + "TMOO", "TPRO", "TSOA", "TSOT", "TSST", "TSOP", 0 + }; + ID3v2::TextIdentificationFrame *frameTDOR = 0; + ID3v2::TextIdentificationFrame *frameTDRC = 0; + ID3v2::TextIdentificationFrame *frameTIPL = 0; + ID3v2::TextIdentificationFrame *frameTMCL = 0; + for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) { + ID3v2::Frame *frame = *it; + ByteVector frameID = frame->header()->frameID(); + for(int i = 0; unsupportedFrames[i]; i++) { + if(frameID == unsupportedFrames[i]) { + debug("A frame that is not supported in ID3v2.3 \'" + + String(frameID) + "\' has been discarded"); + frame = 0; + break; + } + } + if(frame && frameID == "TDOR") { + frameTDOR = dynamic_cast(frame); + frame = 0; + } + if(frame && frameID == "TDRC") { + frameTDRC = dynamic_cast(frame); + frame = 0; + } + if(frame && frameID == "TIPL") { + frameTIPL = dynamic_cast(frame); + frame = 0; + } + if(frame && frameID == "TMCL") { + frameTMCL = dynamic_cast(frame); + frame = 0; + } + if(frame) { + frames->append(frame); + } + } + if(frameTDOR) { + String content = frameTDOR->toString(); + if(content.size() >= 4) { + ID3v2::TextIdentificationFrame *frameTORY = new ID3v2::TextIdentificationFrame("TORY", String::Latin1); + frameTORY->setText(content.substr(0, 4)); + frames->append(frameTORY); + newFrames->append(frameTORY); + } + } + if(frameTDRC) { + String content = frameTDRC->toString(); + if(content.size() >= 4) { + ID3v2::TextIdentificationFrame *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] == '-') { + ID3v2::TextIdentificationFrame *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] == ':') { + ID3v2::TextIdentificationFrame *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) { + ID3v2::TextIdentificationFrame *frameIPLS = new ID3v2::TextIdentificationFrame("IPLS", String::Latin1); + StringList people; + if(frameTMCL) { + StringList v24People = frameTMCL->fieldList(); + for(uint i = 0; i + 1 < v24People.size(); i += 2) { + people.append(v24People[i]); + people.append(v24People[i+1]); + } + } + if(frameTIPL) { + StringList v24People = frameTIPL->fieldList(); + for(uint 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); + } +} + + +ByteVector ID3v2::Tag::render(int 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 @@ -338,11 +563,28 @@ ByteVector ID3v2::Tag::render() const ByteVector tagData; + if(version != 3 && version != 4) { + debug("Unknown ID3v2 version, using ID3v2.4"); + version = 4; + } + // TODO: Render the extended header. // Loop through the frames rendering them and adding them to the tagData. - for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) { + FrameList newFrames; + newFrames.setAutoDelete(true); + + FrameList frameList; + if(version == 4) { + frameList = d->frameList; + } + else { + downgradeFrames(&frameList, &newFrames); + } + + for(FrameList::Iterator it = frameList.begin(); it != frameList.end(); it++) { + (*it)->header()->setVersion(version); if((*it)->header()->frameID().size() != 4) { debug("A frame of unsupported or unknown type \'" + String((*it)->header()->frameID()) + "\' has been discarded"); @@ -364,13 +606,27 @@ ByteVector ID3v2::Tag::render() const tagData.append(ByteVector(paddingSize, char(0))); - // Set the tag size. + // Set the version and data size. + d->header.setMajorVersion(version); d->header.setTagSize(tagData.size()); // TODO: This should eventually include d->footer->render(). return d->header.render() + tagData; } +Latin1StringHandler const *ID3v2::Tag::latin1StringHandler() +{ + return TagPrivate::stringHandler; +} + +void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler) +{ + if(handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// @@ -432,8 +688,9 @@ void ID3v2::Tag::parse(const ByteVector &origData) // portion of the frame data. if(data.at(frameDataPosition) == 0) { - if(d->header.footerPresent()) + if(d->header.footerPresent()) { debug("Padding *and* a footer found. This is not allowed by the spec."); + } d->paddingSize = frameDataLength - frameDataPosition; return; diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.h b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.h index a139d455..5fd5c1f1 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/id3v2/id3v2tag.h @@ -57,6 +57,36 @@ namespace TagLib { typedef List FrameList; typedef Map FrameListMap; + //! 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(); + + /*! + * 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; + }; + //! The main class in the ID3v2 implementation /*! @@ -260,11 +290,91 @@ namespace TagLib { */ 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:" 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:" 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:" 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 "=" will be contained in the returned obejct for each + * - if the frame ID is "TMCL" (musician credit list), then + * "PERFORMER:=" 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; + + /*! + * 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); + + /*! + * Implements the unified property interface -- import function. + * See the comments in properties(). + */ + PropertyMap setProperties(const PropertyMap &); + /*! * 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 the version of the rendered + * ID3v2 tag. It can be either 4 or 3. + */ + // BIC: combine with the above method + ByteVector render(int 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 @@ -286,6 +396,8 @@ namespace TagLib { */ void setTextFrame(const ByteVector &id, const String &value); + void downgradeFrames(FrameList *existingFrames, FrameList *newFrames) const; + private: Tag(const Tag &); Tag &operator=(const Tag &); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.cpp index 2fc1b380..f765befb 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.cpp @@ -35,6 +35,7 @@ #include "mpegfile.h" #include "mpegheader.h" +#include "tpropertymap.h" using namespace TagLib; @@ -113,6 +114,16 @@ MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory, read(readProperties, propertiesStyle); } +MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, + bool readProperties, Properties::ReadStyle propertiesStyle) : + TagLib::File(stream) +{ + d = new FilePrivate(frameFactory); + + if(isOpen()) + read(readProperties, propertiesStyle); +} + MPEG::File::~File() { delete d; @@ -123,6 +134,37 @@ TagLib::Tag *MPEG::File::tag() const return &d->tag; } +PropertyMap MPEG::File::properties() const +{ + // once Tag::properties() is virtual, this case distinction could actually be done + // within TagUnion. + if(d->hasID3v2) + return d->tag.access(ID3v2Index, false)->properties(); + if(d->hasAPE) + return d->tag.access(APEIndex, false)->properties(); + if(d->hasID3v1) + return d->tag.access(ID3v1Index, false)->properties(); + return PropertyMap(); +} + +void MPEG::File::removeUnsupportedProperties(const StringList &properties) +{ + if(d->hasID3v2) + d->tag.access(ID3v2Index, false)->removeUnsupportedProperties(properties); + else if(d->hasAPE) + d->tag.access(APEIndex, false)->removeUnsupportedProperties(properties); + else if(d->hasID3v1) + d->tag.access(ID3v1Index, false)->removeUnsupportedProperties(properties); +} + +PropertyMap MPEG::File::setProperties(const PropertyMap &properties) +{ + if(d->hasID3v1) + // update ID3v1 tag if it exists, but ignore the return value + d->tag.access(ID3v1Index, false)->setProperties(properties); + return d->tag.access(ID3v2Index, true)->setProperties(properties); +} + MPEG::Properties *MPEG::File::audioProperties() const { return d->properties; @@ -139,6 +181,16 @@ bool MPEG::File::save(int tags) } bool MPEG::File::save(int tags, bool stripOthers) +{ + return save(tags, stripOthers, 4); +} + +bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version) +{ + return save(tags, stripOthers, id3v2Version, true); +} + +bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags) { if(tags == NoTags && stripOthers) return strip(AllTags); @@ -156,14 +208,19 @@ bool MPEG::File::save(int tags, bool stripOthers) return false; } - // Create the tags if we've been asked to. Copy the values from the tag that - // does exist into the new tag. + // Create the tags if we've been asked to. - if((tags & ID3v2) && ID3v1Tag()) - Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); + if (duplicateTags) { - if((tags & ID3v1) && d->tag[ID3v2Index]) - Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false); + // 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() && !(stripOthers && !(tags & ID3v1))) + Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); + + if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2))) + Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false); + } bool success = true; @@ -174,7 +231,7 @@ bool MPEG::File::save(int tags, bool stripOthers) if(!d->hasID3v2) d->ID3v2Location = 0; - insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize); + insert(ID3v2Tag()->render(id3v2Version), d->ID3v2Location, d->ID3v2OriginalSize); d->hasID3v2 = true; @@ -186,7 +243,7 @@ bool MPEG::File::save(int tags, bool stripOthers) // APE tag location has changed, update if it exists if(APETag()) - findAPE(); + findAPE(); } else if(stripOthers) success = strip(ID3v2, false) && success; @@ -224,9 +281,10 @@ bool MPEG::File::save(int tags, bool stripOthers) else { seek(0, End); d->APELocation = tell(); - d->APEFooterLocation = d->APELocation - + d->tag.access(APEIndex, false)->footer()->completeTagSize() - - APE::Footer::size(); + APE::Tag *apeTag = d->tag.access(APEIndex, false); + d->APEFooterLocation = d->APELocation + + apeTag->footer()->completeTagSize() + - APE::Footer::size(); writeBlock(APETag()->render()); d->APEOriginalSize = APETag()->footer()->completeTagSize(); d->hasAPE = true; @@ -386,6 +444,21 @@ long MPEG::File::lastFrameOffset() return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length()); } +bool MPEG::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool MPEG::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} + +bool MPEG::File::hasAPETag() const +{ + return d->hasAPE; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -572,7 +645,7 @@ void MPEG::File::findAPE() seek(d->APEFooterLocation); APE::Footer footer(readBlock(APE::Footer::size())); d->APELocation = d->APEFooterLocation - footer.completeTagSize() - + APE::Footer::size(); + + APE::Footer::size(); return; } } @@ -583,9 +656,6 @@ void MPEG::File::findAPE() bool MPEG::File::secondSynchByte(char byte) { - if(uchar(byte) == 0xff) - return false; - std::bitset<8> b(byte); // check to see if the byte matches 111xxxxx diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.h b/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.h index 282af775..3fc01e68 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.h +++ b/Plugins/PluginNowPlaying/taglib/mpeg/mpegfile.h @@ -28,6 +28,7 @@ #include "taglib_export.h" #include "tfile.h" +#include "tag.h" #include "mpegproperties.h" @@ -70,9 +71,10 @@ namespace TagLib { }; /*! - * Contructs an MPEG file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs an MPEG file from \a file. If \a readProperties is true the + * file's audio properties will also be read. + * + * \note In the current implementation, \a propertiesStyle is ignored. * * \deprecated This constructor will be dropped in favor of the one below * in a future version. @@ -81,16 +83,35 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Contructs an MPEG file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. The frames will be created using + * Constructs an MPEG file from \a file. If \a readProperties is true the + * file's audio properties will also be read. + * + * If this file contains and ID3v2 tag the frames will be created using * \a frameFactory. + * + * \note In the current implementation, \a propertiesStyle is ignored. */ // BIC: merge with the above constructor File(FileName file, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); + /*! + * Constructs an MPEG file from \a stream. If \a readProperties is 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 and ID3v2 tag the frames will be created using + * \a frameFactory. + * + * \note In the current implementation, \a propertiesStyle is ignored. + */ + File(IOStream *stream, ID3v2::FrameFactory *frameFactory, + bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + /*! * Destroys this instance of the File. */ @@ -115,6 +136,26 @@ namespace TagLib { */ virtual Tag *tag() const; + /*! + * 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; + + void removeUnsupportedProperties(const StringList &properties); + + /*! + * 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 &); + /*! * Returns the MPEG::Properties for this file. If no audio properties * were read then this will return a null pointer. @@ -161,42 +202,93 @@ namespace TagLib { // BIC: combine with the above method bool save(int tags, bool stripOthers); + /*! + * Save the file. This will attempt to save all of the tag types that are + * specified by OR-ing together TagTypes values. The save() method above + * uses AllTags. This returns true if saving was successful. + * + * If \a stripOthers is true this strips all tags not included in the mask, + * but does not modify them in memory, so later calls to save() which make + * use of these tags will remain valid. This also strips empty tags. + * + * The \a id3v2Version parameter specifies the version of the saved + * ID3v2 tag. It can be either 4 or 3. + */ + // BIC: combine with the above method + bool save(int tags, bool stripOthers, int id3v2Version); + + /*! + * Save the file. This will attempt to save all of the tag types that are + * specified by OR-ing together TagTypes values. The save() method above + * uses AllTags. This returns true if saving was successful. + * + * If \a stripOthers is true this strips all tags not included in the mask, + * but does not modify them in memory, so later calls to save() which make + * use of these tags will remain valid. This also strips empty tags. + * + * The \a id3v2Version parameter specifies the version of the saved + * ID3v2 tag. It can be either 4 or 3. + * + * If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 -- + * exists this will duplicate its content into the other tag. + */ + // BIC: combine with the above method + bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags); + /*! * Returns a pointer to the ID3v2 tag of the file. * - * If \a create is false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid ID3v2 tag. If \a create is true it will create - * an ID3v2 tag if one does not exist. + * 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 is still 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 false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. + * 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 is still 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 false (the default) this will return a null pointer + * If \a create is false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is true it will create - * an APE tag if one does not exist. + * 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 is still 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); @@ -252,6 +344,27 @@ namespace TagLib { */ long 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; + private: File(const File &); File &operator=(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/mpegheader.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/mpegheader.cpp index c715dbc1..a582f758 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/mpegheader.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/mpegheader.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "trefcounter.h" #include "mpegheader.h" diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/mpegproperties.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/mpegproperties.cpp index 028ee061..3af95664 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/mpegproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/mpegproperties.cpp @@ -221,7 +221,7 @@ void MPEG::Properties::read() double length = timePerFrame * d->xingHeader->totalFrames(); d->length = int(length); - d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0; + d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0; } else { // Since there was no valid Xing header found, we hope that we're in a constant diff --git a/Plugins/PluginNowPlaying/taglib/mpeg/xingheader.cpp b/Plugins/PluginNowPlaying/taglib/mpeg/xingheader.cpp index 1ba932de..9e20127e 100644 --- a/Plugins/PluginNowPlaying/taglib/mpeg/xingheader.cpp +++ b/Plugins/PluginNowPlaying/taglib/mpeg/xingheader.cpp @@ -108,8 +108,8 @@ void MPEG::XingHeader::parse(const ByteVector &data) return; } - d->frames = data.mid(8, 4).toUInt(); - d->size = data.mid(12, 4).toUInt(); + d->frames = data.toUInt(8U); + d->size = data.toUInt(12U); d->valid = true; } diff --git a/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.cpp b/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.cpp index 22d7b81f..bdf82459 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "oggflacfile.h" @@ -72,7 +73,16 @@ Ogg::FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +Ogg::FLAC::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } Ogg::FLAC::File::~File() @@ -85,6 +95,16 @@ Ogg::XiphComment *Ogg::FLAC::File::tag() const return d->comment; } +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; @@ -117,6 +137,11 @@ bool Ogg::FLAC::File::save() return Ogg::File::save(); } +bool Ogg::FLAC::File::hasXiphComment() const +{ + return d->hasXiphComment; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -221,7 +246,7 @@ void Ogg::FLAC::File::scan() char blockType = header[0] & 0x7f; bool lastBlock = (header[0] & 0x80) != 0; - uint length = header.mid(1, 3).toUInt(); + uint length = header.toUInt(1, 3, true); overhead += length; // Sanity: First block should be the stream_info metadata @@ -231,7 +256,7 @@ void Ogg::FLAC::File::scan() return; } - d->streamInfoData = metadataHeader.mid(4,length); + d->streamInfoData = metadataHeader.mid(4, length); // Search through the remaining metadata @@ -244,7 +269,7 @@ void Ogg::FLAC::File::scan() header = metadataHeader.mid(0, 4); blockType = header[0] & 0x7f; lastBlock = (header[0] & 0x80) != 0; - length = header.mid(1, 3).toUInt(); + length = header.toUInt(1, 3, true); overhead += length; if(blockType == 1) { @@ -256,9 +281,9 @@ void Ogg::FLAC::File::scan() d->hasXiphComment = true; d->commentPacket = ipacket; } - else if(blockType > 5) + else if(blockType > 5) { debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); - + } } // End of metadata, now comes the datastream diff --git a/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.h b/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.h index 5882a696..8d87e486 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/flac/oggflacfile.h @@ -64,13 +64,26 @@ namespace TagLib { { public: /*! - * Contructs an Ogg/FLAC file from \a file. If \a readProperties is true - * the file's audio properties will also be read using \a propertiesStyle. - * If false, \a propertiesStyle is ignored. + * Constructs an Ogg/FLAC file from \a file. If \a readProperties is 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 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. */ @@ -78,6 +91,16 @@ namespace TagLib { /*! * 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 is still 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() */ virtual XiphComment *tag() const; @@ -87,6 +110,20 @@ namespace TagLib { */ virtual Properties *audioProperties() const; + + /*! + * Implements the unified property interface -- export function. + * This forwards directly to XiphComment::properties(). + */ + PropertyMap properties() const; + + /*! + * Implements the unified tag dictionary interface -- import function. + * Like properties(), this is a forwarder to the file's XiphComment. + */ + PropertyMap setProperties(const PropertyMap &); + + /*! * Save the file. This will primarily save and update the XiphComment. * Returns true if the save is successful. @@ -99,6 +136,13 @@ namespace TagLib { */ long streamLength(); + /*! + * Returns whether or not the file on disk actually has a XiphComment. + * + * \see tag() + */ + bool hasXiphComment() const; + private: File(const File &); File &operator=(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/ogg/oggfile.cpp b/Plugins/PluginNowPlaying/taglib/ogg/oggfile.cpp index f29ba14a..cc30f9ce 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/oggfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/oggfile.cpp @@ -213,6 +213,11 @@ Ogg::File::File(FileName file) : TagLib::File(file) d = new FilePrivate; } +Ogg::File::File(IOStream *stream) : TagLib::File(stream) +{ + d = new FilePrivate; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -276,8 +281,8 @@ void Ogg::File::writePageGroup(const List &thePageGroup) return; - // pages in the pageGroup and packets must be equivalent - // (originalSize and size of packets would not work together), + // pages in the pageGroup and packets must be equivalent + // (originalSize and size of packets would not work together), // therefore we sometimes have to add pages to the group List pageGroup(thePageGroup); while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) { @@ -336,7 +341,7 @@ void Ogg::File::writePageGroup(const List &thePageGroup) if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) { - // TODO: change the internal data structure so that we don't need to hold the + // TODO: change the internal data structure so that we don't need to hold the // complete file in memory (is unavoidable at the moment) // read the complete stream diff --git a/Plugins/PluginNowPlaying/taglib/ogg/oggfile.h b/Plugins/PluginNowPlaying/taglib/ogg/oggfile.h index da1fcb69..8fed4ba0 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/oggfile.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/oggfile.h @@ -82,9 +82,7 @@ namespace TagLib { protected: /*! - * Contructs an Ogg file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * 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 @@ -92,6 +90,18 @@ namespace TagLib { */ 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: File(const File &); File &operator=(const File &); diff --git a/Plugins/PluginNowPlaying/taglib/ogg/oggpage.cpp b/Plugins/PluginNowPlaying/taglib/ogg/oggpage.cpp index 732f01dd..e5f767ec 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/oggpage.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/oggpage.cpp @@ -116,9 +116,9 @@ Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const flags = ContainsPacketFlags(flags | CompletePacket); } - // Or if there is more than one page and the page is - // (a) the first page and it's complete or - // (b) the last page and it's complete or + // 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()) || @@ -266,7 +266,7 @@ List Ogg::Page::paginate(const ByteVectorList &packets, } Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued, - lastPacketInList ? lastPacketCompleted : true, + lastPacketInList ? lastPacketCompleted : true, isVeryLastPacket); pageIndex++; diff --git a/Plugins/PluginNowPlaying/taglib/ogg/oggpage.h b/Plugins/PluginNowPlaying/taglib/ogg/oggpage.h index a8d5c1a3..e9f4840c 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/oggpage.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/oggpage.h @@ -70,9 +70,9 @@ namespace TagLib { */ const PageHeader *header() const; - /*! + /*! * Returns a copy of the page with \a sequenceNumber set as sequence number. - * + * * \see header() * \see PageHeader::setPageSequenceNumber() */ diff --git a/Plugins/PluginNowPlaying/taglib/ogg/oggpageheader.cpp b/Plugins/PluginNowPlaying/taglib/ogg/oggpageheader.cpp index f9608ab7..b9333135 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/oggpageheader.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/oggpageheader.cpp @@ -255,9 +255,9 @@ void Ogg::PageHeader::read() d->firstPageOfStream = flags.test(1); d->lastPageOfStream = flags.test(2); - d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false); - d->streamSerialNumber = data.mid(14, 4).toUInt(false); - d->pageSequenceNumber = data.mid(18, 4).toUInt(false); + 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 diff --git a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.cpp b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.cpp index 604ac7c6..e83f0ad9 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "speexfile.h" @@ -62,7 +63,16 @@ Speex::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +Speex::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } Speex::File::~File() @@ -75,6 +85,16 @@ Ogg::XiphComment *Speex::File::tag() const return d->comment; } +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; diff --git a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.h b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.h index 508b7aaa..880e6890 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexfile.h @@ -56,13 +56,26 @@ namespace TagLib { { public: /*! - * Contructs a Speex file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs a Speex file from \a file. If \a readProperties is 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 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. */ @@ -75,12 +88,26 @@ namespace TagLib { */ virtual Ogg::XiphComment *tag() const; + /*! + * Implements the unified property interface -- export function. + * This forwards directly to XiphComment::properties(). + */ + PropertyMap properties() const; + + /*! + * Implements the unified tag dictionary interface -- import function. + * Like properties(), this is a forwarder to the file's XiphComment. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the Speex::Properties for this file. If no audio properties * were read then this will return a null pointer. */ virtual Properties *audioProperties() const; + + virtual bool save(); private: diff --git a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexproperties.cpp b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexproperties.cpp index 980f12df..5aaa9153 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/speex/speexproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/speex/speexproperties.cpp @@ -113,32 +113,32 @@ void Speex::Properties::read() ByteVector data = d->file->packet(0); - int pos = 28; + uint pos = 28; // speex_version_id; /**< Version for Speex (for checking compatibility) */ - d->speexVersion = data.mid(pos, 4).toUInt(false); + 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.mid(pos, 4).toUInt(false); + d->sampleRate = data.toUInt(pos, false); pos += 4; // mode; /**< Mode used (0 for narrowband, 1 for wideband) */ - d->mode = data.mid(pos, 4).toUInt(false); + 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.mid(pos, 4).toUInt(false); + d->channels = data.toUInt(pos, false); pos += 4; // bitrate; /**< Bit-rate used */ - d->bitrate = data.mid(pos, 4).toUInt(false); + d->bitrate = data.toUInt(pos, false); pos += 4; // frame_size; /**< Size of frames */ @@ -146,7 +146,7 @@ void Speex::Properties::read() pos += 4; // vbr; /**< 1 for a VBR encoding, 0 otherwise */ - d->vbr = data.mid(pos, 4).toUInt(false) == 1; + d->vbr = data.toUInt(pos, false) == 1; pos += 4; // frames_per_packet; /**< Number of frames stored per Ogg packet */ diff --git a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.cpp b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.cpp index 1098ec38..82984536 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.cpp @@ -27,9 +27,11 @@ #include #include +#include #include "vorbisfile.h" + using namespace TagLib; class Vorbis::File::FilePrivate @@ -65,7 +67,16 @@ Vorbis::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); +} + +Vorbis::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +{ + d = new FilePrivate; + if(isOpen()) + read(readProperties, propertiesStyle); } Vorbis::File::~File() @@ -78,6 +89,16 @@ Ogg::XiphComment *Vorbis::File::tag() const return d->comment; } +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; diff --git a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.h b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.h index 3e33c113..7735a11b 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisfile.h @@ -63,13 +63,26 @@ namespace TagLib { { public: /*! - * Contructs a Vorbis file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. + * Constructs a Vorbis file from \a file. If \a readProperties is 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 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. */ @@ -82,6 +95,19 @@ namespace TagLib { */ virtual Ogg::XiphComment *tag() const; + + /*! + * Implements the unified property interface -- export function. + * This forwards directly to XiphComment::properties(). + */ + PropertyMap properties() const; + + /*! + * Implements the unified tag dictionary interface -- import function. + * Like properties(), this is a forwarder to the file's XiphComment. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the Vorbis::Properties for this file. If no audio properties * were read then this will return a null pointer. diff --git a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisproperties.cpp b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisproperties.cpp index 6de6a599..b5e88bfd 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/vorbis/vorbisproperties.cpp @@ -133,7 +133,7 @@ void Vorbis::Properties::read() ByteVector data = d->file->packet(0); - int pos = 0; + uint pos = 0; if(data.mid(pos, 7) != vorbisSetupHeaderID) { debug("Vorbis::Properties::read() -- invalid Vorbis identification header"); @@ -142,22 +142,22 @@ void Vorbis::Properties::read() pos += 7; - d->vorbisVersion = data.mid(pos, 4).toUInt(false); + d->vorbisVersion = data.toUInt(pos, false); pos += 4; d->channels = uchar(data[pos]); pos += 1; - d->sampleRate = data.mid(pos, 4).toUInt(false); + d->sampleRate = data.toUInt(pos, false); pos += 4; - d->bitrateMaximum = data.mid(pos, 4).toUInt(false); + d->bitrateMaximum = data.toUInt(pos, false); pos += 4; - d->bitrateNominal = data.mid(pos, 4).toUInt(false); + d->bitrateNominal = data.toUInt(pos, false); pos += 4; - d->bitrateMinimum = data.mid(pos, 4).toUInt(false); + d->bitrateMinimum = data.toUInt(pos, false); // TODO: Later this should be only the "fast" mode. d->bitrate = d->bitrateNominal; @@ -173,7 +173,7 @@ void Vorbis::Properties::read() long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0 && d->sampleRate > 0) - d->length = (end - start) / (long long) d->sampleRate; + d->length = (int)((end - start) / (long long) d->sampleRate); else debug("Vorbis::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); diff --git a/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.cpp b/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.cpp index b9f408f6..51c2f9f0 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.cpp +++ b/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.cpp @@ -27,6 +27,7 @@ #include #include +#include using namespace TagLib; @@ -188,6 +189,58 @@ 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(FieldListMap::ConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it) + if (!properties.contains(it->first)) + toRemove.append(it->first); + + for(StringList::ConstIterator it = toRemove.begin(); it != toRemove.end(); ++it) + removeField(*it); + + // now go through keys in \a properties and check that the values match those in the xiph comment + PropertyMap invalid; + PropertyMap::ConstIterator it = properties.begin(); + for(; it != properties.end(); ++it) + { + if(!checkKey(it->first)) + invalid.insert(it->first, it->second); + else if(!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) { + const StringList &sl = it->second; + if(sl.size() == 0) + // zero size string list -> remove the tag with all values + removeField(it->first); + else { + // replace all strings in the list for the tag + StringList::ConstIterator valueIterator = sl.begin(); + addField(it->first, *valueIterator, true); + ++valueIterator; + for(; valueIterator != sl.end(); ++valueIterator) + addField(it->first, *valueIterator, false); + } + } + } + return invalid; +} + +bool Ogg::XiphComment::checkKey(const String &key) +{ + if(key.size() < 1) + return false; + for(String::ConstIterator it = key.begin(); it != key.end(); it++) + // forbid non-printable, non-ascii, '=' (#61) and '~' (#126) + if (*it < 32 || *it >= 128 || *it == 61 || *it == 126) + return false; + return true; +} + String Ogg::XiphComment::vendorID() const { return d->vendorID; @@ -285,9 +338,9 @@ void Ogg::XiphComment::parse(const ByteVector &data) // The first thing in the comment data is the vendor ID length, followed by a // UTF8 string with the vendor ID. - int pos = 0; + uint pos = 0; - uint vendorLength = data.mid(0, 4).toUInt(false); + const uint vendorLength = data.toUInt(0, false); pos += 4; d->vendorID = String(data.mid(pos, vendorLength), String::UTF8); @@ -295,7 +348,7 @@ void Ogg::XiphComment::parse(const ByteVector &data) // Next the number of fields in the comment vector. - uint commentFields = data.mid(pos, 4).toUInt(false); + const uint commentFields = data.toUInt(pos, false); pos += 4; if(commentFields > (data.size() - 8) / 4) { @@ -307,7 +360,7 @@ void Ogg::XiphComment::parse(const ByteVector &data) // 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. - uint commentLength = data.mid(pos, 4).toUInt(false); + const uint commentLength = data.toUInt(pos, false); pos += 4; String comment = String(data.mid(pos, commentLength), String::UTF8); diff --git a/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.h b/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.h index b105dd6a..54f3070b 100644 --- a/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.h +++ b/Plugins/PluginNowPlaying/taglib/ogg/xiphcomment.h @@ -140,6 +140,29 @@ namespace TagLib { */ 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; + + /*! + * 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&); + + /*! + * 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". diff --git a/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.cpp b/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.cpp deleted file mode 100644 index 72667f6e..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************** - 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 -#include -#include - -#include "aifffile.h" - -using namespace TagLib; - -class RIFF::AIFF::File::FilePrivate -{ -public: - FilePrivate() : - properties(0), - tag(0), - tagChunkID("ID3 ") - { - - } - - ~FilePrivate() - { - delete properties; - delete tag; - } - - Properties *properties; - ID3v2::Tag *tag; - ByteVector tagChunkID; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::AIFF::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(file, BigEndian) -{ - d = new FilePrivate; - if(isOpen()) - read(readProperties, propertiesStyle); -} - -RIFF::AIFF::File::~File() -{ - delete d; -} - -ID3v2::Tag *RIFF::AIFF::File::tag() const -{ - return d->tag; -} - -RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const -{ - return d->properties; -} - -bool RIFF::AIFF::File::save() -{ - if(readOnly()) { - debug("RIFF::AIFF::File::save() -- File is read only."); - return false; - } - - if(!isValid()) { - debug("RIFF::AIFF::File::save() -- Trying to save invalid file."); - return false; - } - - setChunkData(d->tagChunkID, d->tag->render()); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::AIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) -{ - for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { - d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); - } - else if(chunkName(i) == "COMM" && readProperties) - d->properties = new Properties(chunkData(i), propertiesStyle); - } - - if(!d->tag) - d->tag = new ID3v2::Tag; -} diff --git a/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.h b/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.h deleted file mode 100644 index b9b0809f..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/aiff/aifffile.h +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - 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 implementation of AIFF metadata. - * - * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF - * chunk as well as properties from the file. - */ - - namespace AIFF { - - //! An implementation of TagLib::File with AIFF specific methods - - /*! - * This implements and provides an interface for AIFF files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to AIFF files. - */ - - class TAGLIB_EXPORT File : public TagLib::RIFF::File - { - public: - /*! - * Contructs an AIFF file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. - */ - File(FileName file, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); - - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - /*! - * Returns the Tag for this file. - */ - virtual ID3v2::Tag *tag() const; - - /*! - * Returns the AIFF::Properties for this file. If no audio properties - * were read then this will return a null pointer. - */ - virtual Properties *audioProperties() const; - - /*! - * Saves the file. - */ - virtual bool save(); - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - - class FilePrivate; - FilePrivate *d; - }; - } - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.cpp b/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.cpp deleted file mode 100644 index afb741b7..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -// ldexp is a c99 function, which might not be defined in -// so we pull in math.h too and hope it does the right (wrong) thing -// wrt. c99 functions in C++ -#include - -#include "aiffproperties.h" - -//////////////////////////////////////////////////////////////////////////////// -// nasty 80-bit float helpers -//////////////////////////////////////////////////////////////////////////////// - -#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) - -static double ConvertFromIeeeExtended(unsigned char *bytes) -{ - double f; - int expon; - unsigned long hiMant, loMant; - - expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); - - hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) | - ((unsigned long)(bytes[3] & 0xFF) << 16) | - ((unsigned long)(bytes[4] & 0xFF) << 8) | - ((unsigned long)(bytes[5] & 0xFF)); - - loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) | - ((unsigned long)(bytes[7] & 0xFF) << 16) | - ((unsigned long)(bytes[8] & 0xFF) << 8) | - ((unsigned long)(bytes[9] & 0xFF)); - - if (expon == 0 && hiMant == 0 && loMant == 0) - f = 0; - else { - if(expon == 0x7FFF) /* Infinity or NaN */ - f = HUGE_VAL; - else { - expon -= 16383; - f = ldexp(UnsignedToFloat(hiMant), expon -= 31); - f += ldexp(UnsignedToFloat(loMant), expon -= 32); - } - } - - if(bytes[0] & 0x80) - return -f; - else - return f; -} - -using namespace TagLib; - -class RIFF::AIFF::Properties::PropertiesPrivate -{ -public: - PropertiesPrivate() : - length(0), - bitrate(0), - sampleRate(0), - channels(0), - sampleWidth(0) - { - - } - - int length; - int bitrate; - int sampleRate; - int channels; - int sampleWidth; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::AIFF::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style) -{ - d = new PropertiesPrivate; - read(data); -} - -RIFF::AIFF::Properties::~Properties() -{ - delete d; -} - -int RIFF::AIFF::Properties::length() const -{ - return d->length; -} - -int RIFF::AIFF::Properties::bitrate() const -{ - return d->bitrate; -} - -int RIFF::AIFF::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int RIFF::AIFF::Properties::channels() const -{ - return d->channels; -} - -int RIFF::AIFF::Properties::sampleWidth() const -{ - return d->sampleWidth; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::AIFF::Properties::read(const ByteVector &data) -{ - d->channels = data.mid(0, 2).toShort(); - uint sampleFrames = data.mid(2, 4).toUInt(); - d->sampleWidth = data.mid(6, 2).toShort(); - double sampleRate = ConvertFromIeeeExtended(reinterpret_cast(data.mid(8, 10).data())); - d->sampleRate = sampleRate; - d->bitrate = (sampleRate * d->sampleWidth * d->channels) / 1000.0; - d->length = d->sampleRate > 0 ? sampleFrames / d->sampleRate : 0; -} diff --git a/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.h b/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.h deleted file mode 100644 index 4c578dc6..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/aiff/aiffproperties.h +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************** - 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 "audioproperties.h" - -namespace TagLib { - - namespace RIFF { - - namespace AIFF { - - class File; - - //! An implementation of audio property reading for AIFF - - /*! - * This reads the data from an AIFF stream found in the AudioProperties - * API. - */ - - class TAGLIB_EXPORT Properties : public AudioProperties - { - public: - /*! - * Create an instance of AIFF::Properties with the data read from the - * ByteVector \a data. - */ - Properties(const ByteVector &data, ReadStyle style); - - /*! - * Destroys this AIFF::Properties instance. - */ - virtual ~Properties(); - - // Reimplementations. - - virtual int length() const; - virtual int bitrate() const; - virtual int sampleRate() const; - virtual int channels() const; - - int sampleWidth() const; - - private: - Properties(const Properties &); - Properties &operator=(const Properties &); - - void read(const ByteVector &data); - - class PropertiesPrivate; - PropertiesPrivate *d; - }; - } - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/riff/rifffile.cpp b/Plugins/PluginNowPlaying/taglib/riff/rifffile.cpp deleted file mode 100644 index a3ca0e3e..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/rifffile.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/*************************************************************************** - 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 -#include -#include - -#include "rifffile.h" -#include - -using namespace TagLib; - -struct Chunk -{ - ByteVector name; - TagLib::uint offset; - TagLib::uint size; - char padding; -}; - -class RIFF::File::FilePrivate -{ -public: - FilePrivate() : - endianness(BigEndian), - size(0) - { - - } - Endianness endianness; - ByteVector type; - TagLib::uint size; - ByteVector format; - - std::vector chunks; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::File::~File() -{ - delete d; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file) -{ - d = new FilePrivate; - d->endianness = endianness; - - if(isOpen()) - read(); -} - -TagLib::uint RIFF::File::riffSize() const -{ - return d->size; -} - -TagLib::uint RIFF::File::chunkCount() const -{ - return d->chunks.size(); -} - -TagLib::uint RIFF::File::chunkDataSize(uint i) const -{ - return d->chunks[i].size; -} - -TagLib::uint RIFF::File::chunkOffset(uint i) const -{ - return d->chunks[i].offset; -} - -TagLib::uint RIFF::File::chunkPadding(uint i) const -{ - return d->chunks[i].padding; -} - -ByteVector RIFF::File::chunkName(uint i) const -{ - if(i >= chunkCount()) - return ByteVector::null; - - return d->chunks[i].name; -} - -ByteVector RIFF::File::chunkData(uint i) -{ - if(i >= chunkCount()) - return ByteVector::null; - - // Offset for the first subchunk's data - - long begin = 12 + 8; - - for(uint it = 0; it < i; it++) - begin += 8 + d->chunks[it].size + d->chunks[it].padding; - - seek(begin); - - return readBlock(d->chunks[i].size); -} - -void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) -{ - if(d->chunks.size() == 0) { - debug("RIFF::File::setChunkData - No valid chunks found."); - return; - } - - for(uint i = 0; i < d->chunks.size(); i++) { - if(d->chunks[i].name == name) { - - // First we update the global size - - d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); - insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); - - // Now update the specific chunk - - writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8); - - d->chunks[i].size = data.size(); - d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; - - // Now update the internal offsets - - for(i++; i < d->chunks.size(); i++) - d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding; - - return; - } - } - - // Couldn't find an existing chunk, so let's create a new one. - - uint i = d->chunks.size() - 1; - ulong offset = d->chunks[i].offset + d->chunks[i].size; - - // First we update the global size - - d->size += (offset & 1) + data.size() + 8; - insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); - - // Now add the chunk to the file - - writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0); - - // And update our internal structure - - if (offset & 1) { - d->chunks[i].padding = 1; - offset++; - } - - Chunk chunk; - chunk.name = name; - chunk.size = data.size(); - chunk.offset = offset + 8; - chunk.padding = (data.size() & 0x01) ? 1 : 0; - - d->chunks.push_back(chunk); -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -static bool isValidChunkID(const ByteVector &name) -{ - if(name.size() != 4) { - return false; - } - for(int i = 0; i < 4; i++) { - if(name[i] < 32 || name[i] > 127) { - return false; - } - } - return true; -} - -void RIFF::File::read() -{ - bool bigEndian = (d->endianness == BigEndian); - - d->type = readBlock(4); - d->size = readBlock(4).toUInt(bigEndian); - d->format = readBlock(4); - - // + 8: chunk header at least, fix for additional junk bytes - while(tell() + 8 <= length()) { - ByteVector chunkName = readBlock(4); - uint chunkSize = readBlock(4).toUInt(bigEndian); - - if(!isValidChunkID(chunkName)) { - debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID"); - setValid(false); - break; - } - - if(tell() + chunkSize > uint(length())) { - debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); - setValid(false); - break; - } - - Chunk chunk; - chunk.name = chunkName; - chunk.size = chunkSize; - chunk.offset = tell(); - - seek(chunk.size, Current); - - // check padding - chunk.padding = 0; - long uPosNotPadded = tell(); - if((uPosNotPadded & 0x01) != 0) { - ByteVector iByte = readBlock(1); - if((iByte.size() != 1) || (iByte[0] != 0)) { - // not well formed, re-seek - seek(uPosNotPadded, Beginning); - } - else { - chunk.padding = 1; - } - } - d->chunks.push_back(chunk); - - } -} - -void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, - ulong offset, ulong replace, uint leadingPadding) -{ - ByteVector combined; - if(leadingPadding) { - combined.append(ByteVector(leadingPadding, '\x00')); - } - combined.append(name); - combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian)); - combined.append(data); - if((data.size() & 0x01) != 0) { - combined.append('\x00'); - } - insert(combined, offset, replace); -} diff --git a/Plugins/PluginNowPlaying/taglib/riff/rifffile.h b/Plugins/PluginNowPlaying/taglib/riff/rifffile.h deleted file mode 100644 index 99282564..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/rifffile.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - 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 "taglib_export.h" -#include "tfile.h" - -namespace TagLib { - - //! An implementation of TagLib::File with RIFF specific methods - - namespace RIFF { - - //! An RIFF file class with some useful methods specific to RIFF - - /*! - * This implements the generic TagLib::File API and additionally provides - * access to properties that are distinct to RIFF files, notably access - * to the different ID3 tags. - */ - - class TAGLIB_EXPORT File : public TagLib::File - { - public: - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - protected: - - enum Endianness { BigEndian, LittleEndian }; - - File(FileName file, Endianness endianness); - - /*! - * \return The size of the main RIFF chunk. - */ - uint riffSize() const; - - /*! - * \return The number of chunks in the file. - */ - uint chunkCount() const; - - /*! - * \return The offset within the file for the selected chunk number. - */ - uint chunkOffset(uint i) const; - - /*! - * \return The size of the chunk data. - */ - uint chunkDataSize(uint i) const; - - /*! - * \return The size of the padding after the chunk (can be either 0 or 1). - */ - uint chunkPadding(uint i) const; - - /*! - * \return The name of the specified chunk, for instance, "COMM" or "ID3 " - */ - ByteVector chunkName(uint i) const; - - /*! - * Reads the chunk data from the file and returns it. - * - * \note This \e will move the read pointer for the file. - */ - ByteVector chunkData(uint i); - - /*! - * Sets the data for the chunk \a name to \a data. If a chunk with the - * given name already exists it will be overwritten, otherwise it will be - * created after the existing chunks. - * - * \warning This will update the file immediately. - */ - void setChunkData(const ByteVector &name, const ByteVector &data); - - private: - File(const File &); - File &operator=(const File &); - - void read(); - void writeChunk(const ByteVector &name, const ByteVector &data, - ulong offset, ulong replace = 0, - uint leadingPadding = 0); - - class FilePrivate; - FilePrivate *d; - }; - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.cpp b/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.cpp deleted file mode 100644 index 37d8a4d2..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************** - 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 -#include -#include - -#include "wavfile.h" - -using namespace TagLib; - -class RIFF::WAV::File::FilePrivate -{ -public: - FilePrivate() : - properties(0), - tag(0), - tagChunkID("ID3 ") - { - - } - - ~FilePrivate() - { - delete properties; - delete tag; - } - - Properties *properties; - ID3v2::Tag *tag; - ByteVector tagChunkID; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::WAV::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(file, LittleEndian) -{ - d = new FilePrivate; - if(isOpen()) - read(readProperties, propertiesStyle); -} - -RIFF::WAV::File::~File() -{ - delete d; -} - -ID3v2::Tag *RIFF::WAV::File::tag() const -{ - return d->tag; -} - -RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const -{ - return d->properties; -} - -bool RIFF::WAV::File::save() -{ - if(readOnly()) { - debug("RIFF::WAV::File::save() -- File is read only."); - return false; - } - - if(!isValid()) { - debug("RIFF::WAV::File::save() -- Trying to save invalid file."); - return false; - } - - setChunkData(d->tagChunkID, d->tag->render()); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) -{ - ByteVector formatData; - uint streamLength = 0; - for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { - d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); - } - else if(chunkName(i) == "fmt " && readProperties) - formatData = chunkData(i); - else if(chunkName(i) == "data" && readProperties) - streamLength = chunkDataSize(i); - } - - if(!formatData.isEmpty()) - d->properties = new Properties(formatData, streamLength, propertiesStyle); - - if(!d->tag) - d->tag = new ID3v2::Tag; -} diff --git a/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.h b/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.h deleted file mode 100644 index b44668c3..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/wav/wavfile.h +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - 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 "wavproperties.h" - -namespace TagLib { - - namespace RIFF { - - //! An implementation of WAV metadata - - /*! - * This is implementation of WAV metadata. - * - * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF - * chunk as well as properties from the file. - */ - - namespace WAV { - - //! An implementation of TagLib::File with WAV specific methods - - /*! - * This implements and provides an interface for WAV files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to WAV files. - */ - - class TAGLIB_EXPORT File : public TagLib::RIFF::File - { - public: - /*! - * Contructs an WAV file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. - */ - File(FileName file, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); - - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - /*! - * Returns the Tag for this file. - */ - virtual ID3v2::Tag *tag() const; - - /*! - * Returns the WAV::Properties for this file. If no audio properties - * were read then this will return a null pointer. - */ - virtual Properties *audioProperties() const; - - /*! - * Saves the file. - */ - virtual bool save(); - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - - class FilePrivate; - FilePrivate *d; - }; - } - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.cpp b/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.cpp deleted file mode 100644 index 37216860..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -#include - -using namespace TagLib; - -class RIFF::WAV::Properties::PropertiesPrivate -{ -public: - PropertiesPrivate(uint streamLength = 0) : - format(0), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - sampleWidth(0), - streamLength(streamLength) - { - - } - - short format; - int length; - int bitrate; - int sampleRate; - int channels; - int sampleWidth; - uint streamLength; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -RIFF::WAV::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style) -{ - d = new PropertiesPrivate(); - read(data); -} - -RIFF::WAV::Properties::Properties(const ByteVector &data, uint streamLength, ReadStyle style) : AudioProperties(style) -{ - d = new PropertiesPrivate(streamLength); - read(data); -} - -RIFF::WAV::Properties::~Properties() -{ - delete d; -} - -int RIFF::WAV::Properties::length() const -{ - return d->length; -} - -int RIFF::WAV::Properties::bitrate() const -{ - return d->bitrate; -} - -int RIFF::WAV::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int RIFF::WAV::Properties::channels() const -{ - return d->channels; -} - -int RIFF::WAV::Properties::sampleWidth() const -{ - return d->sampleWidth; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void RIFF::WAV::Properties::read(const ByteVector &data) -{ - d->format = data.mid(0, 2).toShort(false); - d->channels = data.mid(2, 2).toShort(false); - d->sampleRate = data.mid(4, 4).toUInt(false); - d->sampleWidth = data.mid(14, 2).toShort(false); - - uint byteRate = data.mid(8, 4).toUInt(false); - d->bitrate = byteRate * 8 / 1000; - - d->length = byteRate > 0 ? d->streamLength / byteRate : 0; -} diff --git a/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.h b/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.h deleted file mode 100644 index bf87ffe2..00000000 --- a/Plugins/PluginNowPlaying/taglib/riff/wav/wavproperties.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************** - 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 "taglib.h" -#include "audioproperties.h" - -namespace TagLib { - - class ByteVector; - - namespace RIFF { - - namespace WAV { - - class File; - - //! An implementation of audio property reading for WAV - - /*! - * This reads the data from an WAV stream found in the AudioProperties - * API. - */ - - class TAGLIB_EXPORT Properties : public AudioProperties - { - public: - /*! - * Create an instance of WAV::Properties with the data read from the - * ByteVector \a data. - */ - Properties(const ByteVector &data, ReadStyle style); - - /*! - * Create an instance of WAV::Properties with the data read from the - * ByteVector \a data and the length calculated using \a streamLength. - */ - Properties(const ByteVector &data, uint streamLength, ReadStyle style); - - /*! - * Destroys this WAV::Properties instance. - */ - virtual ~Properties(); - - // Reimplementations. - - virtual int length() const; - virtual int bitrate() const; - virtual int sampleRate() const; - virtual int channels() const; - - int sampleWidth() const; - - private: - Properties(const Properties &); - Properties &operator=(const Properties &); - - void read(const ByteVector &data); - - class PropertiesPrivate; - PropertiesPrivate *d; - }; - } - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/tag.cpp b/Plugins/PluginNowPlaying/taglib/tag.cpp index 8be33c80..67634081 100644 --- a/Plugins/PluginNowPlaying/taglib/tag.cpp +++ b/Plugins/PluginNowPlaying/taglib/tag.cpp @@ -24,6 +24,8 @@ ***************************************************************************/ #include "tag.h" +#include "tstringlist.h" +#include "tpropertymap.h" using namespace TagLib; @@ -53,6 +55,101 @@ bool Tag::isEmpty() const track() == 0); } +PropertyMap Tag::properties() const +{ + PropertyMap map; + if(!(title().isNull())) + map["TITLE"].append(title()); + if(!(artist().isNull())) + map["ARTIST"].append(artist()); + if(!(album().isNull())) + map["ALBUM"].append(album()); + if(!(comment().isNull())) + map["COMMENT"].append(comment()); + if(!(genre().isNull())) + 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 properties(origProps); + properties.removeEmpty(); + StringList oneValueSet; + // can this be simplified by using some preprocessor defines / function pointers? + if(properties.contains("TITLE")) { + setTitle(properties["TITLE"].front()); + oneValueSet.append("TITLE"); + } else + setTitle(String::null); + + if(properties.contains("ARTIST")) { + setArtist(properties["ARTIST"].front()); + oneValueSet.append("ARTIST"); + } else + setArtist(String::null); + + if(properties.contains("ALBUM")) { + setAlbum(properties["ALBUM"].front()); + oneValueSet.append("ALBUM"); + } else + setAlbum(String::null); + + if(properties.contains("COMMENT")) { + setComment(properties["COMMENT"].front()); + oneValueSet.append("COMMENT"); + } else + setComment(String::null); + + if(properties.contains("GENRE")) { + setGenre(properties["GENRE"].front()); + oneValueSet.append("GENRE"); + } else + setGenre(String::null); + + if(properties.contains("DATE")) { + bool ok; + int date = properties["DATE"].front().toInt(&ok); + if(ok) { + setYear(date); + oneValueSet.append("DATE"); + } else + setYear(0); + } + else + setYear(0); + + if(properties.contains("TRACKNUMBER")) { + bool ok; + int track = properties["TRACKNUMBER"].front().toInt(&ok); + if(ok) { + setTrack(track); + oneValueSet.append("TRACKNUMBER"); + } else + setTrack(0); + } + else + setYear(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(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { + if(properties[*it].size() == 1) + properties.erase(*it); + else + properties[*it].erase( properties[*it].begin() ); + } + return properties; +} + void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static { if(overwrite) { diff --git a/Plugins/PluginNowPlaying/taglib/tag.h b/Plugins/PluginNowPlaying/taglib/tag.h index c8f12a85..76c9a82a 100644 --- a/Plugins/PluginNowPlaying/taglib/tag.h +++ b/Plugins/PluginNowPlaying/taglib/tag.h @@ -41,6 +41,8 @@ namespace TagLib { * in TagLib::AudioProperties, TagLib::File and TagLib::FileRef. */ + class PropertyMap; + class TAGLIB_EXPORT Tag { public: @@ -50,6 +52,32 @@ namespace TagLib { */ virtual ~Tag(); + /*! + * 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. + */ + 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(). + * BIC: Will become virtual in future releases. Currently the non-virtual + * standard implementation of TagLib::Tag does nothing, since there are + * no unsupported elements. + */ + void removeUnsupportedProperties(const StringList& properties); + + /*! + * Sets the tags of this File to those specified in \a properties. 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. + */ + PropertyMap setProperties(const PropertyMap &properties); + /*! * Returns the track name; if no track name is present in the tag * String::null will be returned. diff --git a/Plugins/PluginNowPlaying/taglib/taglib_config.h b/Plugins/PluginNowPlaying/taglib/taglib_config.h index 44f47f34..9d882258 100644 --- a/Plugins/PluginNowPlaying/taglib/taglib_config.h +++ b/Plugins/PluginNowPlaying/taglib/taglib_config.h @@ -1,2 +1,4 @@ -#define TAGLIB_WITH_ASF 1 -#define TAGLIB_WITH_MP4 1 +/* taglib_config.h. Generated by cmake from taglib_config.h.cmake */ + +#define TAGLIB_WITH_ASF 1 +#define TAGLIB_WITH_MP4 1 diff --git a/Plugins/PluginNowPlaying/taglib/taglib_export.h b/Plugins/PluginNowPlaying/taglib/taglib_export.h index 486b39b0..257c3096 100644 --- a/Plugins/PluginNowPlaying/taglib/taglib_export.h +++ b/Plugins/PluginNowPlaying/taglib/taglib_export.h @@ -26,15 +26,13 @@ #ifndef TAGLIB_EXPORT_H #define TAGLIB_EXPORT_H -#define TAGLIB_STATIC - #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) +#define TAGLIB_EXPORT #endif #elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 1) #define TAGLIB_EXPORT __attribute__ ((visibility("default"))) @@ -42,8 +40,4 @@ #define TAGLIB_EXPORT #endif -#ifndef TAGLIB_NO_CONFIG -#include "taglib_config.h" -#endif - #endif diff --git a/Plugins/PluginNowPlaying/taglib/tagunion.cpp b/Plugins/PluginNowPlaying/taglib/tagunion.cpp index 4a9978d0..52d7136b 100644 --- a/Plugins/PluginNowPlaying/taglib/tagunion.cpp +++ b/Plugins/PluginNowPlaying/taglib/tagunion.cpp @@ -24,6 +24,7 @@ ***************************************************************************/ #include "tagunion.h" +#include "tstringlist.h" using namespace TagLib; diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/taglib.h b/Plugins/PluginNowPlaying/taglib/toolkit/taglib.h index 65a0c0b2..cbca1074 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/taglib.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/taglib.h @@ -26,9 +26,11 @@ #ifndef TAGLIB_H #define TAGLIB_H +#include "taglib_config.h" + #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 7 -#define TAGLIB_PATCH_VERSION 0 +#define TAGLIB_MINOR_VERSION 9 +#define TAGLIB_PATCH_VERSION 1 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) #define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") @@ -48,7 +50,7 @@ /*! * This namespace contains everything in TagLib. For projects working with - * TagLib extensively it may be conveniten to add a + * TagLib extensively it may be convenient to add a * \code * using namespace TagLib; * \endcode @@ -58,38 +60,20 @@ namespace TagLib { class String; - typedef wchar_t wchar; - typedef unsigned char uchar; - typedef unsigned int uint; - typedef unsigned long ulong; + typedef wchar_t wchar; // Assumed to be sufficient to store a UTF-16 char. + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long long ulonglong; + + // long/ulong can be either 32-bit or 64-bit wide. + typedef unsigned long ulong; /*! * Unfortunately std::wstring isn't defined on some systems, (i.e. GCC < 3) * so I'm providing something here that should be constant. */ typedef std::basic_string wstring; - -#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class. - /*! - * \internal - * This is just used as a base class for shared classes in TagLib. - * - * \warning This is not part of the TagLib public API! - */ - - class RefCounter - { - public: - RefCounter() : refCount(1) {} - void ref() { refCount++; } - bool deref() { return ! --refCount ; } - int count() { return refCount; } - private: - uint refCount; - }; - -#endif // DO_NOT_DOCUMENT - } /*! @@ -103,7 +87,7 @@ namespace TagLib { * - A clean, high level, C++ API to handling audio meta data. * - Format specific APIs for advanced API users. * - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats. - * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis and Speex file formats. + * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, 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. diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.cpp index d8c4253f..b6582464 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.cpp @@ -23,230 +23,322 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include #include +#include +#include #include #include - -#include +#include "trefcounter.h" +#include "tutils.h" #include "tbytevector.h" // This is a bit ugly to keep writing over and over again. // A rather obscure feature of the C++ spec that I hadn't thought of that makes -// working with C libs much more effecient. There's more here: +// 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 -#define DATA(x) (&(x->data[0])) +#define DATA(x) (&(x->data->data[0])) namespace TagLib { - static const char hexTable[17] = "0123456789abcdef"; - static const uint crcTable[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 - }; +static const char hexTable[17] = "0123456789abcdef"; - /*! - * A templatized KMP find that works both with a ByteVector and a ByteVectorMirror. - */ - - template - int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign) - { - if(pattern.size() > v.size() || offset > v.size() - 1) - return -1; - - // Let's go ahead and special case a pattern of size one since that's common - // and easy to make fast. - - if(pattern.size() == 1) { - char p = pattern[0]; - for(uint i = offset; i < v.size(); i++) { - if(v[i] == p && (i - offset) % byteAlign == 0) - return i; - } - return -1; - } - - uchar lastOccurrence[256]; - - for(uint i = 0; i < 256; ++i) - lastOccurrence[i] = uchar(pattern.size()); - - for(uint i = 0; i < pattern.size() - 1; ++i) - lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1); - - for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) { - int iBuffer = i; - int iPattern = pattern.size() - 1; - - while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) { - --iBuffer; - --iPattern; - } - - if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0) - return iBuffer + 1; - } +static const uint crcTable[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; +/*! + * A templatized straightforward find that works with the types + * std::vector::iterator and std::vector::reverse_iterator. + */ +template +int findChar( + const TIterator dataBegin, const TIterator dataEnd, + char c, uint offset, int byteAlign) +{ + const size_t dataSize = dataEnd - dataBegin; + if(dataSize == 0 || offset > dataSize - 1) return -1; + + // n % 0 is invalid + + if(byteAlign == 0) + return -1; + + for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) { + if(*it == c) + return (it - dataBegin); } - /*! - * Wraps the accessors to a ByteVector to make the search algorithm access the - * elements in reverse. - * - * \see vectorFind() - * \see ByteVector::rfind() - */ - - class ByteVectorMirror - { - public: - ByteVectorMirror(const ByteVector &source) : v(source) {} - - char operator[](int index) const - { - return v[v.size() - index - 1]; - } - - char at(int index) const - { - return v.at(v.size() - index - 1); - } - - ByteVectorMirror mid(uint index, uint length = 0xffffffff) const - { - return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length); - } - - uint size() const - { - return v.size(); - } - - int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const - { - ByteVectorMirror v(*this); - - if(offset > 0) { - offset = size() - offset - pattern.size(); - if(offset >= size()) - offset = 0; - } - - const int pos = vectorFind(v, pattern, offset, byteAlign); - - // If the offset is zero then we need to adjust the location in the search - // to be appropriately reversed. If not we need to account for the fact - // that the recursive call (called from the above line) has already ajusted - // for this but that the normal templatized find above will add the offset - // to the returned value. - // - // This is a little confusing at first if you don't first stop to think - // through the logic involved in the forward search. - - if(pos == -1) - return -1; - - return size() - pos - pattern.size(); - } - - private: - const ByteVector &v; - }; - - template - T toNumber(const std::vector &data, bool mostSignificantByteFirst) - { - T sum = 0; - - if(data.size() <= 0) { - debug("ByteVectorMirror::toNumber() -- data is empty, returning 0"); - return sum; - } - - uint size = sizeof(T); - uint last = data.size() > size ? size - 1 : data.size() - 1; - - for(uint i = 0; i <= last; i++) - sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8); - - return sum; - } - - template - ByteVector fromNumber(T value, bool mostSignificantByteFirst) - { - int size = sizeof(T); - - ByteVector v(size, 0); - - for(int i = 0; i < size; i++) - v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff); - - return v; - } + return -1; } -using namespace TagLib; +/*! + * A templatized KMP find that works with the types + * std::vector::iterator and std::vector::reverse_iterator. + */ +template +int findVector( + const TIterator dataBegin, const TIterator dataEnd, + const TIterator patternBegin, const TIterator patternEnd, + uint offset, int byteAlign) +{ + const size_t dataSize = dataEnd - dataBegin; + const size_t patternSize = patternEnd - patternBegin; + if(patternSize > dataSize || offset > dataSize - 1) + return -1; + + // n % 0 is invalid + + if(byteAlign == 0) + return -1; + + // Special case that pattern contains just single char. + + if(patternSize == 1) + return findChar(dataBegin, dataEnd, *patternBegin, offset, byteAlign); + + size_t lastOccurrence[256]; + + for(size_t i = 0; i < 256; ++i) + lastOccurrence[i] = patternSize; + + for(size_t i = 0; i < patternSize - 1; ++i) + lastOccurrence[static_cast(*(patternBegin + i))] = patternSize - i - 1; + + TIterator it = dataBegin + patternSize - 1 + offset; + while(true) + { + TIterator itBuffer = it; + TIterator itPattern = patternBegin + patternSize - 1; + + while(*itBuffer == *itPattern) + { + if(itPattern == patternBegin) + { + if((itBuffer - dataBegin - offset) % byteAlign == 0) + return (itBuffer - dataBegin); + else + break; + } + + --itBuffer; + --itPattern; + } + + const size_t step = lastOccurrence[static_cast(*it)]; + if(dataEnd - step <= it) + break; + + it += step; + } + + return -1; +} + +template +T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignificantByteFirst) +{ + if(offset >= v.size()) { + debug("toNumber() -- 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(static_cast(v[offset + i])) << shift; + } + + return sum; +} + +template +T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst) +{ + static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian); + const bool swap = (mostSignificantByteFirst != isBigEndian); + + if(offset + sizeof(T) > v.size()) + return toNumber(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); + else + return tmp; +} + +template +ByteVector fromNumber(T value, bool mostSignificantByteFirst) +{ + static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian); + const bool swap = (mostSignificantByteFirst != isBigEndian); + + if(swap) + value = Utils::byteSwap(value); + + return ByteVector(reinterpret_cast(&value), sizeof(T)); +} + +class DataPrivate : public RefCounter +{ +public: + DataPrivate() + { + } + + DataPrivate(const std::vector &v, uint offset, uint length) + : data(v.begin() + offset, v.begin() + offset + length) + { + } + + // A char* can be an iterator. + DataPrivate(const char *begin, const char *end) + : data(begin, end) + { + } + + DataPrivate(uint len, char c) + : data(len, c) + { + } + + std::vector data; +}; class ByteVector::ByteVectorPrivate : public RefCounter { public: - ByteVectorPrivate() : RefCounter(), size(0) {} - ByteVectorPrivate(const std::vector &v) : RefCounter(), data(v), size(v.size()) {} - ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {} + ByteVectorPrivate() + : RefCounter() + , data(new DataPrivate()) + , offset(0) + , length(0) + { + } - std::vector data; + ByteVectorPrivate(ByteVectorPrivate *d, uint o, uint l) + : RefCounter() + , data(d->data) + , offset(d->offset + o) + , length(l) + { + data->ref(); + } - // std::vector::size() is very slow, so we'll cache the value + ByteVectorPrivate(const std::vector &v, uint o, uint l) + : RefCounter() + , data(new DataPrivate(v, o, l)) + , offset(0) + , length(l) + { + } - uint size; + ByteVectorPrivate(uint l, char c) + : RefCounter() + , data(new DataPrivate(l, c)) + , offset(0) + , length(l) + { + } + + ByteVectorPrivate(const char *s, uint l) + : RefCounter() + , data(new DataPrivate(s, s + l)) + , offset(0) + , length(l) + { + } + + void detach() + { + if(data->count() > 1) { + data->deref(); + data = new DataPrivate(data->data, offset, length); + offset = 0; + } + } + + ~ByteVectorPrivate() + { + if(data->deref()) + delete data; + } + + ByteVectorPrivate &operator=(const ByteVectorPrivate &x) + { + if(&x != this) + { + if(data->deref()) + delete data; + + data = x.data; + data->ref(); + } + + return *this; + } + + DataPrivate *data; + uint offset; + uint length; }; //////////////////////////////////////////////////////////////////////////////// @@ -257,14 +349,10 @@ ByteVector ByteVector::null; ByteVector ByteVector::fromCString(const char *s, uint length) { - ByteVector v; - if(length == 0xffffffff) - v.setData(s); + return ByteVector(s, ::strlen(s)); else - v.setData(s, length); - - return v; + return ByteVector(s, length); } ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst) @@ -274,12 +362,12 @@ ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst) ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst) { - return fromNumber(value, mostSignificantByteFirst); + return fromNumber(value, mostSignificantByteFirst); } ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst) { - return fromNumber(value, mostSignificantByteFirst); + return fromNumber(value, mostSignificantByteFirst); } //////////////////////////////////////////////////////////////////////////////// @@ -287,37 +375,39 @@ ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFir //////////////////////////////////////////////////////////////////////////////// ByteVector::ByteVector() + : d(new ByteVectorPrivate()) { - d = new ByteVectorPrivate; } ByteVector::ByteVector(uint size, char value) + : d(new ByteVectorPrivate(size, value)) { - d = new ByteVectorPrivate(size, value); } -ByteVector::ByteVector(const ByteVector &v) : d(v.d) +ByteVector::ByteVector(const ByteVector &v) + : d(v.d) { d->ref(); } -ByteVector::ByteVector(char c) +ByteVector::ByteVector(const ByteVector &v, uint offset, uint length) + : d(new ByteVectorPrivate(v.d, offset, length)) +{ +} + +ByteVector::ByteVector(char c) + : d(new ByteVectorPrivate(1, c)) { - d = new ByteVectorPrivate; - d->data.push_back(c); - d->size = 1; } ByteVector::ByteVector(const char *data, uint length) + : d(new ByteVectorPrivate(data, length)) { - d = new ByteVectorPrivate; - setData(data, length); } ByteVector::ByteVector(const char *data) + : d(new ByteVectorPrivate(data, ::strlen(data))) { - d = new ByteVectorPrivate; - setData(data); } ByteVector::~ByteVector() @@ -326,74 +416,68 @@ ByteVector::~ByteVector() delete d; } -ByteVector &ByteVector::setData(const char *data, uint length) +ByteVector &ByteVector::setData(const char *s, uint length) { - detach(); - - resize(length); - - if(length > 0) - ::memcpy(DATA(d), data, length); - + *this = ByteVector(s, length); return *this; } ByteVector &ByteVector::setData(const char *data) { - return setData(data, ::strlen(data)); + *this = ByteVector(data); + return *this; } char *ByteVector::data() { detach(); - return size() > 0 ? DATA(d) : 0; + return size() > 0 ? (DATA(d) + d->offset) : 0; } const char *ByteVector::data() const { - return size() > 0 ? DATA(d) : 0; + return size() > 0 ? (DATA(d) + d->offset) : 0; } ByteVector ByteVector::mid(uint index, uint length) const { - ByteVector v; + index = std::min(index, size()); + length = std::min(length, size() - index); - if(index > size()) - return v; - - ConstIterator endIt; - - if(length < size() - index) - endIt = d->data.begin() + index + length; - else - endIt = d->data.end(); - - v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt); - v.d->size = v.d->data.size(); - - return v; + return ByteVector(*this, index, length); } char ByteVector::at(uint index) const { - return index < size() ? d->data[index] : 0; + return index < size() ? DATA(d)[d->offset + index] : 0; } int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const { - return vectorFind(*this, pattern, offset, byteAlign); + return findVector( + begin(), end(), pattern.begin(), pattern.end(), offset, byteAlign); +} + +int ByteVector::find(char c, uint offset, int byteAlign) const +{ + return findChar(begin(), end(), c, offset, byteAlign); } int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const { - // Ok, this is a little goofy, but pretty cool after it sinks in. Instead of - // reversing the find method's Boyer-Moore search algorithm I created a "mirror" - // for a ByteVector to reverse the behavior of the accessors. + if(offset > 0) { + offset = size() - offset - pattern.size(); + if(offset >= size()) + offset = 0; + } - ByteVectorMirror v(*this); - ByteVectorMirror p(pattern); + const int pos = findVector( + rbegin(), rend(), pattern.rbegin(), pattern.rend(), offset, byteAlign); - return v.find(p, offset, byteAlign); + if(pos == -1) + return -1; + else + return size() - pos - pattern.size(); } bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const @@ -402,18 +486,11 @@ bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint pattern patternLength = pattern.size(); // do some sanity checking -- all of these things are needed for the search to be valid - - if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0) + const uint compareLength = patternLength - patternOffset; + if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0) return false; - - // loop through looking for a mismatch - - for(uint i = 0; i < patternLength - patternOffset; i++) { - if(at(i + offset) != pattern[i + patternOffset]) - return false; - } - - return true; + + return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0); } bool ByteVector::startsWith(const ByteVector &pattern) const @@ -431,29 +508,63 @@ ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &wit if(pattern.size() == 0 || pattern.size() > size()) return *this; - const int patternSize = pattern.size(); - const int withSize = with.size(); + const uint withSize = with.size(); + const uint patternSize = pattern.size(); + int offset = 0; - int offset = find(pattern); - - while(offset >= 0) { - - const int originalSize = size(); - - if(withSize > patternSize) - resize(originalSize + withSize - patternSize); - - if(patternSize != withSize) - ::memcpy(data() + offset + withSize, mid(offset + patternSize).data(), originalSize - offset - patternSize); - - if(withSize < patternSize) - resize(originalSize + withSize - patternSize); - - ::memcpy(data() + offset, with.data(), withSize); - - offset = find(pattern, offset + withSize); + if(withSize == patternSize) { + // I think this case might be common enough to optimize it + detach(); + offset = find(pattern); + while(offset >= 0) { + ::memcpy(data() + offset, with.data(), withSize); + offset = find(pattern, offset + withSize); + } + return *this; } + // calculate new size: + uint newSize = 0; + for(;;) { + int next = find(pattern, offset); + if(next < 0) { + if(offset == 0) + // pattern not found, do nothing: + return *this; + newSize += size() - offset; + break; + } + newSize += (next - offset) + withSize; + offset = next + patternSize; + } + + // new private data of appropriate size: + ByteVectorPrivate *newData = new ByteVectorPrivate(newSize, 0); + char *target = DATA(newData); + const char *source = data(); + + // copy modified data into new private data: + offset = 0; + for(;;) { + int next = find(pattern, offset); + if(next < 0) { + ::memcpy(target, source + offset, size() - offset); + break; + } + int chunkSize = next - offset; + ::memcpy(target, source + offset, chunkSize); + target += chunkSize; + ::memcpy(target, with.data(), withSize); + target += withSize; + offset += chunkSize + patternSize; + } + + // replace private data: + if(d->deref()) + delete d; + + d = newData; + return *this; } @@ -477,74 +588,92 @@ int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const ByteVector &ByteVector::append(const ByteVector &v) { - if(v.d->size == 0) - return *this; // Simply return if appending nothing. + if(v.d->length != 0) + { + detach(); - detach(); - - uint originalSize = d->size; - resize(d->size + v.d->size); - ::memcpy(DATA(d) + originalSize, DATA(v.d), v.size()); + uint originalSize = size(); + resize(originalSize + v.size()); + ::memcpy(data() + originalSize, v.data(), v.size()); + } return *this; } ByteVector &ByteVector::clear() { - detach(); - d->data.clear(); - d->size = 0; - + *this = ByteVector(); return *this; } TagLib::uint ByteVector::size() const { - return d->size; + return d->length; } ByteVector &ByteVector::resize(uint size, char padding) { - if(d->size < size) { - d->data.reserve(size); - d->data.insert(d->data.end(), size - d->size, padding); + if(size != d->length) { + detach(); + d->data->data.resize(d->offset + size, padding); + d->length = size; } - else - d->data.erase(d->data.begin() + size, d->data.end()); - - d->size = size; return *this; } ByteVector::Iterator ByteVector::begin() { - return d->data.begin(); + return d->data->data.begin() + d->offset; } ByteVector::ConstIterator ByteVector::begin() const { - return d->data.begin(); + return d->data->data.begin() + d->offset; } ByteVector::Iterator ByteVector::end() { - return d->data.end(); + return d->data->data.begin() + d->offset + d->length; } ByteVector::ConstIterator ByteVector::end() const { - return d->data.end(); + return d->data->data.begin() + d->offset + d->length; +} + +ByteVector::ReverseIterator ByteVector::rbegin() +{ + std::vector &v = d->data->data; + return v.rbegin() + (v.size() - (d->offset + d->length)); +} + +ByteVector::ConstReverseIterator ByteVector::rbegin() const +{ + std::vector &v = d->data->data; + return v.rbegin() + (v.size() - (d->offset + d->length)); +} + +ByteVector::ReverseIterator ByteVector::rend() +{ + std::vector &v = d->data->data; + return v.rbegin() + (v.size() - d->offset); +} + +ByteVector::ConstReverseIterator ByteVector::rend() const +{ + std::vector &v = d->data->data; + return v.rbegin() + (v.size() - d->offset); } bool ByteVector::isNull() const { - return d == null.d; + return (d == null.d); } bool ByteVector::isEmpty() const { - return d->data.size() == 0; + return (d->length == 0); } TagLib::uint ByteVector::checksum() const @@ -557,42 +686,66 @@ TagLib::uint ByteVector::checksum() const TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const { - return toNumber(d->data, mostSignificantByteFirst); + return toNumber(*this, 0, mostSignificantByteFirst); +} + +TagLib::uint ByteVector::toUInt(uint offset, bool mostSignificantByteFirst) const +{ + return toNumber(*this, offset, mostSignificantByteFirst); +} + +TagLib::uint ByteVector::toUInt(uint offset, uint length, bool mostSignificantByteFirst) const +{ + return toNumber(*this, offset, length, mostSignificantByteFirst); } short ByteVector::toShort(bool mostSignificantByteFirst) const { - return toNumber(d->data, mostSignificantByteFirst); + return toNumber(*this, 0, mostSignificantByteFirst); +} + +short ByteVector::toShort(uint offset, bool mostSignificantByteFirst) const +{ + return toNumber(*this, offset, mostSignificantByteFirst); } unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const { - return toNumber(d->data, mostSignificantByteFirst); + return toNumber(*this, 0, mostSignificantByteFirst); +} + +unsigned short ByteVector::toUShort(uint offset, bool mostSignificantByteFirst) const +{ + return toNumber(*this, offset, mostSignificantByteFirst); } long long ByteVector::toLongLong(bool mostSignificantByteFirst) const { - return toNumber(d->data, mostSignificantByteFirst); + return toNumber(*this, 0, mostSignificantByteFirst); +} + +long long ByteVector::toLongLong(uint offset, bool mostSignificantByteFirst) const +{ + return toNumber(*this, offset, mostSignificantByteFirst); } const char &ByteVector::operator[](int index) const { - return d->data[index]; + return d->data->data[d->offset + index]; } char &ByteVector::operator[](int index) { detach(); - - return d->data[index]; + return d->data->data[d->offset + index]; } bool ByteVector::operator==(const ByteVector &v) const { - if(d->size != v.d->size) + if(size() != v.size()) return false; - return ::memcmp(data(), v.data(), size()) == 0; + return (::memcmp(data(), v.data(), size()) == 0); } bool ByteVector::operator!=(const ByteVector &v) const @@ -602,10 +755,10 @@ bool ByteVector::operator!=(const ByteVector &v) const bool ByteVector::operator==(const char *s) const { - if(d->size != ::strlen(s)) + if(size() != ::strlen(s)) return false; - return ::memcmp(data(), s, d->size) == 0; + return (::memcmp(data(), s, size()) == 0); } bool ByteVector::operator!=(const char *s) const @@ -615,8 +768,7 @@ bool ByteVector::operator!=(const char *s) const bool ByteVector::operator<(const ByteVector &v) const { - int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size); - + const int result = ::memcmp(data(), v.data(), std::min(size(), v.size())); if(result != 0) return result < 0; else @@ -663,12 +815,12 @@ ByteVector &ByteVector::operator=(const char *data) ByteVector ByteVector::toHex() const { ByteVector encoded(size() * 2); + char *p = encoded.data(); - uint j = 0; for(uint i = 0; i < size(); i++) { - unsigned char c = d->data[i]; - encoded[j++] = hexTable[(c >> 4) & 0x0F]; - encoded[j++] = hexTable[(c ) & 0x0F]; + unsigned char c = data()[i]; + *p++ = hexTable[(c >> 4) & 0x0F]; + *p++ = hexTable[(c ) & 0x0F]; } return encoded; @@ -680,17 +832,24 @@ ByteVector ByteVector::toHex() const void ByteVector::detach() { + if(d->data->count() > 1) { + d->data->deref(); + d->data = new DataPrivate(d->data->data, d->offset, d->length); + d->offset = 0; + } + if(d->count() > 1) { d->deref(); - d = new ByteVectorPrivate(d->data); + d = new ByteVectorPrivate(d->data->data, d->offset, d->length); } } +} //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// -std::ostream &operator<<(std::ostream &s, const ByteVector &v) +std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v) { for(TagLib::uint i = 0; i < v.size(); i++) s << v[i]; diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.h b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.h index b52a2149..538565b0 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevector.h @@ -48,6 +48,8 @@ namespace TagLib { #ifndef DO_NOT_DOCUMENT typedef std::vector::iterator Iterator; typedef std::vector::const_iterator ConstIterator; + typedef std::vector::reverse_iterator ReverseIterator; + typedef std::vector::const_reverse_iterator ConstReverseIterator; #endif /*! @@ -62,12 +64,17 @@ namespace TagLib { ByteVector(uint size, char value = 0); /*! - * Contructs a byte vector that is a copy of \a v. + * Constructs a byte vector that is a copy of \a v. */ ByteVector(const ByteVector &v); /*! - * Contructs a byte vector that contains \a c. + * Constructs a byte vector that is a copy of \a v. + */ + ByteVector(const ByteVector &v, uint offset, uint length); + + /*! + * Constructs a byte vector that contains \a c. */ ByteVector(char c); @@ -135,6 +142,14 @@ namespace TagLib { */ int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const; + /*! + * Searches the char for \a c starting at \a offset and returns + * the offset. Returns \a npos 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, uint offset = 0, int byteAlign = 1) const; + /*! * Searches the ByteVector for \a pattern starting from either the end of the * vector or \a offset and returns the offset. Returns -1 if the pattern was @@ -222,6 +237,26 @@ namespace TagLib { */ ConstIterator end() 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 true if the vector is null. * @@ -256,7 +291,32 @@ namespace TagLib { uint toUInt(bool mostSignificantByteFirst = true) const; /*! - * Converts the first 2 bytes of the vector to a short. + * Converts the 4 bytes at \a offset of the vector to an unsigned integer. + * + * If \a mostSignificantByteFirst is true this will operate left to right + * evaluating the integer. For example if \a mostSignificantByteFirst is + * true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 == + * 0x01000000 == 1. + * + * \see fromUInt() + */ + uint toUInt(uint 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 true this will operate left to right + * evaluating the integer. For example if \a mostSignificantByteFirst is + * true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 == + * 0x01000000 == 1. + * + * \see fromUInt() + */ + uint toUInt(uint offset, uint length, bool mostSignificantByteFirst = true) const; + + /*! + * Converts the first 2 bytes of the vector to a (signed) short. * * If \a mostSignificantByteFirst is true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is @@ -266,6 +326,17 @@ namespace TagLib { */ short toShort(bool mostSignificantByteFirst = true) const; + /*! + * Converts the 2 bytes at \a offset of the vector to a (signed) short. + * + * If \a mostSignificantByteFirst is true this will operate left to right + * evaluating the integer. For example if \a mostSignificantByteFirst is + * true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1. + * + * \see fromShort() + */ + short toShort(uint offset, bool mostSignificantByteFirst = true) const; + /*! * Converts the first 2 bytes of the vector to a unsigned short. * @@ -277,6 +348,17 @@ namespace TagLib { */ unsigned short toUShort(bool mostSignificantByteFirst = true) const; + /*! + * Converts the 2 bytes at \a offset of the vector to a unsigned short. + * + * If \a mostSignificantByteFirst is true this will operate left to right + * evaluating the integer. For example if \a mostSignificantByteFirst is + * true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1. + * + * \see fromShort() + */ + unsigned short toUShort(uint offset, bool mostSignificantByteFirst = true) const; + /*! * Converts the first 8 bytes of the vector to a (signed) long long. * @@ -289,6 +371,18 @@ namespace TagLib { */ 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 true this will operate left to right + * evaluating the integer. For example if \a mostSignificantByteFirst is + * true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1, + * if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1. + * + * \see fromUInt() + */ + long long toLongLong(uint offset, bool mostSignificantByteFirst = true) const; + /*! * Creates a 4 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is true, then this will operate left to right @@ -397,8 +491,8 @@ namespace TagLib { static ByteVector null; /*! - * Returns a hex-encoded copy of the byte vector. - */ + * Returns a hex-encoded copy of the byte vector. + */ ByteVector toHex() const; protected: @@ -413,7 +507,6 @@ namespace TagLib { class ByteVectorPrivate; ByteVectorPrivate *d; }; - } /*! diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.cpp new file mode 100644 index 00000000..dc480a26 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** + 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 "tstring.h" +#include "tdebug.h" + +#include +#include + +#include + +using namespace TagLib; + +class ByteVectorStream::ByteVectorStreamPrivate +{ +public: + ByteVectorStreamPrivate(const ByteVector &data); + + ByteVector data; + long position; +}; + +ByteVectorStream::ByteVectorStreamPrivate::ByteVectorStreamPrivate(const ByteVector &data) : + data(data), + position(0) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +ByteVectorStream::ByteVectorStream(const ByteVector &data) +{ + d = new ByteVectorStreamPrivate(data); +} + +ByteVectorStream::~ByteVectorStream() +{ + delete d; +} + +FileName ByteVectorStream::name() const +{ + return FileName(""); // XXX do we need a name? +} + +ByteVector ByteVectorStream::readBlock(ulong length) +{ + if(length == 0) + return ByteVector::null; + + ByteVector v = d->data.mid(d->position, length); + d->position += v.size(); + return v; +} + +void ByteVectorStream::writeBlock(const ByteVector &data) +{ + uint size = data.size(); + if(long(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, ulong start, ulong replace) +{ + long sizeDiff = data.size() - replace; + if(sizeDiff < 0) { + removeBlock(start + data.size(), -sizeDiff); + } + else if(sizeDiff > 0) { + truncate(length() + sizeDiff); + ulong readPosition = start + replace; + ulong writePosition = start + data.size(); + memmove(d->data.data() + writePosition, d->data.data() + readPosition, length() - sizeDiff - readPosition); + } + seek(start); + writeBlock(data); +} + +void ByteVectorStream::removeBlock(ulong start, ulong length) +{ + ulong readPosition = start + length; + ulong writePosition = start; + if(readPosition < ulong(ByteVectorStream::length())) { + ulong 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(long offset, Position p) +{ + switch(p) { + case Beginning: + d->position = offset; + break; + case Current: + d->position += offset; + break; + case End: + d->position = length() - offset; + break; + } +} + +void ByteVectorStream::clear() +{ +} + +long ByteVectorStream::tell() const +{ + return d->position; +} + +long ByteVectorStream::length() +{ + return d->data.size(); +} + +void ByteVectorStream::truncate(long length) +{ + d->data.resize(length); +} + +ByteVector *ByteVectorStream::data() +{ + return &d->data; +} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.h b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.h new file mode 100644 index 00000000..456b854e --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tbytevectorstream.h @@ -0,0 +1,145 @@ +/*************************************************************************** + 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 "taglib_export.h" +#include "taglib.h" +#include "tbytevector.h" +#include "tiostream.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 File object and opens the \a file. \a file should be a + * be a C-string in the local file system encoding. + */ + ByteVectorStream(const ByteVector &data); + + /*! + * Destroys this ByteVectorStream instance. + */ + virtual ~ByteVectorStream(); + + /*! + * Returns the file name in the local file system encoding. + */ + FileName name() const; + + /*! + * Reads a block of size \a length at the current get pointer. + */ + ByteVector readBlock(ulong length); + + /*! + * Attempts to write the block \a data at the current get pointer. If the + * file is currently only opened read only -- i.e. readOnly() returns true -- + * this attempts to reopen the file in read/write mode. + * + * \note This should be used instead of using the streaming output operator + * for a ByteVector. And even this function is significantly slower than + * doing output with a char[]. + */ + void writeBlock(const ByteVector &data); + + /*! + * Insert \a data at position \a start in the file overwriting \a replace + * bytes of the original content. + * + * \note This method is slow since it requires rewriting all of the file + * after the insertion point. + */ + void insert(const ByteVector &data, ulong start = 0, ulong replace = 0); + + /*! + * Removes a block of the file starting a \a start and continuing for + * \a length bytes. + * + * \note This method is slow since it involves rewriting all of the file + * after the removed portion. + */ + void removeBlock(ulong start = 0, ulong length = 0); + + /*! + * Returns true if the file is read only (or if the file can not be opened). + */ + bool readOnly() const; + + /*! + * Since the file can currently only be opened as an argument to the + * constructor (sort-of by design), this returns if that open succeeded. + */ + bool isOpen() const; + + /*! + * Move the I/O pointer to \a offset in the file from position \a p. This + * defaults to seeking from the beginning of the file. + * + * \see Position + */ + void seek(long offset, Position p = Beginning); + + /*! + * Reset the end-of-file and error flags on the file. + */ + void clear(); + + /*! + * Returns the current offset within the file. + */ + long tell() const; + + /*! + * Returns the length of the file. + */ + long length(); + + /*! + * Truncates the file to a \a length. + */ + void truncate(long length); + + ByteVector *data(); + + protected: + + private: + class ByteVectorStreamPrivate; + ByteVectorStreamPrivate *d; + }; + +} + +#endif diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.cpp index 522b68c9..71350af7 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.cpp @@ -23,33 +23,77 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifndef NDEBUG -#include -#include +#ifdef HAVE_CONFIG_H +#include +#endif #include "tdebug.h" #include "tstring.h" +#include "tdebuglistener.h" + +#include +#include +#include using namespace TagLib; -void TagLib::debug(const String &s) +namespace { - std::cerr << "TagLib: " << s << std::endl; -} + String format(const char *fmt, ...) + { + va_list args; + va_start(args, fmt); -void TagLib::debugData(const ByteVector &v) -{ - for(uint i = 0; i < v.size(); i++) { + char buf[256]; - std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i]) - << std::endl; +#if defined(HAVE_SNPRINTF) - std::bitset<8> b(v[i]); + vsnprintf(buf, sizeof(buf), fmt, args); - for(int j = 0; j < 8; j++) - std::cout << i << ":" << j << " " << b.test(j) << std::endl; +#elif defined(HAVE_SPRINTF_S) - std::cout << std::endl; + vsprintf_s(buf, fmt, args); + +#else + + // Be careful. May cause a buffer overflow. + vsprintf(buf, fmt, args); + +#endif + + va_end(args); + + return String(buf); } } + +namespace TagLib +{ + // The instance is defined in tdebuglistener.cpp. + extern DebugListener *debugListener; + + void debug(const String &s) + { +#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) + + debugListener->printMessage("TagLib: " + s + "\n"); + #endif + } + + void debugData(const ByteVector &v) + { +#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) + + for(size_t i = 0; i < v.size(); ++i) + { + std::string bits = std::bitset<8>(v[i]).to_string(); + String msg = format("*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n", + i, v[i], v[i], v[i], bits.c_str()); + + debugListener->printMessage(msg); + } + +#endif + } +} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.h b/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.h index 5204fe70..bd94d159 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tdebug.h @@ -32,11 +32,11 @@ namespace TagLib { class ByteVector; #ifndef DO_NOT_DOCUMENT -#ifndef NDEBUG /*! - * A simple function that prints debugging output to cerr if debugging is - * not disabled. + * 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 @@ -45,7 +45,7 @@ namespace TagLib { * \internal */ void debug(const String &s); - + /*! * For debugging binary data. * @@ -56,16 +56,7 @@ namespace TagLib { * \internal */ void debugData(const ByteVector &v); - -#else - - // Define these to an empty statement if debugging is disabled. - -#define debug(x) -#define debugData(x) - -#endif -#endif } #endif +#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.cpp similarity index 60% rename from Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.cpp rename to Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.cpp index 86555fc1..48912222 100644 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.cpp @@ -1,7 +1,7 @@ -/************************************************************************** - copyright : (C) 2009 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ +/*************************************************************************** + copyright : (C) 2013 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * @@ -23,67 +23,63 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include +#include "tdebuglistener.h" + +#include +#include + +#ifdef _WIN32 +# include #endif -#ifdef WITH_MP4 - -#include -#include -#include "mp4coverart.h" - using namespace TagLib; -class MP4::CoverArt::CoverArtPrivate : public RefCounter +namespace { -public: - CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {} + class DefaultListener : public DebugListener + { + public: + virtual void printMessage(const String &msg) + { +#ifdef _WIN32 - Format format; - ByteVector data; -}; + const wstring wstr = msg.toWString(); + const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); + if(len != 0) { + std::vector buf(len); + WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], len, NULL, NULL); -MP4::CoverArt::CoverArt(Format format, const ByteVector &data) -{ - d = new CoverArtPrivate; - d->format = format; - d->data = data; + std::cerr << std::string(&buf[0]); + } + +#else + + std::cerr << msg; + +#endif + } + }; + + DefaultListener defaultListener; } -MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d) +namespace TagLib { - d->ref(); -} + DebugListener *debugListener = &defaultListener; -MP4::CoverArt & -MP4::CoverArt::operator=(const CoverArt &item) -{ - if(d->deref()) { - delete d; + DebugListener::DebugListener() + { } - d = item.d; - d->ref(); - return *this; -} -MP4::CoverArt::~CoverArt() -{ - if(d->deref()) { - delete d; + DebugListener::~DebugListener() + { + } + + void setDebugListener(DebugListener *listener) + { + if(listener) + debugListener = listener; + else + debugListener = &defaultListener; } } - -MP4::CoverArt::Format -MP4::CoverArt::format() const -{ - return d->format; -} - -ByteVector -MP4::CoverArt::data() const -{ - return d->data; -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.h b/Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.h similarity index 53% rename from Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.h rename to Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.h index 26c4f9d9..a32f285f 100644 --- a/Plugins/PluginNowPlaying/taglib/mp4/mp4coverart.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tdebuglistener.h @@ -1,7 +1,7 @@ -/************************************************************************** - copyright : (C) 2009 by Lukáš Lalinský - email : lalinsky@gmail.com - **************************************************************************/ +/*************************************************************************** + copyright : (C) 2013 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ /*************************************************************************** * This library is free software; you can redistribute it and/or modify * @@ -23,49 +23,52 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifndef TAGLIB_MP4COVERART_H -#define TAGLIB_MP4COVERART_H +#ifndef TAGLIB_DEBUGLISTENER_H +#define TAGLIB_DEBUGLISTENER_H -#include "tlist.h" -#include "tbytevector.h" #include "taglib_export.h" +#include "tstring.h" -namespace TagLib { +namespace TagLib +{ + //! An abstraction for the listener to the debug messages. - namespace MP4 { + /*! + * 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(); - class TAGLIB_EXPORT CoverArt - { - public: - /*! - * This describes the image type. - */ - enum Format { - JPEG = 0x0D, - PNG = 0x0E - }; + /*! + * 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; - CoverArt(Format format, const ByteVector &data); - ~CoverArt(); - - CoverArt(const CoverArt &item); - CoverArt &operator=(const CoverArt &item); - - //! Format of the image - Format format() const; - - //! The image data - ByteVector data() const; - - private: - class CoverArtPrivate; - CoverArtPrivate *d; - }; - - typedef List CoverArtList; - - } + private: + // Noncopyable + DebugListener(const DebugListener &); + DebugListener &operator=(const DebugListener &); + }; + /*! + * Sets the listener that decides how the debug messages are redirected. + * If the parameter \a listener is null, the previous listener is released + * and 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); } #endif diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tfile.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tfile.cpp index f0b3c80d..6b167bd5 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tfile.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tfile.cpp @@ -24,24 +24,19 @@ ***************************************************************************/ #include "tfile.h" +#include "tfilestream.h" #include "tstring.h" #include "tdebug.h" - -#include -#include -#include +#include "tpropertymap.h" #ifdef _WIN32 -# include # include # include -# define ftruncate _chsize #else +# include # include #endif -#include - #ifndef R_OK # define R_OK 4 #endif @@ -49,134 +44,208 @@ # define W_OK 2 #endif +#include "asffile.h" +#include "mpegfile.h" +#include "vorbisfile.h" +#include "flacfile.h" +#include "oggflacfile.h" +#include "mpcfile.h" +//#include "mp4file.h" +//#include "wavpackfile.h" +//#include "speexfile.h" +//#include "opusfile.h" +//#include "trueaudiofile.h" +//#include "aifffile.h" +//#include "wavfile.h" +#include "apefile.h" +//#include "modfile.h" +//#include "s3mfile.h" +//#include "itfile.h" +//#include "xmfile.h" +//#include "mp4file.h" + using namespace TagLib; -#ifdef _WIN32 - -typedef FileName FileNameHandle; - -#else - -struct FileNameHandle : public std::string +namespace { - FileNameHandle(FileName name) : std::string(name) {} - operator FileName () const { return c_str(); } -}; - +#ifdef _WIN32 + const TagLib::uint BufferSize = 8192; +#else + const TagLib::uint BufferSize = 1024; #endif +} class File::FilePrivate { public: - FilePrivate(FileName fileName); + FilePrivate(IOStream *stream, bool owner); - FILE *file; - - FileNameHandle name; - - bool readOnly; + IOStream *stream; + bool streamOwner; bool valid; - ulong size; - static const uint bufferSize = 1024; }; -File::FilePrivate::FilePrivate(FileName fileName) : - file(0), - name(fileName), - readOnly(true), - valid(true), - size(0) +File::FilePrivate::FilePrivate(IOStream *stream, bool owner) : + stream(stream), + streamOwner(owner), + valid(true) { - // First try with read / write mode, if that fails, fall back to read only. - -#ifdef _WIN32 - - if(wcslen((const wchar_t *) fileName) > 0) { - - file = _wfopen(name, L"rb+"); - - if(file) - readOnly = false; - else - file = _wfopen(name, L"rb"); - - if(file) - return; - - } - -#endif - - file = fopen(name, "rb+"); - - if(file) - readOnly = false; - else - file = fopen(name, "rb"); - - if(!file) - debug("Could not open file " + String((const char *) name)); } //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -File::File(FileName file) +File::File(FileName fileName) { - d = new FilePrivate(file); + IOStream *stream = new FileStream(fileName); + d = new FilePrivate(stream, true); +} + +File::File(IOStream *stream) +{ + d = new FilePrivate(stream, false); } File::~File() { - if(d->file) - fclose(d->file); + if(d->stream && d->streamOwner) + delete d->stream; delete d; } FileName File::name() const { - return d->name; + return d->stream->name(); +} + +PropertyMap File::properties() const +{ + // ugly workaround until this method is virtual + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); +// if(dynamic_cast(this)) +// return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + return tag()->properties(); +} + +void File::removeUnsupportedProperties(const StringList &properties) +{ + // here we only consider those formats that could possibly contain + // unsupported properties + if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); +// else if(dynamic_cast(this)) +// dynamic_cast(this)->removeUnsupportedProperties(properties); +// else if(dynamic_cast(this)) +// dynamic_cast(this)->removeUnsupportedProperties(properties); +// else if(dynamic_cast(this)) +// dynamic_cast(this)->removeUnsupportedProperties(properties); +// else if(dynamic_cast(this)) +// dynamic_cast(this)->removeUnsupportedProperties(properties); +// else if(dynamic_cast(this)) +// dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else + tag()->removeUnsupportedProperties(properties); +} + +PropertyMap File::setProperties(const PropertyMap &properties) +{ + if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); +// else if(dynamic_cast(this)) +// return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); + else + return tag()->setProperties(properties); } ByteVector File::readBlock(ulong length) { - if(!d->file) { - debug("File::readBlock() -- Invalid File"); - return ByteVector::null; - } - - if(length == 0) - return ByteVector::null; - - if(length > FilePrivate::bufferSize && - length > ulong(File::length())) - { - length = File::length(); - } - - ByteVector v(static_cast(length)); - const int count = fread(v.data(), sizeof(char), length, d->file); - v.resize(count); - return v; + return d->stream->readBlock(length); } void File::writeBlock(const ByteVector &data) { - if(!d->file) - return; - - if(d->readOnly) { - debug("File::writeBlock() -- attempted to write to a file that is not writable"); - return; - } - - fwrite(data.data(), sizeof(char), data.size(), d->file); + d->stream->writeBlock(data); } long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before) { - if(!d->file || pattern.size() > d->bufferSize) + if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. @@ -217,20 +286,20 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be // then check for "before". The order is important because it gives priority // to "real" matches. - for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) { + for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) { // (1) previous partial match - if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) { - const int patternOffset = (d->bufferSize - previousPartialMatch); + if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) { + const int patternOffset = (bufferSize() - previousPartialMatch); if(buffer.containsAt(pattern, 0, patternOffset)) { seek(originalPosition); - return bufferOffset - d->bufferSize + previousPartialMatch; + return bufferOffset - bufferSize() + previousPartialMatch; } } - if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) { - const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch); + if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(bufferSize()) > beforePreviousPartialMatch) { + const int beforeOffset = (bufferSize() - beforePreviousPartialMatch); if(buffer.containsAt(before, 0, beforeOffset)) { seek(originalPosition); return -1; @@ -257,7 +326,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be if(!before.isNull()) beforePreviousPartialMatch = buffer.endsWithPartialMatch(before); - bufferOffset += d->bufferSize; + bufferOffset += bufferSize(); } // Since we hit the end of the file, reset the status before continuing. @@ -272,7 +341,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before) { - if(!d->file || pattern.size() > d->bufferSize) + if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. @@ -296,17 +365,17 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b long bufferOffset; if(fromOffset == 0) { - seek(-1 * int(d->bufferSize), End); + seek(-1 * int(bufferSize()), End); bufferOffset = tell(); } else { - seek(fromOffset + -1 * int(d->bufferSize), Beginning); + seek(fromOffset + -1 * int(bufferSize()), Beginning); bufferOffset = tell(); } // See the notes in find() for an explanation of this algorithm. - for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) { + for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) { // TODO: (1) previous partial match @@ -325,7 +394,7 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b // TODO: (3) partial match - bufferOffset -= d->bufferSize; + bufferOffset -= bufferSize(); seek(bufferOffset); } @@ -340,147 +409,22 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b void File::insert(const ByteVector &data, ulong start, ulong replace) { - if(!d->file) - return; - - if(data.size() == replace) { - seek(start); - writeBlock(data); - return; - } - else if(data.size() < replace) { - seek(start); - writeBlock(data); - removeBlock(start + data.size(), replace - data.size()); - return; - } - - // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore - // and avoid TagLib's high level API for rendering just copying parts of - // the file that don't contain tag data. - // - // Now I'll explain the steps in this ugliness: - - // First, make sure that we're working with a buffer that is longer than - // the *differnce* in the tag sizes. We want to avoid overwriting parts - // that aren't yet in memory, so this is necessary. - - ulong bufferLength = bufferSize(); - - while(data.size() - replace > bufferLength) - bufferLength += bufferSize(); - - // Set where to start the reading and writing. - - long readPosition = start + replace; - long writePosition = start; - - ByteVector buffer; - ByteVector aboutToOverwrite(static_cast(bufferLength)); - - // This is basically a special case of the loop below. Here we're just - // doing the same steps as below, but since we aren't using the same buffer - // size -- instead we're using the tag size -- this has to be handled as a - // special case. We're also using File::writeBlock() just for the tag. - // That's a bit slower than using char *'s so, we're only doing it here. - - seek(readPosition); - int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file); - readPosition += bufferLength; - - seek(writePosition); - writeBlock(data); - writePosition += data.size(); - - buffer = aboutToOverwrite; - - // In case we've already reached the end of file... - - buffer.resize(bytesRead); - - // Ok, here's the main loop. We want to loop until the read fails, which - // means that we hit the end of the file. - - while(!buffer.isEmpty()) { - - // Seek to the current read position and read the data that we're about - // to overwrite. Appropriately increment the readPosition. - - seek(readPosition); - bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file); - aboutToOverwrite.resize(bytesRead); - readPosition += bufferLength; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if(ulong(bytesRead) < bufferLength) - clear(); - - // Seek to the write position and write our buffer. Increment the - // writePosition. - - seek(writePosition); - fwrite(buffer.data(), sizeof(char), buffer.size(), d->file); - writePosition += buffer.size(); - - // Make the current buffer the data that we read in the beginning. - - buffer = aboutToOverwrite; - - // Again, we need this for the last write. We don't want to write garbage - // at the end of our file, so we need to set the buffer size to the amount - // that we actually read. - - bufferLength = bytesRead; - } + d->stream->insert(data, start, replace); } void File::removeBlock(ulong start, ulong length) { - if(!d->file) - return; - - ulong bufferLength = bufferSize(); - - long readPosition = start + length; - long writePosition = start; - - ByteVector buffer(static_cast(bufferLength)); - - ulong bytesRead = 1; - - while(bytesRead != 0) { - seek(readPosition); - bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file); - readPosition += bytesRead; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if(bytesRead < bufferLength) - clear(); - - seek(writePosition); - fwrite(buffer.data(), sizeof(char), bytesRead, d->file); - writePosition += bytesRead; - } - truncate(writePosition); + d->stream->removeBlock(start, length); } bool File::readOnly() const { - return d->readOnly; -} - -bool File::isReadable(const char *file) -{ - return access(file, R_OK) == 0; + return d->stream->readOnly(); } bool File::isOpen() const { - return (d->file != NULL); + return d->stream->isOpen(); } bool File::isValid() const @@ -490,75 +434,70 @@ bool File::isValid() const void File::seek(long offset, Position p) { - if(!d->file) { - debug("File::seek() -- trying to seek in a file that isn't opened."); - return; - } + d->stream->seek(offset, IOStream::Position(p)); +} - switch(p) { - case Beginning: - fseek(d->file, offset, SEEK_SET); - break; - case Current: - fseek(d->file, offset, SEEK_CUR); - break; - case End: - fseek(d->file, offset, SEEK_END); - break; - } +void File::truncate(long length) +{ + d->stream->truncate(length); } void File::clear() { - clearerr(d->file); + d->stream->clear(); } long File::tell() const { - return ftell(d->file); + return d->stream->tell(); } long File::length() { - // Do some caching in case we do multiple calls. + return d->stream->length(); +} - if(d->size > 0) - return d->size; +bool File::isReadable(const char *file) +{ - if(!d->file) - return 0; +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later - long curpos = tell(); + return _access_s(file, R_OK) == 0; - seek(0, End); - long endpos = tell(); +#else - seek(curpos, Beginning); + return access(file, R_OK) == 0; + +#endif - d->size = endpos; - return endpos; } bool File::isWritable(const char *file) { + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + return _access_s(file, W_OK) == 0; + +#else + return access(file, W_OK) == 0; + +#endif + } //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// +TagLib::uint File::bufferSize() +{ + return BufferSize; +} + void File::setValid(bool valid) { d->valid = valid; } -void File::truncate(long length) -{ - ftruncate(fileno(d->file), length); -} - -TagLib::uint File::bufferSize() -{ - return FilePrivate::bufferSize; -} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tfile.h b/Plugins/PluginNowPlaying/taglib/toolkit/tfile.h index da322809..67f6f80f 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tfile.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tfile.h @@ -28,29 +28,16 @@ #include "taglib_export.h" #include "taglib.h" +#include "tag.h" #include "tbytevector.h" +#include "tiostream.h" namespace TagLib { class String; class Tag; class AudioProperties; - -#ifdef _WIN32 - class TAGLIB_EXPORT FileName - { - public: - FileName(const wchar_t *name) : m_wname(name) {} - FileName(const char *name) : m_name(name) {} - operator const wchar_t *() const { return m_wname.c_str(); } - operator const char *() const { return m_name.c_str(); } - private: - std::string m_name; - std::wstring m_wname; - }; -#else - typedef const char *FileName; -#endif + class PropertyMap; //! A file class with some useful methods for tag manipulation @@ -91,6 +78,44 @@ namespace TagLib { */ 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 returend 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 ID3v2 and an ID3v2 + * tag) only the most "modern" one will be exported (ID3v2 in this case). + * BIC: Will be made virtual in future releases. + */ + 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(). + * BIC: Will be mad virtual in future releases. + */ + 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 imported to the specific metadata format, + * the returned PropertyMap will contain those value(s). Otherwise it will be empty, + * indicating that no problems occured. + * 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. + * BIC: will become pure virtual in the future + */ + PropertyMap setProperties(const PropertyMap &properties); + /*! * Returns a pointer to this file's audio properties. This should be * reimplemented in the concrete subclasses. If no audio properties were @@ -188,7 +213,7 @@ namespace TagLib { bool isOpen() const; /*! - * Returns true if the file is open and readble. + * Returns true if the file is open and readable. */ bool isValid() const; @@ -240,6 +265,17 @@ namespace TagLib { */ File(FileName file); + /*! + * 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. * diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.cpp new file mode 100644 index 00000000..eae52573 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.cpp @@ -0,0 +1,492 @@ +/*************************************************************************** + 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" +#include "tstring.h" +#include "tdebug.h" + +#ifdef _WIN32 +# include +#else +# include +# include +#endif + +using namespace TagLib; + +namespace +{ +#ifdef _WIN32 + + // Uses Win32 native API instead of POSIX API to reduce the resource consumption. + + typedef FileName FileNameHandle; + typedef HANDLE FileHandle; + + const TagLib::uint BufferSize = 8192; + const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE; + + inline FileHandle openFile(const FileName &path, bool readOnly) + { + const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); + + if(!path.wstr().empty()) + return CreateFileW(path.wstr().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + else if(!path.str().empty()) + return CreateFileA(path.str().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + else + return InvalidFileHandle; + } + + inline void closeFile(FileHandle file) + { + CloseHandle(file); + } + + inline size_t readFile(FileHandle file, ByteVector &buffer) + { + DWORD length; + if(ReadFile(file, buffer.data(), static_cast(buffer.size()), &length, NULL)) + return static_cast(length); + else + return 0; + } + + inline size_t writeFile(FileHandle file, const ByteVector &buffer) + { + DWORD length; + if(WriteFile(file, buffer.data(), static_cast(buffer.size()), &length, NULL)) + return static_cast(length); + else + return 0; + } + +#else // _WIN32 + + struct FileNameHandle : public std::string + { + FileNameHandle(FileName name) : std::string(name) {} + operator FileName () const { return c_str(); } + }; + + typedef FILE* FileHandle; + + const TagLib::uint BufferSize = 8192; + const FileHandle InvalidFileHandle = 0; + + inline FileHandle openFile(const FileName &path, bool readOnly) + { + return fopen(path, readOnly ? "rb" : "rb+"); + } + + inline void closeFile(FileHandle file) + { + fclose(file); + } + + inline size_t readFile(FileHandle file, ByteVector &buffer) + { + return fread(buffer.data(), sizeof(char), buffer.size(), file); + } + + inline size_t writeFile(FileHandle file, const ByteVector &buffer) + { + return fwrite(buffer.data(), sizeof(char), buffer.size(), file); + } + +#endif // _WIN32 +} + +class FileStream::FileStreamPrivate +{ +public: + FileStreamPrivate(const FileName &fileName) + : file(InvalidFileHandle) + , name(fileName) + , readOnly(true) + { + } + + FileHandle file; + FileNameHandle name; + bool readOnly; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +FileStream::FileStream(FileName fileName, bool openReadOnly) + : d(new 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(d->name))); +# endif + } +} + +FileStream::~FileStream() +{ + if(isOpen()) + closeFile(d->file); + + delete d; +} + +FileName FileStream::name() const +{ + return d->name; +} + +ByteVector FileStream::readBlock(ulong length) +{ + if(!isOpen()) { + debug("File::readBlock() -- invalid file."); + return ByteVector::null; + } + + if(length == 0) + return ByteVector::null; + + const ulong streamLength = static_cast(FileStream::length()); + if(length > bufferSize() && length > streamLength) + length = streamLength; + + ByteVector buffer(static_cast(length)); + + const size_t count = readFile(d->file, buffer); + buffer.resize(static_cast(count)); + + return buffer; +} + +void FileStream::writeBlock(const ByteVector &data) +{ + if(!isOpen()) { + debug("File::writeBlock() -- invalid file."); + return; + } + + if(readOnly()) { + debug("File::writeBlock() -- read only file."); + return; + } + + writeFile(d->file, data); +} + +void FileStream::insert(const ByteVector &data, ulong start, ulong replace) +{ + if(!isOpen()) { + debug("File::insert() -- invalid file."); + return; + } + + if(readOnly()) { + debug("File::insert() -- read only file."); + return; + } + + if(data.size() == replace) { + seek(start); + writeBlock(data); + return; + } + else if(data.size() < replace) { + seek(start); + writeBlock(data); + removeBlock(start + data.size(), replace - data.size()); + return; + } + + // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore + // and avoid TagLib's high level API for rendering just copying parts of + // the file that don't contain tag data. + // + // Now I'll explain the steps in this ugliness: + + // First, make sure that we're working with a buffer that is longer than + // the *differnce* in the tag sizes. We want to avoid overwriting parts + // that aren't yet in memory, so this is necessary. + + ulong bufferLength = bufferSize(); + + while(data.size() - replace > bufferLength) + bufferLength += bufferSize(); + + // Set where to start the reading and writing. + + long readPosition = start + replace; + long writePosition = start; + + ByteVector buffer = data; + ByteVector aboutToOverwrite(static_cast(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 size_t bytesRead = 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(ulong start, ulong length) +{ + if(!isOpen()) { + debug("File::removeBlock() -- invalid file."); + return; + } + + ulong bufferLength = bufferSize(); + + long readPosition = start + length; + long writePosition = start; + + ByteVector buffer(static_cast(bufferLength)); + + for(size_t bytesRead = -1; bytesRead != 0;) + { + seek(readPosition); + bytesRead = 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(long offset, Position p) +{ + if(!isOpen()) { + debug("File::seek() -- invalid file."); + return; + } + +#ifdef _WIN32 + + DWORD whence; + switch(p) { + case Beginning: + whence = FILE_BEGIN; + break; + case Current: + whence = FILE_CURRENT; + break; + case End: + whence = FILE_END; + break; + default: + debug("FileStream::seek() -- Invalid Position value."); + return; + } + + SetFilePointer(d->file, offset, NULL, whence); + if(GetLastError() != NO_ERROR) { + debug("File::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 +} + +long FileStream::tell() const +{ +#ifdef _WIN32 + + const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT); + if(GetLastError() == NO_ERROR) { + return static_cast(position); + } + else { + debug("File::tell() -- Failed to get the file pointer."); + return 0; + } + +#else + + return ftell(d->file); + +#endif +} + +long FileStream::length() +{ + if(!isOpen()) { + debug("File::length() -- invalid file."); + return 0; + } + +#ifdef _WIN32 + + const DWORD fileSize = GetFileSize(d->file, NULL); + if(GetLastError() == NO_ERROR) { + return static_cast(fileSize); + } + else { + debug("File::length() -- Failed to get the file size."); + return 0; + } + +#else + + const long curpos = tell(); + + seek(0, End); + const long endpos = tell(); + + seek(curpos, Beginning); + + return endpos; + +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +void FileStream::truncate(long length) +{ +#ifdef _WIN32 + + const long currentPos = tell(); + + seek(length); + SetEndOfFile(d->file); + if(GetLastError() != NO_ERROR) { + debug("File::truncate() -- Failed to truncate the file."); + } + + seek(currentPos); + +#else + + const int error = ftruncate(fileno(d->file), length); + if(error != 0) { + debug("FileStream::truncate() -- Coundn't truncate the file."); + } + +#endif +} + +TagLib::uint FileStream::bufferSize() +{ + return BufferSize; +} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.h b/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.h new file mode 100644 index 00000000..fa113b73 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tfilestream.h @@ -0,0 +1,154 @@ +/*************************************************************************** + 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 "taglib_export.h" +#include "taglib.h" +#include "tbytevector.h" +#include "tiostream.h" + +namespace TagLib { + + class String; + class Tag; + class AudioProperties; + + //! 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 FileStream : public IOStream + { + public: + /*! + * Construct a File object and opens the \a file. \a file should be a + * be a C-string in the local file system encoding. + */ + FileStream(FileName file, bool openReadOnly = false); + + /*! + * Destroys this FileStream instance. + */ + virtual ~FileStream(); + + /*! + * Returns the file name in the local file system encoding. + */ + FileName name() const; + + /*! + * Reads a block of size \a length at the current get pointer. + */ + ByteVector readBlock(ulong length); + + /*! + * Attempts to write the block \a data at the current get pointer. If the + * file is currently only opened read only -- i.e. readOnly() returns true -- + * this attempts to reopen the file in read/write mode. + * + * \note This should be used instead of using the streaming output operator + * for a ByteVector. And even this function is significantly slower than + * doing output with a char[]. + */ + void writeBlock(const ByteVector &data); + + /*! + * Insert \a data at position \a start in the file overwriting \a replace + * bytes of the original content. + * + * \note This method is slow since it requires rewriting all of the file + * after the insertion point. + */ + void insert(const ByteVector &data, ulong start = 0, ulong replace = 0); + + /*! + * Removes a block of the file starting a \a start and continuing for + * \a length bytes. + * + * \note This method is slow since it involves rewriting all of the file + * after the removed portion. + */ + void removeBlock(ulong start = 0, ulong length = 0); + + /*! + * Returns true if the file is read only (or if the file can not be opened). + */ + bool readOnly() const; + + /*! + * Since the file can currently only be opened as an argument to the + * constructor (sort-of by design), this returns if that open succeeded. + */ + bool isOpen() const; + + /*! + * Move the I/O pointer to \a offset in the file from position \a p. This + * defaults to seeking from the beginning of the file. + * + * \see Position + */ + void seek(long offset, Position p = Beginning); + + /*! + * Reset the end-of-file and error flags on the file. + */ + void clear(); + + /*! + * Returns the current offset within the file. + */ + long tell() const; + + /*! + * Returns the length of the file. + */ + long length(); + + /*! + * Truncates the file to a \a length. + */ + void truncate(long length); + + protected: + + /*! + * Returns the buffer size that is used for internal buffering. + */ + static uint bufferSize(); + + private: + class FileStreamPrivate; + FileStreamPrivate *d; + }; + +} + +#endif diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.cpp new file mode 100644 index 00000000..c2ad2912 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + 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" + +using namespace TagLib; + +#ifdef _WIN32 + +# include "tstring.h" +# include "tdebug.h" +# include + +namespace +{ + // Check if the running system has CreateFileW() function. + // Windows9x systems don't have CreateFileW() or can't accept Unicode file names. + + bool supportsUnicode() + { + const FARPROC p = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW"); + return (p != NULL); + } + + // Indicates whether the system supports Unicode file names. + + const bool SystemSupportsUnicode = supportsUnicode(); + + // Converts a UTF-16 string into a local encoding. + // This function should only be used in Windows9x systems which don't support + // Unicode file names. + + std::string unicodeToAnsi(const wchar_t *wstr) + { + if(SystemSupportsUnicode) { + debug("unicodeToAnsi() - Should not be used on WinNT systems."); + } + + const int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); + if(len == 0) + return std::string(); + + std::string str(len, '\0'); + WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL); + + return str; + } +} + +// If WinNT, stores a Unicode string into m_wname directly. +// If Win9x, converts and stores it into m_name to avoid calling Unicode version functions. + +FileName::FileName(const wchar_t *name) + : m_name (SystemSupportsUnicode ? "" : unicodeToAnsi(name)) + , m_wname(SystemSupportsUnicode ? name : L"") +{ +} + +FileName::FileName(const char *name) + : m_name(name) +{ +} + +FileName::FileName(const FileName &name) + : m_name (name.m_name) + , m_wname(name.m_wname) +{ +} + +FileName::operator const wchar_t *() const +{ + return m_wname.c_str(); +} + +FileName::operator const char *() const +{ + return m_name.c_str(); +} + +const std::wstring &FileName::wstr() const +{ + return m_wname; +} + +const std::string &FileName::str() const +{ + return m_name; +} + +String FileName::toString() const +{ + if(!m_wname.empty()) { + return String(m_wname); + } + else if(!m_name.empty()) { + const int len = MultiByteToWideChar(CP_ACP, 0, m_name.c_str(), -1, NULL, 0); + if(len == 0) + return String::null; + + std::vector buf(len); + MultiByteToWideChar(CP_ACP, 0, m_name.c_str(), -1, &buf[0], len); + + return String(&buf[0]); + } + else { + return String::null; + } +} + + +#endif // _WIN32 + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +IOStream::IOStream() +{ +} + +IOStream::~IOStream() +{ +} + +void IOStream::clear() +{ +} + diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.h b/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.h new file mode 100644 index 00000000..86826964 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tiostream.h @@ -0,0 +1,169 @@ +/*************************************************************************** + 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 "taglib_export.h" +#include "taglib.h" +#include "tbytevector.h" + +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; + operator const char *() const; + + const std::wstring &wstr() const; + const std::string &str() const; + + String toString() const; + + private: + const std::string m_name; + const std::wstring m_wname; + }; +#else + typedef const char *FileName; +#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(); + + /*! + * 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(ulong 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 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, ulong start = 0, ulong 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(ulong start = 0, ulong length = 0) = 0; + + /*! + * Returns 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(long 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 long tell() const = 0; + + /*! + * Returns the length of the stream. + */ + virtual long length() = 0; + + /*! + * Truncates the stream to a \a length. + */ + virtual void truncate(long length) = 0; + + private: + IOStream(const IOStream &); + IOStream &operator=(const IOStream &); + }; + +} + +#endif diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tlist.h b/Plugins/PluginNowPlaying/taglib/toolkit/tlist.h index dce0e1c6..0099dad5 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tlist.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tlist.h @@ -227,6 +227,11 @@ namespace TagLib { */ bool operator==(const List &l) const; + /*! + * Compares this list with \a l and returns true if the lists differ. + */ + bool operator!=(const List &l) const; + protected: /* * If this List is being shared via implicit sharing, do a deep copy of the diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tlist.tcc b/Plugins/PluginNowPlaying/taglib/toolkit/tlist.tcc index a11887d8..72d4767b 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tlist.tcc +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tlist.tcc @@ -24,6 +24,7 @@ ***************************************************************************/ #include +#include "trefcounter.h" namespace TagLib { @@ -38,7 +39,8 @@ namespace TagLib { // A base for the generic and specialized private class types. New // non-templatized members should be added here. -class ListPrivateBase : public RefCounter +// BIC change to RefCounter +class ListPrivateBase : public RefCounterOld { public: ListPrivateBase() : autoDelete(false) {} @@ -300,6 +302,12 @@ bool List::operator==(const List &l) const return d->list == l.d->list; } +template +bool List::operator!=(const List &l) const +{ + return d->list != l.d->list; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tmap.h b/Plugins/PluginNowPlaying/taglib/toolkit/tmap.h index f2f8364c..a7d99303 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tmap.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tmap.h @@ -27,7 +27,6 @@ #define TAGLIB_MAP_H #include -using namespace std; #include "taglib.h" diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tmap.tcc b/Plugins/PluginNowPlaying/taglib/toolkit/tmap.tcc index 0f2b9933..5d3abcad 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tmap.tcc +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tmap.tcc @@ -23,23 +23,26 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include "trefcounter.h" + namespace TagLib { //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// +// BIC change to RefCounter template template -class Map::MapPrivate : public RefCounter +class Map::MapPrivate : public RefCounterOld { public: - MapPrivate() : RefCounter() {} + MapPrivate() : RefCounterOld() {} #ifdef WANT_CLASS_INSTANTIATION_OF_MAP - MapPrivate(const std::map& m) : RefCounter(), map(m) {} + MapPrivate(const std::map& m) : RefCounterOld(), map(m) {} std::map map; #else - MapPrivate(const std::map& m) : RefCounter(), map(m) {} + MapPrivate(const std::map& m) : RefCounterOld(), map(m) {} std::map map; #endif }; diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.cpp new file mode 100644 index 00000000..3317cc92 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + 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 * + ***************************************************************************/ + +#include "tpropertymap.h" +using namespace TagLib; + + +PropertyMap::PropertyMap() : SimplePropertyMap() +{ +} + +PropertyMap::PropertyMap(const PropertyMap &m) : SimplePropertyMap(m), unsupported(m.unsupported) +{ +} + +PropertyMap::PropertyMap(const SimplePropertyMap &m) +{ + for(SimplePropertyMap::ConstIterator it = m.begin(); it != m.end(); ++it){ + String key = it->first.upper(); + if(!key.isNull()) + insert(it->first, it->second); + else + unsupported.append(it->first); + } +} + +PropertyMap::~PropertyMap() +{ +} + +bool PropertyMap::insert(const String &key, const StringList &values) +{ + String realKey = key.upper(); + Iterator 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 +{ + for(ConstIterator it = other.begin(); it != other.end(); ++it) { + if(!SimplePropertyMap::contains(it->first)) + return false; + if ((*this)[it->first] != it->second) + return false; + } + return true; +} + +PropertyMap &PropertyMap::erase(const String &key) +{ + SimplePropertyMap::erase(key.upper()); + return *this; +} + +PropertyMap &PropertyMap::erase(const PropertyMap &other) +{ + for(ConstIterator it = other.begin(); it != other.end(); ++it) + erase(it->first); + return *this; +} + +PropertyMap &PropertyMap::merge(const PropertyMap &other) +{ + for(PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it) + insert(it->first, it->second); + unsupported.append(other.unsupported); + return *this; +} + +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(ConstIterator it = other.begin(); it != other.end(); ++it) { + ConstIterator thisFind = find(it->first); + if( thisFind == end() || (thisFind->second != it->second) ) + return false; + } + for(ConstIterator it = begin(); it != end(); ++it) { + ConstIterator otherFind = other.find(it->first); + if( otherFind == other.end() || (otherFind->second != it->second) ) + return false; + } + return unsupported == other.unsupported; +} + +bool PropertyMap::operator!=(const PropertyMap &other) const +{ + return !(*this == other); +} + +String PropertyMap::toString() const +{ + String ret = ""; + for(ConstIterator it = begin(); it != end(); ++it) + ret += it->first+"="+it->second.toString(", ") + "\n"; + if(!unsupported.isEmpty()) + ret += "Unsupported Data: " + unsupported.toString(", ") + "\n"; + return ret; +} + +void PropertyMap::removeEmpty() +{ + StringList emptyKeys; + for(Iterator it = begin(); it != end(); ++it) + if(it->second.isEmpty()) + emptyKeys.append(it->first); + for(StringList::Iterator emptyIt = emptyKeys.begin(); emptyIt != emptyKeys.end(); emptyIt++ ) + erase(*emptyIt); +} + +StringList &PropertyMap::unsupportedData() +{ + return unsupported; +} + +const StringList &PropertyMap::unsupportedData() const +{ + return unsupported; +} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.h b/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.h new file mode 100644 index 00000000..2be49ddb --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tpropertymap.h @@ -0,0 +1,233 @@ +/*************************************************************************** + 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 * + ***************************************************************************/ + +#ifndef PROPERTYMAP_H_ +#define PROPERTYMAP_H_ + +#include "tmap.h" +#include "tstringlist.h" + +namespace TagLib { + + typedef Map SimplePropertyMap; + + //! A map for format-independent 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 an arbitrary + * unicode String. + * + * 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 + * + * Credits: + * + * - COMPOSER + * - LYRICIST + * - CONDUCTOR + * - REMIXER + * - PERFORMER: + * + * Other tags: + * + * - ISRC + * - ASIN + * - BPM + * - COPYRIGHT + * - ENCODEDBY + * - MOOD + * - COMMENT + * - MEDIA + * - LABEL + * - CATALOGNUMBER + * - BARCODE + * + * MusicBrainz identifiers: + * + * - MUSICBRAINZ_TRACKID + * - MUSICBRAINZ_ALBUMID + * - MUSICBRAINZ_RELEASEGROUPID + * - MUSICBRAINZ_WORKID + * - MUSICBRAINZ_ARTISTID + * - MUSICBRAINZ_ALBUMARTISTID + * - ACOUSTID_ID + * - ACOUSTID_FINGERPRINT + * - MUSICIP_PUID + * + */ + + class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap + { + public: + + typedef SimplePropertyMap::Iterator Iterator; + typedef SimplePropertyMap::ConstIterator ConstIterator; + + PropertyMap(); + + PropertyMap(const PropertyMap &m); + + /*! + * 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. + */ + PropertyMap(const SimplePropertyMap &m); + + virtual ~PropertyMap(); + + /*! + * Inserts \a values under \a key in the map. If \a key already exists, + * then \values will be appended to the existing StringList. + * The returned value indicates success, i.e. whether \a key is a + * valid key. + */ + 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. + */ + bool replace(const String &key, const StringList &values); + + /*! + * Find the first occurrence of \a key. + */ + Iterator find(const String &key); + + /*! + * Find the first occurrence of \a key. + */ + ConstIterator find(const String &key) const; + + /*! + * Returns true if the map contains values for \a key. + */ + bool contains(const String &key) const; + + /*! + * Returns true if this map contains all keys of \a other + * and the values coincide for that keys. Does not take + * the unsupportedData list into account. + */ + bool contains(const PropertyMap &other) const; + + /*! + * Erase the \a key and its values from the map. + */ + PropertyMap &erase(const String &key); + + /*! + * Erases from this map all keys that appear in \a other. + */ + 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. + */ + PropertyMap &merge(const PropertyMap &other); + + /*! + * 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. + */ + 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. + */ + StringList &operator[](const String &key); + + /*! + * Returns true if and only if \other has the same contents as this map. + */ + bool operator==(const PropertyMap &other) const; + + /*! + * Returns false if and only \other has the same contents as this map. + */ + bool operator!=(const PropertyMap &other) const; + + /*! + * If a PropertyMap is read from a File object using File::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. + * You can remove items from the returned list, which tells TagLib to remove + * those unsupported elements if you call File::setProperties() with the + * same PropertyMap as argument. + */ + StringList &unsupportedData(); + const StringList &unsupportedData() const; + + /*! + * Removes all entries which have an empty value list. + */ + void removeEmpty(); + + String toString() const; + + private: + + + StringList unsupported; + }; + +} +#endif /* PROPERTYMAP_H_ */ diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.cpp new file mode 100644 index 00000000..71f3c2f2 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + 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/ * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "trefcounter.h" + +#if defined(HAVE_STD_ATOMIC) +# include +# define ATOMIC_INT std::atomic +# define ATOMIC_INC(x) x.fetch_add(1) +# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1) +#elif defined(HAVE_BOOST_ATOMIC) +# include +# define ATOMIC_INT boost::atomic +# define ATOMIC_INC(x) x.fetch_add(1) +# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1) +#elif defined(HAVE_GCC_ATOMIC) +# define ATOMIC_INT int +# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1) +# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1) +#elif defined(HAVE_WIN_ATOMIC) +# if !defined(NOMINMAX) +# define NOMINMAX +# endif +# include +# define ATOMIC_INT long +# define ATOMIC_INC(x) InterlockedIncrement(&x) +# define ATOMIC_DEC(x) InterlockedDecrement(&x) +#elif defined(HAVE_MAC_ATOMIC) +# include +# define ATOMIC_INT int32_t +# define ATOMIC_INC(x) OSAtomicIncrement32Barrier(&x) +# define ATOMIC_DEC(x) OSAtomicDecrement32Barrier(&x) +#elif defined(HAVE_IA64_ATOMIC) +# include +# define ATOMIC_INT int +# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1) +# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1) +#else +# define ATOMIC_INT int +# define ATOMIC_INC(x) (++x) +# define ATOMIC_DEC(x) (--x) +#endif + +namespace TagLib +{ + class RefCounter::RefCounterPrivate + { + public: + RefCounterPrivate() : refCount(1) {} + + void ref() { ATOMIC_INC(refCount); } + bool deref() { return (ATOMIC_DEC(refCount) == 0); } + int count() const { return refCount; } + + volatile ATOMIC_INT refCount; + }; + + RefCounter::RefCounter() + : d(new RefCounterPrivate()) + { + } + + RefCounter::~RefCounter() + { + delete d; + } + + void RefCounter::ref() + { + d->ref(); + } + + bool RefCounter::deref() + { + return d->deref(); + } + + int RefCounter::count() const + { + return d->count(); + } +} diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.h b/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.h new file mode 100644 index 00000000..75388b10 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/trefcounter.h @@ -0,0 +1,111 @@ +/*************************************************************************** + 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_REFCOUNTER_H +#define TAGLIB_REFCOUNTER_H + +#include "taglib_export.h" +#include "taglib.h" + +#ifdef __APPLE__ +# include +# define TAGLIB_ATOMIC_MAC +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define NOMINMAX +# include +# define TAGLIB_ATOMIC_WIN +#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \ + && (defined(__i386__) || defined(__i486__) || defined(__i586__) || \ + defined(__i686__) || defined(__x86_64) || defined(__ia64)) \ + && !defined(__INTEL_COMPILER) +# define TAGLIB_ATOMIC_GCC +#elif defined(__ia64) && defined(__INTEL_COMPILER) +# include +# define TAGLIB_ATOMIC_GCC +#endif + +#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class. +/*! + * \internal + * This is just used as a base class for shared classes in TagLib. + * + * \warning This is not part of the TagLib public API! + */ +namespace TagLib +{ + + class TAGLIB_EXPORT RefCounter + { + public: + RefCounter(); + virtual ~RefCounter(); + + void ref(); + bool deref(); + int count() const; + + private: + class RefCounterPrivate; + RefCounterPrivate *d; + }; + + // BIC this old class is needed by tlist.tcc and tmap.tcc + class RefCounterOld + { + public: + RefCounterOld() : refCount(1) {} + +#ifdef TAGLIB_ATOMIC_MAC + void ref() { OSAtomicIncrement32Barrier(const_cast(&refCount)); } + bool deref() { return ! OSAtomicDecrement32Barrier(const_cast(&refCount)); } + int32_t count() { return refCount; } + private: + volatile int32_t refCount; +#elif defined(TAGLIB_ATOMIC_WIN) + void ref() { InterlockedIncrement(&refCount); } + bool deref() { return ! InterlockedDecrement(&refCount); } + long count() { return refCount; } + private: + volatile long refCount; +#elif defined(TAGLIB_ATOMIC_GCC) + void ref() { __sync_add_and_fetch(&refCount, 1); } + bool deref() { return ! __sync_sub_and_fetch(&refCount, 1); } + int count() { return refCount; } + private: + volatile int refCount; +#else + void ref() { refCount++; } + bool deref() { return ! --refCount; } + int count() { return refCount; } + private: + uint refCount; +#endif + }; + +} + +#endif // DO_NOT_DOCUMENT +#endif + diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tstring.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/tstring.cpp index 484991ea..75a9833e 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tstring.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tstring.cpp @@ -23,175 +23,254 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +// This class assumes that std::basic_string has a contiguous and null-terminated buffer. + +#ifdef HAVE_CONFIG_H +#include +#endif + #include "tstring.h" -#include "unicode.h" #include "tdebug.h" +#include "tstringlist.h" +#include "trefcounter.h" +#include "tutils.h" #include +#include +#include -#include +#ifdef HAVE_STD_CODECVT +# include +#else +# include "unicode.h" +#endif -namespace TagLib { - - inline unsigned short byteSwap(unsigned short x) - { - return (((x) >> 8) & 0xff) | (((x) & 0xff) << 8); - } +namespace +{ inline unsigned short combine(unsigned char c1, unsigned char c2) { return (c1 << 8) | c2; } + + void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) + { +#ifdef HAVE_STD_CODECVT + + typedef std::codecvt_utf8_utf16 utf8_utf16_t; + + using namespace TagLib; + + const wchar_t *srcBegin = src; + const wchar_t *srcEnd = srcBegin + srcLength; + + char *dstBegin = dst; + char *dstEnd = dstBegin + dstLength; + + std::mbstate_t st; + const wchar_t *source; + char *target; + memset(&st, 0, sizeof(st)); + std::codecvt_base::result result = utf8_utf16_t().out( + st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); + + if(result != utf8_utf16_t::ok) { + debug("String::copyFromUTF8() - Unicode conversion error."); + } + +#else + + using namespace Unicode; + using namespace TagLib; + + const Unicode::UTF16 *srcBegin = src; + const Unicode::UTF16 *srcEnd = srcBegin + srcLength; + + Unicode::UTF8 *dstBegin = reinterpret_cast(dst); + Unicode::UTF8 *dstEnd = dstBegin + dstLength; + + Unicode::ConversionResult result = Unicode::ConvertUTF16toUTF8( + &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + + if(result != Unicode::conversionOK) { + debug("String::to8Bit() - Unicode conversion error."); + } + +#endif + } + + void UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) + { +#ifdef HAVE_STD_CODECVT + + typedef std::codecvt_utf8_utf16 utf8_utf16_t; + + using namespace TagLib; + + const char *srcBegin = src; + const char *srcEnd = srcBegin + srcLength; + + wchar_t *dstBegin = dst; + wchar_t *dstEnd = dstBegin + dstLength; + + std::mbstate_t st; + const char *source; + wchar_t *target; + memset(&st, 0, sizeof(st)); + std::codecvt_base::result result = utf8_utf16_t().in( + st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); + + if(result != utf8_utf16_t::ok) { + debug("String::copyFromUTF8() - Unicode conversion error."); + } + +#else + + using namespace Unicode; + using namespace TagLib; + + const Unicode::UTF8 *srcBegin = reinterpret_cast(src); + const Unicode::UTF8 *srcEnd = srcBegin + srcLength; + + Unicode::UTF16 *dstBegin = dst; + Unicode::UTF16 *dstEnd = dstBegin + dstLength; + + Unicode::ConversionResult result = Unicode::ConvertUTF8toUTF16( + &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + + if(result != Unicode::conversionOK) { + debug("String::copyFromUTF8() - Unicode conversion error."); + } + +#endif + } } -using namespace TagLib; +namespace TagLib { class String::StringPrivate : public RefCounter { public: - StringPrivate(const wstring &s) : - RefCounter(), - data(s), - CString(0) {} - - StringPrivate() : - RefCounter(), - CString(0) {} - - ~StringPrivate() { - delete [] CString; + StringPrivate() + : RefCounter() + { } - wstring data; + StringPrivate(const wstring &s) + : RefCounter() + , data(s) + { + } + + StringPrivate(uint n, wchar_t c) + : RefCounter() + , data(static_cast(n), c) + { + } /*! - * This is only used to hold the a pointer to the most recent value of - * toCString. + * Stores string in UTF-16. The byte order depends on the CPU endian. */ - char *CString; + TagLib::wstring data; + + /*! + * This is only used to hold the the most recent value of toCString(). + */ + std::string cstring; }; String String::null; //////////////////////////////////////////////////////////////////////////////// -String::String() +String::String() + : d(new StringPrivate()) { - d = new StringPrivate; } -String::String(const String &s) : d(s.d) +String::String(const String &s) + : d(s.d) { d->ref(); } String::String(const std::string &s, Type t) + : d(new StringPrivate()) { - d = new StringPrivate; - - if(t == UTF16 || t == UTF16BE || t == UTF16LE) { + if(t == Latin1) + copyFromLatin1(&s[0], s.length()); + else if(t == String::UTF8) + copyFromUTF8(&s[0], s.length()); + else { debug("String::String() -- A std::string should not contain UTF16."); - return; } - - int length = s.length(); - d->data.resize(length); - wstring::iterator targetIt = d->data.begin(); - - for(std::string::const_iterator it = s.begin(); it != s.end(); it++) { - *targetIt = uchar(*it); - ++targetIt; - } - - prepare(t); } String::String(const wstring &s, Type t) + : d(new StringPrivate()) { - d = new StringPrivate(s); - prepare(t); + if(t == UTF16 || t == UTF16BE || t == UTF16LE) + copyFromUTF16(s.c_str(), s.length(), t); + else { + debug("String::String() -- A TagLib::wstring should not contain Latin1 or UTF-8."); + } } String::String(const wchar_t *s, Type t) + : d(new StringPrivate()) { - d = new StringPrivate(s); - prepare(t); + if(t == UTF16 || t == UTF16BE || t == UTF16LE) + copyFromUTF16(s, ::wcslen(s), t); + else { + debug("String::String() -- A const wchar_t * should not contain Latin1 or UTF-8."); + } } String::String(const char *s, Type t) + : d(new StringPrivate()) { - d = new StringPrivate; - - if(t == UTF16 || t == UTF16BE || t == UTF16LE) { + if(t == Latin1) + copyFromLatin1(s, ::strlen(s)); + else if(t == String::UTF8) + copyFromUTF8(s, ::strlen(s)); + else { debug("String::String() -- A const char * should not contain UTF16."); - return; } - - int length = ::strlen(s); - d->data.resize(length); - - wstring::iterator targetIt = d->data.begin(); - - for(int i = 0; i < length; i++) { - *targetIt = uchar(s[i]); - ++targetIt; - } - - prepare(t); } String::String(wchar_t c, Type t) + : d(new StringPrivate()) { - d = new StringPrivate; - d->data += c; - prepare(t); + if(t == UTF16 || t == UTF16BE || t == UTF16LE) + copyFromUTF16(&c, 1, t); + else { + debug("String::String() -- A const wchar_t should not contain Latin1 or UTF-8."); + } } String::String(char c, Type t) + : d(new StringPrivate(1, static_cast(c))) { - d = new StringPrivate; - - if(t == UTF16 || t == UTF16BE || t == UTF16LE) { - debug("String::String() -- A std::string should not contain UTF16."); - return; + if(t != Latin1 && t != UTF8) { + debug("String::String() -- A char should not contain UTF16."); } - - d->data += uchar(c); - prepare(t); } String::String(const ByteVector &v, Type t) + : d(new StringPrivate()) { - d = new StringPrivate; - if(v.isEmpty()) return; - if(t == Latin1 || t == UTF8) { + if(t == Latin1) + copyFromLatin1(v.data(), v.size()); + else if(t == UTF8) + copyFromUTF8(v.data(), v.size()); + else + copyFromUTF16(v.data(), v.size(), t); - int length = 0; - d->data.resize(v.size()); - wstring::iterator targetIt = d->data.begin(); - for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) { - *targetIt = uchar(*it); - ++targetIt; - ++length; - } - d->data.resize(length); - } - else { - d->data.resize(v.size() / 2); - wstring::iterator targetIt = d->data.begin(); - - for(ByteVector::ConstIterator it = v.begin(); - it != v.end() && it + 1 != v.end() && combine(*it, *(it + 1)); - it += 2) - { - *targetIt = combine(*it, *(it + 1)); - ++targetIt; - } - } - prepare(t); + // If we hit a null in the ByteVector, shrink the string again. + d->data.resize(::wcslen(d->data.c_str())); } //////////////////////////////////////////////////////////////////////////////// @@ -205,44 +284,22 @@ String::~String() std::string String::to8Bit(bool unicode) const { std::string s; - s.resize(d->data.size()); if(!unicode) { + s.resize(d->data.size()); + std::string::iterator targetIt = s.begin(); for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { - *targetIt = char(*it); + *targetIt = static_cast(*it); ++targetIt; } - return s; } + else { + s.resize(d->data.size() * 4 + 1); - const int outputBufferSize = d->data.size() * 3 + 1; - - Unicode::UTF16 *sourceBuffer = new Unicode::UTF16[d->data.size() + 1]; - Unicode::UTF8 *targetBuffer = new Unicode::UTF8[outputBufferSize]; - - for(unsigned int i = 0; i < d->data.size(); i++) - sourceBuffer[i] = Unicode::UTF16(d->data[i]); - - const Unicode::UTF16 *source = sourceBuffer; - Unicode::UTF8 *target = targetBuffer; - - Unicode::ConversionResult result = - Unicode::ConvertUTF16toUTF8(&source, sourceBuffer + d->data.size(), - &target, targetBuffer + outputBufferSize, - Unicode::lenientConversion); - - if(result != Unicode::conversionOK) - debug("String::to8Bit() - Unicode conversion error."); - - int newSize = target - targetBuffer; - s.resize(newSize); - targetBuffer[newSize] = 0; - - s = (char *) targetBuffer; - - delete [] sourceBuffer; - delete [] targetBuffer; + UTF16toUTF8(&d->data[0], d->data.size(), &s[0], s.size()); + s.resize(::strlen(s.c_str())); + } return s; } @@ -254,13 +311,13 @@ TagLib::wstring String::toWString() const const char *String::toCString(bool unicode) const { - delete [] d->CString; + d->cstring = to8Bit(unicode); + return d->cstring.c_str(); +} - std::string buffer = to8Bit(unicode); - d->CString = new char[buffer.size() + 1]; - strcpy(d->CString, buffer.c_str()); - - return d->CString; +const wchar_t *String::toCWString() const +{ + return d->data.c_str(); } String::Iterator String::begin() @@ -285,23 +342,32 @@ String::ConstIterator String::end() const int String::find(const String &s, int offset) const { - wstring::size_type position = d->data.find(s.d->data, offset); - - if(position != wstring::npos) - return position; - else - return -1; + return d->data.find(s.d->data, offset); } int String::rfind(const String &s, int offset) const { - wstring::size_type position = - d->data.rfind(s.d->data, offset == -1 ? wstring::npos : offset); + return d->data.rfind(s.d->data, offset); +} - if(position != wstring::npos) - return position; - else - return -1; +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; + } + else + { + list.append(substr(index, sep - index)); + index = sep + separator.size(); + } + } + return list; } bool String::startsWith(const String &s) const @@ -314,12 +380,7 @@ bool String::startsWith(const String &s) const String String::substr(uint position, uint n) const { - if(n > position + d->data.size()) - n = d->data.size() - position; - - String s; - s.d->data = d->data.substr(position, n); - return s; + return String(d->data.substr(position, n)); } String &String::append(const String &s) @@ -367,67 +428,75 @@ bool String::isNull() const ByteVector String::data(Type t) const { - ByteVector v; - - switch(t) { - + switch(t) + { case Latin1: - { - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) - v.append(char(*it)); - break; - } + { + ByteVector v(size(), 0); + char *p = v.data(); + + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) + *p++ = static_cast(*it); + + return v; + } case UTF8: - { - std::string s = to8Bit(true); - v.setData(s.c_str(), s.length()); - break; - } + { + ByteVector v(size() * 4 + 1, 0); + + UTF16toUTF8(&d->data[0], d->data.size(), v.data(), v.size()); + v.resize(::strlen(v.data())); + + return v; + } case UTF16: - { - // Assume that if we're doing UTF16 and not UTF16BE that we want little - // endian encoding. (Byte Order Mark) + { + ByteVector v(2 + size() * 2, 0); + char *p = v.data(); - v.append(char(0xff)); - v.append(char(0xfe)); + // Assume that if we're doing UTF16 and not UTF16BE that we want little + // endian encoding. (Byte Order Mark) - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + *p++ = '\xff'; + *p++ = '\xfe'; - char c1 = *it & 0xff; - char c2 = *it >> 8; + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + *p++ = static_cast(*it & 0xff); + *p++ = static_cast(*it >> 8); + } - v.append(c1); - v.append(c2); + return v; } - break; - } case UTF16BE: - { - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + { + ByteVector v(size() * 2, 0); + char *p = v.data(); - char c1 = *it >> 8; - char c2 = *it & 0xff; + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + *p++ = static_cast(*it >> 8); + *p++ = static_cast(*it & 0xff); + } - v.append(c1); - v.append(c2); + return v; } - break; - } case UTF16LE: - { - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + { + ByteVector v(size() * 2, 0); + char *p = v.data(); - char c1 = *it & 0xff; - char c2 = *it >> 8; + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + *p++ = static_cast(*it & 0xff); + *p++ = static_cast(*it >> 8); + } - v.append(c1); - v.append(c2); + return v; + } + default: + { + debug("String::data() - Invalid Type value."); + return ByteVector(); } - break; } - } - - return v; } int String::toInt() const @@ -502,37 +571,35 @@ bool String::isAscii() const String String::number(int n) // static { - if(n == 0) - return String("0"); + static const size_t BufferSize = 11; // Sufficient to store "-214748364". + static const char *Format = "%d"; - String charStack; + char buffer[BufferSize]; + int length; - bool negative = n < 0; +#if defined(HAVE_SNPRINTF) - if(negative) - n = n * -1; + length = snprintf(buffer, BufferSize, Format, n); - while(n > 0) { - int remainder = n % 10; - charStack += char(remainder + '0'); - n = (n - remainder) / 10; - } +#elif defined(HAVE_SPRINTF_S) - String s; + length = sprintf_s(buffer, Format, n); - if(negative) - s += '-'; +#else - for(int i = charStack.d->data.size() - 1; i >= 0; i--) - s += charStack.d->data[i]; + length = sprintf(buffer, Format, n); - return s; +#endif + + if(length > 0) + return String(buffer); + else + return String::null; } TagLib::wchar &String::operator[](int i) { detach(); - return d->data[i]; } @@ -546,6 +613,11 @@ bool String::operator==(const String &s) const return d == s.d || d->data == s.d->data; } +bool String::operator!=(const String &s) const +{ + return !operator==(s); +} + String &String::operator+=(const String &s) { detach(); @@ -605,14 +677,7 @@ String &String::operator=(const std::string &s) delete d; d = new StringPrivate; - - d->data.resize(s.size()); - - wstring::iterator targetIt = d->data.begin(); - for(std::string::const_iterator it = s.begin(); it != s.end(); it++) { - *targetIt = uchar(*it); - ++targetIt; - } + copyFromLatin1(s.c_str(), s.length()); return *this; } @@ -629,6 +694,7 @@ String &String::operator=(const wchar_t *s) { if(d->deref()) delete d; + d = new StringPrivate(s); return *this; } @@ -637,8 +703,8 @@ String &String::operator=(char c) { if(d->deref()) delete d; - d = new StringPrivate; - d->data += uchar(c); + + d = new StringPrivate(1, static_cast(c)); return *this; } @@ -646,8 +712,8 @@ String &String::operator=(wchar_t c) { if(d->deref()) delete d; - d = new StringPrivate; - d->data += c; + + d = new StringPrivate(1, c); return *this; } @@ -657,15 +723,7 @@ String &String::operator=(const char *s) delete d; d = new StringPrivate; - - int length = ::strlen(s); - d->data.resize(length); - - wstring::iterator targetIt = d->data.begin(); - for(int i = 0; i < length; i++) { - *targetIt = uchar(s[i]); - ++targetIt; - } + copyFromLatin1(s, ::strlen(s)); return *this; } @@ -676,20 +734,10 @@ String &String::operator=(const ByteVector &v) delete d; d = new StringPrivate; - d->data.resize(v.size()); - wstring::iterator targetIt = d->data.begin(); - - uint i = 0; - - for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) { - *targetIt = uchar(*it); - ++targetIt; - ++i; - } + copyFromLatin1(v.data(), v.size()); // If we hit a null in the ByteVector, shrink the string again. - - d->data.resize(i); + d->data.resize(::wcslen(d->data.c_str())); return *this; } @@ -715,97 +763,118 @@ void String::detach() // private members //////////////////////////////////////////////////////////////////////////////// -void String::prepare(Type t) +void String::copyFromLatin1(const char *s, size_t length) { - switch(t) { - case UTF16: - { - if(d->data.size() >= 1 && (d->data[0] == 0xfeff || d->data[0] == 0xfffe)) { - bool swap = d->data[0] != 0xfeff; - d->data.erase(d->data.begin(), d->data.begin() + 1); - if(swap) { - for(uint i = 0; i < d->data.size(); i++) - d->data[i] = byteSwap((unsigned short)d->data[i]); - } - } + d->data.resize(length); + + for(size_t i = 0; i < length; ++i) + d->data[i] = static_cast(s[i]); +} + +void String::copyFromUTF8(const char *s, size_t length) +{ + d->data.resize(length); + + UTF8toUTF16(s, length, &d->data[0], d->data.size()); + d->data.resize(::wcslen(d->data.c_str())); +} + +void String::copyFromUTF16(const wchar_t *s, size_t length, Type t) +{ + bool swap; + if(t == UTF16) { + if(length >= 1 && s[0] == 0xfeff) + swap = false; // Same as CPU endian. No need to swap bytes. + else if(length >= 1 && s[0] == 0xfffe) + swap = true; // Not same as CPU endian. Need to swap bytes. else { - debug("String::prepare() - Invalid UTF16 string."); - d->data.erase(d->data.begin(), d->data.end()); + debug("String::copyFromUTF16() - Invalid UTF16 string."); + return; } - break; + + s++; + length--; } - case UTF8: - { - int bufferSize = d->data.size() + 1; - Unicode::UTF8 *sourceBuffer = new Unicode::UTF8[bufferSize]; - Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize]; + else + swap = (t != WCharByteOrder); - unsigned int i = 0; - for(; i < d->data.size(); i++) - sourceBuffer[i] = Unicode::UTF8(d->data[i]); - sourceBuffer[i] = 0; + d->data.resize(length); + memcpy(&d->data[0], s, length * sizeof(wchar_t)); - const Unicode::UTF8 *source = sourceBuffer; - Unicode::UTF16 *target = targetBuffer; - - Unicode::ConversionResult result = - Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize, - &target, targetBuffer + bufferSize, - Unicode::lenientConversion); - - if(result != Unicode::conversionOK) - debug("String::prepare() - Unicode conversion error."); - - - int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0; - d->data.resize(newSize); - - for(int i = 0; i < newSize; i++) - d->data[i] = targetBuffer[i]; - - delete [] sourceBuffer; - delete [] targetBuffer; - - break; + if(swap) { + for(size_t i = 0; i < length; ++i) + d->data[i] = Utils::byteSwap(static_cast(s[i])); } - case UTF16LE: - { - for(uint i = 0; i < d->data.size(); i++) - d->data[i] = byteSwap((unsigned short)d->data[i]); - break; +} + +void String::copyFromUTF16(const char *s, size_t length, Type t) +{ + bool swap; + if(t == UTF16) { + if(length < 2) { + debug("String::copyFromUTF16() - Invalid UTF16 string."); + return; + } + + // Uses memcpy instead of reinterpret_cast to avoid an alignment exception. + ushort bom; + ::memcpy(&bom, s, 2); + + if(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."); + return; + } + + s += 2; + length -= 2; } - default: - break; + else + swap = (t != WCharByteOrder); + + d->data.resize(length / 2); + for(size_t i = 0; i < length / 2; ++i) { + d->data[i] = swap ? combine(*s, *(s + 1)) : combine(*(s + 1), *s); + s += 2; } } +const String::Type String::WCharByteOrder + = (Utils::SystemByteOrder == Utils::BigEndian) ? String::UTF16BE : String::UTF16LE; + +} + //////////////////////////////////////////////////////////////////////////////// // related functions //////////////////////////////////////////////////////////////////////////////// const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2) { - String s(s1); + TagLib::String s(s1); s.append(s2); return s; } const TagLib::String operator+(const char *s1, const TagLib::String &s2) { - String s(s1); + TagLib::String s(s1); s.append(s2); return s; } const TagLib::String operator+(const TagLib::String &s1, const char *s2) { - String s(s1); + TagLib::String s(s1); s.append(s2); return s; } -std::ostream &operator<<(std::ostream &s, const String &str) +std::ostream &operator<<(std::ostream &s, const TagLib::String &str) { s << str.to8Bit(); return s; } + diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tstring.h b/Plugins/PluginNowPlaying/taglib/toolkit/tstring.h index 218a1dea..57945bee 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/tstring.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tstring.h @@ -56,13 +56,15 @@ namespace TagLib { + class StringList; + //! A \e wide string class suitable for unicode. /*! * This is an implicitly shared \e wide string. For storage it uses * TagLib::wstring, but as this is an implementation detail this of - * course could change. Strings are stored internally as UTF-16BE. (Without - * the BOM (Byte Order Mark) + * 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 @@ -133,12 +135,12 @@ namespace TagLib { /*! * Makes a deep copy of the data in \a s. */ - String(const wstring &s, Type t = UTF16BE); + String(const wstring &s, Type t = WCharByteOrder); /*! * Makes a deep copy of the data in \a s. */ - String(const wchar_t *s, Type t = UTF16BE); + String(const wchar_t *s, Type t = WCharByteOrder); /*! * Makes a deep copy of the data in \a c. @@ -176,34 +178,59 @@ namespace TagLib { virtual ~String(); /*! - * If \a unicode if false (the default) this will return a \e Latin1 encoded - * std::string. If it is true the returned std::wstring will be UTF-8 - * encoded. + * Returns a deep copy of this String as an std::string. The returned string + * is encoded in UTF8 if \a unicode is true, otherwise Latin1. + * + * \see toCString() */ std::string to8Bit(bool unicode = false) const; /*! - * Returns a wstring version of the TagLib string as a wide string. + * Returns a deep copy of this String as a wstring. The returned string is + * encoded in UTF-16 (without BOM/CPU byte order). + * + * \see toCWString() */ wstring toWString() const; /*! - * Creates and returns a C-String based on the data. This string is still - * owned by the String (class) and as such should not be deleted by the user. + * Creates and returns a standard C-style (null-terminated) version of this + * String. The returned string is encoded in UTF8 if \a unicode is true, + * otherwise Latin1. + * + * The returned string is still owned by this String and should not be deleted + * by the user. * - * If \a unicode if false (the default) this string will be encoded in - * \e Latin1. If it is true the returned C-String will be UTF-8 encoded. + * The returned pointer remains valid until this String instance is destroyed + * or toCString() is called again. * - * This string remains valid until the String instance is destroyed or - * another export method is called. - * - * \warning This however has the side effect that this C-String will remain - * in memory in addition to other memory that is consumed by the + * \warning This however has the side effect that the returned string will remain + * in memory in addition to other memory that is consumed by this * String instance. So, this method should not be used on large strings or - * where memory is critical. + * 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). + * + * 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. */ @@ -239,6 +266,11 @@ namespace TagLib { */ 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 true if the strings starts with the substring \a s. */ @@ -299,7 +331,7 @@ namespace TagLib { /*! * Convert the string to an integer. * - * Returns the integer if the conversion was successfull or 0 if the + * Returns the integer if the conversion was successful or 0 if the * string does not represent a number. */ // BIC: merge with the method below @@ -350,6 +382,12 @@ namespace TagLib { */ bool operator==(const String &s) const; + /*! + * Compares each character of the String with each character of \a s and + * returns false if the strings match. + */ + bool operator!=(const String &s) const; + /*! * Appends \a s to the end of the String. */ @@ -438,17 +476,39 @@ namespace TagLib { private: /*! - * This checks to see if the string is in \e UTF-16 (with BOM) or \e UTF-8 - * format and if so converts it to \e UTF-16BE for internal use. \e Latin1 - * does not require conversion since it is a subset of \e UTF-16BE and - * \e UTF16-BE requires no conversion since it is used internally. + * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order) + * and copies it to the internal buffer. */ - void prepare(Type t); + void copyFromLatin1(const char *s, size_t length); + + /*! + * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order) + * and copies it to the internal buffer. + */ + void copyFromUTF8(const char *s, size_t length); + + /*! + * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into + * \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. + */ + void copyFromUTF16(const wchar_t *s, size_t length, Type t); + + /*! + * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into + * \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. + */ + void copyFromUTF16(const char *s, size_t length, Type t); + + /*! + * Indicates which byte order of UTF-16 is used to store strings internally. + * + * \note \e String::UTF16BE or \e String::UTF16LE + */ + static const Type WCharByteOrder; class StringPrivate; StringPrivate *d; }; - } /*! diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/tutils.h b/Plugins/PluginNowPlaying/taglib/toolkit/tutils.h new file mode 100644 index 00000000..4a398d26 --- /dev/null +++ b/Plugins/PluginNowPlaying/taglib/toolkit/tutils.h @@ -0,0 +1,190 @@ +/*************************************************************************** + 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 + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if defined(HAVE_MSC_BYTESWAP) +# include +#elif defined(HAVE_GLIBC_BYTESWAP) +# include +#elif defined(HAVE_MAC_BYTESWAP) +# include +#elif defined(HAVE_OPENBSD_BYTESWAP) +# include +#endif + +namespace TagLib +{ + namespace Utils + { + inline ushort byteSwap(ushort x) + { +#if defined(HAVE_GCC_BYTESWAP_16) + + 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 + } + + inline uint byteSwap(uint x) + { +#if defined(HAVE_GCC_BYTESWAP_32) + + 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 + } + + inline ulonglong byteSwap(ulonglong x) + { +#if defined(HAVE_GCC_BYTESWAP_64) + + 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 + } + + enum ByteOrder + { + LittleEndian, + BigEndian + }; + +#ifdef SYSTEM_BYTEORDER + +# if SYSTEM_BYTEORDER == 1 + + const ByteOrder SystemByteOrder = LittleEndian; + +# else + + const ByteOrder SystemByteOrder = BigEndian; + +# endif + +#else + + inline ByteOrder systemByteOrder() + { + union { + int i; + char c; + } u; + + u.i = 1; + if(u.c == 1) + return LittleEndian; + else + return BigEndian; + } + + const ByteOrder SystemByteOrder = systemByteOrder(); + +#endif + } +} + +#endif + +#endif diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/unicode.cpp b/Plugins/PluginNowPlaying/taglib/toolkit/unicode.cpp index b60264d9..1b26977e 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/unicode.cpp +++ b/Plugins/PluginNowPlaying/taglib/toolkit/unicode.cpp @@ -155,7 +155,7 @@ ConversionResult ConvertUTF16toUTF8 ( case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6; case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6; case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6; - case 1: *--target = ch | firstByteMark[bytesToWrite]; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } @@ -253,7 +253,7 @@ ConversionResult ConvertUTF8toUTF16 ( result = sourceIllegal; break; } else { - *target++ = ch; /* normal case */ + *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_UTF16) { if (flags == strictConversion) { @@ -270,7 +270,7 @@ ConversionResult ConvertUTF8toUTF16 ( result = targetExhausted; break; } ch -= halfBase; - *target++ = (ch >> halfShift) + UNI_SUR_HIGH_START; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (ch & halfMask) + UNI_SUR_LOW_START; } } diff --git a/Plugins/PluginNowPlaying/taglib/toolkit/unicode.h b/Plugins/PluginNowPlaying/taglib/toolkit/unicode.h index cf7eb3c5..ebf1915d 100644 --- a/Plugins/PluginNowPlaying/taglib/toolkit/unicode.h +++ b/Plugins/PluginNowPlaying/taglib/toolkit/unicode.h @@ -106,6 +106,11 @@ bit mask & shift operations. ------------------------------------------------------------------------ */ +// Workaround for when MSVC doesn't have wchar_t as a built-in type. +#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) +# include +#endif + /* Some fundamental constants */ #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD #define UNI_MAX_BMP (UTF32)0x0000FFFF @@ -114,10 +119,10 @@ namespace Unicode { -typedef unsigned long UTF32; /* at least 32 bits */ -typedef unsigned short UTF16; /* at least 16 bits */ -typedef unsigned char UTF8; /* typically 8 bits */ -typedef unsigned char Boolean; /* 0 or 1 */ +typedef unsigned long UTF32; /* at least 32 bits */ +typedef wchar_t UTF16; /* TagLib assumes that wchar_t is sufficient for UTF-16. */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ typedef enum { conversionOK = 0, /* conversion successful */ diff --git a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.cpp b/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.cpp deleted file mode 100644 index 0f362da9..00000000 --- a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -#include - -#include "trueaudiofile.h" -#include "id3v1tag.h" -#include "id3v2tag.h" -#include "id3v2header.h" - -using namespace TagLib; - -namespace -{ - enum { ID3v2Index = 0, ID3v1Index = 1 }; -} - -class TrueAudio::File::FilePrivate -{ -public: - FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) : - ID3v2FrameFactory(frameFactory), - ID3v2Location(-1), - ID3v2OriginalSize(0), - ID3v1Location(-1), - properties(0), - hasID3v1(false), - hasID3v2(false) {} - - ~FilePrivate() - { - delete properties; - } - - const ID3v2::FrameFactory *ID3v2FrameFactory; - long ID3v2Location; - uint ID3v2OriginalSize; - - long ID3v1Location; - - TagUnion tag; - - Properties *properties; - - // These indicate whether the file *on disk* has these tags, not if - // this data structure does. This is used in computing offsets. - - bool hasID3v1; - bool hasID3v2; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -TrueAudio::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) -{ - d = new FilePrivate; - if(isOpen()) - read(readProperties, propertiesStyle); -} - -TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(file) -{ - d = new FilePrivate(frameFactory); - if(isOpen()) - read(readProperties, propertiesStyle); -} - -TrueAudio::File::~File() -{ - delete d; -} - -TagLib::Tag *TrueAudio::File::tag() const -{ - return &d->tag; -} - -TrueAudio::Properties *TrueAudio::File::audioProperties() const -{ - return d->properties; -} - -void TrueAudio::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) -{ - d->ID3v2FrameFactory = factory; -} - -bool TrueAudio::File::save() -{ - if(readOnly()) { - debug("TrueAudio::File::save() -- File is read only."); - return false; - } - - // Update ID3v2 tag - - if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) { - if(!d->hasID3v2) { - d->ID3v2Location = 0; - d->ID3v2OriginalSize = 0; - } - ByteVector data = ID3v2Tag()->render(); - insert(data, d->ID3v2Location, d->ID3v2OriginalSize); - d->ID3v1Location -= d->ID3v2OriginalSize - data.size(); - d->ID3v2OriginalSize = data.size(); - d->hasID3v2 = true; - } - else if(d->hasID3v2) { - removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); - d->ID3v1Location -= d->ID3v2OriginalSize; - d->ID3v2Location = -1; - d->ID3v2OriginalSize = 0; - d->hasID3v2 = false; - } - - // Update ID3v1 tag - - if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) { - if(!d->hasID3v1) { - seek(0, End); - d->ID3v1Location = tell(); - } - else - seek(d->ID3v1Location); - writeBlock(ID3v1Tag()->render()); - d->hasID3v1 = true; - } - else if(d->hasID3v1) { - removeBlock(d->ID3v1Location, 128); - d->ID3v1Location = -1; - d->hasID3v1 = false; - } - - return true; -} - -ID3v1::Tag *TrueAudio::File::ID3v1Tag(bool create) -{ - return d->tag.access(ID3v1Index, create); -} - -ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create) -{ - return d->tag.access(ID3v2Index, create); -} - -void TrueAudio::File::strip(int tags) -{ - if(tags & ID3v1) { - d->tag.set(ID3v1Index, 0); - ID3v2Tag(true); - } - - if(tags & ID3v2) { - d->tag.set(ID3v2Index, 0); - - if(!ID3v1Tag()) - ID3v2Tag(true); - } -} - - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) -{ - // Look for an ID3v2 tag - - d->ID3v2Location = findID3v2(); - - if(d->ID3v2Location >= 0) { - - d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); - - d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); - - if(ID3v2Tag()->header()->tagSize() <= 0) - d->tag.set(ID3v2Index, 0); - else - d->hasID3v2 = true; - } - - // Look for an ID3v1 tag - - d->ID3v1Location = findID3v1(); - - if(d->ID3v1Location >= 0) { - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - d->hasID3v1 = true; - } - - if(!d->hasID3v1) - ID3v2Tag(true); - - // Look for TrueAudio metadata - - if(readProperties) { - if(d->ID3v2Location >= 0) { - seek(d->ID3v2Location + d->ID3v2OriginalSize); - d->properties = new Properties(readBlock(TrueAudio::HeaderSize), - length() - d->ID3v2OriginalSize); - } - else { - seek(0); - d->properties = new Properties(readBlock(TrueAudio::HeaderSize), - length()); - } - } -} - -long TrueAudio::File::findID3v1() -{ - if(!isValid()) - return -1; - - seek(-128, End); - long p = tell(); - - if(readBlock(3) == ID3v1::Tag::fileIdentifier()) - return p; - - return -1; -} - -long TrueAudio::File::findID3v2() -{ - if(!isValid()) - return -1; - - seek(0); - - if(readBlock(3) == ID3v2::Header::fileIdentifier()) - return 0; - - return -1; -} diff --git a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.h b/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.h deleted file mode 100644 index 32cbf4b1..00000000 --- a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudiofile.h +++ /dev/null @@ -1,180 +0,0 @@ -/*************************************************************************** - 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 "tfile.h" -#include "trueaudioproperties.h" - -namespace TagLib { - - class Tag; - - namespace ID3v2 { class Tag; class FrameFactory; } - namespace ID3v1 { class Tag; } - - //! An implementation of TrueAudio metadata - - /*! - * This is implementation of TrueAudio metadata. - * - * This supports ID3v1 and ID3v2 tags as well as reading stream - * properties from the file. - */ - - namespace TrueAudio { - - //! An implementation of TagLib::File with TrueAudio specific methods - - /*! - * This implements and provides an interface for TrueAudio files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to TrueAudio files. - */ - - class TAGLIB_EXPORT File : public TagLib::File - { - public: - /*! - * This set of flags is used for various operations and is suitable for - * being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches ID3v2 tags. - ID3v2 = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Contructs an TrueAudio file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. - */ - File(FileName file, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); - - /*! - * Contructs an TrueAudio file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. The frames will be created using - * \a frameFactory. - */ - File(FileName file, ID3v2::FrameFactory *frameFactory, - bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); - - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - /*! - * Returns the Tag for this file. - */ - virtual TagLib::Tag *tag() const; - - /*! - * Returns the TrueAudio::Properties for this file. If no audio properties - * were read then this will return a null pointer. - */ - virtual Properties *audioProperties() const; - - /*! - * Set the ID3v2::FrameFactory to something other than the default. - * - * \see ID3v2FrameFactory - */ - void setID3v2FrameFactory(const ID3v2::FrameFactory *factory); - - /*! - * Saves the file. - */ - virtual bool save(); - - /*! - * Returns a pointer to the ID3v2 tag of the file. - * - * If \a create is false (the default) this will return a null pointer - * if there is no valid ID3v2 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. If there is already an APE tag, the - * new ID3v1 tag will be placed after it. - * - * \note The Tag is still owned by the TrueAudio::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this will return a null pointer - * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. If there is already an APE tag, the - * new ID3v1 tag will be placed after it. - * - * \note The Tag is still owned by the TrueAudio::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - ID3v2::Tag *ID3v2Tag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the - * file. By default it removes all tags. - * - * \note This will also invalidate pointers to the tags - * as their memory will be freed. - * \note In order to make the removal permanent save() still needs to be called - */ - void strip(int tags = AllTags); - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); - long findID3v1(); - long findID3v2(); - - class FilePrivate; - FilePrivate *d; - }; - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.cpp b/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.cpp deleted file mode 100644 index 3cab855b..00000000 --- a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - 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 -#include -#include - -#include "trueaudioproperties.h" -#include "trueaudiofile.h" - -using namespace TagLib; - -class TrueAudio::Properties::PropertiesPrivate -{ -public: - PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : - data(d), - streamLength(length), - style(s), - version(0), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - bitsPerSample(0) {} - - ByteVector data; - long streamLength; - ReadStyle style; - int version; - int length; - int bitrate; - int sampleRate; - int channels; - int bitsPerSample; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -TrueAudio::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) -{ - d = new PropertiesPrivate(data, streamLength, style); - read(); -} - -TrueAudio::Properties::~Properties() -{ - delete d; -} - -int TrueAudio::Properties::length() const -{ - return d->length; -} - -int TrueAudio::Properties::bitrate() const -{ - return d->bitrate; -} - -int TrueAudio::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int TrueAudio::Properties::bitsPerSample() const -{ - return d->bitsPerSample; -} - -int TrueAudio::Properties::channels() const -{ - return d->channels; -} - -int TrueAudio::Properties::ttaVersion() const -{ - return d->version; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void TrueAudio::Properties::read() -{ - if(!d->data.startsWith("TTA")) - return; - - int pos = 3; - - d->version = d->data[pos] - '0'; - pos += 1; - - // 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) { - // Skip the audio format - pos += 2; - - d->channels = d->data.mid(pos, 2).toShort(false); - pos += 2; - - d->bitsPerSample = d->data.mid(pos, 2).toShort(false); - pos += 2; - - d->sampleRate = d->data.mid(pos, 4).toUInt(false); - pos += 4; - - uint sampleFrames = d->data.mid(pos, 4).toUInt(false); - d->length = d->sampleRate > 0 ? sampleFrames / d->sampleRate : 0; - - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; - } -} diff --git a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.h b/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.h deleted file mode 100644 index f66fd2e9..00000000 --- a/Plugins/PluginNowPlaying/taglib/trueaudio/trueaudioproperties.h +++ /dev/null @@ -1,93 +0,0 @@ -/*************************************************************************** - 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 "audioproperties.h" - -namespace TagLib { - - namespace TrueAudio { - - class File; - - static const uint HeaderSize = 18; - - //! An implementation of audio property reading for TrueAudio - - /*! - * This reads the data from an TrueAudio stream found in the AudioProperties - * API. - */ - - class TAGLIB_EXPORT Properties : public AudioProperties - { - public: - /*! - * Create an instance of TrueAudio::Properties with the data read from the - * ByteVector \a data. - */ - Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); - - /*! - * Destroys this TrueAudio::Properties instance. - */ - virtual ~Properties(); - - // Reimplementations. - - virtual int length() const; - virtual int bitrate() const; - virtual int sampleRate() const; - virtual int channels() const; - - /*! - * Returns number of bits per sample. - */ - int bitsPerSample() const; - - /*! - * Returns the major version number. - */ - int ttaVersion() const; - - private: - Properties(const Properties &); - Properties &operator=(const Properties &); - - void read(); - - class PropertiesPrivate; - PropertiesPrivate *d; - }; - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.cpp b/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.cpp deleted file mode 100644 index 999a5445..00000000 --- a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/*************************************************************************** - 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 -#include -#include -#include - -#include "wavpackfile.h" -#include "id3v1tag.h" -#include "id3v2header.h" -#include "apetag.h" -#include "apefooter.h" - -using namespace TagLib; - -namespace -{ - enum { APEIndex, ID3v1Index }; -} - -class WavPack::File::FilePrivate -{ -public: - FilePrivate() : - APELocation(-1), - APESize(0), - ID3v1Location(-1), - properties(0), - hasAPE(false), - hasID3v1(false) {} - - ~FilePrivate() - { - delete properties; - } - - long APELocation; - uint APESize; - - long ID3v1Location; - - TagUnion tag; - - Properties *properties; - - // These indicate whether the file *on disk* has these tags, not if - // this data structure does. This is used in computing offsets. - - bool hasAPE; - bool hasID3v1; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -WavPack::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) -{ - d = new FilePrivate; - read(readProperties, propertiesStyle); -} - -WavPack::File::~File() -{ - delete d; -} - -TagLib::Tag *WavPack::File::tag() const -{ - return &d->tag; -} - -WavPack::Properties *WavPack::File::audioProperties() const -{ - return d->properties; -} - -bool WavPack::File::save() -{ - if(readOnly()) { - debug("WavPack::File::save() -- File is read only."); - return false; - } - - // Update ID3v1 tag - - if(ID3v1Tag()) { - if(d->hasID3v1) { - seek(d->ID3v1Location); - writeBlock(ID3v1Tag()->render()); - } - else { - seek(0, End); - d->ID3v1Location = tell(); - writeBlock(ID3v1Tag()->render()); - d->hasID3v1 = true; - } - } - else { - if(d->hasID3v1) { - removeBlock(d->ID3v1Location, 128); - d->hasID3v1 = false; - if(d->hasAPE) { - if(d->APELocation > d->ID3v1Location) - d->APELocation -= 128; - } - } - } - - // Update APE tag - - if(APETag()) { - if(d->hasAPE) - insert(APETag()->render(), d->APELocation, d->APESize); - else { - if(d->hasID3v1) { - insert(APETag()->render(), d->ID3v1Location, 0); - d->APESize = APETag()->footer()->completeTagSize(); - d->hasAPE = true; - d->APELocation = d->ID3v1Location; - d->ID3v1Location += d->APESize; - } - else { - seek(0, End); - d->APELocation = tell(); - writeBlock(APETag()->render()); - d->APESize = APETag()->footer()->completeTagSize(); - d->hasAPE = true; - } - } - } - else { - if(d->hasAPE) { - removeBlock(d->APELocation, d->APESize); - d->hasAPE = false; - if(d->hasID3v1) { - if(d->ID3v1Location > d->APELocation) { - d->ID3v1Location -= d->APESize; - } - } - } - } - - return true; -} - -ID3v1::Tag *WavPack::File::ID3v1Tag(bool create) -{ - return d->tag.access(ID3v1Index, create); -} - -APE::Tag *WavPack::File::APETag(bool create) -{ - return d->tag.access(APEIndex, create); -} - -void WavPack::File::strip(int tags) -{ - if(tags & ID3v1) { - d->tag.set(ID3v1Index, 0); - APETag(true); - } - - if(tags & APE) { - d->tag.set(APEIndex, 0); - - if(!ID3v1Tag()) - APETag(true); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) -{ - // Look for an ID3v1 tag - - d->ID3v1Location = findID3v1(); - - if(d->ID3v1Location >= 0) { - d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - d->hasID3v1 = true; - } - - // Look for an APE tag - - d->APELocation = findAPE(); - - if(d->APELocation >= 0) { - d->tag.set(APEIndex, new APE::Tag(this, d->APELocation)); - d->APESize = APETag()->footer()->completeTagSize(); - d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize; - d->hasAPE = true; - } - - if(!d->hasID3v1) - APETag(true); - - // Look for WavPack audio properties - - if(readProperties) { - seek(0); - d->properties = new Properties(this, length() - d->APESize); - } -} - -long WavPack::File::findAPE() -{ - if(!isValid()) - return -1; - - if(d->hasID3v1) - seek(-160, End); - else - seek(-32, End); - - long p = tell(); - - if(readBlock(8) == APE::Tag::fileIdentifier()) - return p; - - return -1; -} - -long WavPack::File::findID3v1() -{ - if(!isValid()) - return -1; - - seek(-128, End); - long p = tell(); - - if(readBlock(3) == ID3v1::Tag::fileIdentifier()) - return p; - - return -1; -} diff --git a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.h b/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.h deleted file mode 100644 index 3415a329..00000000 --- a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackfile.h +++ /dev/null @@ -1,164 +0,0 @@ -/*************************************************************************** - 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 implementation of WavPack metadata. - * - * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream - * properties from the file. - */ - - namespace WavPack { - - //! An implementation of TagLib::File with WavPack specific methods - - /*! - * This implements and provides an interface for WavPack files to the - * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing - * the abstract TagLib::File API as well as providing some additional - * information specific to WavPack files. - */ - - class TAGLIB_EXPORT File : public TagLib::File - { - public: - /*! - * This set of flags is used for various operations and is suitable for - * being OR-ed together. - */ - enum TagTypes { - //! Empty set. Matches no tag types. - NoTags = 0x0000, - //! Matches ID3v1 tags. - ID3v1 = 0x0001, - //! Matches APE tags. - APE = 0x0002, - //! Matches all tag types. - AllTags = 0xffff - }; - - /*! - * Contructs an WavPack file from \a file. If \a readProperties is true the - * file's audio properties will also be read using \a propertiesStyle. If - * false, \a propertiesStyle is ignored. - */ - File(FileName file, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); - - /*! - * Destroys this instance of the File. - */ - virtual ~File(); - - /*! - * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag - * or a combination of the two. - */ - virtual TagLib::Tag *tag() const; - - /*! - * Returns the MPC::Properties for this file. If no audio properties - * were read then this will return a null pointer. - */ - virtual Properties *audioProperties() const; - - /*! - * Saves the file. - */ - virtual bool save(); - - /*! - * Returns a pointer to the ID3v1 tag of the file. - * - * If \a create is false (the default) this will return a null pointer - * if there is no valid ID3v1 tag. If \a create is true it will create - * an ID3v1 tag if one does not exist. If there is already an APE tag, the - * new ID3v1 tag will be placed after it. - * - * \note The Tag is still owned by the APE::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - ID3v1::Tag *ID3v1Tag(bool create = false); - - /*! - * Returns a pointer to the APE tag of the file. - * - * If \a create is false (the default) this will return a null pointer - * if there is no valid APE tag. If \a create is true it will create - * a APE tag if one does not exist. - * - * \note The Tag is still owned by the APE::File and should not be - * deleted by the user. It will be deleted when the file (object) is - * destroyed. - */ - APE::Tag *APETag(bool create = false); - - /*! - * This will remove the tags that match the OR-ed together TagTypes from the - * file. By default it removes all tags. - * - * \note This will also invalidate pointers to the tags - * as their memory will be freed. - * \note In order to make the removal permanent save() still needs to be called - */ - void strip(int tags = AllTags); - - private: - File(const File &); - File &operator=(const File &); - - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); - long findID3v1(); - long findAPE(); - - class FilePrivate; - FilePrivate *d; - }; - } -} - -#endif diff --git a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.cpp b/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.cpp deleted file mode 100644 index 52552a0d..00000000 --- a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/*************************************************************************** - 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 -#include - -#include "wavpackproperties.h" -#include "wavpackfile.h" - -using namespace TagLib; - -class WavPack::Properties::PropertiesPrivate -{ -public: - PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : - data(d), - streamLength(length), - style(s), - length(0), - bitrate(0), - sampleRate(0), - channels(0), - version(0), - bitsPerSample(0), - file(0) {} - - ByteVector data; - long streamLength; - ReadStyle style; - int length; - int bitrate; - int sampleRate; - int channels; - int version; - int bitsPerSample; - File *file; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) -{ - d = new PropertiesPrivate(data, streamLength, style); - read(); -} - -WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style) -{ - ByteVector data = file->readBlock(32); - d = new PropertiesPrivate(data, streamLength, style); - d->file = file; - read(); -} - -WavPack::Properties::~Properties() -{ - delete d; -} - -int WavPack::Properties::length() const -{ - return d->length; -} - -int WavPack::Properties::bitrate() const -{ - return d->bitrate; -} - -int WavPack::Properties::sampleRate() const -{ - return d->sampleRate; -} - -int WavPack::Properties::channels() const -{ - return d->channels; -} - -int WavPack::Properties::version() const -{ - return d->version; -} - -int WavPack::Properties::bitsPerSample() const -{ - return d->bitsPerSample; -} - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -static const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000, - 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; - -#define BYTES_STORED 3 -#define MONO_FLAG 4 - -#define SHIFT_LSB 13 -#define SHIFT_MASK (0x1fL << SHIFT_LSB) - -#define SRATE_LSB 23 -#define SRATE_MASK (0xfL << SRATE_LSB) - -#define MIN_STREAM_VERS 0x402 -#define MAX_STREAM_VERS 0x410 - -#define FINAL_BLOCK 0x1000 - -void WavPack::Properties::read() -{ - if(!d->data.startsWith("wvpk")) - return; - - d->version = d->data.mid(8, 2).toShort(false); - if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) - return; - - unsigned int flags = d->data.mid(24, 4).toUInt(false); - d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - - ((flags & SHIFT_MASK) >> SHIFT_LSB); - d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; - d->channels = (flags & MONO_FLAG) ? 1 : 2; - - unsigned int samples = d->data.mid(12, 4).toUInt(false); - if(samples == ~0u) { - if(d->file && d->style != Fast) { - samples = seekFinalIndex(); - } - else { - samples = 0; - } - } - d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0; - - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; -} - -unsigned int WavPack::Properties::seekFinalIndex() -{ - ByteVector blockID("wvpk", 4); - - long offset = d->streamLength; - while(offset > 0) { - offset = d->file->rfind(blockID, offset); - if(offset == -1) - return 0; - d->file->seek(offset); - ByteVector data = d->file->readBlock(32); - if(data.size() != 32) - return 0; - int version = data.mid(8, 2).toShort(false); - if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) - continue; - unsigned int flags = data.mid(24, 4).toUInt(false); - if(!(flags & FINAL_BLOCK)) - return 0; - unsigned int blockIndex = data.mid(16, 4).toUInt(false); - unsigned int blockSamples = data.mid(20, 4).toUInt(false); - return blockIndex + blockSamples; - } - - return 0; -} - diff --git a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.h b/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.h deleted file mode 100644 index 74d18ea8..00000000 --- a/Plugins/PluginNowPlaying/taglib/wavpack/wavpackproperties.h +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************** - 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 const uint HeaderSize = 32; - - //! An implementation of audio property reading for WavPack - - /*! - * This reads the data from an WavPack stream found in the AudioProperties - * API. - */ - - class TAGLIB_EXPORT Properties : public AudioProperties - { - public: - /*! - * Create an instance of WavPack::Properties with the data read from the - * ByteVector \a data. - * - * \deprecated This constructor will be dropped in favor of the one below - * in a future version. - */ - Properties(const ByteVector &data, long streamLength, ReadStyle style = Average); - - /*! - * Create an instance of WavPack::Properties. - */ - // BIC: merge with the above constructor - Properties(File *file, long streamLength, ReadStyle style = Average); - - /*! - * Destroys this WavPack::Properties instance. - */ - virtual ~Properties(); - - // Reimplementations. - - virtual int length() const; - virtual int bitrate() const; - virtual int sampleRate() const; - virtual int channels() const; - - /*! - * Returns number of bits per sample. - */ - int bitsPerSample() const; - - /*! - * Returns WavPack version. - */ - int version() const; - - private: - Properties(const Properties &); - Properties &operator=(const Properties &); - - void read(); - unsigned int seekFinalIndex(); - - class PropertiesPrivate; - PropertiesPrivate *d; - }; - } -} - -#endif