2018-03-06 23:09:15 +01:00
|
|
|
#ifndef TAG_PARSER_ID3V2TAG_H
|
|
|
|
#define TAG_PARSER_ID3V2TAG_H
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-09-06 19:57:33 +02:00
|
|
|
#include "./id3v2frame.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-09-06 19:57:33 +02:00
|
|
|
#include "../fieldbasedtag.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2021-05-13 16:07:06 +02:00
|
|
|
#include <c++utilities/misc/flagenumclass.h>
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
#include <map>
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
namespace TagParser {
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2021-05-13 16:07:06 +02:00
|
|
|
/*!
|
|
|
|
* \brief The Id3v2Flags enum specifies flags which controls parsing and making of ID3v2 tags.
|
|
|
|
*/
|
|
|
|
enum class Id3v2HandlingFlags : std::uint64_t {
|
|
|
|
None = 0, /**< Regular parsing/making. */
|
|
|
|
ConvertRecordDateFields = (1 << 1), /**< whether record date fields should be converted when parsing/making */
|
|
|
|
Defaults = ConvertRecordDateFields, /**< set of flags considered good defaults */
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace TagParser
|
|
|
|
|
|
|
|
CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(TagParser, TagParser::Id3v2HandlingFlags)
|
|
|
|
|
|
|
|
namespace TagParser {
|
|
|
|
|
2015-12-22 23:54:35 +01:00
|
|
|
class Id3v2Tag;
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
struct TAG_PARSER_EXPORT FrameComparer {
|
2019-03-13 19:06:42 +01:00
|
|
|
bool operator()(std::uint32_t lhs, std::uint32_t rhs) const;
|
2015-12-22 23:54:35 +01:00
|
|
|
};
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
class TAG_PARSER_EXPORT Id3v2TagMaker {
|
2015-12-22 23:54:35 +01:00
|
|
|
friend class Id3v2Tag;
|
|
|
|
|
|
|
|
public:
|
2019-03-13 19:06:42 +01:00
|
|
|
void make(std::ostream &stream, std::uint32_t padding, Diagnostics &diag);
|
2015-12-22 23:54:35 +01:00
|
|
|
const Id3v2Tag &tag() const;
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint64_t requiredSize() const;
|
2015-12-22 23:54:35 +01:00
|
|
|
|
|
|
|
private:
|
2018-03-05 17:49:29 +01:00
|
|
|
Id3v2TagMaker(Id3v2Tag &tag, Diagnostics &diag);
|
2015-12-22 23:54:35 +01:00
|
|
|
|
|
|
|
Id3v2Tag &m_tag;
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint32_t m_framesSize;
|
|
|
|
std::uint32_t m_requiredSize;
|
2015-12-22 23:54:35 +01:00
|
|
|
std::vector<Id3v2FrameMaker> m_maker;
|
2015-04-22 19:22:01 +02:00
|
|
|
};
|
|
|
|
|
2015-12-22 23:54:35 +01:00
|
|
|
/*!
|
|
|
|
* \brief Returns the associated tag.
|
|
|
|
*/
|
|
|
|
inline const Id3v2Tag &Id3v2TagMaker::tag() const
|
|
|
|
{
|
|
|
|
return m_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the number of bytes which will be written when making the tag.
|
|
|
|
* \remarks Excludes padding!
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
inline std::uint64_t Id3v2TagMaker::requiredSize() const
|
2015-12-22 23:54:35 +01:00
|
|
|
{
|
|
|
|
return m_requiredSize;
|
|
|
|
}
|
|
|
|
|
2017-03-07 00:02:59 +01:00
|
|
|
/*!
|
|
|
|
* \brief Defines traits for the TagField implementation of the Id3v2Tag class.
|
|
|
|
*/
|
2018-03-07 01:17:50 +01:00
|
|
|
template <> class TAG_PARSER_EXPORT FieldMapBasedTagTraits<Id3v2Tag> {
|
2017-03-07 00:02:59 +01:00
|
|
|
public:
|
2018-07-11 13:19:43 +02:00
|
|
|
using FieldType = Id3v2Frame;
|
|
|
|
using Compare = FrameComparer;
|
2017-03-07 00:02:59 +01:00
|
|
|
};
|
|
|
|
|
2020-12-18 21:13:52 +01:00
|
|
|
class TAG_PARSER_EXPORT Id3v2Tag final : public FieldMapBasedTag<Id3v2Tag> {
|
2017-03-07 01:52:26 +01:00
|
|
|
friend class FieldMapBasedTag<Id3v2Tag>;
|
2020-04-22 23:54:10 +02:00
|
|
|
friend class Id3v2TagMaker;
|
2017-03-07 01:52:26 +01:00
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
public:
|
|
|
|
Id3v2Tag();
|
|
|
|
|
2017-01-23 00:25:53 +01:00
|
|
|
static constexpr TagType tagType = TagType::Id3v2Tag;
|
2021-01-30 21:53:06 +01:00
|
|
|
static constexpr std::string_view tagName = "ID3v2 tag";
|
2017-03-07 00:02:59 +01:00
|
|
|
static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian;
|
2018-03-07 01:11:42 +01:00
|
|
|
TagTextEncoding proposedTextEncoding() const override;
|
|
|
|
bool canEncodingBeUsed(TagTextEncoding encoding) const override;
|
|
|
|
bool supportsDescription(KnownField field) const override;
|
|
|
|
bool supportsMimeType(KnownField field) const override;
|
2018-07-13 12:25:00 +02:00
|
|
|
bool supportsMultipleValues(KnownField field) const override;
|
2019-06-01 22:36:08 +02:00
|
|
|
void ensureTextValuesAreProperlyEncoded() override;
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2019-03-13 19:06:42 +01:00
|
|
|
void parse(std::istream &sourceStream, const std::uint64_t maximalSize, Diagnostics &diag);
|
2018-03-05 17:49:29 +01:00
|
|
|
Id3v2TagMaker prepareMaking(Diagnostics &diag);
|
2019-03-13 19:06:42 +01:00
|
|
|
void make(std::ostream &targetStream, std::uint32_t padding, Diagnostics &diag);
|
2021-05-13 16:07:06 +02:00
|
|
|
Id3v2HandlingFlags handlingFlags() const;
|
|
|
|
void setHandlingFlags(Id3v2HandlingFlags flags);
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint8_t majorVersion() const;
|
|
|
|
std::uint8_t revisionVersion() const;
|
|
|
|
void setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion);
|
2015-04-22 19:22:01 +02:00
|
|
|
bool isVersionSupported() const;
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint8_t flags() const;
|
2015-04-22 19:22:01 +02:00
|
|
|
bool isUnsynchronisationUsed() const;
|
|
|
|
bool hasExtendedHeader() const;
|
|
|
|
bool isExperimental() const;
|
|
|
|
bool hasFooter() const;
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint32_t extendedHeaderSize() const;
|
2021-03-20 21:26:25 +01:00
|
|
|
std::uint64_t paddingSize() const;
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2017-03-07 01:52:26 +01:00
|
|
|
protected:
|
2017-03-07 17:16:17 +01:00
|
|
|
IdentifierType internallyGetFieldId(KnownField field) const;
|
|
|
|
KnownField internallyGetKnownField(const IdentifierType &id) const;
|
2019-03-13 19:06:42 +01:00
|
|
|
TagDataType internallyGetProposedDataType(const std::uint32_t &id) const;
|
2021-01-30 19:12:04 +01:00
|
|
|
void internallyGetValuesFromField(const FieldType &field, std::vector<const TagValue *> &values) const;
|
2018-07-11 15:53:23 +02:00
|
|
|
bool internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values);
|
2017-03-07 01:52:26 +01:00
|
|
|
|
2020-04-22 23:54:10 +02:00
|
|
|
private:
|
|
|
|
void convertOldRecordDateFields(const std::string &diagContext, Diagnostics &diag);
|
|
|
|
void removeOldRecordDateRelatedFields();
|
|
|
|
void prepareRecordDataForMaking(const std::string &diagContext, Diagnostics &diag);
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
private:
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint8_t m_majorVersion;
|
|
|
|
std::uint8_t m_revisionVersion;
|
|
|
|
std::uint8_t m_flags;
|
|
|
|
std::uint32_t m_sizeExcludingHeader;
|
|
|
|
std::uint32_t m_extendedHeaderSize;
|
2021-03-20 21:26:25 +01:00
|
|
|
std::uint64_t m_paddingSize;
|
2021-05-13 16:07:06 +02:00
|
|
|
Id3v2HandlingFlags m_handlingFlags;
|
2015-04-22 19:22:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new tag.
|
|
|
|
*/
|
2018-03-07 01:17:50 +01:00
|
|
|
inline Id3v2Tag::Id3v2Tag()
|
|
|
|
: m_majorVersion(4)
|
|
|
|
, m_revisionVersion(0)
|
|
|
|
, m_flags(0)
|
|
|
|
, m_sizeExcludingHeader(0)
|
|
|
|
, m_extendedHeaderSize(0)
|
|
|
|
, m_paddingSize(0)
|
2021-05-13 16:07:06 +02:00
|
|
|
, m_handlingFlags(Id3v2HandlingFlags::Defaults)
|
2018-03-07 01:17:50 +01:00
|
|
|
{
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
inline TagTextEncoding Id3v2Tag::proposedTextEncoding() const
|
|
|
|
{
|
2016-11-12 16:24:37 +01:00
|
|
|
return m_majorVersion > 3 ? TagTextEncoding::Utf8 : TagTextEncoding::Utf16LittleEndian;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Id3v2Tag::canEncodingBeUsed(TagTextEncoding encoding) const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
return encoding == TagTextEncoding::Latin1 || (encoding == TagTextEncoding::Utf8 && m_majorVersion > 3)
|
|
|
|
|| encoding == TagTextEncoding::Utf16BigEndian || encoding == TagTextEncoding::Utf16LittleEndian;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Id3v2Tag::supportsDescription(KnownField field) const
|
|
|
|
{
|
2018-07-13 12:25:00 +02:00
|
|
|
switch (field) {
|
|
|
|
case KnownField::Cover:
|
|
|
|
case KnownField::Lyrics:
|
|
|
|
case KnownField::SynchronizedLyrics:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Id3v2Tag::supportsMimeType(KnownField field) const
|
|
|
|
{
|
|
|
|
return field == KnownField::Cover;
|
|
|
|
}
|
|
|
|
|
2021-05-13 16:07:06 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns flags influencing the behavior when parsing/making the ID3v2 tag.
|
|
|
|
*/
|
|
|
|
inline Id3v2HandlingFlags Id3v2Tag::handlingFlags() const
|
|
|
|
{
|
|
|
|
return m_handlingFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Sets flags influencing the behavior when parsing/making the ID3v2 tag.
|
|
|
|
*/
|
|
|
|
inline void Id3v2Tag::setHandlingFlags(Id3v2HandlingFlags flags)
|
|
|
|
{
|
|
|
|
m_handlingFlags = flags;
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns the major version if known; otherwise returns 0.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
inline std::uint8_t Id3v2Tag::majorVersion() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
return m_majorVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the revision version if known; otherwise returns 0.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
inline std::uint8_t Id3v2Tag::revisionVersion() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
return m_revisionVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an indication whether the version is supported by
|
|
|
|
* the Id3v2Tag class.
|
|
|
|
*
|
|
|
|
* Major versions 2, 3 and 4 are currently supported.
|
|
|
|
*/
|
|
|
|
inline bool Id3v2Tag::isVersionSupported() const
|
|
|
|
{
|
|
|
|
return m_majorVersion == 2 || m_majorVersion == 3 || m_majorVersion == 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the flags read from the ID3v2 header.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
inline std::uint8_t Id3v2Tag::flags() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
return m_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an indication whether unsynchronisation is used.
|
|
|
|
*/
|
|
|
|
inline bool Id3v2Tag::isUnsynchronisationUsed() const
|
|
|
|
{
|
2015-12-22 17:01:25 +01:00
|
|
|
return m_flags & 0x80;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an indication whether an extended header is used.
|
|
|
|
*/
|
|
|
|
inline bool Id3v2Tag::hasExtendedHeader() const
|
|
|
|
{
|
2015-12-22 17:01:25 +01:00
|
|
|
return (m_majorVersion >= 3) && (m_flags & 0x40);
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an indication whether the tag is labeled as experimental.
|
|
|
|
*/
|
|
|
|
inline bool Id3v2Tag::isExperimental() const
|
|
|
|
{
|
2015-12-22 17:01:25 +01:00
|
|
|
return (m_majorVersion >= 3) && (m_flags & 0x20);
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns an indication whether a footer is present.
|
|
|
|
*/
|
|
|
|
inline bool Id3v2Tag::hasFooter() const
|
|
|
|
{
|
2015-12-22 17:01:25 +01:00
|
|
|
return (m_majorVersion >= 3) && (m_flags & 0x10);
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the size of the extended header if one is present; otherwise returns 0.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
inline std::uint32_t Id3v2Tag::extendedHeaderSize() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
return m_extendedHeaderSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the size of the padding between the tag and the first MPEG frame
|
|
|
|
* if one is present; otherwise returns 0.
|
|
|
|
*/
|
2021-03-20 21:26:25 +01:00
|
|
|
inline std::uint64_t Id3v2Tag::paddingSize() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
return m_paddingSize;
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
} // namespace TagParser
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2018-03-06 23:09:15 +01:00
|
|
|
#endif // TAG_PARSER_ID3V2TAG_H
|