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) {
82 if(c >=
'0' && c <=
'9') {
95 if(c >=
'0' && c <=
'9') {
96 index = index * 10 + c -
'0';
120 static const string defaultContext(
"parsing ID3v2 frame");
127 setId(reader.readUInt24BE());
128 if(
id() & 0xFFFF0000u) {
141 m_dataSize = reader.readUInt24BE();
142 m_totalSize = m_dataSize + 6;
143 if(m_totalSize > maximalSize) {
155 setId(reader.readUInt32BE());
156 if(
id() & 0xFF000000u) {
169 m_dataSize = version >= 4
170 ? reader.readSynchsafeUInt32BE()
171 : reader.readUInt32BE();
172 m_totalSize = m_dataSize + 10;
173 if(m_totalSize > maximalSize) {
179 m_flag = reader.readUInt16BE();
189 if(m_dataSize <= 0) {
195 unique_ptr<char[]> buffer;
199 uLongf decompressedSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
200 if(decompressedSize < m_dataSize) {
204 auto bufferCompressed = make_unique<char[]>(m_dataSize);;
205 reader.read(bufferCompressed.get(), m_dataSize);
206 buffer = make_unique<char[]>(decompressedSize);
207 switch(uncompress(reinterpret_cast<Bytef *>(buffer.get()), &decompressedSize, reinterpret_cast<Bytef *>(bufferCompressed.get()), m_dataSize)) {
223 m_dataSize = decompressedSize;
225 buffer = make_unique<char[]>(m_dataSize);
226 reader.read(buffer.get(), m_dataSize);
245 }
catch(
const ConversionException &) {
254 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
256 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
257 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
258 milliseconds = string(convertedStringData.first.get(), convertedStringData.second);
260 milliseconds =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
263 }
catch (
const ConversionException &) {
271 auto genreDenotation =
parseWideString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
274 auto genreDenotation =
parseString(buffer.get() + 1, m_dataSize - 1, dataEncoding);
277 if(genreIndex != -1) {
283 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
288 auto substr =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding);
369 m_frameId(m_frame.id()),
372 m_frame.invalidateStatus();
373 const string context(
"making " % m_frame.frameIdString() +
" frame");
376 if(m_frame.value().isEmpty()) {
380 if(m_frame.isEncrypted()) {
381 m_frame.addNotification(
NotificationType::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
384 if(m_frame.hasPaddingReached()) {
388 if(version < 3 && m_frame.isCompressed()) {
389 m_frame.addNotification(
NotificationType::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
391 if(version < 3 && (m_frame.flag() || m_frame.group())) {
392 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);
400 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);
408 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);
425 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toTimeSpan().totalMilliseconds()),
TagTextEncoding::Latin1);
429 m_frame.makeString(m_data, m_decompressedSize, ConversionUtilities::numberToString(m_frame.value().toStandardGenreIndex()),
TagTextEncoding::Latin1);
437 m_frame.makeString(m_data, m_decompressedSize, m_frame.value().toString(), m_frame.value().dataEncoding());
443 m_frame.makePictureConsideringVersion(m_data, m_decompressedSize, m_frame.value(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0,
version);
450 m_frame.makeCommentConsideringVersion(m_data, m_decompressedSize, m_frame.value(),
version);
454 m_data = make_unique<char[]>(m_decompressedSize = m_frame.value().dataSize());
455 copy(m_frame.value().dataPointer(), m_frame.value().dataPointer() + m_decompressedSize, m_data.get());
457 }
catch(
const ConversionException &) {
463 if(version >= 3 && m_frame.isCompressed()) {
464 m_dataSize = compressBound(m_decompressedSize);
465 auto compressedData = make_unique<char[]>(m_decompressedSize);
466 switch(compress(reinterpret_cast<Bytef *>(compressedData.get()), reinterpret_cast<uLongf *>(&m_dataSize),
reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
476 m_data.swap(compressedData);
478 m_dataSize = m_decompressedSize;
483 m_requiredSize = m_dataSize;
489 m_requiredSize += 10;
491 if(m_frame.hasGroupInformation()) {
495 if(version >= 3 && m_frame.isCompressed()) {
511 writer.writeUInt24BE(m_frameId);
512 writer.writeUInt24BE(m_dataSize);
514 writer.writeUInt32BE(m_frameId);
516 writer.writeSynchsafeUInt32BE(m_dataSize);
518 writer.writeUInt32BE(m_dataSize);
520 writer.writeUInt16BE(m_frame.flag());
521 if(m_frame.hasGroupInformation()) {
522 writer.writeByte(m_frame.group());
524 if(m_version >= 3 && m_frame.isCompressed()) {
526 writer.writeSynchsafeUInt32BE(m_decompressedSize);
528 writer.writeUInt32BE(m_decompressedSize);
532 writer.write(m_data.get(), m_dataSize);
543 switch(textEncodingByte) {
563 switch(textEncoding) {
593 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
598 if((bufferSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
605 const char *pos = get<0>(res);
606 for(; *pos != 0x00; ++pos) {
607 if(pos < get<2>(res)) {
616 get<2>(res) = pos + 1;
621 if(bufferSize >= 2) {
622 switch(ConversionUtilities::LE::toUInt16(buffer)) {
635 const uint16 *pos =
reinterpret_cast<const uint16 *
>(get<0>(res));
636 for(; *pos != 0x0000; ++pos) {
637 if(pos < reinterpret_cast<const uint16 *>(get<2>(res))) {
646 get<2>(res) = reinterpret_cast<const char *>(pos + 1);
660 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
661 return string(get<0>(substr), get<1>(substr));
673 auto substr =
parseSubstring(buffer, dataSize, encoding, addWarnings);
674 u16string res(reinterpret_cast<u16string::const_pointer>(get<0>(substr)), get<1>(substr) / 2);
675 TagValue::ensureHostByteOrder(res, encoding);
693 if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFFFE)) {
695 }
else if((maxSize >= 2) && (ConversionUtilities::BE::toUInt16(buffer) == 0xFEFF)) {
700 if((maxSize >= 3) && (ConversionUtilities::BE::toUInt24(buffer) == 0x00EFBBBF)) {
716 static const string context(
"parsing ID3v2.2 picture frame");
721 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);
822 char *bufferDataAddress;
828 buffer = make_unique<char[]>(bufferSize = 1 + dataSize + 1);
830 bufferDataAddress = buffer.get() + 1;
835 buffer = make_unique<char[]>(bufferSize = 1 + 2 + dataSize + 2);
838 bufferDataAddress = buffer.get() + 3;
846 copy(data, data + dataSize, bufferDataAddress);
858 ConversionUtilities::LE::getBytes(static_cast<uint16>(0xFEFF), buffer);
861 ConversionUtilities::BE::getBytes(static_cast<uint16>(0xFEFF), buffer);
875 StringData convertedDescription;
877 if(descriptionSize == string::npos) {
883 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
884 descriptionSize = convertedDescription.second;
890 char *offset = buffer.get();
894 const char *imageFormat;
895 if(picture.
mimeType() ==
"image/jpeg") {
897 }
else if(picture.
mimeType() ==
"image/png") {
899 }
else if(picture.
mimeType() ==
"image/gif") {
901 }
else if(picture.
mimeType() ==
"-->") {
902 imageFormat = picture.
mimeType().data();
906 strncpy(++offset, imageFormat, 3);
908 *(offset += 3) = typeInfo;
910 offset +=
makeBom(offset + 1, descriptionEncoding);
911 if(convertedDescription.first) {
912 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
914 picture.
description().copy(++offset, descriptionSize);
916 *(offset += descriptionSize) = 0x00;
944 StringData convertedDescription;
946 if(descriptionSize == string::npos) {
952 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
953 descriptionSize = convertedDescription.second;
956 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
957 if(mimeTypeSize == string::npos) {
958 mimeTypeSize = picture.
mimeType().length();
964 char *offset = buffer.get();
968 picture.
mimeType().copy(++offset, mimeTypeSize);
969 *(offset += mimeTypeSize) = 0x00;
971 *(++offset) = typeInfo;
973 offset +=
makeBom(offset + 1, descriptionEncoding);
974 if(convertedDescription.first) {
975 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
977 picture.
description().copy(++offset, descriptionSize);
979 *(offset += descriptionSize) = 0x00;
1000 static const string context(
"making comment frame");
1007 const string &lng = comment.
language();
1008 if(lng.length() > 3) {
1012 StringData convertedDescription;
1014 if(descriptionSize == string::npos) {
1020 convertedDescription = convertUtf8ToUtf16LE(comment.
description().data(), descriptionSize);
1021 descriptionSize = convertedDescription.second;
1024 const auto data = comment.
toString(encoding);
1027 char *offset = buffer.get();
1031 for(
unsigned int i = 0; i < 3; ++i) {
1032 *(++offset) = (lng.length() > i) ? lng[i] : 0x00;
1035 offset +=
makeBom(offset + 1, encoding);
1036 if(convertedDescription.first) {
1037 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1039 comment.
description().copy(++offset, descriptionSize);
1041 offset += descriptionSize;
1047 offset +=
makeBom(offset + 1, encoding);
1048 data.copy(++offset, data.size());
Contains utility classes helping to read and write streams.