4 #include "../exceptions.h" 6 #include <c++utilities/conversion/stringconversion.h> 7 #include <c++utilities/conversion/stringbuilder.h> 22 using namespace Id3v2FrameIds;
23 if(m_majorVersion >= 3) {
38 case KnownField::Length:
return lLength;
39 case KnownField::Language:
return lLanguage;
65 case KnownField::Length:
return sLength;
66 case KnownField::Language:
return sLanguage;
83 using namespace Id3v2FrameIds;
98 case lLanguage:
return KnownField::Language;
99 case lLength:
return KnownField::Length;
117 case sLanguage:
return KnownField::Language;
118 case sLength:
return KnownField::Length;
124 default:
return KnownField::Invalid;
130 using namespace Id3v2FrameIds;
133 return TagDataType::TimeSpan;
135 return TagDataType::Integer;
138 return TagDataType::PositionInSet;
140 return TagDataType::Picture;
143 return TagDataType::Text;
150 const TagValue &Id3v2Tag::value(
const typename Id3v2Frame::identifierType &
id)
const 155 bool Id3v2Tag::setValue(
const typename Id3v2Frame::identifierType &
id,
const TagValue &value)
167 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize)
171 static const string context(
"parsing ID3v2 tag");
172 BinaryReader reader(&stream);
173 uint64 startOffset = stream.tellg();
176 if(maximalSize && maximalSize < 10) {
177 addNotification(NotificationType::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
182 if(reader.readUInt24BE() == 0x494433u) {
184 byte majorVersion = reader.readByte();
185 byte revisionVersion = reader.readByte();
186 setVersion(majorVersion, revisionVersion);
187 m_flags = reader.readByte();
188 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
189 m_size = 10 + m_sizeExcludingHeader;
190 if(m_sizeExcludingHeader == 0) {
191 addNotification(NotificationType::Warning,
"ID3v2 tag seems to be empty.", context);
194 if(!isVersionSupported()) {
195 addNotification(NotificationType::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
200 if(hasExtendedHeader()) {
201 if(maximalSize && maximalSize < 14) {
202 addNotification(NotificationType::Critical,
"Extended header denoted but not present.", context);
205 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
206 if(m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
207 addNotification(NotificationType::Critical,
"Extended header is invalid/truncated.", context);
210 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
214 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
215 if(maximalSize && bytesRemaining > maximalSize) {
216 bytesRemaining = maximalSize;
217 addNotification(NotificationType::Critical,
"Frames are truncated.", context);
221 auto pos = stream.tellg();
223 while(bytesRemaining) {
228 frame.
parse(reader, majorVersion, bytesRemaining);
232 addNotification(NotificationType::Warning,
"The text frame " % frame.
frameIdString() +
" exists more than once.", context);
234 fields().insert(make_pair(frame.
id(), frame));
238 m_paddingSize = startOffset + m_size - pos;
246 addNotifications(context, frame);
250 if(frame.
totalSize() <= bytesRemaining) {
254 pos += bytesRemaining;
261 if(maximalSize && m_size + 10 < maximalSize) {
263 stream.seekg(startOffset + (m_size += 10));
264 if(reader.readUInt24LE() != 0x494433u) {
265 addNotification(NotificationType::Critical,
"Footer signature is invalid.", context);
268 stream.seekg(7, ios_base::cur);
270 addNotification(NotificationType::Critical,
"Footer denoted but not present.", context);
276 addNotification(NotificationType::Critical,
"Signature is invalid.", context);
303 void Id3v2Tag::make(ostream &stream, uint32 padding)
305 prepareMaking().make(stream, padding);
312 void Id3v2Tag::setVersion(byte majorVersion, byte revisionVersion)
314 m_majorVersion = majorVersion;
315 m_revisionVersion = revisionVersion;
316 m_version = argsToString(
'2',
'.', majorVersion,
'.', revisionVersion);
329 bool FrameComparer::operator()(
const uint32 &lhs,
const uint32 &rhs)
const 355 if(lhstextfield && !rhstextfield) {
358 if(!lhstextfield && rhstextfield) {
381 Id3v2TagMaker::Id3v2TagMaker(
Id3v2Tag &tag) :
386 static const string context(
"making ID3v2 tag");
396 m_maker.reserve(tag.
fields().size());
397 for(
auto &pair : tag.
fields()) {
399 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion()));
400 m_framesSize += m_maker.back().requiredSize();
401 }
catch(
const Failure &) {
404 m_tag.addNotifications(pair.second);
409 m_requiredSize = 10 + m_framesSize;
421 BinaryWriter writer(&stream);
425 writer.writeUInt24BE(0x494433u);
430 writer.writeByte(m_tag.
flags() & 0xBF);
432 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
435 for(
auto &maker : m_maker) {
440 for(; padding; --padding) {
Contains utility classes helping to read and write streams.