8#include <c++utilities/conversion/stringbuilder.h>
9#include <c++utilities/conversion/stringconversion.h>
24namespace Id3v2TextEncodingBytes {
25enum Id3v2TextEncodingByte : std::uint8_t { Ascii, Utf16WithBom, Utf16BigEndianWithoutBom,
Utf8 };
68template <
class stringtype>
static int parseGenreIndex(
const stringtype &denotation)
71 for (
auto c : denotation) {
82 if (c >=
'0' && c <=
'9') {
95 if (c >=
'0' && c <=
'9') {
96 index = index * 10 + c -
'0';
109static std::string stringFromSubstring(std::tuple<const char *, std::size_t, const char *> substr)
111 return std::string(std::get<0>(substr), std::get<1>(substr));
117static std::u16string wideStringFromSubstring(std::tuple<const char *, std::size_t, const char *> substr,
TagTextEncoding encoding)
119 std::u16string res(
reinterpret_cast<u16string::const_pointer
>(std::get<0>(substr)), std::get<1>(substr) / 2);
127static std::uint64_t readPlayCounter(
const char *begin,
const char *end,
const std::string &context, Diagnostics &diag)
129 auto res = std::uint64_t();
131 if (end - begin > 8) {
132 diag.emplace_back(
DiagLevel::Critical,
"Play counter is bigger than eight bytes and therefore not supported.", context);
135 for (
auto shift = 0; pos >= begin; shift += 8, --pos) {
136 res +=
static_cast<std::uint64_t
>(
static_cast<std::uint8_t
>(*pos)) << shift;
153 static const string defaultContext(
"parsing ID3v2 frame");
160 setId(reader.readUInt24BE());
161 if (
id() & 0xFFFF0000u) {
170 context =
"parsing " %
idToString() +
" frame";
173 m_dataSize = reader.readUInt24BE();
174 m_totalSize = m_dataSize + 6;
175 if (m_totalSize > maximalSize) {
176 diag.emplace_back(
DiagLevel::Warning,
"The frame is truncated and will be ignored.", context);
187 setId(reader.readUInt32BE());
188 if (
id() & 0xFF000000u) {
197 context =
"parsing " %
idToString() +
" frame";
200 m_dataSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
201 m_totalSize = m_dataSize + 10;
202 if (m_totalSize > maximalSize) {
203 diag.emplace_back(
DiagLevel::Warning,
"The frame is truncated and will be ignored.", context);
208 m_flag = reader.readUInt16BE();
220 argsToString(
"The frame is only supported in ID3v2.4 and newer but the tag's version is ID3v2.", version,
'.'), context);
223 argsToString(
"The frame is only supported in ID3v2.3 and older but the tag's version is ID3v2.", version,
'.'), context);
227 if (m_dataSize <= 0) {
233 unique_ptr<char[]> buffer;
237 uLongf decompressedSize = version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
238 if (decompressedSize < m_dataSize) {
239 diag.emplace_back(
DiagLevel::Critical,
"The decompressed size is smaller than the compressed size.", context);
242 const auto bufferCompressed = make_unique<char[]>(m_dataSize);
243 reader.read(bufferCompressed.get(), m_dataSize);
244 buffer = make_unique<char[]>(decompressedSize);
246 uncompress(
reinterpret_cast<Bytef *
>(buffer.get()), &decompressedSize,
reinterpret_cast<Bytef *
>(bufferCompressed.get()), m_dataSize)) {
248 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The source buffer was too small.", context);
251 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The destination buffer was too small.", context);
254 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The input data was corrupted or incomplete.", context);
263 diag.emplace_back(
DiagLevel::Critical,
"The decompressed data exceeds the maximum supported frame size.", context);
266 m_dataSize =
static_cast<std::uint32_t
>(decompressedSize);
268 buffer = make_unique<char[]>(m_dataSize);
269 reader.read(buffer.get(), m_dataSize);
278 const char *currentOffset = buffer.get() + 1;
279 for (
size_t currentIndex = 1; currentIndex < m_dataSize;) {
281 const auto substr(
parseSubstring(currentOffset, m_dataSize - currentIndex, dataEncoding,
false, diag));
284 if (!get<1>(substr)) {
285 if (currentIndex == 1) {
288 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
289 currentOffset = get<2>(substr);
295 if (this->
value().isEmpty()) {
296 return &this->
value();
298 m_additionalValues.emplace_back();
299 return &m_additionalValues.back();
312 }
catch (
const ConversionException &) {
313 diag.emplace_back(
DiagLevel::Warning,
"The value of track/disk position frame is not numeric and will be ignored.", context);
319 const auto milliseconds = [&] {
321 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding,
false, diag);
323 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
324 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
325 return string(convertedStringData.first.get(), convertedStringData.second);
327 return stringFromSubstring(substr);
331 }
catch (
const ConversionException &) {
332 diag.emplace_back(
DiagLevel::Warning,
"The value of the length frame is not numeric and will be ignored.", context);
337 const auto genreIndex = [&] {
339 return parseGenreIndex(wideStringFromSubstring(substr, dataEncoding));
341 return parseGenreIndex(stringFromSubstring(substr));
344 if (genreIndex != -1) {
356 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
357 currentOffset = get<2>(substr);
361 if (version < 4 && !m_additionalValues.empty()) {
363 DiagLevel::Warning,
"Multiple strings found though the tag is pre-ID3v2.4. " + ignoreAdditionalValuesDiagMsg(), context);
391 auto substr =
parseSubstring(buffer.get(), m_dataSize, userEncoding,
true, diag);
392 auto end = buffer.get() + m_dataSize;
393 if (std::get<1>(substr)) {
394 popularity.user.assign(std::get<0>(substr), std::get<1>(substr));
396 auto ratingPos = std::get<2>(substr);
397 if (ratingPos >= end) {
398 diag.emplace_back(
DiagLevel::Critical,
"Popularimeter frame is incomplete (rating is missing).", context);
401 popularity.rating =
static_cast<std::uint8_t
>(*ratingPos);
402 popularity.playCounter = readPlayCounter(ratingPos + 1, end, context, diag);
442void Id3v2Frame::internallyClearValue()
445 m_additionalValues.clear();
451void Id3v2Frame::internallyClearFurtherData()
464std::string Id3v2Frame::ignoreAdditionalValuesDiagMsg()
const
466 if (m_additionalValues.size() == 1) {
467 return argsToString(
"Additional value \"", m_additionalValues.front().toString(
TagTextEncoding::Utf8),
"\" is supposed to be ignored.");
475static std::uint32_t computePlayCounterSize(std::uint64_t playCounter)
487static void writePlayCounter(
char *last, std::uint32_t playCounterSize, std::uint64_t playCounter)
505Id3v2FrameMaker::Id3v2FrameMaker(Id3v2Frame &frame, std::uint8_t version, Diagnostics &diag)
507 , m_frameId(m_frame.id())
510 const string context(
"making " % m_frame.idToString() +
" frame");
513 if (m_frame.isEncrypted()) {
514 diag.emplace_back(
DiagLevel::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
515 throw InvalidDataException();
517 if (m_frame.hasPaddingReached()) {
518 diag.emplace_back(
DiagLevel::Critical,
"Cannot make a frame which is marked as padding.", context);
519 throw InvalidDataException();
521 if (version < 3 && m_frame.isCompressed()) {
522 diag.emplace_back(
DiagLevel::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
524 if (version < 3 && (m_frame.flag() || m_frame.group())) {
526 "The existing flag and group information is not supported by the version of ID3v2 and will be ignored/discarted.", context);
530 vector<const TagValue *> values;
531 values.reserve(1 + frame.additionalValues().size());
532 if (!frame.value().isEmpty()) {
533 values.emplace_back(&frame.value());
535 for (
const auto &
value : frame.additionalValues()) {
537 values.emplace_back(&
value);
542 if (values.empty()) {
543 throw NoDataProvidedException();
548 if (values.size() != 1) {
550 diag.emplace_back(
DiagLevel::Critical,
"Multiple values are not supported for non-text-frames.", context);
551 throw InvalidDataException();
552 }
else if (version < 4) {
554 DiagLevel::Warning,
"Multiple strings assigned to pre-ID3v2.4 text frame. " + frame.ignoreAdditionalValuesDiagMsg(), context);
564 "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.",
566 throw InvalidDataException();
574 "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.",
576 throw InvalidDataException();
584 argsToString(
"The frame is only supported in ID3v2.4 and newer but version of the tag being written is ID3v2.", version,
585 ". The frame is written nevertheless but other tools might not be able to deal with it."),
589 argsToString(
"The frame is only supported in ID3v2.3 and older but version of the tag being written is ID3v2.", version,
590 ". The frame is written nevertheless but other tools might not be able to deal with it."),
598 vector<string> substrings;
599 substrings.reserve(1 + frame.additionalValues().size());
606 for (
const auto *
const value : values) {
615 }
catch (
const ConversionException &) {
617 argsToString(
"The track/disk number \"", substrings.back(),
"\" is not of the expected form, eg. \"4/10\"."), context);
624 for (
const auto *
const value : values) {
628 throw InvalidDataException();
630 substrings.emplace_back(numberToString(
static_cast<std::uint64_t
>(
duration.totalMilliseconds())));
636 for (
const auto *
const value : values) {
662 for (
const auto *
const value : values) {
677 const auto byteOrderMark = [&] {
680 return string({
'\xFF',
'\xFE' });
682 return string({
'\xFE',
'\xFF' });
687 const auto concatenatedSubstrings = joinStrings(substrings,
string(),
false, byteOrderMark,
string(terminationLength,
'\0'));
690 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(1 + concatenatedSubstrings.size()));
692 concatenatedSubstrings.copy(&m_data[1], concatenatedSubstrings.size());
696 m_frame.makePicture(m_data, m_decompressedSize, *values.front(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0, version, diag);
702 m_frame.makeComment(m_data, m_decompressedSize, *values.front(), version, diag);
709 }
catch (
const ConversionException &) {
711 argsToString(
"The play counter \"", values.front()->toDisplayString(),
"\" is not an unsigned integer."), context);
713 m_decompressedSize = computePlayCounterSize(playCounter);
714 m_data = make_unique<char[]>(m_decompressedSize);
715 writePlayCounter(m_data.get() + m_decompressedSize - 1, m_decompressedSize, playCounter);
722 }
catch (
const ConversionException &) {
725 "The popularity \"", values.front()->toDisplayString(),
"\" is not of the expected form, eg. \"user|rating|counter\"."),
729 if (popularity.rating > 0xFF) {
730 popularity.rating = 0xFF;
731 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 255."), context);
732 }
else if (popularity.rating < 0x00) {
733 popularity.rating = 0x00;
734 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 0."), context);
737 m_decompressedSize =
static_cast<std::uint32_t
>(popularity.user.size() + 2);
738 const auto playCounterSize = computePlayCounterSize(popularity.playCounter);
739 m_decompressedSize += playCounterSize;
741 m_data = make_unique<char[]>(m_decompressedSize);
742 auto pos = popularity.user.size() + 1;
743 std::memcpy(m_data.get(), popularity.user.data(), pos);
744 m_data[pos] =
static_cast<char>(popularity.rating);
745 writePlayCounter(m_data.get() + pos + playCounterSize, playCounterSize, popularity.playCounter);
749 const auto &
value(*values.front());
752 throw InvalidDataException();
754 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(
value.
dataSize()));
757 }
catch (
const ConversionException &) {
761 argsToString(
"Assigned value(s) \"",
DiagMessage::formatList(valuesAsString),
"\" can not be converted appropriately."), context);
762 }
catch (
const ConversionException &) {
763 diag.emplace_back(
DiagLevel::Critical,
"Assigned value(s) can not be converted appropriately.", context);
765 throw InvalidDataException();
769 if (version >= 3 && m_frame.isCompressed()) {
770 auto compressedSize = compressBound(m_decompressedSize);
771 auto compressedData = make_unique<char[]>(compressedSize);
772 switch (compress(
reinterpret_cast<Bytef *
>(compressedData.get()),
reinterpret_cast<uLongf *
>(&compressedSize),
773 reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
775 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The source buffer was too small.", context);
776 throw InvalidDataException();
778 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The destination buffer was too small.", context);
779 throw InvalidDataException();
783 diag.emplace_back(
DiagLevel::Critical,
"Compressed size exceeds maximum data size.", context);
784 throw InvalidDataException();
786 m_data.swap(compressedData);
787 m_dataSize =
static_cast<std::uint32_t
>(compressedSize);
789 m_dataSize = m_decompressedSize;
794 m_requiredSize = m_dataSize;
800 m_requiredSize += 10;
802 if (m_frame.hasGroupInformation()) {
806 if (version >= 3 && m_frame.isCompressed()) {
822 writer.writeUInt24BE(m_frameId);
823 writer.writeUInt24BE(m_dataSize);
825 writer.writeUInt32BE(m_frameId);
826 if (m_version >= 4) {
827 writer.writeSynchsafeUInt32BE(m_dataSize);
829 writer.writeUInt32BE(m_dataSize);
831 writer.writeUInt16BE(m_frame.
flag());
833 writer.writeByte(m_frame.
group());
836 if (m_version >= 4) {
837 writer.writeSynchsafeUInt32BE(m_decompressedSize);
839 writer.writeUInt32BE(m_decompressedSize);
843 writer.write(m_data.get(), m_dataSize);
854 switch (textEncodingByte) {
855 case Id3v2TextEncodingBytes::Ascii:
857 case Id3v2TextEncodingBytes::Utf16WithBom:
859 case Id3v2TextEncodingBytes::Utf16BigEndianWithoutBom:
861 case Id3v2TextEncodingBytes::Utf8:
875 switch (textEncoding) {
877 return Id3v2TextEncodingBytes::Ascii;
879 return Id3v2TextEncodingBytes::Utf8;
881 return Id3v2TextEncodingBytes::Utf16WithBom;
883 return Id3v2TextEncodingBytes::Utf16WithBom;
906 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
911 if ((bufferSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
913 diag.emplace_back(
DiagLevel::Critical,
"Denoted character set is Latin-1 but an UTF-8 BOM is present - assuming UTF-8.",
919 const char *pos = get<0>(res);
920 for (; *pos != 0x00; ++pos) {
921 if (pos < get<2>(res)) {
931 get<2>(res) = pos + 1;
936 if (bufferSize >= 2) {
937 switch (LE::toInt<std::uint16_t>(buffer)) {
941 "Denoted character set is UTF-16 Big Endian but UTF-16 Little Endian BOM is present - assuming UTF-16 LE.",
952 const std::uint16_t *pos =
reinterpret_cast<const std::uint16_t *
>(get<0>(res));
953 for (; *pos != 0x0000; ++pos) {
954 if (pos <
reinterpret_cast<const std::uint16_t *
>(get<2>(res))) {
964 get<2>(res) =
reinterpret_cast<const char *
>(pos + 1);
1005 if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFFFE)) {
1007 }
else if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFEFF)) {
1012 if ((maxSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
1014 diag.emplace_back(
DiagLevel::Warning,
"UTF-8 byte order mark found in text frame.",
"parsing byte order mark of frame " +
idToString());
1028 static const string context(
"parsing ID3v2.2 picture frame");
1033 const char *end = buffer + maxSize;
1035 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
1036 auto substr =
parseSubstring(buffer + 5,
static_cast<size_t>(end - 5 - buffer), dataEncoding,
true, diag);
1037 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1038 if (get<2>(substr) >= end) {
1039 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1054 static const string context(
"parsing ID3v2.3 picture frame");
1055 const char *end = buffer + maxSize;
1058 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true, diag);
1059 if (get<1>(substr)) {
1060 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
1062 if (get<2>(substr) >= end) {
1063 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (type info, description and actual data are missing).", context);
1066 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
1067 if (++get<2>(substr) >= end) {
1068 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (description and actual data are missing).", context);
1071 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
true, diag);
1072 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1073 if (get<2>(substr) >= end) {
1074 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1088 static const string context(
"parsing comment/unsynchronized lyrics frame");
1089 const char *end = buffer +
dataSize;
1099 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1100 if (get<2>(substr) > end) {
1101 diag.emplace_back(
DiagLevel::Critical,
"Comment frame is incomplete (description not terminated?).", context);
1104 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
false, diag);
1116 LE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1119 BE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1130 unique_ptr<
char[]> &buffer, std::uint32_t &bufferSize,
const TagValue &picture, std::uint8_t typeInfo,
Diagnostics &diag)
1134 StringData convertedDescription;
1135 string::size_type descriptionSize = picture.
description().find(
1137 if (descriptionSize == string::npos) {
1143 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1144 descriptionSize = convertedDescription.second;
1149 const auto requiredBufferSize = 1 + 3 + 1 + descriptionSize
1152 if (requiredBufferSize > numeric_limits<std::uint32_t>::max()) {
1153 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making legacy picture frame");
1156 buffer = make_unique<char[]>(bufferSize =
static_cast<std::uint32_t
>(requiredBufferSize));
1157 char *offset = buffer.get();
1163 const char *imageFormat;
1164 if (picture.
mimeType() ==
"image/jpeg") {
1165 imageFormat =
"JPG";
1166 }
else if (picture.
mimeType() ==
"image/png") {
1167 imageFormat =
"PNG";
1168 }
else if (picture.
mimeType() ==
"image/gif") {
1169 imageFormat =
"GIF";
1170 }
else if (picture.
mimeType() ==
"-->") {
1171 imageFormat = picture.
mimeType().data();
1173 imageFormat =
"UND";
1175 std::memcpy(++offset, imageFormat, 3);
1178 *(offset += 3) =
static_cast<char>(
typeInfo);
1181 offset +=
makeBom(offset + 1, descriptionEncoding);
1182 if (convertedDescription.first) {
1183 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1185 picture.
description().copy(++offset, descriptionSize);
1187 *(offset += descriptionSize) = 0x00;
1209 StringData convertedDescription;
1210 string::size_type descriptionSize = picture.
description().find(
1212 if (descriptionSize == string::npos) {
1218 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1219 descriptionSize = convertedDescription.second;
1222 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
1223 if (mimeTypeSize == string::npos) {
1224 mimeTypeSize = picture.
mimeType().length();
1229 const auto requiredBufferSize = 1 + mimeTypeSize + 1 + 1 + descriptionSize
1232 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1233 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making picture frame");
1236 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1237 char *offset = buffer.get();
1243 picture.
mimeType().copy(++offset, mimeTypeSize);
1245 *(offset += mimeTypeSize) = 0x00;
1247 *(++offset) =
static_cast<char>(
typeInfo);
1250 offset +=
makeBom(offset + 1, descriptionEncoding);
1251 if (convertedDescription.first) {
1252 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1254 picture.
description().copy(++offset, descriptionSize);
1256 *(offset += descriptionSize) = 0x00;
1270 static const string context(
"making comment frame");
1274 if (!comment.description().empty() && encoding != comment.descriptionEncoding()) {
1275 diag.emplace_back(
DiagLevel::Critical,
"Data encoding and description encoding aren't equal.", context);
1279 if (language.length() > 3) {
1280 diag.emplace_back(
DiagLevel::Critical,
"The language must be 3 bytes long (ISO-639-2).", context);
1283 StringData convertedDescription;
1284 string::size_type descriptionSize = comment.description().find(
1286 if (descriptionSize == string::npos) {
1287 descriptionSize = comment.description().size();
1292 convertedDescription = convertUtf8ToUtf16LE(comment.description().data(), descriptionSize);
1293 descriptionSize = convertedDescription.second;
1298 const auto data = comment.toString(encoding);
1299 const auto requiredBufferSize = 1 + 3 + descriptionSize + data.size()
1301 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1305 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1306 char *offset = buffer.get();
1312 for (
unsigned int i = 0; i < 3; ++i) {
1313 *(++offset) = (language.length() > i) ? language[i] : 0x00;
1317 offset +=
makeBom(offset + 1, encoding);
1318 if (convertedDescription.first) {
1319 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1321 comment.description().copy(++offset, descriptionSize);
1323 offset += descriptionSize;
1330 offset +=
makeBom(offset + 1, encoding);
1331 data.copy(++offset, data.size());
static std::string formatList(const std::vector< std::string > &values)
Concatenates the specified string values to a list.
The Diagnostics class is a container for DiagMessage.
The Id3v2FrameMaker class helps making ID3v2 frames.
void make(CppUtilities::BinaryWriter &writer)
Saves the frame (specified when constructing the object) using the specified writer.
The Id3v2Frame class is used by Id3v2Tag to store the fields.
bool isEncrypted() const
Returns whether the frame is encrypted.
static std::size_t makeBom(char *buffer, TagTextEncoding encoding)
Writes the BOM for the specified encoding to the specified buffer.
std::uint32_t dataSize() const
Returns the size of the data stored in the frame in bytes.
static std::uint8_t makeTextEncodingByte(TagTextEncoding textEncoding)
Returns a text encoding byte for the specified textEncoding.
std::string parseString(const char *buffer, std::size_t maxSize, TagTextEncoding &encoding, bool addWarnings, Diagnostics &diag)
Parses a substring from the specified buffer.
void parseBom(const char *buffer, std::size_t maxSize, TagTextEncoding &encoding, Diagnostics &diag)
Parses a byte order mark from the specified buffer.
static void makeComment(std::unique_ptr< char[]> &buffer, std::uint32_t &bufferSize, const TagValue &comment, std::uint8_t version, Diagnostics &diag)
Writes the specified comment to the specified buffer.
void parseLegacyPicture(const char *buffer, std::size_t maxSize, TagValue &tagValue, std::uint8_t &typeInfo, Diagnostics &diag)
Parses the ID3v2.2 picture from the specified buffer.
void parsePicture(const char *buffer, std::size_t maxSize, TagValue &tagValue, std::uint8_t &typeInfo, Diagnostics &diag)
Parses the ID3v2.3 picture from the specified buffer.
void parse(CppUtilities::BinaryReader &reader, std::uint32_t version, std::uint32_t maximalSize, Diagnostics &diag)
Parses a frame from the stream read using the specified reader.
std::tuple< const char *, std::size_t, const char * > parseSubstring(const char *buffer, std::size_t maxSize, TagTextEncoding &encoding, bool addWarnings, Diagnostics &diag)
Parses a substring from the specified buffer.
bool hasGroupInformation() const
Returns whether the frame contains group information.
std::uint16_t flag() const
Returns the flags.
Id3v2Frame()
Constructs a new Id3v2Frame.
bool isCompressed() const
Returns whether the frame is compressed.
static void makePicture(std::unique_ptr< char[]> &buffer, std::uint32_t &bufferSize, const TagValue &picture, std::uint8_t typeInfo, std::uint8_t version, Diagnostics &diag)
Writes the specified picture to the specified buffer.
TagTextEncoding parseTextEncodingByte(std::uint8_t textEncodingByte, Diagnostics &diag)
Returns the text encoding for the specified textEncodingByte.
Id3v2FrameMaker prepareMaking(std::uint8_t version, Diagnostics &diag)
Prepares making.
void parseComment(const char *buffer, std::size_t maxSize, TagValue &tagValue, Diagnostics &diag)
Parses the comment/unsynchronized lyrics from the specified buffer.
static void makeLegacyPicture(std::unique_ptr< char[]> &buffer, std::uint32_t &bufferSize, const TagValue &picture, std::uint8_t typeInfo, Diagnostics &diag)
Writes the specified picture to the specified buffer (ID3v2.2 compatible).
std::u16string parseWideString(const char *buffer, std::size_t dataSize, TagTextEncoding &encoding, bool addWarnings, Diagnostics &diag)
Parses a substring from the specified buffer.
void make(CppUtilities::BinaryWriter &writer, std::uint8_t version, Diagnostics &diag)
Writes the frame to a stream using the specified writer and the specified ID3v2 version.
friend class Id3v2FrameMaker
std::uint8_t group() const
Returns the group.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
The exception that is thrown when the data to be parsed holds no parsable information (e....
The TagField class is used by FieldMapBasedTag to store the fields.
void setTypeInfo(const TypeInfoType &typeInfo)
const TypeInfoType & typeInfo() const
void setId(const IdentifierType &id)
std::string idToString() const
The TagValue class wraps values of different types.
void setMimeType(std::string_view mimeType)
Sets the MIME type.
const std::string & mimeType() const
Returns the MIME type.
static void ensureHostByteOrder(std::u16string &u16str, TagTextEncoding currentEncoding)
Ensures the byte-order of the specified UTF-16 string matches the byte-order of the machine.
TagTextEncoding dataEncoding() const
Returns the data encoding.
void assignPosition(PositionInSet value)
Assigns the given PositionInSet value.
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
void setDescription(std::string_view value, TagTextEncoding encoding=TagTextEncoding::Latin1)
Sets the description.
PositionInSet toPositionInSet() const
Converts the value of the current TagValue object to its equivalent PositionInSet representation.
TagDataType type() const
Returns the type of the assigned value.
void assignPopularity(const Popularity &value)
Assigns the specified popularity value.
void assignTimeSpan(CppUtilities::TimeSpan value)
Assigns the given TimeSpan value.
static std::vector< std::string > toStrings(const ContainerType &values, TagTextEncoding encoding=TagTextEncoding::Utf8)
Converts the specified values to string using the specified encoding.
void assignUnsignedInteger(std::uint64_t value)
Assigns the given unsigned integer value.
void clearDataAndMetadata()
Wipes assigned data including meta data.
std::size_t dataSize() const
Returns the size of the assigned value in bytes.
TagTextEncoding descriptionEncoding() const
Returns the description encoding.
void assignStandardGenreIndex(int index)
Assigns the given standard genre index to be assigned.
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
void setLocale(const Locale &locale)
Sets the setLocale.
bool isEmpty() const
Returns whether no or an empty value is assigned.
const std::string & description() const
Returns the description.
int toStandardGenreIndex() const
Converts the value of the current TagValue object to its equivalent standard genre index.
CppUtilities::TimeSpan toTimeSpan() const
Converts the value of the current TagValue object to its equivalent TimeSpan representation.
char * dataPointer()
Returns a pointer to the raw data assigned to the current instance.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
The exception that is thrown when an operation fails because the detected or specified version is not...
TAG_PARSER_EXPORT bool isPreId3v24Id(std::uint32_t id)
TAG_PARSER_EXPORT std::uint32_t convertToShortId(std::uint32_t id)
Converts the specified long frame ID to the equivalent short frame ID.
constexpr bool isLongId(std::uint32_t id)
Returns an indication whether the specified id is a long frame id.
constexpr bool isTextFrame(std::uint32_t id)
Returns an indication whether the specified id is a text frame id.
constexpr bool isShortId(std::uint32_t id)
Returns an indication whether the specified id is a short frame id.
TAG_PARSER_EXPORT bool isOnlyId3v24Id(std::uint32_t id)
TAG_PARSER_EXPORT std::uint32_t convertToLongId(std::uint32_t id)
Converts the specified short frame ID to the equivalent long frame ID.
constexpr TAG_PARSER_EXPORT std::string_view duration()
constexpr TAG_PARSER_EXPORT std::string_view playCounter()
Contains all classes and functions of the TagInfo library.
constexpr int characterSize(TagTextEncoding encoding)
Returns the size of one character for the specified encoding in bytes.
constexpr auto maxId3v2FrameDataSize(numeric_limits< std::uint32_t >::max() - 15)
The maximum (supported) size of an ID3v2Frame.
TagTextEncoding
Specifies the text encoding.
The Locale struct specifies a language and/or a country using one or more LocaleDetail objects.
The Popularity class contains a value for ID3v2's "Popularimeter" field.