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.makePictureConsideringVersion(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0,
version);
453 m_frame.makeCommentConsideringVersion(m_data, m_decompressedSize, m_frame.value(),
version);
457 m_data = make_unique<char[]>(m_decompressedSize = m_frame.value().dataSize());
458 copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
460 }
catch(
const ConversionException &) {
466 if(version >= 3 && m_frame.isCompressed()) {
467 m_dataSize = compressBound(m_decompressedSize);
468 auto compressedData = make_unique<char[]>(m_decompressedSize);
469 switch(compress(reinterpret_cast<Bytef *>(compressedData.get()), reinterpret_cast<uLongf *>(&m_dataSize),
reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
479 m_data.swap(compressedData);
481 m_dataSize = m_decompressedSize;
486 m_requiredSize = m_dataSize;
492 m_requiredSize += 10;
494 if(m_frame.hasGroupInformation()) {
498 if(version >= 3 && m_frame.isCompressed()) {
514 writer.writeUInt24BE(m_frameId);
515 writer.writeUInt24BE(m_dataSize);
517 writer.writeUInt32BE(m_frameId);
519 writer.writeSynchsafeUInt32BE(m_dataSize);
521 writer.writeUInt32BE(m_dataSize);
523 writer.writeUInt16BE(m_frame.flag());
524 if(m_frame.hasGroupInformation()) {
525 writer.writeByte(m_frame.group());
527 if(m_version >= 3 && m_frame.isCompressed()) {
529 writer.writeSynchsafeUInt32BE(m_decompressedSize);
531 writer.writeUInt32BE(m_decompressedSize);
535 writer.write(m_data.get(), m_dataSize);
546 switch(textEncodingByte) {
566 switch(textEncoding) {
596 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
601 if((bufferSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
608 const char *pos = get<0>(res);
609 for(; *pos != 0x00; ++pos) {
610 if(pos < get<2>(res)) {
619 get<2>(res) = pos + 1;
624 if(bufferSize >= 2) {
625 switch(ConversionUtilities::LE::toUInt16(buffer)) {
638 const uint16 *pos =
reinterpret_cast<const uint16 *
>(get<0>(res));
639 for(; *pos != 0x0000; ++pos) {
640 if(pos < reinterpret_cast<const uint16 *>(get<2>(res))) {
649 get<2>(res) = reinterpret_cast<const char *>(pos + 1);
663 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
664 return string(get<0>(substr), get<1>(substr));
676 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
677 u16string res(reinterpret_cast<u16string::const_pointer>(get<0>(substr)), get<1>(substr) / 2);
679 #
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
681 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
684 # error
"Host byte order not supported" 689 c = ((c >> 8) & 0x00FF) | ((c << 8) & 0xFF00);
709 if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFFFE)) {
711 }
else if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF)) {
716 if((maxSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
732 static const string context(
"parsing ID3v2.2 picture frame");
737 const char *end = buffer + maxSize;
739 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
740 auto substr =
parseSubstring(buffer + 5, end - 5 - buffer, dataEncoding,
true);
741 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
742 if(get<2>(substr) >= end) {
758 static const string context(
"parsing ID3v2.3 picture frame");
759 const char *end = buffer + maxSize;
762 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true);
764 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
766 if(get<2>(substr) >= end) {
770 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
771 if(++get<2>(substr) >= end) {
775 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
true);
776 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
777 if(get<2>(substr) >= end) {
792 static const string context(
"parsing comment/unsynchronized lyrics frame");
793 const char *end = buffer +
dataSize;
802 auto substr =
parseSubstring(buffer += 3, dataSize -= 4, dataEncoding,
true);
803 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
804 if(get<2>(substr) > end) {
808 substr =
parseSubstring(get<2>(substr), end - get<2>(substr), dataEncoding,
false);
838 char *bufferDataAddress;
844 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 1);
846 bufferDataAddress = buffer.get() + 1;
851 buffer = make_unique<char[]>(bufferSize = 1 + 2 + dataSize + 2);
854 bufferDataAddress = buffer.get() + 3;
858 copy(data, data + dataSize, bufferDataAddress);
870 ConversionUtilities::LE::getBytes(static_cast<uint16>(0xFEFF), buffer);
873 ConversionUtilities::BE::getBytes(static_cast<uint16>(0xFEFF), buffer);
887 StringData convertedDescription;
889 if(descriptionSize == string::npos) {
895 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
896 descriptionSize = convertedDescription.second;
902 char *offset = buffer.get();
906 const char *imageFormat;
907 if(picture.
mimeType() ==
"image/jpeg") {
909 }
else if(picture.
mimeType() ==
"image/png") {
911 }
else if(picture.
mimeType() ==
"image/gif") {
913 }
else if(picture.
mimeType() ==
"-->") {
914 imageFormat = picture.
mimeType().data();
918 strncpy(++offset, imageFormat, 3);
920 *(offset += 3) = typeInfo;
922 offset +=
makeBom(offset + 1, descriptionEncoding);
923 if(convertedDescription.first) {
924 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
926 picture.
description().copy(++offset, descriptionSize);
928 *(offset += descriptionSize) = 0x00;
956 StringData convertedDescription;
958 if(descriptionSize == string::npos) {
964 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
965 descriptionSize = convertedDescription.second;
968 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
969 if(mimeTypeSize == string::npos) {
970 mimeTypeSize = picture.
mimeType().length();
976 char *offset = buffer.get();
980 picture.
mimeType().copy(++offset, mimeTypeSize);
981 *(offset += mimeTypeSize) = 0x00;
983 *(++offset) = typeInfo;
985 offset +=
makeBom(offset + 1, descriptionEncoding);
986 if(convertedDescription.first) {
987 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
989 picture.
description().copy(++offset, descriptionSize);
991 *(offset += descriptionSize) = 0x00;
1012 static const string context(
"making comment frame");
1019 const string &lng = comment.
language();
1020 if(lng.length() > 3) {
1024 StringData convertedDescription;
1026 if(descriptionSize == string::npos) {
1032 convertedDescription = convertUtf8ToUtf16LE(comment.
description().data(), descriptionSize);
1033 descriptionSize = convertedDescription.second;
1036 const auto data = comment.
toString(encoding);
1039 char *offset = buffer.get();
1043 for(
unsigned int i = 0; i < 3; ++i) {
1044 *(++offset) = (lng.length() > i) ? lng[i] : 0x00;
1047 offset +=
makeBom(offset + 1, encoding);
1048 if(convertedDescription.first) {
1049 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1051 comment.
description().copy(++offset, descriptionSize);
1053 offset += descriptionSize;
1059 offset +=
makeBom(offset + 1, encoding);
1060 data.copy(++offset, data.size());
Contains utility classes helping to read and write streams.