5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringconversion.h> 8 #include <c++utilities/misc/memory.h> 30 Id3v2Frame::Id3v2Frame() :
56 template<
class stringtype>
60 for(
auto c : denotation) {
61 if(
sizeof(
typename stringtype::value_type) == 2 && isBigEndian != CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN) {
62 c = swapOrder(static_cast<uint16>(c));
74 if(c >=
'0' && c <=
'9') {
87 if(c >=
'0' && c <=
'9') {
88 index = index * 10 + c -
'0';
112 string context(
"parsing ID3v2 frame");
118 setId(reader.readUInt24BE());
119 if(
id() & 0xFFFF0000u) {
132 m_dataSize = reader.readUInt24BE();
133 m_totalSize = m_dataSize + 6;
134 if(m_totalSize > maximalSize) {
146 setId(reader.readUInt32BE());
147 if(
id() & 0xFF000000u) {
160 m_dataSize = version >= 4
161 ? reader.readSynchsafeUInt32BE()
162 : reader.readUInt32BE();
163 m_totalSize = m_dataSize + 10;
164 if(m_totalSize > maximalSize) {
170 m_flag = reader.readUInt16BE();
180 if(m_dataSize <= 0) {
186 unique_ptr<char[]> buffer;
190 uLongf decompressedSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
191 if(decompressedSize < m_dataSize) {
195 auto bufferCompressed = make_unique<char[]>(m_dataSize);;
196 reader.read(bufferCompressed.get(), m_dataSize);
197 buffer = make_unique<char[]>(decompressedSize);
198 switch(uncompress(reinterpret_cast<Bytef *>(buffer.get()), &decompressedSize, reinterpret_cast<Bytef *>(bufferCompressed.get()), m_dataSize)) {
214 m_dataSize = decompressedSize;
216 buffer = make_unique<char[]>(m_dataSize);
217 reader.read(buffer.get(), m_dataSize);
236 }
catch(
const ConversionException &) {
245 milliseconds = ConversionUtilities::stringToNumber<double>(
parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding), 10);
247 milliseconds = ConversionUtilities::stringToNumber<double>(
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding), 10);
250 }
catch (
const ConversionException &) {
258 auto genreDenotation =
parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
261 auto genreDenotation =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
264 if(genreIndex != -1) {
270 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
275 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
356 m_frameId(m_frame.id()),
359 m_frame.invalidateStatus();
360 const string context(
"making " + m_frame.frameIdString() +
" frame");
363 if(m_frame.value().isEmpty()) {
367 if(m_frame.isEncrypted()) {
368 m_frame.addNotification(
NotificationType::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
371 if(m_frame.hasPaddingReached()) {
375 if(version < 3 && m_frame.isCompressed()) {
376 m_frame.addNotification(
NotificationType::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
378 if(version < 3 && (m_frame.flag() || m_frame.group())) {
379 m_frame.addNotification(
NotificationType::Warning,
"The existing flag and group information is not supported by the version of ID3v2 and will be ignored/discarted.", context);
387 m_frame.addNotification(
NotificationType::Critical,
"The short frame ID can't be converted to its long equivalent which is needed to use the frame in a newer version of ID3v2.", context);
395 m_frame.addNotification(
NotificationType::Critical,
"The long frame ID can't be converted to its short equivalent which is needed to use the frame in the old version of ID3v2.", context);
412 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toTimeSpan().totalMilliseconds()),
TagTextEncoding::Latin1);
416 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toStandardGenreIndex()),
TagTextEncoding::Latin1);
419 m_frame.makeString(m_data, m_decompressedSize, m_frame.value().toString(), m_frame.value().dataEncoding());
424 m_frame.makePicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
428 m_frame.makeLegacyPicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
435 m_frame.makeComment(m_data, m_decompressedSize, m_frame.value());
440 m_data = make_unique<char[]>(m_decompressedSize = m_frame.value().dataSize());
442 copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
444 }
catch(
const ConversionException &) {
450 if(version >= 3 && m_frame.isCompressed()) {
451 m_dataSize = compressBound(m_decompressedSize);
452 auto compressedData = make_unique<char[]>(m_decompressedSize);
453 switch(compress(reinterpret_cast<Bytef *>(compressedData.get()), reinterpret_cast<uLongf *>(&m_dataSize),
reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
463 m_data.swap(compressedData);
465 m_dataSize = m_decompressedSize;
470 m_requiredSize = m_dataSize;
476 m_requiredSize += 10;
478 if(m_frame.hasGroupInformation()) {
482 if(version >= 3 && m_frame.isCompressed()) {
498 writer.writeUInt24BE(m_frameId);
499 writer.writeUInt24BE(m_dataSize);
501 writer.writeUInt32BE(m_frameId);
503 writer.writeSynchsafeUInt32BE(m_dataSize);
505 writer.writeUInt32BE(m_dataSize);
507 writer.writeUInt16BE(m_frame.flag());
508 if(m_frame.hasGroupInformation()) {
509 writer.writeByte(m_frame.group());
511 if(m_version >= 3 && m_frame.isCompressed()) {
513 writer.writeSynchsafeUInt32BE(m_decompressedSize);
515 writer.writeUInt32BE(m_decompressedSize);
519 writer.write(m_data.get(), m_dataSize);
530 switch(textEncodingByte) {
550 switch(textEncoding) {
580 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
584 if(bufferSize >= 2) {
585 if(ConversionUtilities::LE::toUInt16(buffer) == 0xFEFF) {
591 }
else if(ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF) {
599 const uint16 *pos =
reinterpret_cast<const uint16 *
>(get<0>(res));
600 for(; *pos != 0x0000; ++pos) {
601 if(pos < reinterpret_cast<const uint16 *>(get<2>(res))) {
610 get<2>(res) = reinterpret_cast<const char *>(++pos);
614 if((bufferSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
621 const char *pos = get<0>(res);
622 for(; *pos != 0x00; ++pos) {
623 if(pos < get<2>(res)) {
646 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
647 return string(get<0>(substr), get<1>(substr));
659 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
660 u16string res(reinterpret_cast<u16string::const_pointer>(get<0>(substr)), get<1>(substr) / 2);
662 #
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
664 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
667 # error
"Host byte order not supported" 672 c = ((c >> 8) & 0x00FF) | ((c << 8) & 0xFF00);
692 if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFFFE)) {
694 }
else if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF)) {
699 if((maxSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
715 static const string context(
"parsing ID3v2.2 picture frame");
720 const char *end = buffer + maxSize;
723 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
724 auto substr =
parseSubstring(buffer + 5, end - 5 - buffer, dataEncoding,
true);
725 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
726 if(get<2>(substr) >= end) {
742 static const string context(
"parsing ID3v2.3 picture frame");
743 const char *end = buffer + maxSize;
746 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true);
748 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
750 if(get<2>(substr) >= end) {
754 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
755 if(++get<2>(substr) >= end) {
759 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
true);
760 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
761 if(get<2>(substr) >= end) {
776 static const string context(
"parsing comment/unsynchronized lyrics frame");
777 const char *end = buffer +
dataSize;
786 auto substr =
parseSubstring(buffer += 3, dataSize -= 4, dataEncoding,
true);
787 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
788 if(get<2>(substr) > end) {
792 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
false);
827 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 1);
832 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 2);
837 copy(data, data + dataSize, buffer.get() + 1);
849 string::size_type descriptionLength = picture.
description().find(
'\0');
850 if(descriptionLength == string::npos) {
851 descriptionLength = picture.
description().length();
855 char *offset = buffer.get();
859 const char *imageFormat;
860 if(picture.
mimeType() ==
"image/jpeg") {
862 }
else if(picture.
mimeType() ==
"image/png") {
864 }
else if(picture.
mimeType() ==
"image/gif") {
866 }
else if(picture.
mimeType() ==
"-->") {
867 imageFormat = picture.
mimeType().data();
871 strncpy(++offset, imageFormat, 3);
873 *(offset += 3) = typeInfo;
875 picture.
description().copy(++offset, descriptionLength);
876 *(offset += descriptionLength) = 0x00;
892 string::size_type mimeTypeLength = picture.
mimeType().find(
'\0');
893 if(mimeTypeLength == string::npos) {
894 mimeTypeLength = picture.
mimeType().length();
896 string::size_type descriptionLength = picture.
description().find(
'\0');
897 if(descriptionLength == string::npos) {
898 descriptionLength = picture.
description().length();
902 char *offset = buffer.get();
906 picture.
mimeType().copy(++offset, mimeTypeLength);
907 *(offset += mimeTypeLength) = 0x00;
909 *(++offset) = typeInfo;
911 picture.
description().copy(++offset, descriptionLength);
912 *(offset += descriptionLength) = 0x00;
925 static const string context(
"making comment frame");
932 const string &lng = comment.
language();
933 if(lng.length() > 3) {
938 string::size_type descriptionLength = comment.
description().find(
'\0');
939 if(descriptionLength == string::npos) {
940 descriptionLength = comment.
description().length();
942 const auto data = comment.
toString();
945 char *offset = buffer.get();
949 for(
unsigned int i = 0; i < 3; ++i) {
950 *(++offset) = (lng.length() > i) ? lng[i] : 0x00;
953 comment.
description().copy(++offset, descriptionLength);
954 offset += descriptionLength;
960 data.copy(++offset, data.size());
Contains utility classes helping to read and write streams.