4 #include "../exceptions.h" 6 #include <c++utilities/conversion/stringconversion.h> 21 using namespace Id3v2FrameIds;
22 if(m_majorVersion >= 3) {
37 case KnownField::Length:
return lLength;
38 case KnownField::Language:
return lLanguage;
64 case KnownField::Length:
return sLength;
65 case KnownField::Language:
return sLanguage;
82 using namespace Id3v2FrameIds;
97 case lLanguage:
return KnownField::Language;
98 case lLength:
return KnownField::Length;
116 case sLanguage:
return KnownField::Language;
117 case sLength:
return KnownField::Length;
123 default:
return KnownField::Invalid;
129 using namespace Id3v2FrameIds;
132 return TagDataType::TimeSpan;
134 return TagDataType::Integer;
137 return TagDataType::PositionInSet;
139 return TagDataType::Picture;
142 return TagDataType::Text;
149 const TagValue &Id3v2Tag::value(
const typename Id3v2Frame::identifierType &
id)
const 154 bool Id3v2Tag::setValue(
const typename Id3v2Frame::identifierType &
id,
const TagValue &value)
166 void Id3v2Tag::parse(istream &stream,
const uint64 maximalSize)
170 const string context(
"parsing ID3v2 tag");
171 BinaryReader reader(&stream);
172 uint64 startOffset = stream.tellg();
175 if(maximalSize && maximalSize < 10) {
176 addNotification(NotificationType::Critical,
"ID3v2 header is truncated (at least 10 bytes expected).", context);
181 if(reader.readUInt24BE() == 0x494433u) {
183 byte majorVersion = reader.readByte();
184 byte revisionVersion = reader.readByte();
185 setVersion(majorVersion, revisionVersion);
186 m_flags = reader.readByte();
187 m_sizeExcludingHeader = reader.readSynchsafeUInt32BE();
188 m_size = 10 + m_sizeExcludingHeader;
189 if(m_sizeExcludingHeader == 0) {
190 addNotification(NotificationType::Warning,
"ID3v2 tag seems to be empty.", context);
193 if(!isVersionSupported()) {
194 addNotification(NotificationType::Critical,
"The ID3v2 tag couldn't be parsed, because its version is not supported.", context);
199 if(hasExtendedHeader()) {
200 if(maximalSize && maximalSize < 14) {
201 addNotification(NotificationType::Critical,
"Extended header denoted but not present.", context);
204 m_extendedHeaderSize = reader.readSynchsafeUInt32BE();
205 if(m_extendedHeaderSize < 6 || m_extendedHeaderSize > m_sizeExcludingHeader || (maximalSize && maximalSize < (10 + m_extendedHeaderSize))) {
206 addNotification(NotificationType::Critical,
"Extended header is invalid/truncated.", context);
209 stream.seekg(m_extendedHeaderSize - 4, ios_base::cur);
213 uint32 bytesRemaining = m_sizeExcludingHeader - m_extendedHeaderSize;
214 if(maximalSize && bytesRemaining > maximalSize) {
215 bytesRemaining = maximalSize;
216 addNotification(NotificationType::Critical,
"Frames are truncated.", context);
220 auto pos = stream.tellg();
222 while(bytesRemaining) {
227 frame.
parse(reader, majorVersion, bytesRemaining);
231 addNotification(NotificationType::Warning,
"The text frame " + frame.
frameIdString() +
" exists more than once.", context);
233 fields().insert(make_pair(frame.
id(), frame));
237 m_paddingSize = startOffset + m_size - pos;
245 addNotifications(context, frame);
249 if(frame.
totalSize() <= bytesRemaining) {
253 pos += bytesRemaining;
260 if(maximalSize && m_size + 10 < maximalSize) {
262 stream.seekg(startOffset + (m_size += 10));
263 if(reader.readUInt24LE() != 0x494433u) {
264 addNotification(NotificationType::Critical,
"Footer signature is invalid.", context);
267 stream.seekg(7, ios_base::cur);
269 addNotification(NotificationType::Critical,
"Footer denoted but not present.", context);
275 addNotification(NotificationType::Critical,
"Signature is invalid.", context);
302 void Id3v2Tag::make(ostream &stream, uint32 padding)
304 prepareMaking().make(stream, padding);
311 void Id3v2Tag::setVersion(byte majorVersion, byte revisionVersion)
313 m_majorVersion = majorVersion;
314 m_revisionVersion = revisionVersion;
315 stringstream versionStream(stringstream::in | stringstream::out);
316 versionStream <<
"2." <<
static_cast<int>(majorVersion) <<
"." << static_cast<int>(revisionVersion);
317 m_version = versionStream.str();
330 bool FrameComparer::operator()(
const uint32 &lhs,
const uint32 &rhs)
const 356 if(lhstextfield && !rhstextfield) {
359 if(!lhstextfield && rhstextfield) {
382 Id3v2TagMaker::Id3v2TagMaker(
Id3v2Tag &tag) :
387 const string context(
"making ID3v2 tag");
392 tag.
addNotification(NotificationType::Critical,
"The ID3v2 tag version isn't supported.", context);
397 m_maker.reserve(tag.
fields().size());
398 for(
auto &pair : tag.
fields()) {
400 m_maker.emplace_back(pair.second.prepareMaking(tag.
majorVersion()));
401 m_framesSize += m_maker.back().requiredSize();
405 m_tag.addNotifications(pair.second);
410 m_requiredSize = 10 + m_framesSize;
420 void Id3v2TagMaker::make(std::ostream &stream, uint32 padding)
422 BinaryWriter writer(&stream);
426 writer.writeUInt24BE(0x494433u);
428 writer.writeByte(m_tag.majorVersion());
429 writer.writeByte(m_tag.revisionVersion());
431 writer.writeByte(m_tag.flags() & 0xBF);
433 writer.writeSynchsafeUInt32BE(m_framesSize + padding);
436 for(
auto &maker : m_maker) {
441 for(; padding; --padding) {
Contains utility classes helping to read and write streams.