4 #include "../diagnostics.h" 5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringbuilder.h> 8 #include <c++utilities/conversion/stringconversion.h> 23 using namespace Id3v2FrameIds;
24 if (m_majorVersion >= 3) {
34 case KnownField::RecordDate:
52 case KnownField::Length:
54 case KnownField::Language:
56 case KnownField::EncoderSettings:
60 case KnownField::SynchronizedLyrics:
82 case KnownField::RecordDate:
100 case KnownField::Length:
102 case KnownField::Language:
104 case KnownField::EncoderSettings:
108 case KnownField::SynchronizedLyrics:
124 KnownField Id3v2Tag::internallyGetKnownField(
const IdentifierType &
id)
const 126 using namespace Id3v2FrameIds;
137 return KnownField::RecordDate;
155 return KnownField::Language;
157 return KnownField::Length;
159 return KnownField::EncoderSettings;
163 return KnownField::SynchronizedLyrics;
177 return KnownField::RecordDate;
193 return KnownField::Language;
195 return KnownField::Length;
197 return KnownField::EncoderSettings;
201 return KnownField::SynchronizedLyrics;
207 return KnownField::Invalid;
211 TagDataType Id3v2Tag::internallyGetProposedDataType(
const uint32 &
id)
const 213 using namespace Id3v2FrameIds;
217 return TagDataType::TimeSpan;
220 return TagDataType::Integer;
224 return TagDataType::PositionInSet;
227 return TagDataType::Picture;
230 return TagDataType::Text;
244 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize,
Diagnostics &diag)
247 static const string context(
"parsing ID3v2 tag");
248 BinaryReader reader(&stream);
249 uint64 startOffset = stream.tellg();
252 if (maximalSize && maximalSize < 10) {
253 diag.emplace_back(DiagLevel::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
258 if (reader.readUInt24BE() != 0x494433u) {
259 diag.emplace_back(DiagLevel::Critical,
"Signature is invalid.", context);
263 byte majorVersion = reader.readByte();
264 byte revisionVersion = reader.readByte();
265 setVersion(majorVersion, revisionVersion);
266 m_flags = reader.readByte();
267 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
268 m_size = 10 + m_sizeExcludingHeader;
269 if (m_sizeExcludingHeader == 0) {
270 diag.emplace_back(DiagLevel::Warning,
"ID3v2 tag seems to be empty.", context);
275 if (!isVersionSupported()) {
276 diag.emplace_back(DiagLevel::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
281 if (hasExtendedHeader()) {
282 if (maximalSize && maximalSize < 14) {
283 diag.emplace_back(DiagLevel::Critical,
"Extended header denoted but not present.", context);
286 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
287 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
288 diag.emplace_back(DiagLevel::Critical,
"Extended header is invalid/truncated.", context);
291 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
295 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
296 if (maximalSize && bytesRemaining > maximalSize) {
297 bytesRemaining = maximalSize;
298 diag.emplace_back(DiagLevel::Critical,
"Frames are truncated.", context);
302 auto pos = stream.tellg();
303 while (bytesRemaining) {
309 frame.
parse(reader, majorVersion, bytesRemaining, diag);
311 diag.emplace_back(DiagLevel::Warning,
"The text frame " % frame.
frameIdString() +
" exists more than once.", context);
313 fields().emplace(frame.
id(), move(frame));
316 m_paddingSize = startOffset + m_size - pos;
323 if (frame.
totalSize() <= bytesRemaining) {
327 pos += bytesRemaining;
336 if (maximalSize && m_size + 10 < maximalSize) {
338 stream.seekg(startOffset + (m_size += 10));
339 if (reader.readUInt24LE() != 0x494433u) {
340 diag.emplace_back(DiagLevel::Critical,
"Footer signature is invalid.", context);
343 stream.seekg(7, ios_base::cur);
345 diag.emplace_back(DiagLevel::Critical,
"Footer denoted but not present.", context);
372 void Id3v2Tag::make(ostream &stream, uint32 padding,
Diagnostics &diag)
374 prepareMaking(diag).make(stream, padding, diag);
381 void Id3v2Tag::setVersion(byte majorVersion, byte revisionVersion)
383 m_majorVersion = majorVersion;
384 m_revisionVersion = revisionVersion;
385 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
398 bool FrameComparer::operator()(
const uint32 &lhs,
const uint32 &rhs)
const 424 if (lhstextfield && !rhstextfield) {
427 if (!lhstextfield && rhstextfield) {
454 static const string context(
"making ID3v2 tag");
464 m_maker.reserve(tag.
fields().size());
465 for (
auto &pair : tag.
fields()) {
467 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion(), diag));
468 m_framesSize += m_maker.back().requiredSize();
469 }
catch (
const Failure &) {
475 m_requiredSize = 10 + m_framesSize;
487 BinaryWriter writer(&stream);
491 writer.writeUInt24BE(0x494433u);
496 writer.writeByte(m_tag.
flags() & 0xBF);
498 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
501 for (
auto &maker : m_maker) {
506 for (; padding; --padding) {
FieldMapBasedTagTraits< Id3v2Tag >::FieldType::IdentifierType IdentifierType
bool hasPaddingReached() const
Returns whether the padding has reached.
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.
bool isLongId(uint32 id)
Returns an indication whether the specified id is a long 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.
std::string frameIdString() const
Returns the frame ID as string.
byte majorVersion() const
Returns the major version if known; otherwise returns 0.
const IdentifierType & id() const
Returns the id of the current TagField.
bool isTextFrame(uint32 id)
Returns an indication whether the specified id is a text frame id.
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.
void parse(IoUtilities::BinaryReader &reader, uint32 version, uint32 maximalSize, Diagnostics &diag)
Parses a frame from the stream read using the specified reader.
uint32 totalSize() const
Returns the total size of the frame in bytes.