diff --git a/3rdparty/taglib/CMakeLists.txt b/3rdparty/taglib/CMakeLists.txt index cacd58f54..fe2cf4f8b 100644 --- a/3rdparty/taglib/CMakeLists.txt +++ b/3rdparty/taglib/CMakeLists.txt @@ -7,6 +7,15 @@ math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSI math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}") math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}") +include(TestBigEndian) +test_big_endian(IS_BIG_ENDIAN) + +if(NOT IS_BIG_ENDIAN) + add_definitions(-DSYSTEM_BYTEORDER=1) +else() + add_definitions(-DSYSTEM_BYTEORDER=2) +endif() + configure_file(taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -21,6 +30,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex + ${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1 @@ -60,6 +70,8 @@ set(tag_HDRS toolkit/tmap.h toolkit/tmap.tcc toolkit/tpropertymap.h + toolkit/trefcounter.h + toolkit/tdebuglistener.h mpeg/mpegfile.h mpeg/mpegproperties.h mpeg/mpegheader.h @@ -94,6 +106,8 @@ set(tag_HDRS ogg/flac/oggflacfile.h ogg/speex/speexfile.h ogg/speex/speexproperties.h + ogg/opus/opusfile.h + ogg/opus/opusproperties.h flac/flacfile.h flac/flacpicture.h flac/flacproperties.h @@ -114,6 +128,7 @@ set(tag_HDRS riff/aiff/aiffproperties.h riff/wav/wavfile.h riff/wav/wavproperties.h + riff/wav/infotag.h asf/asffile.h asf/asfproperties.h asf/asftag.h @@ -230,6 +245,11 @@ set(speex_SRCS ogg/speex/speexproperties.cpp ) +set(opus_SRCS + ogg/opus/opusfile.cpp + ogg/opus/opusproperties.cpp +) + set(trueaudio_SRCS trueaudio/trueaudiofile.cpp trueaudio/trueaudioproperties.cpp @@ -255,6 +275,7 @@ set(aiff_SRCS set(wav_SRCS riff/wav/wavfile.cpp riff/wav/wavproperties.cpp + riff/wav/infotag.cpp ) set(mod_SRCS @@ -290,6 +311,8 @@ set(toolkit_SRCS toolkit/tfilestream.cpp toolkit/tdebug.cpp toolkit/tpropertymap.cpp + toolkit/trefcounter.cpp + toolkit/tdebuglistener.cpp toolkit/unicode.cpp ) @@ -297,7 +320,7 @@ set(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS} ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} - ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} + ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/3rdparty/taglib/ape/apefile.cpp b/3rdparty/taglib/ape/apefile.cpp index cb6522427..3cb9d9a70 100644 --- a/3rdparty/taglib/ape/apefile.cpp +++ b/3rdparty/taglib/ape/apefile.cpp @@ -47,7 +47,7 @@ using namespace TagLib; namespace { - enum { ApeAPEIndex, ApeID3v1Index }; + enum { ApeAPEIndex = 0, ApeID3v1Index = 1 }; } class APE::File::FilePrivate @@ -90,14 +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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } APE::File::~File() @@ -129,12 +131,9 @@ void APE::File::removeUnsupportedProperties(const StringList &properties) PropertyMap APE::File::setProperties(const PropertyMap &properties) { - if(d->hasAPE) - return d->tag.access(ApeAPEIndex, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(ApeID3v1Index, false)->setProperties(properties); - else - return d->tag.access(ApeAPEIndex, true)->setProperties(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 @@ -236,6 +235,16 @@ void APE::File::strip(int tags) } } +bool APE::File::hasAPETag() const +{ + return d->hasAPE; +} + +bool APE::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/ape/apefile.h b/3rdparty/taglib/ape/apefile.h index 8b187f6a6..f7b509f40 100644 --- a/3rdparty/taglib/ape/apefile.h +++ b/3rdparty/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,20 +84,22 @@ 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); /*! - * 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 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); @@ -128,10 +130,11 @@ namespace TagLib { /*! * Implements the unified property interface -- import function. - * As for the export, only one tag is taken into account. If the file - * has no tag at all, APE will be created. + * 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. @@ -149,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); @@ -183,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/3rdparty/taglib/ape/apefooter.cpp b/3rdparty/taglib/ape/apefooter.cpp index 688649016..539832baf 100644 --- a/3rdparty/taglib/ape/apefooter.cpp +++ b/3rdparty/taglib/ape/apefooter.cpp @@ -177,19 +177,19 @@ void APE::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]; diff --git a/3rdparty/taglib/ape/apeitem.cpp b/3rdparty/taglib/ape/apeitem.cpp index 9d9647ec3..3490173a2 100644 --- a/3rdparty/taglib/ape/apeitem.cpp +++ b/3rdparty/taglib/ape/apeitem.cpp @@ -125,6 +125,7 @@ void APE::Item::setBinaryData(const ByteVector &value) { d->type = Binary; d->value = value; + d->text.clear(); } ByteVector APE::Item::value() const @@ -137,31 +138,35 @@ 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->type = Text; - d->text = value; + d->type = Text; + d->text = value; + d->value.clear(); } void APE::Item::setValues(const StringList &value) { - d->type = Text; - d->text = value; + d->type = Text; + d->text = value; + d->value.clear(); } void APE::Item::appendValue(const String &value) { - d->type = Text; - d->text.append(value); + d->type = Text; + d->text.append(value); + d->value.clear(); } void APE::Item::appendValues(const StringList &values) { - d->type = Text; - d->text.append(values); + d->type = Text; + d->text.append(values); + d->value.clear(); } int APE::Item::size() const @@ -200,7 +205,10 @@ 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 @@ -229,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(Text == d->type) - 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/3rdparty/taglib/ape/apeitem.h b/3rdparty/taglib/ape/apeitem.h index f7fd05e3c..0588d1850 100644 --- a/3rdparty/taglib/ape/apeitem.h +++ b/3rdparty/taglib/ape/apeitem.h @@ -97,7 +97,7 @@ namespace TagLib { /*! * Returns the binary value. - * If the item type is not \a Binary, the returned contents are undefined + * If the item type is not \a Binary, always returns an empty ByteVector. */ ByteVector binaryData() const; @@ -152,8 +152,9 @@ 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; @@ -163,7 +164,8 @@ namespace TagLib { #endif /*! - * Returns the list of text 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/3rdparty/taglib/ape/apeproperties.cpp b/3rdparty/taglib/ape/apeproperties.cpp index b4fcec6ad..4940edead 100644 --- a/3rdparty/taglib/ape/apeproperties.cpp +++ b/3rdparty/taglib/ape/apeproperties.cpp @@ -125,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(); @@ -182,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); @@ -191,14 +191,14 @@ 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); + 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; @@ -207,13 +207,13 @@ void APE::Properties::analyzeCurrent() 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; @@ -221,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/3rdparty/taglib/asf/asfattribute.cpp b/3rdparty/taglib/asf/asfattribute.cpp index 2cfada7b0..4ee2d0a18 100644 --- a/3rdparty/taglib/asf/asfattribute.cpp +++ b/3rdparty/taglib/asf/asfattribute.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include "trefcounter.h" #include "asfattribute.h" #include "asffile.h" diff --git a/3rdparty/taglib/asf/asffile.cpp b/3rdparty/taglib/asf/asffile.cpp index 455631f82..241998ca8 100644 --- a/3rdparty/taglib/asf/asffile.cpp +++ b/3rdparty/taglib/asf/asffile.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include #include #include "asffile.h" #include "asftag.h" @@ -186,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() @@ -197,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() @@ -372,14 +370,16 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper : 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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } ASF::File::~File() @@ -401,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; diff --git a/3rdparty/taglib/asf/asffile.h b/3rdparty/taglib/asf/asffile.h index 3a7eef565..5ccf2fdeb 100644 --- a/3rdparty/taglib/asf/asffile.h +++ b/3rdparty/taglib/asf/asffile.h @@ -48,30 +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. - * - * \note TagLib will *not* take ownership of the stream, the caller is - * responsible for deleting it after the File object. + * \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); /*! - * 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 stream. * * \note In the current implementation, both \a readProperties and - * \a propertiesStyle are ignored. + * \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); + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); /*! * Destroys this instance of the File. @@ -90,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. */ @@ -103,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/3rdparty/taglib/asf/asfpicture.cpp b/3rdparty/taglib/asf/asfpicture.cpp index c36ffa3a9..999f92043 100644 --- a/3rdparty/taglib/asf/asfpicture.cpp +++ b/3rdparty/taglib/asf/asfpicture.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include "trefcounter.h" #include "asfattribute.h" #include "asffile.h" #include "asfpicture.h" @@ -149,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); diff --git a/3rdparty/taglib/asf/asfproperties.cpp b/3rdparty/taglib/asf/asfproperties.cpp index 835cbdf98..b82c131a7 100644 --- a/3rdparty/taglib/asf/asfproperties.cpp +++ b/3rdparty/taglib/asf/asfproperties.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "asfproperties.h" diff --git a/3rdparty/taglib/asf/asftag.cpp b/3rdparty/taglib/asf/asftag.cpp index b7b541fcb..c07abe94d 100644 --- a/3rdparty/taglib/asf/asftag.cpp +++ b/3rdparty/taglib/asf/asftag.cpp @@ -23,10 +23,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - +#include #include "asftag.h" using namespace TagLib; @@ -196,3 +193,162 @@ bool ASF::Tag::isEmpty() const d->attributeListMap.isEmpty(); } +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/3rdparty/taglib/asf/asftag.h b/3rdparty/taglib/asf/asftag.h index 6b2d2bf35..f68579d88 100644 --- a/3rdparty/taglib/asf/asftag.h +++ b/3rdparty/taglib/asf/asftag.h @@ -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/3rdparty/taglib/fileref.cpp b/3rdparty/taglib/fileref.cpp index 5c42ed4c1..4403a5fbf 100644 --- a/3rdparty/taglib/fileref.cpp +++ b/3rdparty/taglib/fileref.cpp @@ -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" @@ -45,6 +42,7 @@ #include "mp4file.h" #include "wavpackfile.h" #include "speexfile.h" +#include "opusfile.h" #include "trueaudiofile.h" #include "aifffile.h" #include "wavfile.h" @@ -217,21 +215,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") @@ -252,6 +257,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, 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") diff --git a/3rdparty/taglib/flac/flacfile.cpp b/3rdparty/taglib/flac/flacfile.cpp index d30825675..5340250ea 100644 --- a/3rdparty/taglib/flac/flacfile.cpp +++ b/3rdparty/taglib/flac/flacfile.cpp @@ -70,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; @@ -108,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, @@ -117,7 +119,8 @@ 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, @@ -126,7 +129,8 @@ FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, { d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::~File() @@ -164,14 +168,7 @@ void FLAC::File::removeUnsupportedProperties(const StringList &unsupported) PropertyMap FLAC::File::setProperties(const PropertyMap &properties) { - if(d->hasXiphComment) - return d->tag.access(FlacXiphIndex, false)->setProperties(properties); - else if(d->hasID3v2) - return d->tag.access(FlacID3v2Index, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(FlacID3v1Index, false)->setProperties(properties); - else - return d->tag.access(FlacXiphIndex, true)->setProperties(properties); + return d->tag.access(FlacXiphIndex, true)->setProperties(properties); } FLAC::Properties *FLAC::File::audioProperties() const @@ -402,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 @@ -431,10 +428,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; @@ -564,3 +561,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/3rdparty/taglib/flac/flacfile.h b/3rdparty/taglib/flac/flacfile.h index 716d4478c..0963f4af6 100644 --- a/3rdparty/taglib/flac/flacfile.h +++ b/3rdparty/taglib/flac/flacfile.h @@ -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,12 +79,13 @@ 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, @@ -91,15 +93,16 @@ 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 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 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. */ // BIC: merge with the above constructor File(IOStream *stream, ID3v2::FrameFactory *frameFactory, @@ -133,8 +136,10 @@ namespace TagLib { /*! * Implements the unified property interface -- import function. - * As with the export, only one tag is taken into account. If the file - * has no tag at all, a XiphComment will be created. + * 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 &); @@ -156,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); @@ -241,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/3rdparty/taglib/flac/flacmetadatablock.cpp b/3rdparty/taglib/flac/flacmetadatablock.cpp index 7d161c27d..17ab05f37 100644 --- a/3rdparty/taglib/flac/flacmetadatablock.cpp +++ b/3rdparty/taglib/flac/flacmetadatablock.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "flacmetadatablock.h" diff --git a/3rdparty/taglib/flac/flacpicture.cpp b/3rdparty/taglib/flac/flacpicture.cpp index 36019248a..a2a9000b6 100644 --- a/3rdparty/taglib/flac/flacpicture.cpp +++ b/3rdparty/taglib/flac/flacpicture.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "flacpicture.h" @@ -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."); diff --git a/3rdparty/taglib/flac/flacproperties.cpp b/3rdparty/taglib/flac/flacproperties.cpp index 8bdc5d8dc..e591193ef 100644 --- a/3rdparty/taglib/flac/flacproperties.cpp +++ b/3rdparty/taglib/flac/flacproperties.cpp @@ -124,7 +124,7 @@ void FLAC::Properties::read() return; } - int pos = 0; + uint pos = 0; // Minimum block size (in samples) pos += 2; @@ -138,7 +138,7 @@ 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; @@ -149,7 +149,7 @@ void FLAC::Properties::read() // stream length in samples. (Audio files measured in days) unsigned long long hi = flags & 0xf; - unsigned long long lo = d->data.mid(pos, 4).toUInt(true); + unsigned long long lo = d->data.toUInt(pos, true); pos += 4; d->sampleFrames = (hi << 32) | lo; diff --git a/3rdparty/taglib/flac/flacunknownmetadatablock.cpp b/3rdparty/taglib/flac/flacunknownmetadatablock.cpp index 1265affbb..dcd5d6515 100644 --- a/3rdparty/taglib/flac/flacunknownmetadatablock.cpp +++ b/3rdparty/taglib/flac/flacunknownmetadatablock.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include diff --git a/3rdparty/taglib/it/itfile.cpp b/3rdparty/taglib/it/itfile.cpp index 4e049518e..ad5cf0b85 100644 --- a/3rdparty/taglib/it/itfile.cpp +++ b/3rdparty/taglib/it/itfile.cpp @@ -45,7 +45,8 @@ IT::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::File(IOStream *stream, bool readProperties, @@ -53,7 +54,8 @@ IT::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::~File() diff --git a/3rdparty/taglib/it/itfile.h b/3rdparty/taglib/it/itfile.h index 5584b7cd9..19327dc65 100644 --- a/3rdparty/taglib/it/itfile.h +++ b/3rdparty/taglib/it/itfile.h @@ -36,23 +36,27 @@ namespace TagLib { class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! - * Contructs a Impulse Tracker 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 Impulse Tracker file from \a file. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! - * Contructs a Impulse Tracker file from \a stream. 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 Impulse Tracker file from \a stream. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ - File(IOStream *stram, bool readProperties = true, + File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); diff --git a/3rdparty/taglib/mod/modfile.cpp b/3rdparty/taglib/mod/modfile.cpp index 25fc8715a..ce974c161 100644 --- a/3rdparty/taglib/mod/modfile.cpp +++ b/3rdparty/taglib/mod/modfile.cpp @@ -45,7 +45,8 @@ Mod::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::File(IOStream *stream, bool readProperties, @@ -53,7 +54,8 @@ Mod::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::~File() diff --git a/3rdparty/taglib/mod/modfile.h b/3rdparty/taglib/mod/modfile.h index ad1e43b88..c45ede242 100644 --- a/3rdparty/taglib/mod/modfile.h +++ b/3rdparty/taglib/mod/modfile.h @@ -37,18 +37,22 @@ namespace TagLib { { public: /*! - * Contructs a Protracker 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 Protracker file from \a file. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! - * Contructs a Protracker file from \a stream. 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 Protracker file from \a stream. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. diff --git a/3rdparty/taglib/mp4/mp4atom.cpp b/3rdparty/taglib/mp4/mp4atom.cpp index 1681ec805..7b87a4796 100644 --- a/3rdparty/taglib/mp4/mp4atom.cpp +++ b/3rdparty/taglib/mp4/mp4atom.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "mp4atom.h" @@ -52,10 +48,10 @@ MP4::Atom::Atom(File *file) return; } - length = header.mid(0, 4).toUInt(); + length = header.toUInt(); if (length == 1) { - long long longLength = file->readBlock(8).toLongLong(); + const 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; diff --git a/3rdparty/taglib/mp4/mp4coverart.cpp b/3rdparty/taglib/mp4/mp4coverart.cpp index 928e3c4aa..2746469d3 100644 --- a/3rdparty/taglib/mp4/mp4coverart.cpp +++ b/3rdparty/taglib/mp4/mp4coverart.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include "trefcounter.h" #include "mp4coverart.h" using namespace TagLib; diff --git a/3rdparty/taglib/mp4/mp4coverart.h b/3rdparty/taglib/mp4/mp4coverart.h index 804ab7852..64115b458 100644 --- a/3rdparty/taglib/mp4/mp4coverart.h +++ b/3rdparty/taglib/mp4/mp4coverart.h @@ -42,10 +42,11 @@ namespace TagLib { * This describes the image type. */ enum Format { - JPEG = TypeJPEG, - PNG = TypePNG, - BMP = TypeBMP, - GIF = TypeGIF + JPEG = TypeJPEG, + PNG = TypePNG, + BMP = TypeBMP, + GIF = TypeGIF, + Unknown = TypeImplicit, }; CoverArt(Format format, const ByteVector &data); diff --git a/3rdparty/taglib/mp4/mp4file.cpp b/3rdparty/taglib/mp4/mp4file.cpp index 02185575d..aab1a2bea 100644 --- a/3rdparty/taglib/mp4/mp4file.cpp +++ b/3rdparty/taglib/mp4/mp4file.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include #include "mp4atom.h" #include "mp4tag.h" #include "mp4file.h" @@ -67,14 +64,16 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a : TagLib::File(file) { d = new FilePrivate; - read(readProperties, audioPropertiesStyle); + if(isOpen()) + read(readProperties, audioPropertiesStyle); } MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, audioPropertiesStyle); + if(isOpen()) + read(readProperties, audioPropertiesStyle); } MP4::File::~File() @@ -88,6 +87,21 @@ MP4::File::tag() const return d->tag; } +PropertyMap MP4::File::properties() const +{ + return d->tag->properties(); +} + +void MP4::File::removeUnsupportedProperties(const StringList &properties) +{ + d->tag->removeUnsupportedProperties(properties); +} + +PropertyMap MP4::File::setProperties(const PropertyMap &properties) +{ + return d->tag->setProperties(properties); +} + MP4::Properties * MP4::File::audioProperties() const { diff --git a/3rdparty/taglib/mp4/mp4file.h b/3rdparty/taglib/mp4/mp4file.h index 2ed3bea58..39693d369 100644 --- a/3rdparty/taglib/mp4/mp4file.h +++ b/3rdparty/taglib/mp4/mp4file.h @@ -49,27 +49,25 @@ namespace TagLib { { 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. + * Constructs an MP4 file from \a file. If \a readProperties is true the + * file's audio properties will also be read. * - * \note In the current implementation, both \a readProperties and - * \a propertiesStyle are ignored. + * \note In the current implementation, \a propertiesStyle is ignored. */ - File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average); + File(FileName file, bool readProperties = true, + Properties::ReadStyle audioPropertiesStyle = Properties::Average); /*! - * 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. + * Constructs an MP4 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 audioPropertiesStyle = Properties::Average); + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle audioPropertiesStyle = Properties::Average); /*! * Destroys this instance of the File. @@ -88,6 +86,22 @@ namespace TagLib { */ 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 MP4 audio properties for this file. */ diff --git a/3rdparty/taglib/mp4/mp4item.cpp b/3rdparty/taglib/mp4/mp4item.cpp index af2cc65c8..671f26b4d 100644 --- a/3rdparty/taglib/mp4/mp4item.cpp +++ b/3rdparty/taglib/mp4/mp4item.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include "trefcounter.h" #include "mp4item.h" using namespace TagLib; diff --git a/3rdparty/taglib/mp4/mp4properties.cpp b/3rdparty/taglib/mp4/mp4properties.cpp index d2d04167c..058cc61cf 100644 --- a/3rdparty/taglib/mp4/mp4properties.cpp +++ b/3rdparty/taglib/mp4/mp4properties.cpp @@ -23,10 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include "mp4file.h" @@ -96,8 +92,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) 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(); + const long long unit = data.toLongLong(28U); + const long long length = data.toLongLong(36U); d->length = unit ? int(length / unit) : 0; } else { @@ -105,8 +101,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) 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(); + const unsigned int unit = data.toUInt(20U); + const unsigned int length = data.toUInt(24U); d->length = unit ? length / unit : 0; } @@ -118,11 +114,11 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) 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(); + d->channels = data.toShort(40U); + d->bitsPerSample = data.toShort(42U); + d->sampleRate = data.toUInt(46U); if(data.mid(56, 4) == "esds" && data[64] == 0x03) { - long pos = 65; + uint pos = 65; if(data.mid(pos, 3) == "\x80\x80\x80") { pos += 3; } @@ -133,16 +129,16 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) pos += 3; } pos += 10; - d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000; + d->bitrate = (data.toUInt(pos) + 500) / 1000; } } } else if (data.mid(20, 4) == "alac") { if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); - d->channels = data.at(73); - d->bitrate = data.mid(80, 4).toUInt() / 1000; - d->sampleRate = data.mid(84, 4).toUInt(); + d->channels = data.at(73); + d->bitrate = data.toUInt(80U) / 1000; + d->sampleRate = data.toUInt(84U); } } diff --git a/3rdparty/taglib/mp4/mp4tag.cpp b/3rdparty/taglib/mp4/mp4tag.cpp index fc75fb456..bbe3301d9 100644 --- a/3rdparty/taglib/mp4/mp4tag.cpp +++ b/3rdparty/taglib/mp4/mp4tag.cpp @@ -23,12 +23,9 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include +#include #include "mp4atom.h" #include "mp4tag.h" #include "id3v1genres.h" @@ -113,9 +110,9 @@ MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, boo int i = 0; unsigned int pos = 0; while(pos < data.size()) { - int length = data.mid(pos, 4).toUInt(); + const int length = static_cast(data.toUInt(pos)); ByteVector name = data.mid(pos + 4, 4); - int flags = data.mid(pos + 8, 4).toUInt(); + const int flags = static_cast(data.toUInt(pos + 8)); if(freeForm && i < 2) { if(i == 0 && name != "mean") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\""); @@ -158,7 +155,7 @@ 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()); + addItem(atom->name, (int)data[0].toShort()); } } @@ -167,7 +164,7 @@ MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file) { ByteVectorList data = parseData(atom, file); if(data.size()) { - d->items.insert(atom->name, data[0].toUInt()); + addItem(atom->name, data[0].toUInt()); } } @@ -176,7 +173,7 @@ MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file) { ByteVectorList data = parseData(atom, file); if(data.size()) { - d->items.insert(atom->name, data[0].toLongLong()); + addItem(atom->name, data[0].toLongLong()); } } @@ -185,7 +182,7 @@ MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file) { ByteVectorList data = parseData(atom, file); if(data.size()) { - d->items.insert(atom->name, (uchar)data[0].at(0)); + addItem(atom->name, (uchar)data[0].at(0)); } } @@ -195,8 +192,8 @@ 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))); + if(idx > 0) { + addItem("\251gen", StringList(ID3v1::genre(idx - 1))); } } } @@ -206,9 +203,9 @@ 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)); + const int a = data[0].toShort(2U); + const int b = data[0].toShort(4U); + addItem(atom->name, MP4::Item(a, b)); } } @@ -218,7 +215,7 @@ 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); + addItem(atom->name, value); } } @@ -231,7 +228,7 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags) for(unsigned int i = 0; i < data.size(); i++) { value.append(String(data[i], String::UTF8)); } - d->items.insert(atom->name, value); + addItem(atom->name, value); } } @@ -255,7 +252,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) } Item item(value); item.setAtomDataType(type); - d->items.insert(name, item); + addItem(name, item); } else { ByteVectorList value; @@ -264,7 +261,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) } Item item(value); item.setAtomDataType(type); - d->items.insert(name, item); + addItem(name, item); } } } @@ -276,21 +273,24 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) ByteVector data = file->readBlock(atom->length - 8); unsigned int pos = 0; while(pos < data.size()) { - int length = data.mid(pos, 4).toUInt(); + const int length = static_cast(data.toUInt(pos)); ByteVector name = data.mid(pos + 4, 4); - int flags = data.mid(pos + 8, 4).toUInt(); + const int flags = static_cast(data.toUInt(pos + 8)); if(name != "data") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); break; } - if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF) { + if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) { value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), data.mid(pos + 16, length - 16))); } + else { + debug("MP4: Unknown covr format " + String::number(flags)); + } pos += length; } if(value.size() > 0) - d->items.insert(atom->name, value); + addItem(atom->name, value); } ByteVector @@ -526,11 +526,11 @@ MP4::Tag::updateOffsets(long delta, long offset) } d->file->seek(atom->offset + 12); ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.mid(0, 4).toUInt(); + unsigned int count = data.toUInt(); d->file->seek(atom->offset + 16); - int pos = 4; + uint pos = 4; while(count--) { - long o = data.mid(pos, 4).toUInt(); + long o = static_cast(data.toUInt(pos)); if(o > offset) { o += delta; } @@ -547,11 +547,11 @@ MP4::Tag::updateOffsets(long delta, long offset) } d->file->seek(atom->offset + 12); ByteVector data = d->file->readBlock(atom->length - 12); - unsigned int count = data.mid(0, 4).toUInt(); + unsigned int count = data.toUInt(); d->file->seek(atom->offset + 16); - int pos = 4; + uint pos = 4; while(count--) { - long long o = data.mid(pos, 8).toLongLong(); + long long o = data.toLongLong(pos); if(o > offset) { o += delta; } @@ -570,10 +570,10 @@ MP4::Tag::updateOffsets(long delta, long 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(); + ByteVector data = d->file->readBlock(atom->length - 9); + const unsigned int flags = data.toUInt(0, 3, true); if(flags & 1) { - long long o = data.mid(7, 8).toLongLong(); + long long o = data.toLongLong(7U); if(o > offset) { o += delta; } @@ -756,3 +756,162 @@ MP4::Tag::itemListMap() return d->items; } +static const char *keyTranslation[][2] = { + { "\251nam", "TITLE" }, + { "\251ART", "ARTIST" }, + { "\251alb", "ALBUM" }, + { "\251cmt", "COMMENT" }, + { "\251gen", "GENRE" }, + { "\251day", "DATE" }, + { "\251wrt", "COMPOSER" }, + { "\251grp", "GROUPING" }, + { "trkn", "TRACKNUMBER" }, + { "disk", "DISCNUMBER" }, + { "cpil", "COMPILATION" }, + { "tmpo", "BPM" }, + { "cprt", "COPYRIGHT" }, + { "\251lyr", "LYRICS" }, + { "\251too", "ENCODEDBY" }, + { "soal", "ALBUMSORT" }, + { "soaa", "ALBUMARTISTSORT" }, + { "soar", "ARTISTSORT" }, + { "sonm", "TITLESORT" }, + { "soco", "COMPOSERSORT" }, + { "sosn", "SHOWSORT" }, + { "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" }, + { "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, + { "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, + { "----:com.apple.iTunes:ASIN", "ASIN" }, + { "----:com.apple.iTunes:LABEL", "LABEL" }, + { "----:com.apple.iTunes:LYRICIST", "LYRICIST" }, + { "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" }, + { "----:com.apple.iTunes:REMIXER", "REMIXER" }, + { "----:com.apple.iTunes:ENGINEER", "ENGINEER" }, + { "----:com.apple.iTunes:PRODUCER", "PRODUCER" }, + { "----:com.apple.iTunes:DJMIXER", "DJMIXER" }, + { "----:com.apple.iTunes:MIXER", "MIXER" }, + { "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" }, + { "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" }, + { "----:com.apple.iTunes:MOOD", "MOOD" }, + { "----:com.apple.iTunes:ISRC", "ISRC" }, + { "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" }, + { "----:com.apple.iTunes:BARCODE", "BARCODE" }, + { "----:com.apple.iTunes:SCRIPT", "SCRIPT" }, + { "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" }, + { "----:com.apple.iTunes:LICENSE", "LICENSE" }, + { "----:com.apple.iTunes:MEDIA", "MEDIA" }, +}; + +PropertyMap MP4::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; + MP4::ItemListMap::ConstIterator it = d->items.begin(); + for(; it != d->items.end(); ++it) { + if(keyMap.contains(it->first)) { + String key = keyMap[it->first]; + if(key == "TRACKNUMBER" || key == "DISCNUMBER") { + MP4::Item::IntPair ip = it->second.toIntPair(); + String value = String::number(ip.first); + if(ip.second) { + value += "/" + String::number(ip.second); + } + props[key] = value; + } + else if(key == "BPM") { + props[key] = String::number(it->second.toInt()); + } + else if(key == "COMPILATION") { + props[key] = String::number(it->second.toBool()); + } + else { + props[key] = it->second.toStringList(); + } + } + else { + props.unsupportedData().append(it->first); + } + } + return props; +} + +void MP4::Tag::removeUnsupportedProperties(const StringList &props) +{ + StringList::ConstIterator it = props.begin(); + for(; it != props.end(); ++it) + d->items.erase(*it); +} + +PropertyMap MP4::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()) { + d->items.erase(reverseKeyMap[it->first]); + } + } + + PropertyMap ignoredProps; + it = props.begin(); + for(; it != props.end(); ++it) { + if(reverseKeyMap.contains(it->first)) { + String name = reverseKeyMap[it->first]; + if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") { + int first = 0, second = 0; + StringList parts = StringList::split(it->second.front(), "/"); + if(parts.size() > 0) { + first = parts[0].toInt(); + if(parts.size() > 1) { + second = parts[1].toInt(); + } + d->items[name] = MP4::Item(first, second); + } + } + else if(it->first == "BPM") { + int value = it->second.front().toInt(); + d->items[name] = MP4::Item(value); + } + else if(it->first == "COMPILATION") { + bool value = (it->second.front().toInt() != 0); + d->items[name] = MP4::Item(value); + } + else { + d->items[name] = it->second; + } + } + else { + ignoredProps.insert(it->first, it->second); + } + } + + return ignoredProps; +} + +void MP4::Tag::addItem(const String &name, const Item &value) +{ + if(!d->items.contains(name)) { + d->items.insert(name, value); + } + else { + debug("MP4: Ignoring duplicate atom \"" + name + "\""); + } +} diff --git a/3rdparty/taglib/mp4/mp4tag.h b/3rdparty/taglib/mp4/mp4tag.h index b5ea6ebbc..48d71fcb9 100644 --- a/3rdparty/taglib/mp4/mp4tag.h +++ b/3rdparty/taglib/mp4/mp4tag.h @@ -67,6 +67,10 @@ namespace TagLib { ItemListMap &itemListMap(); + PropertyMap properties() const; + void removeUnsupportedProperties(const StringList& properties); + PropertyMap setProperties(const PropertyMap &properties); + private: AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); @@ -101,6 +105,8 @@ namespace TagLib { void saveNew(TagLib::ByteVector &data); void saveExisting(TagLib::ByteVector &data, AtomList &path); + void addItem(const String &name, const Item &value); + class TagPrivate; TagPrivate *d; }; diff --git a/3rdparty/taglib/mpc/mpcfile.cpp b/3rdparty/taglib/mpc/mpcfile.cpp index 519a04675..979c035e9 100644 --- a/3rdparty/taglib/mpc/mpcfile.cpp +++ b/3rdparty/taglib/mpc/mpcfile.cpp @@ -39,7 +39,7 @@ using namespace TagLib; namespace { - enum { MPCAPEIndex, MPCID3v1Index }; + enum { MPCAPEIndex = 0, MPCID3v1Index = 1 }; } class MPC::File::FilePrivate @@ -94,14 +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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } MPC::File::~File() @@ -133,15 +135,11 @@ void MPC::File::removeUnsupportedProperties(const StringList &properties) PropertyMap MPC::File::setProperties(const PropertyMap &properties) { - if(d->hasAPE) - return d->tag.access(MPCAPEIndex, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(MPCID3v1Index, false)->setProperties(properties); - else - return d->tag.access(APE, true)->setProperties(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; @@ -258,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 diff --git a/3rdparty/taglib/mpc/mpcfile.h b/3rdparty/taglib/mpc/mpcfile.h index 167b768e1..1eef81032 100644 --- a/3rdparty/taglib/mpc/mpcfile.h +++ b/3rdparty/taglib/mpc/mpcfile.h @@ -84,20 +84,22 @@ 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); /*! - * 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 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); @@ -124,8 +126,8 @@ namespace TagLib { /*! * Implements the unified property interface -- import function. - * As with the export, only one tag is taken into account. If the file - * has no tag at all, an APE tag will be created. + * 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 &); @@ -143,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); @@ -185,6 +198,20 @@ 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 &); File &operator=(const File &); diff --git a/3rdparty/taglib/mpc/mpcproperties.cpp b/3rdparty/taglib/mpc/mpcproperties.cpp index 23d419872..d406f8d73 100644 --- a/3rdparty/taglib/mpc/mpcproperties.cpp +++ b/3rdparty/taglib/mpc/mpcproperties.cpp @@ -207,7 +207,7 @@ void MPC::Properties::readSV8(File *file) d->sampleFrames = readSize(data.mid(pos), pos); ulong begSilence = readSize(data.mid(pos), pos); - std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(pos, 2).toUShort(true))); + std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true))); pos += 2; d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]]; @@ -228,10 +228,10 @@ void MPC::Properties::readSV8(File *file) int replayGainVersion = data[0]; if(replayGainVersion == 1) { - d->trackGain = data.mid(1, 2).toUInt(true); - d->trackPeak = data.mid(3, 2).toUInt(true); - d->albumGain = data.mid(5, 2).toUInt(true); - d->albumPeak = data.mid(7, 2).toUInt(true); + d->trackGain = data.toShort(1, true); + d->trackPeak = data.toShort(3, true); + d->albumGain = data.toShort(5, true); + d->albumPeak = data.toShort(7, true); } } @@ -252,18 +252,18 @@ void MPC::Properties::readSV7(const ByteVector &data) if(d->version < 7) return; - d->totalFrames = data.mid(4, 4).toUInt(false); + d->totalFrames = data.toUInt(4, false); - std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(8, 4).toUInt(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.mid(5, 4).toUInt(false); + uint gapless = data.toUInt(5, false); - d->trackGain = data.mid(14,2).toShort(false); - d->trackPeak = data.mid(12,2).toUInt(false); - d->albumGain = data.mid(18,2).toShort(false); - d->albumPeak = data.mid(16,2).toUInt(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) { @@ -293,7 +293,7 @@ void MPC::Properties::readSV7(const ByteVector &data) d->sampleFrames = d->totalFrames * 1152 - 576; } else { - uint headerData = data.mid(0, 4).toUInt(false); + uint headerData = data.toUInt(0, false); d->bitrate = (headerData >> 23) & 0x01ff; d->version = (headerData >> 11) & 0x03ff; @@ -301,9 +301,9 @@ void MPC::Properties::readSV7(const ByteVector &data) d->channels = 2; if(d->version >= 5) - d->totalFrames = data.mid(4, 4).toUInt(false); + d->totalFrames = data.toUInt(4, false); else - d->totalFrames = data.mid(6, 2).toUInt(false); + d->totalFrames = data.toUShort(6, false); d->sampleFrames = d->totalFrames * 1152 - 576; } diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp b/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp index e9eb47bf1..9fc8cfd7d 100644 --- a/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/3rdparty/taglib/mpeg/id3v1/id3v1tag.cpp @@ -182,16 +182,26 @@ 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 (handler) diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h b/3rdparty/taglib/mpeg/id3v1/id3v1tag.h index 9332d4dce..7b26d023e 100644 --- a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h +++ b/3rdparty/taglib/mpeg/id3v1/id3v1tag.h @@ -140,16 +140,31 @@ 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 diff --git a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp index cfe8c9f44..3d4429f7d 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.cpp +++ b/3rdparty/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/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp index 955b3ad03..e3efbc380 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/3rdparty/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/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 3287063ca..70ea50f8f 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -381,18 +381,12 @@ void UserTextIdentificationFrame::setDescription(const String &s) PropertyMap UserTextIdentificationFrame::asProperties() const { - String tagName = description(); - PropertyMap map; - String key = tagName.upper(); - if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list - map.unsupportedData().append(L"TXXX/" + description()); - else { - StringList v = fieldList(); - for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it) - if(*it != description()) - map.insert(key, *it); - } + 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; } diff --git a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp index e12583ade..a0e842e00 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ b/3rdparty/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/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h index 0cf4b8f6e..add5a2e0c 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h @@ -94,6 +94,16 @@ 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; diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp index e15adc8c0..c5c5585d4 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp @@ -44,6 +44,7 @@ #include "frames/urllinkframe.h" #include "frames/unsynchronizedlyricsframe.h" #include "frames/commentsframe.h" +#include "frames/uniquefileidentifierframe.h" #include "frames/unknownframe.h" using namespace TagLib; @@ -120,16 +121,20 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8); frame->setText(values); return frame; - } else if(values.size() == 1){ // URL frame (not WXXX); support only one value + } 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(); + UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8); frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size())); frame->setText(values.front()); return frame; @@ -144,12 +149,14 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // // -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); - frame->setDescription(key == "COMMENT" ? key : key.substr(commentPrefix.size())); + 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(key, values, String::UTF8); + return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8); } Frame::~Frame() @@ -348,7 +355,7 @@ static const char *frameTranslation[][2] = { { "TLAN", "LANGUAGE" }, { "TLEN", "LENGTH" }, //{ "TMCL", "MUSICIANCREDITS" }, handled separately - { "TMED", "MEDIATYPE" }, + { "TMED", "MEDIA" }, { "TMOO", "MOOD" }, { "TOAL", "ORIGINALALBUM" }, { "TOFN", "ORIGINALFILENAME" }, @@ -361,7 +368,7 @@ static const char *frameTranslation[][2] = { { "TPE4", "REMIXER" }, // could also be ARRANGER { "TPOS", "DISCNUMBER" }, { "TPRO", "PRODUCEDNOTICE" }, - { "TPUB", "PUBLISHER" }, + { "TPUB", "LABEL" }, { "TRCK", "TRACKNUMBER" }, { "TRSN", "RADIOSTATION" }, { "TRSO", "RADIOSTATIONOWNER" }, @@ -385,6 +392,18 @@ static const char *frameTranslation[][2] = { //{ "USLT", "LYRICS" }, handled specially }; +static const TagLib::uint txxxFrameTranslationSize = 7; +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; @@ -394,6 +413,18 @@ Map &idMap() 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] = { @@ -433,6 +464,26 @@ ByteVector Frame::keyToFrameID(const String &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)) { @@ -454,6 +505,8 @@ PropertyMap Frame::asProperties() const 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; @@ -589,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; } @@ -617,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]); @@ -664,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; } diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h b/3rdparty/taglib/mpeg/id3v2/id3v2frame.h index 95c4070b4..3e257d4f5 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2frame.h @@ -274,6 +274,15 @@ namespace TagLib { */ 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 diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp index 7e2e98a8a..3371ca7dd 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -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,6 +107,17 @@ 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 < '0' || *it > '9') ) { delete header; @@ -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); diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp index d4bab6795..4acfd9147 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2synchdata.cpp +++ b/3rdparty/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/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp index 54e639203..57637a36e 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp @@ -23,6 +23,10 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include "id3v2tag.h" @@ -379,10 +383,12 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++) if (dynamic_cast(*fit) != 0) removeFrame(*fit); - } else if(it->size() == 4){ + } + else if(it->size() == 4){ ByteVector id = it->data(String::Latin1); removeFrames(id); - } else { + } + else { ByteVector id = it->substr(0,4).data(String::Latin1); if(it->size() <= 5) continue; // invalid specification @@ -396,6 +402,8 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) 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); } diff --git a/3rdparty/taglib/mpeg/mpegfile.cpp b/3rdparty/taglib/mpeg/mpegfile.cpp index 6ebff897a..f765befb0 100644 --- a/3rdparty/taglib/mpeg/mpegfile.cpp +++ b/3rdparty/taglib/mpeg/mpegfile.cpp @@ -156,16 +156,13 @@ void MPEG::File::removeUnsupportedProperties(const StringList &properties) else if(d->hasID3v1) d->tag.access(ID3v1Index, false)->removeUnsupportedProperties(properties); } + PropertyMap MPEG::File::setProperties(const PropertyMap &properties) { - if(d->hasID3v2) - return d->tag.access(ID3v2Index, false)->setProperties(properties); - else if(d->hasAPE) - return d->tag.access(APEIndex, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(ID3v1Index, false)->setProperties(properties); - else - return d->tag.access(ID3v2Index, true)->setProperties(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 @@ -189,6 +186,11 @@ bool MPEG::File::save(int tags, bool stripOthers) } 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); @@ -206,14 +208,19 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version) return false; } - // Create the tags if we've been asked to. Copy the values from the tag that - // does exist into the new tag, except if the existing tag is to be stripped. + // Create the tags if we've been asked to. - if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1))) - Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false); + if (duplicateTags) { - if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2))) - 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; @@ -437,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 //////////////////////////////////////////////////////////////////////////////// @@ -634,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/3rdparty/taglib/mpeg/mpegfile.h b/3rdparty/taglib/mpeg/mpegfile.h index 9967a9912..3fc01e680 100644 --- a/3rdparty/taglib/mpeg/mpegfile.h +++ b/3rdparty/taglib/mpeg/mpegfile.h @@ -71,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. @@ -82,28 +83,31 @@ 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. * - * \deprecated This constructor will be dropped in favor of the one below - * in a future version. + * \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); /*! - * Contructs an MPEG file from \a stream. 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. + * 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. */ - // BIC: merge with the above constructor File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); @@ -133,7 +137,7 @@ namespace TagLib { virtual Tag *tag() const; /*! - * Implements the unified property interface -- export function. + * 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. @@ -143,9 +147,12 @@ namespace TagLib { void removeUnsupportedProperties(const StringList &properties); /*! - * Implements the unified tag dictionary interface -- import function. - * As with the export, only one tag is taken into account. If the file - * has no tag at all, ID3v2 will be created. + * 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 &); @@ -210,42 +217,78 @@ namespace TagLib { // 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. * - * A tag will always be returned, regardless of whether there is a - * tag in the file or not. Use ID3v2::Tag::isEmpty() to check if - * the tag contains no data. + * 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 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. * - * A tag will always be returned, regardless of whether there is a - * tag in the file or not. Use Tag::isEmpty() to check if - * the tag contains no data. + * 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 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); @@ -301,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/3rdparty/taglib/mpeg/mpegheader.cpp b/3rdparty/taglib/mpeg/mpegheader.cpp index c715dbc14..a582f7589 100644 --- a/3rdparty/taglib/mpeg/mpegheader.cpp +++ b/3rdparty/taglib/mpeg/mpegheader.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "trefcounter.h" #include "mpegheader.h" diff --git a/3rdparty/taglib/mpeg/xingheader.cpp b/3rdparty/taglib/mpeg/xingheader.cpp index 1ba932de3..9e20127ee 100644 --- a/3rdparty/taglib/mpeg/xingheader.cpp +++ b/3rdparty/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/3rdparty/taglib/ogg/flac/oggflacfile.cpp b/3rdparty/taglib/ogg/flac/oggflacfile.cpp index 9d9c303d7..bdf824595 100644 --- a/3rdparty/taglib/ogg/flac/oggflacfile.cpp +++ b/3rdparty/taglib/ogg/flac/oggflacfile.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "oggflacfile.h" @@ -72,14 +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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Ogg::FLAC::File::~File() @@ -92,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; @@ -124,6 +137,11 @@ bool Ogg::FLAC::File::save() return Ogg::File::save(); } +bool Ogg::FLAC::File::hasXiphComment() const +{ + return d->hasXiphComment; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -228,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 @@ -238,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 @@ -251,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) { diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.h b/3rdparty/taglib/ogg/flac/oggflacfile.h index 8558cfdfa..8d87e486b 100644 --- a/3rdparty/taglib/ogg/flac/oggflacfile.h +++ b/3rdparty/taglib/ogg/flac/oggflacfile.h @@ -64,20 +64,22 @@ 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); /*! - * 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 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); @@ -89,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; @@ -98,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. @@ -110,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/3rdparty/taglib/ogg/oggfile.h b/3rdparty/taglib/ogg/oggfile.h index b36daecb1..8fed4ba09 100644 --- a/3rdparty/taglib/ogg/oggfile.h +++ b/3rdparty/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 @@ -93,9 +91,7 @@ namespace TagLib { File(FileName file); /*! - * 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 stream. * * \note This constructor is protected since Ogg::File shouldn't be * instantiated directly but rather should be used through the codec diff --git a/3rdparty/taglib/ogg/oggpageheader.cpp b/3rdparty/taglib/ogg/oggpageheader.cpp index f9608ab7f..b93331354 100644 --- a/3rdparty/taglib/ogg/oggpageheader.cpp +++ b/3rdparty/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/3rdparty/taglib/ogg/opus/opusfile.cpp b/3rdparty/taglib/ogg/opus/opusfile.cpp new file mode 100644 index 000000000..cb81a32b0 --- /dev/null +++ b/3rdparty/taglib/ogg/opus/opusfile.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include + +#include +#include +#include + +#include "opusfile.h" + +using namespace TagLib; +using namespace TagLib::Ogg; + +class Opus::File::FilePrivate +{ +public: + FilePrivate() : + comment(0), + properties(0) {} + + ~FilePrivate() + { + delete comment; + delete properties; + } + + Ogg::XiphComment *comment; + Properties *properties; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : + Ogg::File(file), + d(new FilePrivate()) +{ + if(isOpen()) + read(readProperties, propertiesStyle); +} + +Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : + Ogg::File(stream), + d(new FilePrivate()) +{ + if(isOpen()) + read(readProperties, propertiesStyle); +} + +Opus::File::~File() +{ + delete d; +} + +Ogg::XiphComment *Opus::File::tag() const +{ + return d->comment; +} + +PropertyMap Opus::File::properties() const +{ + return d->comment->properties(); +} + +PropertyMap Opus::File::setProperties(const PropertyMap &properties) +{ + return d->comment->setProperties(properties); +} + +Opus::Properties *Opus::File::audioProperties() const +{ + return d->properties; +} + +bool Opus::File::save() +{ + if(!d->comment) + d->comment = new Ogg::XiphComment; + + setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); + + return Ogg::File::save(); +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +{ + ByteVector opusHeaderData = packet(0); + + if(!opusHeaderData.startsWith("OpusHead")) { + setValid(false); + debug("Opus::File::read() -- invalid Opus identification header"); + return; + } + + ByteVector commentHeaderData = packet(1); + + if(!commentHeaderData.startsWith("OpusTags")) { + setValid(false); + debug("Opus::File::read() -- invalid Opus tags header"); + return; + } + + d->comment = new Ogg::XiphComment(commentHeaderData.mid(8)); + + if(readProperties) + d->properties = new Properties(this, propertiesStyle); +} diff --git a/3rdparty/taglib/ogg/opus/opusfile.h b/3rdparty/taglib/ogg/opus/opusfile.h new file mode 100644 index 000000000..275167e40 --- /dev/null +++ b/3rdparty/taglib/ogg/opus/opusfile.h @@ -0,0 +1,124 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) +***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_OPUSFILE_H +#define TAGLIB_OPUSFILE_H + +#include "oggfile.h" +#include "xiphcomment.h" + +#include "opusproperties.h" + +namespace TagLib { + + namespace Ogg { + + //! A namespace containing classes for Opus metadata + + namespace Opus { + + //! An implementation of Ogg::File with Opus specific methods + + /*! + * This is the central class in the Ogg Opus metadata processing collection + * of classes. It's built upon Ogg::File which handles processing of the Ogg + * logical bitstream and breaking it down into pages which are handled by + * the codec implementations, in this case Opus specifically. + */ + + class TAGLIB_EXPORT File : public Ogg::File + { + public: + /*! + * Constructs an Opus file from \a file. If \a readProperties is true the + * file's audio properties will also be read. + * + * \note In the current implementation, \a propertiesStyle is ignored. + */ + File(FileName file, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Constructs an Opus file from \a stream. If \a readProperties is 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. + */ + virtual ~File(); + + /*! + * Returns the XiphComment for this file. XiphComment implements the tag + * interface, so this serves as the reimplementation of + * TagLib::File::tag(). + */ + virtual Ogg::XiphComment *tag() const; + + /*! + * 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 Opus::Properties for this file. If no audio properties + * were read then this will return a null pointer. + */ + virtual Properties *audioProperties() const; + + virtual bool save(); + + private: + File(const File &); + File &operator=(const File &); + + void read(bool readProperties, Properties::ReadStyle propertiesStyle); + + class FilePrivate; + FilePrivate *d; + }; + } + } +} + +#endif diff --git a/3rdparty/taglib/ogg/opus/opusproperties.cpp b/3rdparty/taglib/ogg/opus/opusproperties.cpp new file mode 100644 index 000000000..7bdcd39df --- /dev/null +++ b/3rdparty/taglib/ogg/opus/opusproperties.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include +#include + +#include + +#include "opusproperties.h" +#include "opusfile.h" + +using namespace TagLib; +using namespace TagLib::Ogg; + +class Opus::Properties::PropertiesPrivate +{ +public: + PropertiesPrivate(File *f, ReadStyle s) : + file(f), + style(s), + length(0), + inputSampleRate(0), + channels(0), + opusVersion(0) {} + + File *file; + ReadStyle style; + int length; + int inputSampleRate; + int channels; + int opusVersion; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +{ + d = new PropertiesPrivate(file, style); + read(); +} + +Opus::Properties::~Properties() +{ + delete d; +} + +int Opus::Properties::length() const +{ + return d->length; +} + +int Opus::Properties::bitrate() const +{ + return 0; +} + +int Opus::Properties::sampleRate() const +{ + // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz, + // so there is no single sample rate. Let's assume it's the highest + // possible. + return 48000; +} + +int Opus::Properties::channels() const +{ + return d->channels; +} + +int Opus::Properties::inputSampleRate() const +{ + return d->inputSampleRate; +} + +int Opus::Properties::opusVersion() const +{ + return d->opusVersion; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void Opus::Properties::read() +{ + // Get the identification header from the Ogg implementation. + + // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1 + + ByteVector data = d->file->packet(0); + + // *Magic Signature* + uint pos = 8; + + // *Version* (8 bits, unsigned) + d->opusVersion = uchar(data.at(pos)); + pos += 1; + + // *Output Channel Count* 'C' (8 bits, unsigned) + d->channels = uchar(data.at(pos)); + pos += 1; + + // *Pre-skip* (16 bits, unsigned, little endian) + const ushort preSkip = data.toUShort(pos, false); + pos += 2; + + // *Input Sample Rate* (32 bits, unsigned, little endian) + d->inputSampleRate = data.toUInt(pos, false); + pos += 4; + + // *Output Gain* (16 bits, signed, little endian) + pos += 2; + + // *Channel Mapping Family* (8 bits, unsigned) + pos += 1; + + const Ogg::PageHeader *first = d->file->firstPageHeader(); + const Ogg::PageHeader *last = d->file->lastPageHeader(); + + if(first && last) { + long long start = first->absoluteGranularPosition(); + long long end = last->absoluteGranularPosition(); + + if(start >= 0 && end >= 0) + d->length = (int) ((end - start - preSkip) / 48000); + else { + debug("Opus::Properties::read() -- The PCM values for the start or " + "end of this file was incorrect."); + } + } + else + debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages."); +} diff --git a/3rdparty/taglib/ogg/opus/opusproperties.h b/3rdparty/taglib/ogg/opus/opusproperties.h new file mode 100644 index 000000000..946f16757 --- /dev/null +++ b/3rdparty/taglib/ogg/opus/opusproperties.h @@ -0,0 +1,96 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) +***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_OPUSPROPERTIES_H +#define TAGLIB_OPUSPROPERTIES_H + +#include "audioproperties.h" + +namespace TagLib { + + namespace Ogg { + + namespace Opus { + + class File; + + //! An implementation of audio property reading for Ogg Opus + + /*! + * This reads the data from an Ogg Opus stream found in the AudioProperties + * API. + */ + + class TAGLIB_EXPORT Properties : public AudioProperties + { + public: + /*! + * Create an instance of Opus::Properties with the data read from the + * Opus::File \a file. + */ + Properties(File *file, ReadStyle style = Average); + + /*! + * Destroys this Opus::Properties instance. + */ + virtual ~Properties(); + + // Reimplementations. + + virtual int length() const; + virtual int bitrate() const; + virtual int sampleRate() const; + virtual int channels() const; + + /*! + * The Opus codec supports decoding at multiple sample rates, there is no + * single sample rate of the encoded stream. This returns the sample rate + * of the original audio stream. + */ + int inputSampleRate() const; + + /*! + * Returns the Opus version, currently "0" (as specified by the spec). + */ + int opusVersion() const; + + private: + Properties(const Properties &); + Properties &operator=(const Properties &); + + void read(); + + class PropertiesPrivate; + PropertiesPrivate *d; + }; + } + } +} + +#endif diff --git a/3rdparty/taglib/ogg/speex/speexfile.cpp b/3rdparty/taglib/ogg/speex/speexfile.cpp index 3a4940a26..e83f0ad9b 100644 --- a/3rdparty/taglib/ogg/speex/speexfile.cpp +++ b/3rdparty/taglib/ogg/speex/speexfile.cpp @@ -31,6 +31,7 @@ #include #include +#include #include "speexfile.h" @@ -62,14 +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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Speex::File::~File() @@ -82,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/3rdparty/taglib/ogg/speex/speexfile.h b/3rdparty/taglib/ogg/speex/speexfile.h index dfe51ec4d..880e68904 100644 --- a/3rdparty/taglib/ogg/speex/speexfile.h +++ b/3rdparty/taglib/ogg/speex/speexfile.h @@ -56,20 +56,22 @@ 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); /*! - * 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 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); @@ -86,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/3rdparty/taglib/ogg/speex/speexproperties.cpp b/3rdparty/taglib/ogg/speex/speexproperties.cpp index 980f12dfd..5aaa91530 100644 --- a/3rdparty/taglib/ogg/speex/speexproperties.cpp +++ b/3rdparty/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/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp b/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp index e2eed9e25..82984536b 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp +++ b/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp @@ -67,14 +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; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Vorbis::File::~File() diff --git a/3rdparty/taglib/ogg/vorbis/vorbisfile.h b/3rdparty/taglib/ogg/vorbis/vorbisfile.h index 6e4d4fc42..7735a11be 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.h +++ b/3rdparty/taglib/ogg/vorbis/vorbisfile.h @@ -63,20 +63,22 @@ 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); /*! - * 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 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); diff --git a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp b/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp index c67e16773..b5e88bfd0 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/3rdparty/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; diff --git a/3rdparty/taglib/ogg/xiphcomment.cpp b/3rdparty/taglib/ogg/xiphcomment.cpp index 675614b3f..51c2f9f04 100644 --- a/3rdparty/taglib/ogg/xiphcomment.cpp +++ b/3rdparty/taglib/ogg/xiphcomment.cpp @@ -340,7 +340,7 @@ void Ogg::XiphComment::parse(const ByteVector &data) 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); @@ -348,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) { @@ -360,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/3rdparty/taglib/riff/aiff/aifffile.cpp b/3rdparty/taglib/riff/aiff/aifffile.cpp index ece84f058..d20c148be 100644 --- a/3rdparty/taglib/riff/aiff/aifffile.cpp +++ b/3rdparty/taglib/riff/aiff/aifffile.cpp @@ -90,6 +90,11 @@ PropertyMap RIFF::AIFF::File::properties() const return d->tag->properties(); } +void RIFF::AIFF::File::removeUnsupportedProperties(const StringList &unsupported) +{ + d->tag->removeUnsupportedProperties(unsupported); +} + PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties) { return d->tag->setProperties(properties); @@ -118,6 +123,7 @@ bool RIFF::AIFF::File::save() return true; } + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/riff/aiff/aifffile.h b/3rdparty/taglib/riff/aiff/aifffile.h index e1284db08..c75b94b4c 100644 --- a/3rdparty/taglib/riff/aiff/aifffile.h +++ b/3rdparty/taglib/riff/aiff/aifffile.h @@ -58,20 +58,22 @@ namespace TagLib { { 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. + * Constructs an AIFF 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); /*! - * 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. + * Constructs an AIFF 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); @@ -83,6 +85,12 @@ namespace TagLib { /*! * Returns the Tag for this file. + * + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file + * on disk actually has an ID3v2 tag. + * + * \see hasID3v2Tag() */ virtual ID3v2::Tag *tag() const; @@ -92,6 +100,8 @@ namespace TagLib { */ PropertyMap properties() const; + void removeUnsupportedProperties(const StringList &properties); + /*! * Implements the unified property interface -- import function. * This method forwards to ID3v2::Tag::setProperties(). @@ -109,6 +119,13 @@ namespace TagLib { */ virtual bool save(); + /*! + * 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/3rdparty/taglib/riff/aiff/aiffproperties.cpp b/3rdparty/taglib/riff/aiff/aiffproperties.cpp index 3ea275825..18b973eb0 100644 --- a/3rdparty/taglib/riff/aiff/aiffproperties.cpp +++ b/3rdparty/taglib/riff/aiff/aiffproperties.cpp @@ -39,7 +39,7 @@ #define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) -static double ConvertFromIeeeExtended(unsigned char *bytes) +static double ConvertFromIeeeExtended(const TagLib::uchar *bytes) { double f; int expon; @@ -150,10 +150,10 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const void RIFF::AIFF::Properties::read(const ByteVector &data) { - d->channels = data.mid(0, 2).toShort(); - d->sampleFrames = data.mid(2, 4).toUInt(); - d->sampleWidth = data.mid(6, 2).toShort(); - double sampleRate = ConvertFromIeeeExtended(reinterpret_cast(data.mid(8, 10).data())); + d->channels = data.toShort(0U); + d->sampleFrames = data.toUInt(2U); + d->sampleWidth = data.toShort(6U); + double sampleRate = ConvertFromIeeeExtended(reinterpret_cast(data.data() + 8)); d->sampleRate = (int)sampleRate; d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; diff --git a/3rdparty/taglib/riff/rifffile.cpp b/3rdparty/taglib/riff/rifffile.cpp index df3b36635..20eec5ffe 100644 --- a/3rdparty/taglib/riff/rifffile.cpp +++ b/3rdparty/taglib/riff/rifffile.cpp @@ -138,34 +138,49 @@ ByteVector RIFF::File::chunkData(uint i) return readBlock(d->chunks[i].size); } +void RIFF::File::setChunkData(uint i, const ByteVector &data) +{ + // 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(chunkName(i), 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; +} + void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) +{ + setChunkData(name, data, false); +} + +void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { if(d->chunks.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) { + if(alwaysCreate && name != "LIST") { + debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks."); + return; + } - // 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; + if(!alwaysCreate) { + for(uint i = 0; i < d->chunks.size(); i++) { + if(d->chunks[i].name == name) { + setChunkData(i, data); + return; + } } } @@ -181,7 +196,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) // Now add the chunk to the file - writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0); + writeChunk(name, data, offset, std::max(0, length() - offset), (offset & 1) ? 1 : 0); // And update our internal structure @@ -199,6 +214,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) d->chunks.push_back(chunk); } +void RIFF::File::removeChunk(uint i) +{ + if(i >= d->chunks.size()) + return; + + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + d->chunks.erase(d->chunks.begin() + i); +} + +void RIFF::File::removeChunk(const ByteVector &name) +{ + std::vector newChunks; + for(size_t i = 0; i < d->chunks.size(); ++i) { + if(d->chunks[i].name == name) + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + else + newChunks.push_back(d->chunks[i]); + } + + d->chunks.swap(newChunks); +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/riff/rifffile.h b/3rdparty/taglib/riff/rifffile.h index e418dbb67..6b8fe197f 100644 --- a/3rdparty/taglib/riff/rifffile.h +++ b/3rdparty/taglib/riff/rifffile.h @@ -95,6 +95,13 @@ namespace TagLib { */ ByteVector chunkData(uint i); + /*! + * Sets the data for the the specified chunk to \a data. + * + * \warning This will update the file immediately. + */ + void setChunkData(uint i, const ByteVector &data); + /*! * Sets the data for the chunk \a name to \a data. If a chunk with the * given name already exists it will be overwritten, otherwise it will be @@ -104,6 +111,34 @@ namespace TagLib { */ void setChunkData(const ByteVector &name, const ByteVector &data); + /*! + * Sets the data for the chunk \a name to \a data. If a chunk with the + * given name already exists it will be overwritten, otherwise it will be + * created after the existing chunks. + * + * \note If \a alwaysCreate is true, a new chunk is created regardless of + * whether or not the chunk \a name exists. It should only be used for + * "LIST" chunks. + * + * \warning This will update the file immediately. + */ + void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate); + + /*! + * Removes the specified chunk. + * + * \warning This will update the file immediately. + */ + void removeChunk(uint i); + + /*! + * Removes the chunk \a name. + * + * \warning This will update the file immediately. + * \warning This removes all the chunks with the given name. + */ + void removeChunk(const ByteVector &name); + private: File(const File &); File &operator=(const File &); diff --git a/3rdparty/taglib/riff/wav/infotag.cpp b/3rdparty/taglib/riff/wav/infotag.cpp new file mode 100644 index 000000000..658ede886 --- /dev/null +++ b/3rdparty/taglib/riff/wav/infotag.cpp @@ -0,0 +1,261 @@ +/*************************************************************************** + copyright : (C) 2012 by Tsuda Kageyu + 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 "infotag.h" + +using namespace TagLib; +using namespace RIFF::Info; + +namespace { + 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; + } +} + +class RIFF::Info::Tag::TagPrivate +{ +public: + TagPrivate() + {} + + FieldListMap fieldListMap; + + static const StringHandler *stringHandler; +}; + +//////////////////////////////////////////////////////////////////////////////// +// StringHandler implementation +//////////////////////////////////////////////////////////////////////////////// + +StringHandler::StringHandler() +{ +} + +StringHandler::~StringHandler() +{ +} + +String RIFF::Info::StringHandler::parse(const ByteVector &data) const +{ + return String(data, String::UTF8); +} + +ByteVector RIFF::Info::StringHandler::render(const String &s) const +{ + return s.data(String::UTF8); +} + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +static const StringHandler defaultStringHandler; +const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler; + +RIFF::Info::Tag::Tag(const ByteVector &data) + : TagLib::Tag() + , d(new TagPrivate()) +{ + parse(data); +} + +RIFF::Info::Tag::Tag() + : TagLib::Tag() + , d(new TagPrivate()) +{ +} + +RIFF::Info::Tag::~Tag() +{ + delete d; +} + +String RIFF::Info::Tag::title() const +{ + return fieldText("INAM"); +} + +String RIFF::Info::Tag::artist() const +{ + return fieldText("IART"); +} + +String RIFF::Info::Tag::album() const +{ + return fieldText("IPRD"); +} + +String RIFF::Info::Tag::comment() const +{ + return fieldText("ICMT"); +} + +String RIFF::Info::Tag::genre() const +{ + return fieldText("IGNR"); +} + +TagLib::uint RIFF::Info::Tag::year() const +{ + return fieldText("ICRD").substr(0, 4).toInt(); +} + +TagLib::uint RIFF::Info::Tag::track() const +{ + return fieldText("IPRT").toInt(); +} + +void RIFF::Info::Tag::setTitle(const String &s) +{ + setFieldText("INAM", s); +} + +void RIFF::Info::Tag::setArtist(const String &s) +{ + setFieldText("IART", s); +} + +void RIFF::Info::Tag::setAlbum(const String &s) +{ + setFieldText("IPRD", s); +} + +void RIFF::Info::Tag::setComment(const String &s) +{ + setFieldText("ICMT", s); +} + +void RIFF::Info::Tag::setGenre(const String &s) +{ + setFieldText("IGNR", s); +} + +void RIFF::Info::Tag::setYear(uint i) +{ + if(i != 0) + setFieldText("ICRD", String::number(i)); + else + d->fieldListMap.erase("ICRD"); +} + +void RIFF::Info::Tag::setTrack(uint i) +{ + if(i != 0) + setFieldText("IPRT", String::number(i)); + else + d->fieldListMap.erase("IPRT"); +} + +bool RIFF::Info::Tag::isEmpty() const +{ + return d->fieldListMap.isEmpty(); +} + +String RIFF::Info::Tag::fieldText(const ByteVector &id) const +{ + if(d->fieldListMap.contains(id)) + return String(d->fieldListMap[id]); + else + return String(); +} + +void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) +{ + // id must be four-byte long pure ascii string. + if(!isValidChunkID(id)) + return; + + if(!s.isEmpty()) + d->fieldListMap[id] = s; + else + removeField(id); +} + +void RIFF::Info::Tag::removeField(const ByteVector &id) +{ + if(d->fieldListMap.contains(id)) + d->fieldListMap.erase(id); +} + +ByteVector RIFF::Info::Tag::render() const +{ + ByteVector data("INFO"); + + FieldListMap::ConstIterator it = d->fieldListMap.begin(); + for(; it != d->fieldListMap.end(); ++it) { + ByteVector text = TagPrivate::stringHandler->render(it->second); + if(text.isEmpty()) + continue; + + data.append(it->first); + data.append(ByteVector::fromUInt(text.size() + 1, false)); + data.append(text); + + do { + data.append('\0'); + } while(data.size() & 1); + } + + if(data.size() == 4) + return ByteVector(); + else + return data; +} + +void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) +{ + if(handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +void RIFF::Info::Tag::parse(const ByteVector &data) +{ + uint p = 4; + while(p < data.size()) { + const uint size = data.toUInt(p + 4, false); + d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + + p += ((size + 1) & ~1) + 8; + } +} + diff --git a/3rdparty/taglib/riff/wav/infotag.h b/3rdparty/taglib/riff/wav/infotag.h new file mode 100644 index 000000000..9a26d4cb4 --- /dev/null +++ b/3rdparty/taglib/riff/wav/infotag.h @@ -0,0 +1,180 @@ +/*************************************************************************** + copyright : (C) 2012 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_INFOTAG_H +#define TAGLIB_INFOTAG_H + +#include "tag.h" +#include "tmap.h" +#include "tstring.h" +#include "tstringlist.h" +#include "tbytevector.h" +#include "taglib_export.h" + +namespace TagLib { + + class File; + + //! A RIFF Info tag implementation. + namespace RIFF { + namespace Info { + + typedef Map FieldListMap; + + //! A abstraction for the string to data encoding in Info tags. + + /*! + * RIFF Info tag has no clear definitions about character encodings. + * In practice, local encoding of each system is largely used and UTF-8 is + * popular too. + * + * Here is an option to read and write tags in your preferrd encoding + * by subclassing this class, reimplementing parse() and render() and setting + * your reimplementation as the default with Info::Tag::setStringHandler(). + * + * \see ID3v1::Tag::setStringHandler() + */ + + class TAGLIB_EXPORT StringHandler + { + public: + StringHandler(); + ~StringHandler(); + + /*! + * Decode a string from \a data. The default implementation assumes that + * \a data is an UTF-8 character array. + */ + virtual String parse(const ByteVector &data) const; + + /*! + * Encode a ByteVector with the data from \a s. The default implementation + * assumes that \a s is an UTF-8 string. + */ + virtual ByteVector render(const String &s) const; + }; + + //! The main class in the ID3v2 implementation + + /*! + * This is the main class in the INFO tag implementation. RIFF INFO tag is a + * metadata format found in WAV audio and AVI video files. Though it is a part + * of Microsoft/IBM's RIFF specification, the author could not find the official + * documents about it. So, this implementation is referring to unofficial documents + * online and some applications' behaviors especially Windows Explorer. + */ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: + /*! + * Constructs an empty Info tag. + */ + Tag(); + + /*! + * Constructs an Info tag read from \a data which is contents of "LIST" chunk. + */ + Tag(const ByteVector &data); + + virtual ~Tag(); + + // Reimplementations + + virtual String title() const; + virtual String artist() const; + virtual String album() const; + virtual String comment() const; + virtual String genre() const; + virtual uint year() const; + virtual uint track() const; + + virtual void setTitle(const String &s); + virtual void setArtist(const String &s); + virtual void setAlbum(const String &s); + virtual void setComment(const String &s); + virtual void setGenre(const String &s); + virtual void setYear(uint i); + virtual void setTrack(uint i); + + virtual bool isEmpty() const; + + /* + * Gets the value of the field with the ID \a id. + */ + String fieldText(const ByteVector &id) const; + + /* + * Sets the value of the field with the ID \a id to \a s. + * If the field does not exist, it is created. + * If \s is empty, the field is removed. + * + * \note fieldId must be four-byte long pure ASCII string. This function + * performs nothing if fieldId is invalid. + */ + void setFieldText(const ByteVector &id, const String &s); + + /* + * Removes the field with the ID \a id. + */ + void removeField(const ByteVector &id); + + /*! + * Render the tag back to binary data, suitable to be written to disk. + * + * \note Returns empty ByteVector is the tag contains no fields. + */ + ByteVector render() const; + + /*! + * Sets the string handler that decides how the text data will be + * converted to and from binary data. + * If the parameter \a handler is null, the previous handler is + * released and default UTF-8 handler is restored. + * + * \note The caller is responsible for deleting the previous handler + * as needed after it is released. + * + * \see StringHandler + */ + static void setStringHandler(const StringHandler *handler); + + protected: + /*! + * Pareses the body of the tag in \a data. + */ + void parse(const ByteVector &data); + + + private: + Tag(const Tag &); + Tag &operator=(const Tag &); + + class TagPrivate; + TagPrivate *d; + }; + }} +} + +#endif diff --git a/3rdparty/taglib/riff/wav/wavfile.cpp b/3rdparty/taglib/riff/wav/wavfile.cpp index 613db4ef9..abecea351 100644 --- a/3rdparty/taglib/riff/wav/wavfile.cpp +++ b/3rdparty/taglib/riff/wav/wavfile.cpp @@ -23,36 +23,47 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include -#include -#include -#include -#include +#include "tbytevector.h" +#include "tdebug.h" +#include "tstringlist.h" +#include "tpropertymap.h" #include "wavfile.h" +#include "id3v2tag.h" +#include "infotag.h" +#include "tagunion.h" using namespace TagLib; +namespace +{ + enum { ID3v2Index = 0, InfoIndex = 1 }; +} + class RIFF::WAV::File::FilePrivate { public: FilePrivate() : properties(0), - tag(0), - tagChunkID("ID3 ") + tagChunkID("ID3 "), + hasID3v2(false), + hasInfo(false) { - } ~FilePrivate() { delete properties; - delete tag; } Properties *properties; - ID3v2::Tag *tag; + ByteVector tagChunkID; + + TagUnion tag; + + bool hasID3v2; + bool hasInfo; }; //////////////////////////////////////////////////////////////////////////////// @@ -82,26 +93,45 @@ RIFF::WAV::File::~File() ID3v2::Tag *RIFF::WAV::File::tag() const { - return d->tag; + return ID3v2Tag(); +} + +ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const +{ + return d->tag.access(ID3v2Index, false); +} + +RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const +{ + return d->tag.access(InfoIndex, false); } PropertyMap RIFF::WAV::File::properties() const { - return d->tag->properties(); + return tag()->properties(); +} + +void RIFF::WAV::File::removeUnsupportedProperties(const StringList &unsupported) +{ + tag()->removeUnsupportedProperties(unsupported); } PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties) { - return d->tag->setProperties(properties); + return tag()->setProperties(properties); } - RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const { return d->properties; } bool RIFF::WAV::File::save() +{ + return RIFF::WAV::File::save(AllTags); +} + +bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version) { if(readOnly()) { debug("RIFF::WAV::File::save() -- File is read only."); @@ -113,11 +143,43 @@ bool RIFF::WAV::File::save() return false; } - setChunkData(d->tagChunkID, d->tag->render()); + if(stripOthers) + strip(static_cast(AllTags & ~tags)); + + ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); + if(!id3v2tag->isEmpty()) { + if(tags & ID3v2) { + setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); + d->hasID3v2 = true; + } + } + + Info::Tag *infotag = d->tag.access(InfoIndex, false); + if(!infotag->isEmpty()) { + if(tags & Info) { + int chunkId = findInfoTagChunk(); + if(chunkId != -1) + setChunkData(chunkId, infotag->render()); + else + setChunkData("LIST", infotag->render(), true); + + d->hasInfo = true; + } + } return true; } +bool RIFF::WAV::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} + +bool RIFF::WAV::File::hasInfoTag() const +{ + return d->hasInfo; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -127,19 +189,56 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties ByteVector formatData; uint streamLength = 0; for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { + String name = chunkName(i); + if(name == "ID3 " || name == "id3 ") { d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); + d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); + d->hasID3v2 = true; } - else if(chunkName(i) == "fmt " && readProperties) + else if(name == "fmt " && readProperties) formatData = chunkData(i); - else if(chunkName(i) == "data" && readProperties) + else if(name == "data" && readProperties) streamLength = chunkDataSize(i); + else if(name == "LIST") { + ByteVector data = chunkData(i); + ByteVector type = data.mid(0, 4); + + if(type == "INFO") { + d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); + d->hasInfo = true; + } + } } + if (!d->tag[ID3v2Index]) + d->tag.set(ID3v2Index, new ID3v2::Tag); + + if (!d->tag[InfoIndex]) + d->tag.set(InfoIndex, new RIFF::Info::Tag); + if(!formatData.isEmpty()) d->properties = new Properties(formatData, streamLength, propertiesStyle); - - if(!d->tag) - d->tag = new ID3v2::Tag; +} + +void RIFF::WAV::File::strip(TagTypes tags) +{ + if(tags & ID3v2) + removeChunk(d->tagChunkID); + + if(tags & Info){ + TagLib::uint chunkId = findInfoTagChunk(); + if(chunkId != TagLib::uint(-1)) + removeChunk(chunkId); + } +} + +TagLib::uint RIFF::WAV::File::findInfoTagChunk() +{ + for(uint i = 0; i < chunkCount(); ++i) { + if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") { + return i; + } + } + + return TagLib::uint(-1); } diff --git a/3rdparty/taglib/riff/wav/wavfile.h b/3rdparty/taglib/riff/wav/wavfile.h index 1c4708707..2f83332a2 100644 --- a/3rdparty/taglib/riff/wav/wavfile.h +++ b/3rdparty/taglib/riff/wav/wavfile.h @@ -28,6 +28,7 @@ #include "rifffile.h" #include "id3v2tag.h" +#include "infotag.h" #include "wavproperties.h" namespace TagLib { @@ -57,21 +58,34 @@ namespace TagLib { class TAGLIB_EXPORT File : public TagLib::RIFF::File { public: + enum TagTypes { + //! Empty set. Matches no tag types. + NoTags = 0x0000, + //! Matches ID3v2 tags. + ID3v2 = 0x0001, + //! Matches Info tags. + Info = 0x0002, + //! Matches all tag types. + AllTags = 0xffff + }; + /*! - * 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. + * Constructs a WAV 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); /*! - * 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. + * Constructs a WAV 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); @@ -82,9 +96,34 @@ namespace TagLib { virtual ~File(); /*! - * Returns the Tag for this file. + * Returns the ID3v2 Tag for this file. + * + * \note This method does not return all the tags for this file for + * backward compatibility. Will be fixed in TagLib 2.0. */ - virtual ID3v2::Tag *tag() const; + ID3v2::Tag *tag() const; + + /*! + * Returns the ID3v2 Tag for this file. + * + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the + * file on disk actually has an ID3v2 tag. + * + * \see hasID3v2Tag() + */ + ID3v2::Tag *ID3v2Tag() const; + + /*! + * Returns the RIFF INFO Tag for this file. + * + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the + * file on disk actually has a RIFF INFO tag. + * + * \see hasInfoTag() + */ + Info::Tag *InfoTag() const; /*! * Implements the unified property interface -- export function. @@ -92,6 +131,8 @@ namespace TagLib { */ PropertyMap properties() const; + void removeUnsupportedProperties(const StringList &properties); + /*! * Implements the unified property interface -- import function. * This method forwards to ID3v2::Tag::setProperties(). @@ -109,12 +150,35 @@ namespace TagLib { */ virtual bool save(); + bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4); + + /*! + * Returns whether or not the file on disk actually has an ID3v2 tag. + * + * \see ID3v2Tag() + */ + bool hasID3v2Tag() const; + + /*! + * Returns whether or not the file on disk actually has a RIFF INFO tag. + * + * \see InfoTag() + */ + bool hasInfoTag() const; + private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void strip(TagTypes tags); + + /*! + * Returns the index of the chunk that its name is "LIST" and list type is "INFO". + */ + uint findInfoTagChunk(); + class FilePrivate; FilePrivate *d; }; diff --git a/3rdparty/taglib/riff/wav/wavproperties.cpp b/3rdparty/taglib/riff/wav/wavproperties.cpp index 9e7ea70cc..8062df5f7 100644 --- a/3rdparty/taglib/riff/wav/wavproperties.cpp +++ b/3rdparty/taglib/riff/wav/wavproperties.cpp @@ -115,12 +115,12 @@ TagLib::uint RIFF::WAV::Properties::sampleFrames() const 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); + d->format = data.toShort(0, false); + d->channels = data.toShort(2, false); + d->sampleRate = data.toUInt(4, false); + d->sampleWidth = data.toShort(14, false); - uint byteRate = data.mid(8, 4).toUInt(false); + const uint byteRate = data.toUInt(8, false); d->bitrate = byteRate * 8 / 1000; d->length = byteRate > 0 ? d->streamLength / byteRate : 0; diff --git a/3rdparty/taglib/s3m/s3mfile.cpp b/3rdparty/taglib/s3m/s3mfile.cpp index 7ffdf910b..371340a59 100644 --- a/3rdparty/taglib/s3m/s3mfile.cpp +++ b/3rdparty/taglib/s3m/s3mfile.cpp @@ -47,7 +47,8 @@ S3M::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::File(IOStream *stream, bool readProperties, @@ -55,7 +56,8 @@ S3M::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::~File() diff --git a/3rdparty/taglib/s3m/s3mfile.h b/3rdparty/taglib/s3m/s3mfile.h index c862108ee..5f4529772 100644 --- a/3rdparty/taglib/s3m/s3mfile.h +++ b/3rdparty/taglib/s3m/s3mfile.h @@ -36,18 +36,22 @@ namespace TagLib { class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! - * Contructs a ScreamTracker III 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 ScreamTracker III from \a file. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! - * Contructs a ScreamTracker III file from \a stream. 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 ScreamTracker III file from \a stream. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. diff --git a/3rdparty/taglib/taglib_export.h b/3rdparty/taglib/taglib_export.h index 3e8685517..737ae6442 100644 --- a/3rdparty/taglib/taglib_export.h +++ b/3rdparty/taglib/taglib_export.h @@ -40,8 +40,4 @@ #define TAGLIB_EXPORT #endif -#ifndef TAGLIB_NO_CONFIG -#include "taglib_config.h" -#endif - #endif diff --git a/3rdparty/taglib/toolkit/taglib.h b/3rdparty/taglib/toolkit/taglib.h index dda9c83c2..f93f280d6 100644 --- a/3rdparty/taglib/toolkit/taglib.h +++ b/3rdparty/taglib/toolkit/taglib.h @@ -26,8 +26,10 @@ #ifndef TAGLIB_H #define TAGLIB_H +#include "taglib_config.h" + #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 7 +#define TAGLIB_MINOR_VERSION 8 #define TAGLIB_PATCH_VERSION 0 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) @@ -44,23 +46,6 @@ #include -#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 - //! A namespace for all TagLib related classes and functions /*! @@ -75,10 +60,13 @@ namespace TagLib { class String; - typedef wchar_t wchar; - typedef unsigned char uchar; - typedef unsigned short ushort; - typedef unsigned int uint; + 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; /*! @@ -86,50 +74,6 @@ namespace TagLib { * 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) {} - -#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 - } /*! @@ -143,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/3rdparty/taglib/toolkit/tbytevector.cpp b/3rdparty/taglib/toolkit/tbytevector.cpp index 59e3a9ed3..ddc3d4f43 100644 --- a/3rdparty/taglib/toolkit/tbytevector.cpp +++ b/3rdparty/taglib/toolkit/tbytevector.cpp @@ -23,230 +23,327 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + #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) +{ + 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 SYSTEM_BYTEORDER == 1 + const bool swap = mostSignificantByteFirst; +#else + const bool swap != mostSignificantByteFirst; +#endif + if(swap) + return byteSwap(tmp); + else + return tmp; +} + +template +ByteVector fromNumber(T value, bool mostSignificantByteFirst) +{ + const size_t size = sizeof(T); + +#if SYSTEM_BYTEORDER == 1 + const bool swap = mostSignificantByteFirst; +#else + const bool swap != mostSignificantByteFirst; +#endif + if(swap) + value = byteSwap(value); + + return ByteVector(reinterpret_cast(&value), size); +} + +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 +354,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 +367,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 +380,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 +421,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 +491,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 @@ -511,74 +593,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 @@ -591,42 +691,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 @@ -636,10 +760,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 @@ -649,8 +773,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 @@ -697,12 +820,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; @@ -714,17 +837,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/3rdparty/taglib/toolkit/tbytevector.h b/3rdparty/taglib/toolkit/tbytevector.h index 0c5832630..538565b0a 100644 --- a/3rdparty/taglib/toolkit/tbytevector.h +++ b/3rdparty/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 @@ -413,7 +507,6 @@ namespace TagLib { class ByteVectorPrivate; ByteVectorPrivate *d; }; - } /*! diff --git a/3rdparty/taglib/toolkit/tdebug.cpp b/3rdparty/taglib/toolkit/tdebug.cpp index 522b68c97..71350af7c 100644 --- a/3rdparty/taglib/toolkit/tdebug.cpp +++ b/3rdparty/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/3rdparty/taglib/toolkit/tdebug.h b/3rdparty/taglib/toolkit/tdebug.h index 5204fe707..bd94d159f 100644 --- a/3rdparty/taglib/toolkit/tdebug.h +++ b/3rdparty/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/3rdparty/taglib/toolkit/tdebuglistener.cpp b/3rdparty/taglib/toolkit/tdebuglistener.cpp new file mode 100644 index 000000000..48912222d --- /dev/null +++ b/3rdparty/taglib/toolkit/tdebuglistener.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + copyright : (C) 2013 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "tdebuglistener.h" + +#include +#include + +#ifdef _WIN32 +# include +#endif + +using namespace TagLib; + +namespace +{ + class DefaultListener : public DebugListener + { + public: + virtual void printMessage(const String &msg) + { +#ifdef _WIN32 + + 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); + + std::cerr << std::string(&buf[0]); + } + +#else + + std::cerr << msg; + +#endif + } + }; + + DefaultListener defaultListener; +} + +namespace TagLib +{ + DebugListener *debugListener = &defaultListener; + + DebugListener::DebugListener() + { + } + + DebugListener::~DebugListener() + { + } + + void setDebugListener(DebugListener *listener) + { + if(listener) + debugListener = listener; + else + debugListener = &defaultListener; + } +} diff --git a/3rdparty/taglib/toolkit/tdebuglistener.h b/3rdparty/taglib/toolkit/tdebuglistener.h new file mode 100644 index 000000000..a32f285f4 --- /dev/null +++ b/3rdparty/taglib/toolkit/tdebuglistener.h @@ -0,0 +1,74 @@ +/*************************************************************************** + copyright : (C) 2013 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_DEBUGLISTENER_H +#define TAGLIB_DEBUGLISTENER_H + +#include "taglib_export.h" +#include "tstring.h" + +namespace TagLib +{ + //! An abstraction for the listener to the debug messages. + + /*! + * This class enables you to handle the debug messages in your preferred + * way by subclassing this class, reimplementing printMessage() and setting + * your reimplementation as the default with setDebugListener(). + * + * \see setDebugListener() + */ + class TAGLIB_EXPORT DebugListener + { + public: + DebugListener(); + virtual ~DebugListener(); + + /*! + * When overridden in a derived class, redirects \a msg to your preferred + * channel such as stderr, Windows debugger or so forth. + */ + virtual void printMessage(const String &msg) = 0; + + private: + // 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/3rdparty/taglib/toolkit/tfile.cpp b/3rdparty/taglib/toolkit/tfile.cpp index d0a6116fb..4a05b05f0 100644 --- a/3rdparty/taglib/toolkit/tfile.cpp +++ b/3rdparty/taglib/toolkit/tfile.cpp @@ -29,21 +29,14 @@ #include "tdebug.h" #include "tpropertymap.h" -#include -#include -#include - #ifdef _WIN32 -# include # include # include -# define ftruncate _chsize #else +# include # include #endif -#include - #ifndef R_OK # define R_OK 4 #endif @@ -60,6 +53,7 @@ #include "mp4file.h" #include "wavpackfile.h" #include "speexfile.h" +#include "opusfile.h" #include "trueaudiofile.h" #include "aifffile.h" #include "wavfile.h" @@ -68,9 +62,19 @@ #include "s3mfile.h" #include "itfile.h" #include "xmfile.h" +#include "mp4file.h" using namespace TagLib; +namespace +{ +#ifdef _WIN32 + const TagLib::uint BufferSize = 8192; +#else + const TagLib::uint BufferSize = 1024; +#endif +} + class File::FilePrivate { public: @@ -79,7 +83,6 @@ public: IOStream *stream; bool streamOwner; bool valid; - static const uint bufferSize = 1024; }; File::FilePrivate::FilePrivate(IOStream *stream, bool owner) : @@ -135,6 +138,8 @@ PropertyMap File::properties() const 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)) @@ -149,12 +154,10 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); - // no specialized implementation available -> use generic one - // - ASF: ugly format, largely undocumented, not worth implementing - // dict interface ... - // - MP4: taglib's MP4::Tag does not really support anything beyond - // the basic implementation, therefor we use just the default Tag - // interface + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); return tag()->properties(); } @@ -170,24 +173,20 @@ void File::removeUnsupportedProperties(const StringList &properties) 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 if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else tag()->removeUnsupportedProperties(properties); } @@ -210,6 +209,8 @@ PropertyMap File::setProperties(const PropertyMap &properties) 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)) @@ -224,6 +225,10 @@ PropertyMap File::setProperties(const PropertyMap &properties) 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); } @@ -240,7 +245,7 @@ void File::writeBlock(const ByteVector &data) long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before) { - if(!d->stream || pattern.size() > d->bufferSize) + if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. @@ -281,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; @@ -321,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. @@ -336,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->stream || pattern.size() > d->bufferSize) + if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. @@ -360,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 @@ -389,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); } @@ -488,7 +493,7 @@ bool File::isWritable(const char *file) TagLib::uint File::bufferSize() { - return FilePrivate::bufferSize; + return BufferSize; } void File::setValid(bool valid) diff --git a/3rdparty/taglib/toolkit/tfile.h b/3rdparty/taglib/toolkit/tfile.h index 7e6f2b936..67f6f80f5 100644 --- a/3rdparty/taglib/toolkit/tfile.h +++ b/3rdparty/taglib/toolkit/tfile.h @@ -80,12 +80,14 @@ namespace TagLib { /*! * Exports the tags of the file as dictionary mapping (human readable) tag - * names (Strings) to StringLists of tag values. Calls the according specialization - * in the File subclasses. + * 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; @@ -105,9 +107,15 @@ namespace TagLib { * 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 @@ -205,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; diff --git a/3rdparty/taglib/toolkit/tfilestream.cpp b/3rdparty/taglib/toolkit/tfilestream.cpp index b1fd9f80a..eae525733 100644 --- a/3rdparty/taglib/toolkit/tfilestream.cpp +++ b/3rdparty/taglib/toolkit/tfilestream.cpp @@ -27,137 +27,145 @@ #include "tstring.h" #include "tdebug.h" -#include -#include -#include - #ifdef _WIN32 -# include # include -# include -# define ftruncate _chsize #else +# include # include #endif -#include - -#ifndef R_OK -# define R_OK 4 -#endif -#ifndef W_OK -# define W_OK 2 -#endif - using namespace TagLib; +namespace +{ #ifdef _WIN32 -typedef FileName FileNameHandle; + // Uses Win32 native API instead of POSIX API to reduce the resource consumption. -#else + typedef FileName FileNameHandle; + typedef HANDLE FileHandle; -struct FileNameHandle : public std::string -{ - FileNameHandle(FileName name) : std::string(name) {} - operator FileName () const { return c_str(); } -}; + const TagLib::uint BufferSize = 8192; + const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE; -#endif - -namespace { - FILE *openFile(const FileName &path, bool readOnly) + inline FileHandle openFile(const FileName &path, bool readOnly) { - // Calls a proper variation of fopen() depending on the compiling environment. + const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); -#if defined(_WIN32) - -# if defined(_MSC_VER) && (_MSC_VER >= 1400) - - // Visual C++ 2005 or later. - - FILE *file; - errno_t err; - - if(wcslen(path) > 0) - err = _wfopen_s(&file, path, readOnly ? L"rb" : L"rb+"); + 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 - err = fopen_s(&file, path, readOnly ? "rb" : "rb+"); - - if(err == 0) - return file; - else - return NULL; - -# else // defined(_MSC_VER) && (_MSC_VER >= 1400) - - // Visual C++.NET 2003 or earlier. - - if(wcslen(path) > 0) - return _wfopen(path, readOnly ? L"rb" : L"rb+"); - else - return fopen(path, readOnly ? "rb" : "rb+"); - -# endif // defined(_MSC_VER) && (_MSC_VER >= 1400) - -#else // defined(_WIN32) - - // Non-Win32 - - return fopen(path, readOnly ? "rb" : "rb+"); - -#endif // defined(_WIN32) + 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(FileName fileName, bool openReadOnly); - - FILE *file; - - FileNameHandle name; - - bool readOnly; - ulong size; - static const uint bufferSize = 1024; -}; - -FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openReadOnly) : - file(0), - name(fileName), - readOnly(true), - size(0) -{ - // First try with read / write mode, if that fails, fall back to read only. - - if(!openReadOnly) - file = openFile(name, false); - - if(file) - readOnly = false; - else - file = openFile(name, true); - - if(!file) { - debug("Could not open file " + String((const char *) name)); + FileStreamPrivate(const FileName &fileName) + : file(InvalidFileHandle) + , name(fileName) + , readOnly(true) + { } -} + + FileHandle file; + FileNameHandle name; + bool readOnly; +}; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -FileStream::FileStream(FileName file, bool openReadOnly) +FileStream::FileStream(FileName fileName, bool openReadOnly) + : d(new FileStreamPrivate(fileName)) { - d = new FileStreamPrivate(file, openReadOnly); + // 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(d->file) - fclose(d->file); + if(isOpen()) + closeFile(d->file); + delete d; } @@ -168,43 +176,52 @@ FileName FileStream::name() const ByteVector FileStream::readBlock(ulong length) { - if(!d->file) { - debug("FileStream::readBlock() -- Invalid File"); + if(!isOpen()) { + debug("File::readBlock() -- invalid file."); return ByteVector::null; } if(length == 0) return ByteVector::null; - if(length > FileStreamPrivate::bufferSize && - length > ulong(FileStream::length())) - { - length = FileStream::length(); - } + const ulong streamLength = static_cast(FileStream::length()); + if(length > bufferSize() && length > streamLength) + length = streamLength; - ByteVector v(static_cast(length)); - const int count = fread(v.data(), sizeof(char), length, d->file); - v.resize(count); - return v; + 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(!d->file) - return; - - if(d->readOnly) { - debug("File::writeBlock() -- attempted to write to a file that is not writable"); + if(!isOpen()) { + debug("File::writeBlock() -- invalid file."); return; } - fwrite(data.data(), sizeof(char), data.size(), d->file); + if(readOnly()) { + debug("File::writeBlock() -- read only file."); + return; + } + + writeFile(d->file, data); } void FileStream::insert(const ByteVector &data, ulong start, ulong replace) { - if(!d->file) + if(!isOpen()) { + debug("File::insert() -- invalid file."); return; + } + + if(readOnly()) { + debug("File::insert() -- read only file."); + return; + } if(data.size() == replace) { seek(start); @@ -212,10 +229,10 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace) return; } else if(data.size() < replace) { - seek(start); - writeBlock(data); - removeBlock(start + data.size(), replace - data.size()); - return; + 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 @@ -238,71 +255,50 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace) long readPosition = start + replace; long writePosition = start; - ByteVector buffer; + ByteVector buffer = data; 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()) { - + while(true) + { // 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); + 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(ulong(bytesRead) < bufferLength) + if(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); + 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; - - // Again, we need this for the last write. We don't want to write garbage - // at the end of our file, so we need to set the buffer size to the amount - // that we actually read. - - bufferLength = bytesRead; } } void FileStream::removeBlock(ulong start, ulong length) { - if(!d->file) + if(!isOpen()) { + debug("File::removeBlock() -- invalid file."); return; + } ulong bufferLength = bufferSize(); @@ -311,23 +307,26 @@ void FileStream::removeBlock(ulong start, ulong length) ByteVector buffer(static_cast(bufferLength)); - ulong bytesRead = 1; - - while(bytesRead != 0) { + for(size_t bytesRead = -1; bytesRead != 0;) + { seek(readPosition); - bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file); + 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 < bufferLength) + if(bytesRead < buffer.size()) { clear(); + buffer.resize(bytesRead); + } seek(writePosition); - fwrite(buffer.data(), sizeof(char), bytesRead, d->file); + writeFile(d->file, buffer); + writePosition += bytesRead; } + truncate(writePosition); } @@ -338,58 +337,125 @@ bool FileStream::readOnly() const bool FileStream::isOpen() const { - return (d->file != NULL); + return (d->file != InvalidFileHandle); } void FileStream::seek(long offset, Position p) { - if(!d->file) { - debug("File::seek() -- trying to seek in a file that isn't opened."); + if(!isOpen()) { + debug("File::seek() -- invalid file."); return; } +#ifdef _WIN32 + + DWORD whence; switch(p) { case Beginning: - fseek(d->file, offset, SEEK_SET); + whence = FILE_BEGIN; break; case Current: - fseek(d->file, offset, SEEK_CUR); + whence = FILE_CURRENT; break; case End: - fseek(d->file, offset, SEEK_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() { - // Do some caching in case we do multiple calls. - - if(d->size > 0) - return d->size; - - if(!d->file) + if(!isOpen()) { + debug("File::length() -- invalid file."); return 0; + } - long curpos = tell(); +#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); - long endpos = tell(); + const long endpos = tell(); seek(curpos, Beginning); - d->size = endpos; return endpos; + +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -398,20 +464,29 @@ long FileStream::length() void FileStream::truncate(long length) { +#ifdef _WIN32 -#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + const long currentPos = tell(); - ftruncate(_fileno(d->file), length); + seek(length); + SetEndOfFile(d->file); + if(GetLastError() != NO_ERROR) { + debug("File::truncate() -- Failed to truncate the file."); + } + + seek(currentPos); #else - ftruncate(fileno(d->file), length); + 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 FileStreamPrivate::bufferSize; + return BufferSize; } diff --git a/3rdparty/taglib/toolkit/tiostream.cpp b/3rdparty/taglib/toolkit/tiostream.cpp index e7b60dd75..c2ad29125 100644 --- a/3rdparty/taglib/toolkit/tiostream.cpp +++ b/3rdparty/taglib/toolkit/tiostream.cpp @@ -27,6 +27,111 @@ 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 //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/toolkit/tiostream.h b/3rdparty/taglib/toolkit/tiostream.h index 3e7de22aa..868269644 100644 --- a/3rdparty/taglib/toolkit/tiostream.h +++ b/3rdparty/taglib/toolkit/tiostream.h @@ -36,13 +36,22 @@ namespace TagLib { 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(); } + 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: - std::string m_name; - std::wstring m_wname; + const std::string m_name; + const std::wstring m_wname; }; #else typedef const char *FileName; diff --git a/3rdparty/taglib/toolkit/tlist.tcc b/3rdparty/taglib/toolkit/tlist.tcc index 37817f059..81e291533 100644 --- a/3rdparty/taglib/toolkit/tlist.tcc +++ b/3rdparty/taglib/toolkit/tlist.tcc @@ -24,6 +24,7 @@ ***************************************************************************/ #include +#include "trefcounter.h" namespace TagLib { diff --git a/3rdparty/taglib/toolkit/tmap.tcc b/3rdparty/taglib/toolkit/tmap.tcc index 0f2b99332..d6f51c071 100644 --- a/3rdparty/taglib/toolkit/tmap.tcc +++ b/3rdparty/taglib/toolkit/tmap.tcc @@ -23,6 +23,8 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include "trefcounter.h" + namespace TagLib { //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/toolkit/tpropertymap.h b/3rdparty/taglib/toolkit/tpropertymap.h index 7f59b21ea..2be49ddb0 100644 --- a/3rdparty/taglib/toolkit/tpropertymap.h +++ b/3rdparty/taglib/toolkit/tpropertymap.h @@ -40,6 +40,65 @@ namespace TagLib { * 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 diff --git a/3rdparty/taglib/toolkit/trefcounter.cpp b/3rdparty/taglib/toolkit/trefcounter.cpp new file mode 100644 index 000000000..71f3c2f28 --- /dev/null +++ b/3rdparty/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/3rdparty/taglib/toolkit/trefcounter.h b/3rdparty/taglib/toolkit/trefcounter.h new file mode 100644 index 000000000..53c6f1c05 --- /dev/null +++ b/3rdparty/taglib/toolkit/trefcounter.h @@ -0,0 +1,58 @@ +/*************************************************************************** + 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" + +#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; + }; +} + +#endif // DO_NOT_DOCUMENT +#endif diff --git a/3rdparty/taglib/toolkit/tstring.cpp b/3rdparty/taglib/toolkit/tstring.cpp index 292f35333..c83118d12 100644 --- a/3rdparty/taglib/toolkit/tstring.cpp +++ b/3rdparty/taglib/toolkit/tstring.cpp @@ -23,176 +23,251 @@ * 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) { - - 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(t == Latin1) + copyFromLatin1(v.data(), v.size()); + else if(t == UTF8) + copyFromUTF8(v.data(), v.size()); + else + copyFromUTF16(v.data(), v.size(), t); } //////////////////////////////////////////////////////////////////////////////// @@ -206,46 +281,23 @@ 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."); + UTF16toUTF8(&d->data[0], d->data.size(), &s[0], s.size()); + s.resize(::strlen(s.c_str())); } - int newSize = target - targetBuffer; - s.resize(newSize); - targetBuffer[newSize] = 0; - - s = (char *) targetBuffer; - - delete [] sourceBuffer; - delete [] targetBuffer; - return s; } @@ -256,22 +308,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]; - -#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later - - strcpy_s(d->CString, buffer.size() + 1, buffer.c_str()); - -#else - - strcpy(d->CString, buffer.c_str()); - -#endif - - return d->CString; +const wchar_t *String::toCWString() const +{ + return d->data.c_str(); } String::Iterator String::begin() @@ -296,23 +339,12 @@ 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); - - if(position != wstring::npos) - return position; - else - return -1; + return d->data.rfind(s.d->data, offset); } StringList String::split(const String &separator) const @@ -345,9 +377,7 @@ bool String::startsWith(const String &s) const String String::substr(uint position, uint n) const { - 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) @@ -395,67 +425,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 @@ -530,37 +568,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]; } @@ -638,14 +674,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; } @@ -662,6 +691,7 @@ String &String::operator=(const wchar_t *s) { if(d->deref()) delete d; + d = new StringPrivate(s); return *this; } @@ -670,8 +700,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; } @@ -679,8 +709,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; } @@ -690,15 +720,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; } @@ -709,20 +731,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; } @@ -748,68 +760,95 @@ 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()); - } - break; - } - case UTF8: - { - int bufferSize = d->data.size() + 1; - Unicode::UTF8 *sourceBuffer = new Unicode::UTF8[bufferSize]; - Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize]; - - unsigned int i = 0; - for(; i < d->data.size(); i++) - sourceBuffer[i] = Unicode::UTF8(d->data[i]); - sourceBuffer[i] = 0; - - const Unicode::UTF8 *source = sourceBuffer; - Unicode::UTF16 *target = targetBuffer; - - Unicode::ConversionResult result = - Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize, - &target, targetBuffer + bufferSize, - Unicode::lenientConversion); - - if(result != Unicode::conversionOK) { - debug("String::prepare() - Unicode conversion error."); + debug("String::copyFromUTF16() - Invalid UTF16 string."); + return; } - 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; + s++; + length--; } - case UTF16LE: - { - for(uint i = 0; i < d->data.size(); i++) - d->data[i] = byteSwap((unsigned short)d->data[i]); - break; + else + swap = (t != WCharByteOrder); + + d->data.resize(length); + memcpy(&d->data[0], s, length * sizeof(wchar_t)); + + if(swap) { + for(size_t i = 0; i < length; ++i) + d->data[i] = byteSwap(static_cast(s[i])); } - default: - 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; } + 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; + } +} + +#if SYSTEM_BYTEORDER == 1 + +const String::Type String::WCharByteOrder = String::UTF16LE; + +#else + +const String::Type String::WCharByteOrder = String::UTF16BE; + +#endif + } //////////////////////////////////////////////////////////////////////////////// @@ -818,27 +857,28 @@ void String::prepare(Type t) 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/3rdparty/taglib/toolkit/tstring.h b/3rdparty/taglib/toolkit/tstring.h index 759a175ae..57945beed 100644 --- a/3rdparty/taglib/toolkit/tstring.h +++ b/3rdparty/taglib/toolkit/tstring.h @@ -63,8 +63,8 @@ namespace TagLib { /*! * 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 @@ -135,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. @@ -178,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. */ @@ -306,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 @@ -451,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/3rdparty/taglib/toolkit/tutils.h b/3rdparty/taglib/toolkit/tutils.h new file mode 100644 index 000000000..73c35716d --- /dev/null +++ b/3rdparty/taglib/toolkit/tutils.h @@ -0,0 +1,151 @@ +/*************************************************************************** + 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 +{ + + 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 + } + +}; + +#endif + +#endif diff --git a/3rdparty/taglib/toolkit/unicode.h b/3rdparty/taglib/toolkit/unicode.h index cf7eb3c56..b9de0ea21 100644 --- a/3rdparty/taglib/toolkit/unicode.h +++ b/3rdparty/taglib/toolkit/unicode.h @@ -115,8 +115,8 @@ 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 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 { diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.cpp b/3rdparty/taglib/trueaudio/trueaudiofile.cpp index 9efc6e9de..f98d6addc 100644 --- a/3rdparty/taglib/trueaudio/trueaudiofile.cpp +++ b/3rdparty/taglib/trueaudio/trueaudiofile.cpp @@ -139,14 +139,17 @@ PropertyMap TrueAudio::File::properties() const return PropertyMap(); } -PropertyMap TrueAudio::File::setProperties(const PropertyMap &properties) +void TrueAudio::File::removeUnsupportedProperties(const StringList &unsupported) { if(d->hasID3v2) - return d->tag.access(TrueAudioID3v2Index, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(TrueAudioID3v1Index, false)->setProperties(properties); - else - return d->tag.access(TrueAudioID3v2Index, true)->setProperties(properties); + d->tag.access(TrueAudioID3v2Index, false)->removeUnsupportedProperties(unsupported); +} + +PropertyMap TrueAudio::File::setProperties(const PropertyMap &properties) +{ + if(d->hasID3v1) + d->tag.access(TrueAudioID3v1Index, false)->setProperties(properties); + return d->tag.access(TrueAudioID3v2Index, true)->setProperties(properties); } TrueAudio::Properties *TrueAudio::File::audioProperties() const @@ -233,6 +236,16 @@ void TrueAudio::File::strip(int tags) } } +bool TrueAudio::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool TrueAudio::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} + //////////////////////////////////////////////////////////////////////////////// // private members diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.h b/3rdparty/taglib/trueaudio/trueaudiofile.h index e3e1fe628..dbaafcdb9 100644 --- a/3rdparty/taglib/trueaudio/trueaudiofile.h +++ b/3rdparty/taglib/trueaudio/trueaudiofile.h @@ -79,42 +79,50 @@ namespace TagLib { }; /*! - * 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. + * Constructs a TrueAudio 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); /*! - * 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 + * Constructs a TrueAudio 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. */ File(FileName file, ID3v2::FrameFactory *frameFactory, 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. + * Constructs a TrueAudio 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); /*! - * 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. + * Constructs a TrueAudio 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, @@ -139,11 +147,13 @@ namespace TagLib { /*! * Implements the unified property interface -- import function. - * As with the export, only one tag is taken into account. If the file - * has no tag at all, ID3v2 will be created. + * Creates in ID3v2 tag if necessary. If an ID3v1 tag exists, it will + * be updated as well, within the limitations of ID3v1. */ PropertyMap setProperties(const PropertyMap &); + void removeUnsupportedProperties(const StringList &properties); + /*! * Returns the TrueAudio::Properties for this file. If no audio properties * were read then this will return a null pointer. @@ -163,30 +173,40 @@ namespace TagLib { virtual bool save(); /*! - * Returns a pointer to the ID3v2 tag of the file. + * 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 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. + * 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 and returns a valid pointer. * - * \note The Tag is still owned by the TrueAudio::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 ID3v1 tag of the file. + * 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 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 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 and returns a valid pointer. * - * \note The Tag is still owned by the TrueAudio::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); @@ -199,7 +219,21 @@ namespace TagLib { * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); + + /*! + * Returns whether or not the file on disk actually has an ID3v1 tag. + * + * \see ID3v1Tag() + */ + bool hasID3v1Tag() const; + /*! + * Returns whether or not the file on disk actually has an ID3v2 tag. + * + * \see ID3v2Tag() + */ + bool hasID3v2Tag() const; + private: File(const File &); File &operator=(const File &); diff --git a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp b/3rdparty/taglib/trueaudio/trueaudioproperties.cpp index 9b251ff18..dedd74e94 100644 --- a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp +++ b/3rdparty/taglib/trueaudio/trueaudioproperties.cpp @@ -133,16 +133,16 @@ void TrueAudio::Properties::read() // Skip the audio format pos += 2; - d->channels = d->data.mid(pos, 2).toShort(false); + d->channels = d->data.toShort(pos, false); pos += 2; - d->bitsPerSample = d->data.mid(pos, 2).toShort(false); + d->bitsPerSample = d->data.toShort(pos, false); pos += 2; - d->sampleRate = d->data.mid(pos, 4).toUInt(false); + d->sampleRate = d->data.toUInt(pos, false); pos += 4; - d->sampleFrames = d->data.mid(pos, 4).toUInt(false); + d->sampleFrames = d->data.toUInt(pos, false); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; diff --git a/3rdparty/taglib/wavpack/wavpackfile.cpp b/3rdparty/taglib/wavpack/wavpackfile.cpp index 49f7923e7..13b8c570d 100644 --- a/3rdparty/taglib/wavpack/wavpackfile.cpp +++ b/3rdparty/taglib/wavpack/wavpackfile.cpp @@ -86,14 +86,16 @@ WavPack::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } WavPack::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } WavPack::File::~File() @@ -115,14 +117,19 @@ PropertyMap WavPack::File::properties() const return PropertyMap(); } -PropertyMap WavPack::File::setProperties(const PropertyMap &properties) + +void WavPack::File::removeUnsupportedProperties(const StringList &unsupported) { if(d->hasAPE) - return d->tag.access(WavAPEIndex, false)->setProperties(properties); - else if(d->hasID3v1) - return d->tag.access(WavID3v1Index, false)->setProperties(properties); - else - return d->tag.access(APE, true)->setProperties(properties); + d->tag.access(WavAPEIndex, false)->removeUnsupportedProperties(unsupported); +} + + +PropertyMap WavPack::File::setProperties(const PropertyMap &properties) +{ + if(d->hasID3v1) + d->tag.access(WavID3v1Index, false)->setProperties(properties); + return d->tag.access(WavAPEIndex, true)->setProperties(properties); } WavPack::Properties *WavPack::File::audioProperties() const @@ -224,6 +231,16 @@ void WavPack::File::strip(int tags) } } +bool WavPack::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool WavPack::File::hasAPETag() const +{ + return d->hasAPE; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/wavpack/wavpackfile.h b/3rdparty/taglib/wavpack/wavpackfile.h index 5bbbc65a1..c85c75f2b 100644 --- a/3rdparty/taglib/wavpack/wavpackfile.h +++ b/3rdparty/taglib/wavpack/wavpackfile.h @@ -80,15 +80,15 @@ namespace TagLib { }; /*! - * Contructs an WavPack file from \a file. If \a readProperties is true the + * Constructs a 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. + * false, \a propertiesStyle is ignored */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Contructs an WavPack file from \a file. If \a readProperties is true the + * Constructs 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. * @@ -116,10 +116,12 @@ namespace TagLib { */ PropertyMap properties() const; + void removeUnsupportedProperties(const StringList &properties); + /*! * Implements the unified property interface -- import function. - * As for the export, only one tag is taken into account. If the file - * has no tag at all, APE will be created. + * Creates an APE tag if it does not exists and calls setProperties() on + * that. Any existing ID3v1 tag will be updated as well. */ PropertyMap setProperties(const PropertyMap&); @@ -137,27 +139,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); @@ -170,7 +183,21 @@ namespace TagLib { * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); + + /*! + * Returns whether or not the file on disk actually has an ID3v1 tag. + * + * \see ID3v1Tag() + */ + bool hasID3v1Tag() const; + /*! + * Returns whether or not the file on disk actually has an APE tag. + * + * \see APETag() + */ + bool hasAPETag() const; + private: File(const File &); File &operator=(const File &); diff --git a/3rdparty/taglib/wavpack/wavpackproperties.cpp b/3rdparty/taglib/wavpack/wavpackproperties.cpp index 1715f425d..3f7915443 100644 --- a/3rdparty/taglib/wavpack/wavpackproperties.cpp +++ b/3rdparty/taglib/wavpack/wavpackproperties.cpp @@ -148,17 +148,17 @@ void WavPack::Properties::read() if(!d->data.startsWith("wvpk")) return; - d->version = d->data.mid(8, 2).toShort(false); + d->version = d->data.toShort(8, false); if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) return; - unsigned int flags = d->data.mid(24, 4).toUInt(false); + const unsigned int flags = d->data.toUInt(24, 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); + unsigned int samples = d->data.toUInt(12, false); if(samples == ~0u) { if(d->file && d->style != Fast) { samples = seekFinalIndex(); @@ -186,14 +186,14 @@ unsigned int WavPack::Properties::seekFinalIndex() ByteVector data = d->file->readBlock(32); if(data.size() != 32) return 0; - int version = data.mid(8, 2).toShort(false); + const int version = data.toShort(8, false); if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) continue; - unsigned int flags = data.mid(24, 4).toUInt(false); + const unsigned int flags = data.toUInt(24, 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); + const unsigned int blockIndex = data.toUInt(16, false); + const unsigned int blockSamples = data.toUInt(20, false); return blockIndex + blockSamples; } diff --git a/3rdparty/taglib/xm/xmfile.cpp b/3rdparty/taglib/xm/xmfile.cpp index 272e5fe0b..d8373a781 100644 --- a/3rdparty/taglib/xm/xmfile.cpp +++ b/3rdparty/taglib/xm/xmfile.cpp @@ -359,7 +359,8 @@ XM::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::File(IOStream *stream, bool readProperties, @@ -367,7 +368,8 @@ XM::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::~File() @@ -443,7 +445,7 @@ bool XM::File::save() return false; uint len = std::min(22UL, instrumentHeaderSize - 4U); - if(i > lines.size()) + if(i >= lines.size()) writeString(String::null, len); else writeString(lines[i], len); diff --git a/3rdparty/taglib/xm/xmfile.h b/3rdparty/taglib/xm/xmfile.h index 9d1bb71c6..ae48a511d 100644 --- a/3rdparty/taglib/xm/xmfile.h +++ b/3rdparty/taglib/xm/xmfile.h @@ -36,18 +36,22 @@ namespace TagLib { class TAGLIB_EXPORT File : public Mod::FileBase { public: /*! - * Contructs a Extended Module 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 Extended Module file from \a file. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. */ File(FileName file, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! - * Contructs a Extended Module file from \a stream. 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 Extended Module file from \a stream. + * + * \note In the current implementation, both \a readProperties and + * \a propertiesStyle are ignored. The audio properties are always + * read. * * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. diff --git a/CMakeLists.txt b/CMakeLists.txt index d1d738ab4..7bf220a9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,16 +105,17 @@ if (USE_BUILTIN_TAGLIB AND set(TAGLIB_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/taglib/;${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/") set(TAGLIB_LIBRARY_DIRS "") set(TAGLIB_LIBRARIES tag) + set(TAGLIB_HAS_OPUS ON) add_subdirectory(3rdparty/taglib) +else() + set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}") + check_cxx_source_compiles("#include + int main() { char *s; TagLib::Ogg::Opus::File opusfile(s); return 0;}" TAGLIB_HAS_OPUS) + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) endif() -set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDE_DIRS}") -set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}") -check_cxx_source_compiles("#include - int main() { char *s; TagLib::Ogg::Opus::File opusfile(s); return 0;}" TAGLIB_HAS_OPUS) -set(CMAKE_REQUIRED_INCLUDES) -set(CMAKE_REQUIRED_LIBRARIES) - if(LASTFM_INCLUDE_DIRS AND LASTFM1_INCLUDE_DIRS) set(HAVE_LIBLASTFM1 ON) endif()