diff --git a/taglib/asf/asfpicture.cpp b/taglib/asf/asfpicture.cpp index 17023e7c..6e2f2628 100644 --- a/taglib/asf/asfpicture.cpp +++ b/taglib/asf/asfpicture.cpp @@ -133,7 +133,7 @@ void ASF::Picture::parse(const ByteVector& bytes) if(bytes.size() < 9) return; int pos = 0; - d->type = static_cast(bytes[0]); ++pos; + d->type = typeFromByte(bytes[0]); ++pos; const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4; const ByteVector nullStringTerminator(2, 0); diff --git a/taglib/flac/flacpicture.cpp b/taglib/flac/flacpicture.cpp index af3cfaa4..73abd343 100644 --- a/taglib/flac/flacpicture.cpp +++ b/taglib/flac/flacpicture.cpp @@ -68,7 +68,7 @@ bool FLAC::Picture::parse(const ByteVector &data) } unsigned int pos = 0; - d->type = static_cast(data.toUInt(pos)); + d->type = typeFromUInt(data.toUInt(pos)); pos += 4; unsigned int mimeTypeLength = data.toUInt(pos); pos += 4; diff --git a/taglib/mp4/mp4itemfactory.cpp b/taglib/mp4/mp4itemfactory.cpp index 1028ef50..e2747f0a 100644 --- a/taglib/mp4/mp4itemfactory.cpp +++ b/taglib/mp4/mp4itemfactory.cpp @@ -41,6 +41,21 @@ namespace { constexpr char freeFormPrefix[] = "----:com.apple.iTunes:"; +/*! + * Returns the atom data type denoted by \a flags, the type field of an + * iTunes metadata atom, or TypeUndefined if it cannot be represented. + * + * The field is 32 bits wide and comes from the file, while the range of the + * enumeration is 0..255, so it cannot simply be cast. + */ +MP4::AtomDataType atomDataTypeFromFlags(int flags) +{ + if(flags >= MP4::TypeImplicit && flags <= MP4::TypeUndefined) + return static_cast(flags); + + return MP4::TypeUndefined; +} + MP4::CoverArt::Format detectImageFormat(const ByteVector &payload) { const unsigned int size = payload.size(); @@ -452,7 +467,7 @@ MP4::AtomDataList ItemFactory::parseData2( debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\""); return result; } - result.append(AtomData(static_cast(flags), + result.append(AtomData(atomDataTypeFromFlags(flags), data.mid(pos + 12, length - 12))); } else { @@ -461,7 +476,7 @@ MP4::AtomDataList ItemFactory::parseData2( return result; } if(expectedFlags == -1 || flags == expectedFlags) { - result.append(AtomData(static_cast(flags), + result.append(AtomData(atomDataTypeFromFlags(flags), data.mid(pos + 16, length - 16))); } } diff --git a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp index 8b88b983..0a34f565 100644 --- a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp +++ b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp @@ -26,6 +26,7 @@ #include "attachedpictureframe.h" #include "tstringlist.h" +#include "tutils.h" #include "tdebug.h" using namespace TagLib; @@ -132,7 +133,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); int pos = 1; @@ -143,7 +144,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data) return; } - d->type = static_cast(data[pos++]); + d->type = typeFromByte(data[pos++]); d->description = readStringField(data, d->textEncoding, &pos); d->data = data.mid(pos); @@ -188,7 +189,7 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); int pos = 1; @@ -204,7 +205,7 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data) d->mimeType = "image/" + fixedString; } - d->type = static_cast(data[pos++]); + d->type = typeFromByte(data[pos++]); d->description = readStringField(data, d->textEncoding, &pos); d->data = data.mid(pos); diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp index 2a464fcc..40bba7d0 100644 --- a/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -28,6 +28,7 @@ #include #include "tbytevectorlist.h" +#include "tutils.h" #include "tdebug.h" #include "tstringlist.h" #include "tpropertymap.h" @@ -143,7 +144,7 @@ void CommentsFrame::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); d->language = data.mid(1, 3); int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2; diff --git a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp index bf4bc3db..b75dd09f 100644 --- a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp +++ b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp @@ -34,6 +34,23 @@ using namespace TagLib; using namespace ID3v2; +namespace +{ + /*! + * Returns the timestamp format denoted by \a c, or Unknown if it denotes + * nothing. The range of the enumeration is only 0..3, so the byte read from + * the file cannot simply be cast. + */ + EventTimingCodesFrame::TimestampFormat timestampFormatFromByte(char c) + { + if(const auto value = static_cast(c); + value <= EventTimingCodesFrame::AbsoluteMilliseconds) + return static_cast(value); + + return EventTimingCodesFrame::Unknown; + } +} // namespace + class EventTimingCodesFrame::EventTimingCodesFramePrivate { public: @@ -101,7 +118,7 @@ void EventTimingCodesFrame::parseFields(const ByteVector &data) return; } - d->timestampFormat = static_cast(data[0]); + d->timestampFormat = timestampFormatFromByte(data[0]); int pos = 1; d->synchedEvents.clear(); diff --git a/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp b/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp index f665279e..8c34629d 100644 --- a/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp +++ b/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp @@ -28,6 +28,7 @@ #include "generalencapsulatedobjectframe.h" +#include "tutils.h" #include "tdebug.h" #include "tstringlist.h" @@ -142,7 +143,7 @@ void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); int pos = 1; diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/taglib/mpeg/id3v2/frames/ownershipframe.cpp index 2d47055c..bed996cf 100644 --- a/taglib/mpeg/id3v2/frames/ownershipframe.cpp +++ b/taglib/mpeg/id3v2/frames/ownershipframe.cpp @@ -25,6 +25,7 @@ #include "ownershipframe.h" +#include "tutils.h" #include "tstringlist.h" #include "id3v2tag.h" @@ -124,7 +125,7 @@ void OwnershipFrame::parseFields(const ByteVector &data) } // Get the text encoding - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); pos += 1; // Read the price paid, this is a null terminated string diff --git a/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp index 52a31ec3..e29c6441 100644 --- a/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp @@ -32,6 +32,25 @@ using namespace TagLib; using namespace ID3v2; +namespace +{ + /*! + * Returns the channel type denoted by \a c, the channel byte of an RVA2 + * channel record, or Other if it denotes nothing. + * + * The byte comes from the file, and the range of the enumeration is only + * 0..15, so the value cannot simply be cast. + */ + RelativeVolumeFrame::ChannelType channelTypeFromByte(char c) + { + if(const auto value = static_cast(c); + value <= RelativeVolumeFrame::Subwoofer) + return static_cast(value); + + return RelativeVolumeFrame::Other; + } +} // namespace + struct ChannelData { RelativeVolumeFrame::ChannelType channelType { RelativeVolumeFrame::Other }; @@ -133,7 +152,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data) while(pos <= static_cast(data.size()) - 4) { - auto type = static_cast(data[pos]); + auto type = channelTypeFromByte(data[pos]); pos += 1; ChannelData &channel = d->channels[type]; diff --git a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp index 3a8b6f08..b1be81d5 100644 --- a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp @@ -28,6 +28,7 @@ #include #include "tbytevectorlist.h" +#include "tutils.h" #include "tdebug.h" #include "tpropertymap.h" #include "id3v2tag.h" @@ -35,6 +36,36 @@ using namespace TagLib; using namespace ID3v2; +namespace +{ + /*! + * Returns the timestamp format denoted by \a c, or Unknown if it denotes + * nothing. The range of the enumeration is only 0..3, so the byte read from + * the file cannot simply be cast. + */ + SynchronizedLyricsFrame::TimestampFormat timestampFormatFromByte(char c) + { + if(const auto value = static_cast(c); + value <= SynchronizedLyricsFrame::AbsoluteMilliseconds) + return static_cast(value); + + return SynchronizedLyricsFrame::Unknown; + } + + /*! + * Returns the content type denoted by \a c, or Other if it denotes nothing. + * The range of the enumeration is only 0..15. + */ + SynchronizedLyricsFrame::Type contentTypeFromByte(char c) + { + if(const auto value = static_cast(c); + value <= SynchronizedLyricsFrame::ImageUrls) + return static_cast(value); + + return SynchronizedLyricsFrame::Other; + } +} // namespace + class SynchronizedLyricsFrame::SynchronizedLyricsFramePrivate { public: @@ -146,10 +177,10 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); d->language = data.mid(1, 3); - d->timestampFormat = static_cast(data[4]); - d->type = static_cast(data[5]); + d->timestampFormat = timestampFormatFromByte(data[4]); + d->type = contentTypeFromByte(data[5]); int pos = 6; diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index b91be78e..4406eaa5 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -29,6 +29,7 @@ #include #include +#include "tutils.h" #include "tpropertymap.h" #include "id3v1genres.h" #include "id3v2tag.h" @@ -218,7 +219,7 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) // read the string data type (the first byte of the field data) - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); // split the byte array into chunks based on the string type (two byte delimiter // for unicode encodings) diff --git a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 04460967..13d1ca53 100644 --- a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -31,6 +31,7 @@ #include #include "tbytevectorlist.h" +#include "tutils.h" #include "tdebug.h" #include "tpropertymap.h" #include "id3v2tag.h" @@ -144,7 +145,7 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) return; } - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); d->language = data.mid(1, 3); int byteAlign diff --git a/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 8f399f90..39ee89ca 100644 --- a/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -30,6 +30,7 @@ #include +#include "tutils.h" #include "tdebug.h" #include "tstringlist.h" #include "tpropertymap.h" @@ -196,7 +197,7 @@ void UserUrlLinkFrame::parseFields(const ByteVector &data) int pos = 0; - d->textEncoding = static_cast(data[0]); + d->textEncoding = Utils::textEncodingFromByte(data[0]); pos += 1; if(d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) { diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 662ab2bf..380db478 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -28,6 +28,7 @@ #include #include +#include "tutils.h" #include "tdebug.h" #include "tzlib.h" #include "id3v2synchdata.h" @@ -373,13 +374,13 @@ void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const tdat && tdat->data().size() >= 5) { - String date(tdat->data().mid(1), static_cast(tdat->data()[0])); + String date(tdat->data().mid(1), Utils::textEncodingFromByte(tdat->data()[0])); if(date.length() == 4) { tdrc->setText(tdrc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2)); if(tag->frameList("TIME").size() == 1) { auto timeframe = dynamic_cast(tag->frameList("TIME").front()); if(timeframe && timeframe->data().size() >= 5) { - String time(timeframe->data().mid(1), static_cast(timeframe->data()[0])); + String time(timeframe->data().mid(1), Utils::textEncodingFromByte(timeframe->data()[0])); if(time.length() == 4) { tdrc->setText(tdrc->toString() + 'T' + time.substr(0, 2) + ':' + time.substr(2, 2)); } diff --git a/taglib/toolkit/tpicturetype.cpp b/taglib/toolkit/tpicturetype.cpp index cb93b5c0..c04e4ea5 100644 --- a/taglib/toolkit/tpicturetype.cpp +++ b/taglib/toolkit/tpicturetype.cpp @@ -74,3 +74,11 @@ int Utils::pictureTypeFromString(const String& str) } return 0; } + +int Utils::pictureTypeFromUInt(unsigned int value) +{ + if(value < std::size(typeStrs)) + return static_cast(value); + + return 0; +} diff --git a/taglib/toolkit/tpicturetype.h b/taglib/toolkit/tpicturetype.h index f16fa833..3ae59485 100644 --- a/taglib/toolkit/tpicturetype.h +++ b/taglib/toolkit/tpicturetype.h @@ -96,6 +96,15 @@ static TagLib::String typeToString(name type) { \ static name typeFromString(const TagLib::String &str) { \ return static_cast( \ TagLib::Utils::pictureTypeFromString(str)); \ +} \ +static name typeFromUInt(unsigned int value) { \ + return static_cast( \ + TagLib::Utils::pictureTypeFromUInt(value)); \ +} \ +static name typeFromByte(char value) { \ + return static_cast( \ + TagLib::Utils::pictureTypeFromUInt( \ + static_cast(value))); \ } namespace TagLib { @@ -114,6 +123,16 @@ namespace TagLib { */ int TAGLIB_EXPORT pictureTypeFromString(const String& str); + /*! + * Get picture type from the \a value read from a file, or Other if it + * denotes no picture type. + * + * The value cannot simply be cast: loading an enumeration object whose + * value is outside the range of the enumeration is undefined, and the + * range here is only 0..31. + */ + int TAGLIB_EXPORT pictureTypeFromUInt(unsigned int value); + } // namespace Utils } // namespace TagLib diff --git a/taglib/toolkit/tutils.h b/taglib/toolkit/tutils.h index d18aa2ee..7b5c22b8 100644 --- a/taglib/toolkit/tutils.h +++ b/taglib/toolkit/tutils.h @@ -58,6 +58,29 @@ namespace TagLib namespace { + /*! + * Returns the String::Type denoted by \a c, the text encoding byte at the + * start of an ID3v2 frame, or String::Latin1 if it denotes nothing. + * + * The byte comes from the file, so it cannot simply be cast: loading an + * enumeration object whose value is outside the enumeration is undefined, and + * String::data() switches on the type without a default case. Latin1 is what + * the frames already declare as their default encoding. + */ + inline String::Type textEncodingFromByte(char c) + { + switch(static_cast(c)) { + case String::Latin1: + case String::UTF16: + case String::UTF16BE: + case String::UTF8: + case String::UTF16LE: + return static_cast(static_cast(c)); + default: + return String::Latin1; + } + } + /*! * Reverses the order of bytes in a 16-bit integer. */