4 #include "../diagnostics.h"
5 #include "../exceptions.h"
7 #include <c++utilities/conversion/stringbuilder.h>
8 #include <c++utilities/conversion/stringconversion.h>
26 bool Id3v2Tag::supportsMultipleValues(
KnownField field)
const
32 case KnownField::RecordDate:
40 case KnownField::Length:
41 case KnownField::Language:
42 case KnownField::EncoderSettings:
47 return m_majorVersion > 3;
52 case KnownField::SynchronizedLyrics:
60 void Id3v2Tag::ensureTextValuesAreProperlyEncoded()
62 const auto encoding = proposedTextEncoding();
63 for (
auto &field : fields()) {
64 auto &value = field.second.value();
65 value.convertDataEncoding(encoding);
66 value.convertDescriptionEncoding(encoding);
73 std::vector<const TagValue *> Id3v2Tag::internallyGetValues(
const IdentifierType &
id)
const
75 auto range = fields().equal_range(
id);
76 std::vector<const TagValue *> values;
77 for (
auto i = range.first; i != range.second; ++i) {
78 const auto &frame(i->second);
79 if (!frame.value().isEmpty()) {
80 values.push_back(&frame.value());
82 for (
const auto &value : frame.additionalValues()) {
83 values.push_back(&value);
95 bool Id3v2Tag::internallySetValues(
const IdentifierType &
id,
const std::vector<TagValue> &values)
99 return CRTPBase::internallySetValues(
id, values);
103 auto range = fields().equal_range(
id);
104 auto frameIterator = range.first;
107 auto valuesIterator = values.cbegin();
108 if (frameIterator != range.second) {
111 if (valuesIterator != values.cend()) {
112 frameIterator->second.setValue(*valuesIterator);
115 frameIterator->second.value().clearDataAndMetadata();
119 if (valuesIterator == values.cend()) {
123 frameIterator = fields().insert(make_pair(
id,
Id3v2Frame(
id, *valuesIterator)));
128 frameIterator->second.additionalValues() = vector<TagValue>(valuesIterator, values.cend());
131 for (; range.first != range.second; ++range.first) {
132 range.first->second.setValue(
TagValue());
139 using namespace Id3v2FrameIds;
140 if (m_majorVersion >= 3) {
150 case KnownField::RecordDate:
168 case KnownField::Length:
170 case KnownField::Language:
172 case KnownField::EncoderSettings:
176 case KnownField::SynchronizedLyrics:
200 case KnownField::RecordDate:
218 case KnownField::Length:
220 case KnownField::Language:
222 case KnownField::EncoderSettings:
226 case KnownField::SynchronizedLyrics:
244 KnownField Id3v2Tag::internallyGetKnownField(
const IdentifierType &
id)
const
246 using namespace Id3v2FrameIds;
257 return KnownField::RecordDate;
275 return KnownField::Language;
277 return KnownField::Length;
279 return KnownField::EncoderSettings;
283 return KnownField::SynchronizedLyrics;
299 return KnownField::RecordDate;
315 return KnownField::Language;
317 return KnownField::Length;
319 return KnownField::EncoderSettings;
323 return KnownField::SynchronizedLyrics;
329 return KnownField::Invalid;
333 TagDataType Id3v2Tag::internallyGetProposedDataType(
const std::uint32_t &
id)
const
335 using namespace Id3v2FrameIds;
339 return TagDataType::TimeSpan;
342 return TagDataType::Integer;
346 return TagDataType::PositionInSet;
349 return TagDataType::Picture;
352 return TagDataType::Text;
366 void Id3v2Tag::parse(istream &stream,
const std::uint64_t maximalSize,
Diagnostics &diag)
369 static const string context(
"parsing ID3v2 tag");
370 BinaryReader reader(&stream);
371 const auto startOffset = static_cast<std::uint64_t>(stream.tellg());
374 if (maximalSize && maximalSize < 10) {
375 diag.emplace_back(DiagLevel::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
380 if (reader.readUInt24BE() != 0x494433u) {
381 diag.emplace_back(DiagLevel::Critical,
"Signature is invalid.", context);
385 std::uint8_t majorVersion = reader.readByte();
386 std::uint8_t revisionVersion = reader.readByte();
387 setVersion(majorVersion, revisionVersion);
388 m_flags = reader.readByte();
389 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
390 m_size = 10 + m_sizeExcludingHeader;
391 if (m_sizeExcludingHeader == 0) {
392 diag.emplace_back(DiagLevel::Warning,
"ID3v2 tag seems to be empty.", context);
397 if (!isVersionSupported()) {
398 diag.emplace_back(DiagLevel::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
403 if (hasExtendedHeader()) {
404 if (maximalSize && maximalSize < 14) {
405 diag.emplace_back(DiagLevel::Critical,
"Extended header denoted but not present.", context);
408 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
409 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
410 diag.emplace_back(DiagLevel::Critical,
"Extended header is invalid/truncated.", context);
413 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
417 std::uint32_t bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
418 if (maximalSize && bytesRemaining > maximalSize) {
419 bytesRemaining = static_cast<std::uint32_t>(maximalSize);
420 diag.emplace_back(DiagLevel::Critical,
"Frames are truncated.", context);
424 auto pos = static_cast<std::uint64_t>(stream.tellg());
425 while (bytesRemaining) {
427 stream.seekg(static_cast<streamoff>(pos));
431 frame.
parse(reader, majorVersion, bytesRemaining, diag);
433 diag.emplace_back(DiagLevel::Warning,
"The text frame " % frame.
idToString() +
" exists more than once.", context);
435 fields().emplace(frame.
id(), move(frame));
438 m_paddingSize = startOffset + m_size - pos;
445 if (frame.
totalSize() <= bytesRemaining) {
449 pos += bytesRemaining;
458 if (maximalSize && m_size + 10 < maximalSize) {
460 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
461 if (reader.readUInt24LE() != 0x494433u) {
462 diag.emplace_back(DiagLevel::Critical,
"Footer signature is invalid.", context);
465 stream.seekg(7, ios_base::cur);
467 diag.emplace_back(DiagLevel::Critical,
"Footer denoted but not present.", context);
494 void Id3v2Tag::make(ostream &stream, std::uint32_t padding,
Diagnostics &diag)
496 prepareMaking(diag).make(stream, padding, diag);
503 void Id3v2Tag::setVersion(std::uint8_t majorVersion, std::uint8_t revisionVersion)
505 m_majorVersion = majorVersion;
506 m_revisionVersion = revisionVersion;
507 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
522 bool FrameComparer::operator()(std::uint32_t lhs, std::uint32_t rhs)
const
530 if (lhsLong != rhsLong) {
536 }
else if (!rhsLong) {
559 if (lhstextfield && !rhstextfield) {
562 if (!lhstextfield && rhstextfield) {
590 static const string context(
"making ID3v2 tag");
600 m_maker.reserve(tag.
fields().size());
601 for (
auto &pair : tag.
fields()) {
603 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion(), diag));
604 m_framesSize += m_maker.back().requiredSize();
605 }
catch (
const Failure &) {
611 m_requiredSize = 10 + m_framesSize;
623 CPP_UTILITIES_UNUSED(diag)
625 BinaryWriter writer(&stream);
629 writer.writeUInt24BE(0x494433u);
634 writer.writeByte(m_tag.
flags() & 0xBF);
636 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
639 for (
auto &maker : m_maker) {
644 for (; padding; --padding) {