4 #include "../diagnostics.h" 5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringbuilder.h> 8 #include <c++utilities/conversion/stringconversion.h> 27 bool Id3v2Tag::supportsMultipleValues(
KnownField field)
const 33 case KnownField::RecordDate:
41 case KnownField::Length:
42 case KnownField::Language:
43 case KnownField::EncoderSettings:
47 return m_majorVersion > 3;
52 case KnownField::SynchronizedLyrics:
63 std::vector<const TagValue *> Id3v2Tag::internallyGetValues(
const IdentifierType &
id)
const 65 auto range = fields().equal_range(
id);
66 std::vector<const TagValue *> values;
67 for (
auto i = range.first; i != range.second; ++i) {
68 const auto &frame(i->second);
69 if (!frame.value().isEmpty()) {
70 values.push_back(&frame.value());
72 for (
const auto &value : frame.additionalValues()) {
73 values.push_back(&value);
85 bool Id3v2Tag::internallySetValues(
const IdentifierType &
id,
const std::vector<TagValue> &values)
89 return CRTPBase::internallySetValues(
id, values);
93 auto range = fields().equal_range(
id);
94 auto frameIterator = range.first;
97 auto valuesIterator = values.cbegin();
98 if (frameIterator != range.second) {
101 if (valuesIterator != values.cend()) {
102 frameIterator->second.setValue(*valuesIterator);
105 frameIterator->second.value().clearDataAndMetadata();
109 if (valuesIterator == values.cend()) {
113 frameIterator = fields().insert(make_pair(
id,
Id3v2Frame(
id, *valuesIterator)));
118 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
121 for (; range.first != range.second; ++range.first) {
122 range.first->second.setValue(
TagValue());
129 using namespace Id3v2FrameIds;
130 if (m_majorVersion >= 3) {
140 case KnownField::RecordDate:
158 case KnownField::Length:
160 case KnownField::Language:
162 case KnownField::EncoderSettings:
166 case KnownField::SynchronizedLyrics:
188 case KnownField::RecordDate:
206 case KnownField::Length:
208 case KnownField::Language:
210 case KnownField::EncoderSettings:
214 case KnownField::SynchronizedLyrics:
230 KnownField Id3v2Tag::internallyGetKnownField(
const IdentifierType &
id)
const 232 using namespace Id3v2FrameIds;
243 return KnownField::RecordDate;
261 return KnownField::Language;
263 return KnownField::Length;
265 return KnownField::EncoderSettings;
269 return KnownField::SynchronizedLyrics;
283 return KnownField::RecordDate;
299 return KnownField::Language;
301 return KnownField::Length;
303 return KnownField::EncoderSettings;
307 return KnownField::SynchronizedLyrics;
313 return KnownField::Invalid;
317 TagDataType Id3v2Tag::internallyGetProposedDataType(
const uint32 &
id)
const 319 using namespace Id3v2FrameIds;
323 return TagDataType::TimeSpan;
326 return TagDataType::Integer;
330 return TagDataType::PositionInSet;
333 return TagDataType::Picture;
336 return TagDataType::Text;
350 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize,
Diagnostics &diag)
353 static const string context(
"parsing ID3v2 tag");
354 BinaryReader reader(&stream);
355 const auto startOffset =
static_cast<uint64
>(stream.tellg());
358 if (maximalSize && maximalSize < 10) {
359 diag.emplace_back(DiagLevel::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
364 if (reader.readUInt24BE() != 0x494433u) {
365 diag.emplace_back(DiagLevel::Critical,
"Signature is invalid.", context);
369 byte majorVersion = reader.readByte();
370 byte revisionVersion = reader.readByte();
371 setVersion(majorVersion, revisionVersion);
372 m_flags = reader.readByte();
373 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
374 m_size = 10 + m_sizeExcludingHeader;
375 if (m_sizeExcludingHeader == 0) {
376 diag.emplace_back(DiagLevel::Warning,
"ID3v2 tag seems to be empty.", context);
381 if (!isVersionSupported()) {
382 diag.emplace_back(DiagLevel::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
387 if (hasExtendedHeader()) {
388 if (maximalSize && maximalSize < 14) {
389 diag.emplace_back(DiagLevel::Critical,
"Extended header denoted but not present.", context);
392 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
393 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
394 diag.emplace_back(DiagLevel::Critical,
"Extended header is invalid/truncated.", context);
397 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
401 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
402 if (maximalSize && bytesRemaining > maximalSize) {
403 bytesRemaining =
static_cast<uint32
>(maximalSize);
404 diag.emplace_back(DiagLevel::Critical,
"Frames are truncated.", context);
408 auto pos =
static_cast<uint64
>(stream.tellg());
409 while (bytesRemaining) {
411 stream.seekg(static_cast<streamoff>(pos));
415 frame.
parse(reader, majorVersion, bytesRemaining, diag);
417 diag.emplace_back(DiagLevel::Warning,
"The text frame " % frame.
idToString() +
" exists more than once.", context);
419 fields().emplace(frame.
id(), move(frame));
422 m_paddingSize = startOffset + m_size - pos;
429 if (frame.
totalSize() <= bytesRemaining) {
433 pos += bytesRemaining;
442 if (maximalSize && m_size + 10 < maximalSize) {
444 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
445 if (reader.readUInt24LE() != 0x494433u) {
446 diag.emplace_back(DiagLevel::Critical,
"Footer signature is invalid.", context);
449 stream.seekg(7, ios_base::cur);
451 diag.emplace_back(DiagLevel::Critical,
"Footer denoted but not present.", context);
478 void Id3v2Tag::make(ostream &stream, uint32 padding,
Diagnostics &diag)
480 prepareMaking(diag).make(stream, padding, diag);
487 void Id3v2Tag::setVersion(byte majorVersion, byte revisionVersion)
489 m_majorVersion = majorVersion;
490 m_revisionVersion = revisionVersion;
491 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
506 bool FrameComparer::operator()(uint32 lhs, uint32 rhs)
const 514 if (lhsLong != rhsLong) {
520 }
else if (!rhsLong) {
543 if (lhstextfield && !rhstextfield) {
546 if (!lhstextfield && rhstextfield) {
574 static const string context(
"making ID3v2 tag");
584 m_maker.reserve(tag.
fields().size());
585 for (
auto &pair : tag.
fields()) {
587 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion(), diag));
588 m_framesSize += m_maker.back().requiredSize();
589 }
catch (
const Failure &) {
595 m_requiredSize = 10 + m_framesSize;
609 BinaryWriter writer(&stream);
613 writer.writeUInt24BE(0x494433u);
618 writer.writeByte(m_tag.
flags() & 0xBF);
620 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
623 for (
auto &maker : m_maker) {
628 for (; padding; --padding) {
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
bool hasPaddingReached() const
Returns whether the padding has reached.
typename FieldMapBasedTagTraits< Id3v2Tag >::FieldType::IdentifierType IdentifierType
The exception that is thrown when an operation fails because the detected or specified version is not...
constexpr bool isLongId(uint32 id)
Returns an indication whether the specified id is a long frame id.
TagDataType
Specifies the data type.
byte flags() const
Returns the flags read from the ID3v2 header.
uint32 convertToLongId(uint32 id)
Converts the specified short frame ID to the equivalent long frame ID.
KnownField
Specifies the field.
byte revisionVersion() const
Returns the revision version if known; otherwise returns 0.
The Id3v2Frame class is used by Id3v2Tag to store the fields.
The Id3v2TagMaker class helps writing ID3v2 tags.
std::string idToString() const
constexpr bool isTextFrame(uint32 id)
Returns an indication whether the specified id is a text frame id.
Contains utility classes helping to read and write streams.
bool isVersionSupported() const
Returns an indication whether the version is supported by the Id3v2Tag class.
The exception that is thrown when the data to be parsed holds no parsable information.
byte majorVersion() const
Returns the major version if known; otherwise returns 0.
const IdentifierType & id() const
Returns the id of the current TagField.
void make(std::ostream &stream, uint32 padding, Diagnostics &diag)
Saves the tag (specified when constructing the object) to the specified stream.
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
Returns the fields of the tag by providing direct access to the field map of the tag.
Implementation of TagParser::Tag for ID3v2 tags.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
The TagValue class wraps values of different types.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
void parse(IoUtilities::BinaryReader &reader, uint32 version, uint32 maximalSize, Diagnostics &diag)
Parses a frame from the stream read using the specified reader.
Contains all classes and functions of the TagInfo library.
The Diagnostics class is a container for DiagMessage.
uint32 totalSize() const
Returns the total size of the frame in bytes.