5 #include "../exceptions.h" 7 #include <c++utilities/conversion/stringconversion.h> 8 #include <c++utilities/conversion/stringbuilder.h> 23 namespace Id3v2TextEncodingBytes {
41 Id3v2Frame::Id3v2Frame() :
67 template<
class stringtype>
71 for(
auto c : denotation) {
72 if(
sizeof(
typename stringtype::value_type) == 2 && isBigEndian != CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN) {
73 c = swapOrder(static_cast<uint16>(c));
85 if(c >=
'0' && c <=
'9') {
98 if(c >=
'0' && c <=
'9') {
99 index = index * 10 + c -
'0';
123 static const string defaultContext(
"parsing ID3v2 frame");
130 setId(reader.readUInt24BE());
131 if(
id() & 0xFFFF0000u) {
144 m_dataSize = reader.readUInt24BE();
145 m_totalSize = m_dataSize + 6;
146 if(m_totalSize > maximalSize) {
158 setId(reader.readUInt32BE());
159 if(
id() & 0xFF000000u) {
172 m_dataSize = version >= 4
173 ? reader.readSynchsafeUInt32BE()
174 : reader.readUInt32BE();
175 m_totalSize = m_dataSize + 10;
176 if(m_totalSize > maximalSize) {
182 m_flag = reader.readUInt16BE();
192 if(m_dataSize <= 0) {
198 unique_ptr<char[]> buffer;
202 uLongf decompressedSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
203 if(decompressedSize < m_dataSize) {
207 auto bufferCompressed = make_unique<char[]>(m_dataSize);;
208 reader.read(bufferCompressed.get(), m_dataSize);
209 buffer = make_unique<char[]>(decompressedSize);
210 switch(uncompress(reinterpret_cast<Bytef *>(buffer.get()), &decompressedSize, reinterpret_cast<Bytef *>(bufferCompressed.get()), m_dataSize)) {
226 m_dataSize = decompressedSize;
228 buffer = make_unique<char[]>(m_dataSize);
229 reader.read(buffer.get(), m_dataSize);
248 }
catch(
const ConversionException &) {
257 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
259 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
260 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
261 milliseconds = string(convertedStringData.first.get(), convertedStringData.second);
263 milliseconds =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
266 }
catch (
const ConversionException &) {
274 auto genreDenotation =
parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
277 auto genreDenotation =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
280 if(genreIndex != -1) {
286 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
291 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
372 m_frameId(m_frame.id()),
375 m_frame.invalidateStatus();
376 const string context(
"making " % m_frame.frameIdString() +
" frame");
379 if(m_frame.value().isEmpty()) {
383 if(m_frame.isEncrypted()) {
384 m_frame.addNotification(
NotificationType::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
387 if(m_frame.hasPaddingReached()) {
391 if(version < 3 && m_frame.isCompressed()) {
392 m_frame.addNotification(
NotificationType::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
394 if(version < 3 && (m_frame.flag() || m_frame.group())) {
395 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);
403 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);
411 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);
428 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toTimeSpan().totalMilliseconds()),
TagTextEncoding::Latin1);
432 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toStandardGenreIndex()),
TagTextEncoding::Latin1);
440 m_frame.makeString(m_data, m_decompressedSize, m_frame.value().toString(), m_frame.value().dataEncoding());
446 m_frame.makePicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
450 m_frame.makeLegacyPicture(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0);
457 m_frame.makeCommentConsideringVersion(m_data, m_decompressedSize, m_frame.value(),
version);
461 m_data = make_unique<char[]>(m_decompressedSize = m_frame.value().dataSize());
462 copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
464 }
catch(
const ConversionException &) {
470 if(version >= 3 && m_frame.isCompressed()) {
471 m_dataSize = compressBound(m_decompressedSize);
472 auto compressedData = make_unique<char[]>(m_decompressedSize);
473 switch(compress(reinterpret_cast<Bytef *>(compressedData.get()), reinterpret_cast<uLongf *>(&m_dataSize),
reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
483 m_data.swap(compressedData);
485 m_dataSize = m_decompressedSize;
490 m_requiredSize = m_dataSize;
496 m_requiredSize += 10;
498 if(m_frame.hasGroupInformation()) {
502 if(version >= 3 && m_frame.isCompressed()) {
518 writer.writeUInt24BE(m_frameId);
519 writer.writeUInt24BE(m_dataSize);
521 writer.writeUInt32BE(m_frameId);
523 writer.writeSynchsafeUInt32BE(m_dataSize);
525 writer.writeUInt32BE(m_dataSize);
527 writer.writeUInt16BE(m_frame.flag());
528 if(m_frame.hasGroupInformation()) {
529 writer.writeByte(m_frame.group());
531 if(m_version >= 3 && m_frame.isCompressed()) {
533 writer.writeSynchsafeUInt32BE(m_decompressedSize);
535 writer.writeUInt32BE(m_decompressedSize);
539 writer.write(m_data.get(), m_dataSize);
550 switch(textEncodingByte) {
570 switch(textEncoding) {
600 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
605 if((bufferSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
612 const char *pos = get<0>(res);
613 for(; *pos != 0x00; ++pos) {
614 if(pos < get<2>(res)) {
623 get<2>(res) = pos + 1;
628 if(bufferSize >= 2) {
629 switch(ConversionUtilities::LE::toUInt16(buffer)) {
642 const uint16 *pos =
reinterpret_cast<const uint16 *
>(get<0>(res));
643 for(; *pos != 0x0000; ++pos) {
644 if(pos < reinterpret_cast<const uint16 *>(get<2>(res))) {
653 get<2>(res) = reinterpret_cast<const char *>(pos + 1);
667 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
668 return string(get<0>(substr), get<1>(substr));
680 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
681 u16string res(reinterpret_cast<u16string::const_pointer>(get<0>(substr)), get<1>(substr) / 2);
683 #
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
685 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
688 # error
"Host byte order not supported" 693 c = ((c >> 8) & 0x00FF) | ((c << 8) & 0xFF00);
713 if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFFFE)) {
715 }
else if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF)) {
720 if((maxSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
736 static const string context(
"parsing ID3v2.2 picture frame");
741 const char *end = buffer + maxSize;
743 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
744 auto substr =
parseSubstring(buffer + 5, end - 5 - buffer, dataEncoding,
true);
745 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
746 if(get<2>(substr) >= end) {
762 static const string context(
"parsing ID3v2.3 picture frame");
763 const char *end = buffer + maxSize;
766 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true);
768 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
770 if(get<2>(substr) >= end) {
774 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
775 if(++get<2>(substr) >= end) {
779 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
true);
780 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
781 if(get<2>(substr) >= end) {
796 static const string context(
"parsing comment/unsynchronized lyrics frame");
797 const char *end = buffer +
dataSize;
806 auto substr =
parseSubstring(buffer += 3, dataSize -= 4, dataEncoding,
true);
807 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
808 if(get<2>(substr) > end) {
812 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
false);
842 char *bufferDataAddress;
848 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 1);
850 bufferDataAddress = buffer.get() + 1;
855 buffer = make_unique<char[]>(bufferSize = 1 + 2 + dataSize + 2);
858 bufferDataAddress = buffer.get() + 3;
862 copy(data, data + dataSize, bufferDataAddress);
874 ConversionUtilities::LE::getBytes(static_cast<uint16>(0xFEFF), buffer);
877 ConversionUtilities::BE::getBytes(static_cast<uint16>(0xFEFF), buffer);
891 StringData convertedDescription;
897 descriptionSize = convertedDescription.second;
900 if(descriptionSize == string::npos) {
908 char *offset = buffer.get();
912 const char *imageFormat;
913 if(picture.
mimeType() ==
"image/jpeg") {
915 }
else if(picture.
mimeType() ==
"image/png") {
917 }
else if(picture.
mimeType() ==
"image/gif") {
919 }
else if(picture.
mimeType() ==
"-->") {
920 imageFormat = picture.
mimeType().data();
924 strncpy(++offset, imageFormat, 3);
926 *(offset += 3) = typeInfo;
928 offset += makeBom(offset + 1, descriptionEncoding);
929 if(convertedDescription.first) {
930 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
932 picture.
description().copy(++offset, descriptionSize);
934 *(offset += descriptionSize) = 0x00;
949 StringData convertedDescription;
955 descriptionSize = convertedDescription.second;
958 if(descriptionSize == string::npos) {
963 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
964 if(mimeTypeSize == string::npos) {
965 mimeTypeSize = picture.
mimeType().length();
971 char *offset = buffer.get();
975 picture.
mimeType().copy(++offset, mimeTypeSize);
976 *(offset += mimeTypeSize) = 0x00;
978 *(++offset) = typeInfo;
980 offset += makeBom(offset + 1, descriptionEncoding);
981 if(convertedDescription.first) {
982 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
984 picture.
description().copy(++offset, descriptionSize);
986 *(offset += descriptionSize) = 0x00;
999 makeCommentConsideringVersion(buffer, bufferSize, comment, 3);
1005 void Id3v2Frame::makeCommentConsideringVersion(unique_ptr<
char[]> &buffer, uint32 &bufferSize,
const TagValue &
comment, byte version)
1007 static const string context(
"making comment frame");
1014 const string &lng = comment.
language();
1015 if(lng.length() > 3) {
1019 StringData convertedDescription;
1020 string::size_type descriptionSize;
1025 descriptionSize = convertedDescription.second;
1028 if(descriptionSize == string::npos) {
1033 const auto data = comment.
toString(encoding);
1036 char *offset = buffer.get();
1040 for(
unsigned int i = 0; i < 3; ++i) {
1041 *(++offset) = (lng.length() > i) ? lng[i] : 0x00;
1044 offset += makeBom(offset + 1, encoding);
1045 if(convertedDescription.first) {
1046 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1048 comment.
description().copy(++offset, descriptionSize);
1050 offset += descriptionSize;
1056 offset += makeBom(offset + 1, encoding);
1057 data.copy(++offset, data.size());
Contains utility classes helping to read and write streams.