5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringconversion.h> 8 #include <c++utilities/conversion/stringbuilder.h> 31 Id3v2Frame::Id3v2Frame() :
57 template<
class stringtype>
61 for(
auto c : denotation) {
62 if(
sizeof(
typename stringtype::value_type) == 2 && isBigEndian != CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN) {
63 c = swapOrder(static_cast<uint16>(c));
75 if(c >=
'0' && c <=
'9') {
88 if(c >=
'0' && c <=
'9') {
89 index = index * 10 + c -
'0';
113 static const string defaultContext(
"parsing ID3v2 frame");
120 setId(reader.readUInt24BE());
121 if(
id() & 0xFFFF0000u) {
134 m_dataSize = reader.readUInt24BE();
135 m_totalSize = m_dataSize + 6;
136 if(m_totalSize > maximalSize) {
148 setId(reader.readUInt32BE());
149 if(
id() & 0xFF000000u) {
162 m_dataSize = version >= 4
163 ? reader.readSynchsafeUInt32BE()
164 : reader.readUInt32BE();
165 m_totalSize = m_dataSize + 10;
166 if(m_totalSize > maximalSize) {
172 m_flag = reader.readUInt16BE();
182 if(m_dataSize <= 0) {
188 unique_ptr<char[]> buffer;
192 uLongf decompressedSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
193 if(decompressedSize < m_dataSize) {
197 auto bufferCompressed = make_unique<char[]>(m_dataSize);;
198 reader.read(bufferCompressed.get(), m_dataSize);
199 buffer = make_unique<char[]>(decompressedSize);
200 switch(uncompress(reinterpret_cast<Bytef *>(buffer.get()), &decompressedSize, reinterpret_cast<Bytef *>(bufferCompressed.get()), m_dataSize)) {
216 m_dataSize = decompressedSize;
218 buffer = make_unique<char[]>(m_dataSize);
219 reader.read(buffer.get(), m_dataSize);
238 }
catch(
const ConversionException &) {
247 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
249 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
250 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
251 milliseconds = string(convertedStringData.first.get(), convertedStringData.second);
253 milliseconds =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
256 }
catch (
const ConversionException &) {
264 auto genreDenotation =
parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
267 auto genreDenotation =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
270 if(genreIndex != -1) {
276 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
281 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
362 m_frameId(m_frame.id()),
365 m_frame.invalidateStatus();
366 const string context(
"making " % m_frame.frameIdString() +
" frame");
369 if(m_frame.value().isEmpty()) {
373 if(m_frame.isEncrypted()) {
374 m_frame.addNotification(
NotificationType::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
377 if(m_frame.hasPaddingReached()) {
381 if(version < 3 && m_frame.isCompressed()) {
382 m_frame.addNotification(
NotificationType::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
384 if(version < 3 && (m_frame.flag() || m_frame.group())) {
385 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);
393 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);
401 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);
418 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toTimeSpan().totalMilliseconds()),
TagTextEncoding::Latin1);
422 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toStandardGenreIndex()),
TagTextEncoding::Latin1);
425 m_frame.makeString(m_data, m_decompressedSize, m_frame.value().toString(), m_frame.value().dataEncoding());
430 m_frame.makePicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
434 m_frame.makeLegacyPicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
441 m_frame.makeComment(m_data, m_decompressedSize, m_frame.value());
446 m_data = make_unique<char[]>(m_decompressedSize = m_frame.value().dataSize());
448 copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
450 }
catch(
const ConversionException &) {
456 if(version >= 3 && m_frame.isCompressed()) {
457 m_dataSize = compressBound(m_decompressedSize);
458 auto compressedData = make_unique<char[]>(m_decompressedSize);
459 switch(compress(reinterpret_cast<Bytef *>(compressedData.get()), reinterpret_cast<uLongf *>(&m_dataSize),
reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
469 m_data.swap(compressedData);
471 m_dataSize = m_decompressedSize;
476 m_requiredSize = m_dataSize;
482 m_requiredSize += 10;
484 if(m_frame.hasGroupInformation()) {
488 if(version >= 3 && m_frame.isCompressed()) {
504 writer.writeUInt24BE(m_frameId);
505 writer.writeUInt24BE(m_dataSize);
507 writer.writeUInt32BE(m_frameId);
509 writer.writeSynchsafeUInt32BE(m_dataSize);
511 writer.writeUInt32BE(m_dataSize);
513 writer.writeUInt16BE(m_frame.flag());
514 if(m_frame.hasGroupInformation()) {
515 writer.writeByte(m_frame.group());
517 if(m_version >= 3 && m_frame.isCompressed()) {
519 writer.writeSynchsafeUInt32BE(m_decompressedSize);
521 writer.writeUInt32BE(m_decompressedSize);
525 writer.write(m_data.get(), m_dataSize);
536 switch(textEncodingByte) {
556 switch(textEncoding) {
586 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
590 if(bufferSize >= 2) {
591 if(ConversionUtilities::LE::toUInt16(buffer) == 0xFEFF) {
597 }
else if(ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF) {
605 const uint16 *pos =
reinterpret_cast<const uint16 *
>(get<0>(res));
606 for(; *pos != 0x0000; ++pos) {
607 if(pos < reinterpret_cast<const uint16 *>(get<2>(res))) {
616 get<2>(res) = reinterpret_cast<const char *>(++pos);
620 if((bufferSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
627 const char *pos = get<0>(res);
628 for(; *pos != 0x00; ++pos) {
629 if(pos < get<2>(res)) {
652 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
653 return string(get<0>(substr), get<1>(substr));
665 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
666 u16string res(reinterpret_cast<u16string::const_pointer>(get<0>(substr)), get<1>(substr) / 2);
668 #
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
670 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
673 # error
"Host byte order not supported" 678 c = ((c >> 8) & 0x00FF) | ((c << 8) & 0xFF00);
698 if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFFFE)) {
700 }
else if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF)) {
705 if((maxSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
721 static const string context(
"parsing ID3v2.2 picture frame");
726 const char *end = buffer + maxSize;
729 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
730 auto substr =
parseSubstring(buffer + 5, end - 5 - buffer, dataEncoding,
true);
731 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
732 if(get<2>(substr) >= end) {
748 static const string context(
"parsing ID3v2.3 picture frame");
749 const char *end = buffer + maxSize;
752 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true);
754 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
756 if(get<2>(substr) >= end) {
760 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
761 if(++get<2>(substr) >= end) {
765 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
true);
766 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
767 if(get<2>(substr) >= end) {
782 static const string context(
"parsing comment/unsynchronized lyrics frame");
783 const char *end = buffer +
dataSize;
792 auto substr =
parseSubstring(buffer += 3, dataSize -= 4, dataEncoding,
true);
793 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
794 if(get<2>(substr) > end) {
798 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
false);
833 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 1);
838 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 2);
843 copy(data, data + dataSize, buffer.get() + 1);
855 string::size_type descriptionLength = picture.
description().find(
'\0');
856 if(descriptionLength == string::npos) {
857 descriptionLength = picture.
description().length();
861 char *offset = buffer.get();
865 const char *imageFormat;
866 if(picture.
mimeType() ==
"image/jpeg") {
868 }
else if(picture.
mimeType() ==
"image/png") {
870 }
else if(picture.
mimeType() ==
"image/gif") {
872 }
else if(picture.
mimeType() ==
"-->") {
873 imageFormat = picture.
mimeType().data();
877 strncpy(++offset, imageFormat, 3);
879 *(offset += 3) = typeInfo;
881 picture.
description().copy(++offset, descriptionLength);
882 *(offset += descriptionLength) = 0x00;
898 string::size_type mimeTypeLength = picture.
mimeType().find(
'\0');
899 if(mimeTypeLength == string::npos) {
900 mimeTypeLength = picture.
mimeType().length();
902 string::size_type descriptionLength = picture.
description().find(
'\0');
903 if(descriptionLength == string::npos) {
904 descriptionLength = picture.
description().length();
908 char *offset = buffer.get();
912 picture.
mimeType().copy(++offset, mimeTypeLength);
913 *(offset += mimeTypeLength) = 0x00;
915 *(++offset) = typeInfo;
917 picture.
description().copy(++offset, descriptionLength);
918 *(offset += descriptionLength) = 0x00;
931 static const string context(
"making comment frame");
938 const string &lng = comment.
language();
939 if(lng.length() > 3) {
944 string::size_type descriptionLength = comment.
description().find(
'\0');
945 if(descriptionLength == string::npos) {
946 descriptionLength = comment.
description().length();
948 const auto data = comment.
toString();
951 char *offset = buffer.get();
955 for(
unsigned int i = 0; i < 3; ++i) {
956 *(++offset) = (lng.length() > i) ? lng[i] : 0x00;
959 comment.
description().copy(++offset, descriptionLength);
960 offset += descriptionLength;
966 data.copy(++offset, data.size());
Contains utility classes helping to read and write streams.