4 #include "../diagnostics.h" 5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringbuilder.h> 8 #include <c++utilities/conversion/stringconversion.h> 25 using namespace Id3v2FrameIds;
26 if (m_majorVersion >= 3) {
36 case KnownField::RecordDate:
54 case KnownField::Length:
56 case KnownField::Language:
58 case KnownField::EncoderSettings:
62 case KnownField::SynchronizedLyrics:
84 case KnownField::RecordDate:
102 case KnownField::Length:
104 case KnownField::Language:
106 case KnownField::EncoderSettings:
110 case KnownField::SynchronizedLyrics:
126 KnownField Id3v2Tag::internallyGetKnownField(
const IdentifierType &
id)
const 128 using namespace Id3v2FrameIds;
139 return KnownField::RecordDate;
157 return KnownField::Language;
159 return KnownField::Length;
161 return KnownField::EncoderSettings;
165 return KnownField::SynchronizedLyrics;
179 return KnownField::RecordDate;
195 return KnownField::Language;
197 return KnownField::Length;
199 return KnownField::EncoderSettings;
203 return KnownField::SynchronizedLyrics;
209 return KnownField::Invalid;
213 TagDataType Id3v2Tag::internallyGetProposedDataType(
const uint32 &
id)
const 215 using namespace Id3v2FrameIds;
219 return TagDataType::TimeSpan;
222 return TagDataType::Integer;
226 return TagDataType::PositionInSet;
229 return TagDataType::Picture;
232 return TagDataType::Text;
246 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize,
Diagnostics &diag)
249 static const string context(
"parsing ID3v2 tag");
250 BinaryReader reader(&stream);
251 const auto startOffset =
static_cast<uint64
>(stream.tellg());
254 if (maximalSize && maximalSize < 10) {
255 diag.emplace_back(DiagLevel::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
260 if (reader.readUInt24BE() != 0x494433u) {
261 diag.emplace_back(DiagLevel::Critical,
"Signature is invalid.", context);
265 byte majorVersion = reader.readByte();
266 byte revisionVersion = reader.readByte();
267 setVersion(majorVersion, revisionVersion);
268 m_flags = reader.readByte();
269 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
270 m_size = 10 + m_sizeExcludingHeader;
271 if (m_sizeExcludingHeader == 0) {
272 diag.emplace_back(DiagLevel::Warning,
"ID3v2 tag seems to be empty.", context);
277 if (!isVersionSupported()) {
278 diag.emplace_back(DiagLevel::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
283 if (hasExtendedHeader()) {
284 if (maximalSize && maximalSize < 14) {
285 diag.emplace_back(DiagLevel::Critical,
"Extended header denoted but not present.", context);
288 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
289 if (m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
290 diag.emplace_back(DiagLevel::Critical,
"Extended header is invalid/truncated.", context);
293 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
297 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
298 if (maximalSize && bytesRemaining > maximalSize) {
299 bytesRemaining =
static_cast<uint32
>(maximalSize);
300 diag.emplace_back(DiagLevel::Critical,
"Frames are truncated.", context);
304 auto pos =
static_cast<uint64
>(stream.tellg());
305 while (bytesRemaining) {
307 stream.seekg(static_cast<streamoff>(pos));
311 frame.
parse(reader, majorVersion, bytesRemaining, diag);
313 diag.emplace_back(DiagLevel::Warning,
"The text frame " % frame.
frameIdString() +
" exists more than once.", context);
315 fields().emplace(frame.
id(), move(frame));
318 m_paddingSize = startOffset + m_size - pos;
325 if (frame.
totalSize() <= bytesRemaining) {
329 pos += bytesRemaining;
338 if (maximalSize && m_size + 10 < maximalSize) {
340 stream.seekg(static_cast<streamoff>(startOffset + (m_size += 10)));
341 if (reader.readUInt24LE() != 0x494433u) {
342 diag.emplace_back(DiagLevel::Critical,
"Footer signature is invalid.", context);
345 stream.seekg(7, ios_base::cur);
347 diag.emplace_back(DiagLevel::Critical,
"Footer denoted but not present.", context);
374 void Id3v2Tag::make(ostream &stream, uint32 padding,
Diagnostics &diag)
376 prepareMaking(diag).make(stream, padding, diag);
383 void Id3v2Tag::setVersion(byte majorVersion, byte revisionVersion)
385 m_majorVersion = majorVersion;
386 m_revisionVersion = revisionVersion;
387 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
401 bool FrameComparer::operator()(
const uint32 &lhsRef,
const uint32 &rhsRef)
const 412 if (lhsLong != rhsLong) {
415 }
else if (!rhsLong) {
435 if (lhstextfield && !rhstextfield) {
438 if (!lhstextfield && rhstextfield) {
466 static const string context(
"making ID3v2 tag");
476 m_maker.reserve(tag.
fields().size());
477 for (
auto &pair : tag.
fields()) {
479 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion(), diag));
480 m_framesSize += m_maker.back().requiredSize();
481 }
catch (
const Failure &) {
487 m_requiredSize = 10 + m_framesSize;
501 BinaryWriter writer(&stream);
505 writer.writeUInt24BE(0x494433u);
510 writer.writeByte(m_tag.
flags() & 0xBF);
512 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
515 for (
auto &maker : m_maker) {
520 for (; padding; --padding) {
FieldMapBasedTagTraits< Id3v2Tag >::FieldType::IdentifierType IdentifierType
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.
The exception that is thrown when an operation fails because the detected or specified version is not...
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.
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.
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.
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.
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 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.