4 #include "../diagnostics.h" 5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringbuilder.h> 8 #include <c++utilities/conversion/stringconversion.h> 14 using namespace ConversionUtilities;
27 bool Id3v2Tag::supportsMultipleValues(
KnownField field)
const 33 case KnownField::RecordDate:
41 case KnownField::Length:
42 case KnownField::Language:
43 case KnownField::EncoderSettings:
48 return m_majorVersion > 3;
53 case KnownField::SynchronizedLyrics:
64 std::vector<const TagValue *> Id3v2Tag::internallyGetValues(
const IdentifierType &
id)
const 66 auto range = fields().equal_range(
id);
67 std::vector<const TagValue *> values;
68 for (
auto i = range.first; i != range.second; ++i) {
69 const auto &frame(i->second);
70 if (!frame.value().isEmpty()) {
71 values.push_back(&frame.value());
73 for (
const auto &value : frame.additionalValues()) {
74 values.push_back(&value);
86 bool Id3v2Tag::internallySetValues(
const IdentifierType &
id,
const std::vector<TagValue> &values)
90 return CRTPBase::internallySetValues(
id, values);
94 auto range = fields().equal_range(
id);
95 auto frameIterator = range.first;
98 auto valuesIterator = values.cbegin();
99 if (frameIterator != range.second) {
102 if (valuesIterator != values.cend()) {
103 frameIterator->second.setValue(*valuesIterator);
106 frameIterator->second.value().clearDataAndMetadata();
110 if (valuesIterator == values.cend()) {
114 frameIterator = fields().insert(make_pair(
id,
Id3v2Frame(
id, *valuesIterator)));
119 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
122 for (; range.first != range.second; ++range.first) {
123 range.first->second.setValue(
TagValue());
130 using namespace Id3v2FrameIds;
131 if (m_majorVersion >= 3) {
141 case KnownField::RecordDate:
159 case KnownField::Length:
161 case KnownField::Language:
163 case KnownField::EncoderSettings:
167 case KnownField::SynchronizedLyrics:
191 case KnownField::RecordDate:
209 case KnownField::Length:
211 case KnownField::Language:
213 case KnownField::EncoderSettings:
217 case KnownField::SynchronizedLyrics:
235 KnownField Id3v2Tag::internallyGetKnownField(
const IdentifierType &
id)
const 237 using namespace Id3v2FrameIds;
248 return KnownField::RecordDate;
266 return KnownField::Language;
268 return KnownField::Length;
270 return KnownField::EncoderSettings;
274 return KnownField::SynchronizedLyrics;
290 return KnownField::RecordDate;
306 return KnownField::Language;
308 return KnownField::Length;
310 return KnownField::EncoderSettings;
314 return KnownField::SynchronizedLyrics;
320 return KnownField::Invalid;
324 TagDataType Id3v2Tag::internallyGetProposedDataType(
const uint32 &
id)
const 326 using namespace Id3v2FrameIds;
330 return TagDataType::TimeSpan;
333 return TagDataType::Integer;
337 return TagDataType::PositionInSet;
340 return TagDataType::Picture;
343 return TagDataType::Text;
357 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize,
Diagnostics &diag)
360 static const string context(
"parsing ID3v2 tag");
361 BinaryReader reader(&stream);
362 const auto startOffset = static_cast<uint64>(stream.tellg());
365 if (maximalSize && maximalSize < 10) {
366 diag.emplace_back(DiagLevel::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
371 if (reader.readUInt24BE() != 0x494433u) {
372 diag.emplace_back(DiagLevel::Critical,
"Signature is invalid.", context);
376 byte majorVersion = reader.readByte();
377 byte revisionVersion = reader.readByte();
378 setVersion(majorVersion, revisionVersion);
379 m_flags = reader.readByte();
380 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
381 m_size = 10 + m_sizeExcludingHeader;
382 if (m_sizeExcludingHeader == 0) {
383 diag.emplace_back(DiagLevel::Warning,
"ID3v2 tag seems to be empty.", context);
388 if (!isVersionSupported()) {
389 diag.emplace_back(DiagLevel::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
394 if (hasExtendedHeader()) {
395 if (maximalSize && maximalSize < 14) {
396 diag.emplace_back(DiagLevel::Critical,
"Extended header denoted but not present.", context);
399 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
400 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
401 diag.emplace_back(DiagLevel::Critical,
"Extended header is invalid/truncated.", context);
404 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
408 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
409 if (maximalSize && bytesRemaining > maximalSize) {
410 bytesRemaining = static_cast<uint32>(maximalSize);
411 diag.emplace_back(DiagLevel::Critical,
"Frames are truncated.", context);
415 auto pos = static_cast<uint64>(stream.tellg());
416 while (bytesRemaining) {
418 stream.seekg(static_cast<streamoff>(pos));
422 frame.
parse(reader, majorVersion, bytesRemaining, diag);
424 diag.emplace_back(DiagLevel::Warning,
"The text frame " % frame.
idToString() +
" exists more than once.", context);
426 fields().emplace(frame.
id(), move(frame));
429 m_paddingSize = startOffset + m_size - pos;
436 if (frame.
totalSize() <= bytesRemaining) {
440 pos += bytesRemaining;
449 if (maximalSize && m_size + 10 < maximalSize) {
451 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
452 if (reader.readUInt24LE() != 0x494433u) {
453 diag.emplace_back(DiagLevel::Critical,
"Footer signature is invalid.", context);
456 stream.seekg(7, ios_base::cur);
458 diag.emplace_back(DiagLevel::Critical,
"Footer denoted but not present.", context);
485 void Id3v2Tag::make(ostream &stream, uint32 padding,
Diagnostics &diag)
487 prepareMaking(diag).make(stream, padding, diag);
494 void Id3v2Tag::setVersion(
byte majorVersion,
byte revisionVersion)
496 m_majorVersion = majorVersion;
497 m_revisionVersion = revisionVersion;
498 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
513 bool FrameComparer::operator()(uint32 lhs, uint32 rhs)
const 521 if (lhsLong != rhsLong) {
527 }
else if (!rhsLong) {
550 if (lhstextfield && !rhstextfield) {
553 if (!lhstextfield && rhstextfield) {
581 static const string context(
"making ID3v2 tag");
591 m_maker.reserve(tag.
fields().size());
592 for (
auto &pair : tag.
fields()) {
594 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion(), diag));
595 m_framesSize += m_maker.back().requiredSize();
596 }
catch (
const Failure &) {
602 m_requiredSize = 10 + m_framesSize;
616 BinaryWriter writer(&stream);
620 writer.writeUInt24BE(0x494433u);
625 writer.writeByte(m_tag.
flags() & 0xBF);
627 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
630 for (
auto &maker : m_maker) {
635 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 (e....
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.