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);
275 if (isTextFrame || isUrlFrame) {
277 const char *currentOffset = buffer.get();
281 for (
size_t currentIndex = 1; currentIndex < m_dataSize;) {
283 const auto substr(
parseSubstring(currentOffset, m_dataSize - currentIndex, dataEncoding,
false, diag));
286 if (!get<1>(substr)) {
287 if (currentIndex == 1) {
290 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
291 currentOffset = get<2>(substr);
297 if (this->
value().isEmpty()) {
298 return &this->
value();
300 m_additionalValues.emplace_back();
301 return &m_additionalValues.back();
314 }
catch (
const ConversionException &) {
315 diag.emplace_back(
DiagLevel::Warning,
"The value of track/disk position frame is not numeric and will be ignored.", context);
321 const auto milliseconds = [&] {
323 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding,
false, diag);
325 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
326 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
327 return string(convertedStringData.first.get(), convertedStringData.second);
329 return stringFromSubstring(substr);
333 }
catch (
const ConversionException &) {
334 diag.emplace_back(
DiagLevel::Warning,
"The value of the length frame is not numeric and will be ignored.", context);
339 const auto genreIndex = [&] {
341 return parseGenreIndex(wideStringFromSubstring(substr, dataEncoding));
343 return parseGenreIndex(stringFromSubstring(substr));
346 if (genreIndex != -1) {
358 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
359 currentOffset = get<2>(substr);
363 if (version < 4 && !m_additionalValues.empty()) {
365 DiagLevel::Warning,
"Multiple strings found though the tag is pre-ID3v2.4. " + ignoreAdditionalValuesDiagMsg(), context);
393 auto substr =
parseSubstring(buffer.get(), m_dataSize, userEncoding,
true, diag);
394 auto end = buffer.get() + m_dataSize;
395 if (std::get<1>(substr)) {
396 popularity.user.assign(std::get<0>(substr), std::get<1>(substr));
398 auto ratingPos = std::get<2>(substr);
399 if (ratingPos >= end) {
400 diag.emplace_back(
DiagLevel::Critical,
"Popularimeter frame is incomplete (rating is missing).", context);
403 popularity.rating =
static_cast<std::uint8_t
>(*ratingPos);
404 popularity.playCounter = readPlayCounter(ratingPos + 1, end, context, diag);
444void Id3v2Frame::internallyClearValue()
447 m_additionalValues.clear();
453void Id3v2Frame::internallyClearFurtherData()
466std::string Id3v2Frame::ignoreAdditionalValuesDiagMsg()
const
468 if (m_additionalValues.size() == 1) {
469 return argsToString(
"Additional value \"", m_additionalValues.front().toString(
TagTextEncoding::Utf8),
"\" is supposed to be ignored.");
477static std::uint32_t computePlayCounterSize(std::uint64_t playCounter)
489static void writePlayCounter(
char *last, std::uint32_t playCounterSize, std::uint64_t playCounter)
507Id3v2FrameMaker::Id3v2FrameMaker(Id3v2Frame &frame, std::uint8_t version, Diagnostics &diag)
509 , m_frameId(m_frame.id())
512 const string context(
"making " % m_frame.idToString() +
" frame");
515 if (m_frame.isEncrypted()) {
516 diag.emplace_back(
DiagLevel::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
517 throw InvalidDataException();
519 if (m_frame.hasPaddingReached()) {
520 diag.emplace_back(
DiagLevel::Critical,
"Cannot make a frame which is marked as padding.", context);
521 throw InvalidDataException();
523 if (version < 3 && m_frame.isCompressed()) {
524 diag.emplace_back(
DiagLevel::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
526 if (version < 3 && (m_frame.flag() || m_frame.group())) {
528 "The existing flag and group information is not supported by the version of ID3v2 and will be ignored/discarted.", context);
532 auto values = std::vector<const TagValue *>();
533 values.reserve(1 + frame.additionalValues().size());
534 if (!frame.value().isEmpty()) {
535 values.emplace_back(&frame.value());
537 for (
const auto &
value : frame.additionalValues()) {
539 values.emplace_back(&
value);
544 if (values.empty()) {
545 throw NoDataProvidedException();
551 if (values.size() != 1) {
553 diag.emplace_back(
DiagLevel::Critical,
"Multiple values are not supported for non-text-frames.", context);
554 throw InvalidDataException();
555 }
else if (version < 4) {
557 DiagLevel::Warning,
"Multiple strings assigned to pre-ID3v2.4 text frame. " + frame.ignoreAdditionalValuesDiagMsg(), context);
567 "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.",
569 throw InvalidDataException();
577 "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.",
579 throw InvalidDataException();
587 argsToString(
"The frame is only supported in ID3v2.4 and newer but version of the tag being written is ID3v2.", version,
588 ". The frame is written nevertheless but other tools might not be able to deal with it."),
592 argsToString(
"The frame is only supported in ID3v2.3 and older but version of the tag being written is ID3v2.", version,
593 ". The frame is written nevertheless but other tools might not be able to deal with it."),
599 if (isTextFrame || isUrlFrame) {
601 auto substrings = std::vector<std::string>();
602 substrings.reserve(1 + frame.additionalValues().size());
609 for (
const auto *
const value : values) {
618 }
catch (
const ConversionException &) {
620 argsToString(
"The track/disk number \"", substrings.back(),
"\" is not of the expected form, eg. \"4/10\"."), context);
627 for (
const auto *
const value : values) {
631 throw InvalidDataException();
633 substrings.emplace_back(numberToString(
static_cast<std::uint64_t
>(
duration.totalMilliseconds())));
636 }
else if (isUrlFrame) {
639 for (
const auto *
const value : values) {
646 for (
const auto *
const value : values) {
672 for (
const auto *
const value : values) {
687 const auto byteOrderMark = [&] {
690 return std::string({
'\xFF',
'\xFE' });
692 return std::string({
'\xFE',
'\xFF' });
694 return std::string();
697 const auto concatenatedSubstrings = joinStrings(substrings, std::string(),
false, byteOrderMark, std::string(terminationLength,
'\0'));
701 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(1 + concatenatedSubstrings.size()));
703 concatenatedSubstrings.copy(&m_data[1], concatenatedSubstrings.size());
705 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(concatenatedSubstrings.size()));
706 concatenatedSubstrings.copy(m_data.get(), concatenatedSubstrings.size());
711 m_frame.makePicture(m_data, m_decompressedSize, *values.front(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0, version, diag);
717 m_frame.makeComment(m_data, m_decompressedSize, *values.front(), version, diag);
724 }
catch (
const ConversionException &) {
726 argsToString(
"The play counter \"", values.front()->toDisplayString(),
"\" is not an unsigned integer."), context);
728 m_decompressedSize = computePlayCounterSize(playCounter);
729 m_data = make_unique<char[]>(m_decompressedSize);
730 writePlayCounter(m_data.get() + m_decompressedSize - 1, m_decompressedSize, playCounter);
737 }
catch (
const ConversionException &) {
740 "The popularity \"", values.front()->toDisplayString(),
"\" is not of the expected form, eg. \"user|rating|counter\"."),
744 if (popularity.rating > 0xFF) {
745 popularity.rating = 0xFF;
746 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 255."), context);
747 }
else if (popularity.rating < 0x00) {
748 popularity.rating = 0x00;
749 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 0."), context);
752 m_decompressedSize =
static_cast<std::uint32_t
>(popularity.user.size() + 2);
753 const auto playCounterSize = computePlayCounterSize(popularity.playCounter);
754 m_decompressedSize += playCounterSize;
756 m_data = make_unique<char[]>(m_decompressedSize);
757 auto pos = popularity.user.size() + 1;
758 std::memcpy(m_data.get(), popularity.user.data(), pos);
759 m_data[pos] =
static_cast<char>(popularity.rating);
760 writePlayCounter(m_data.get() + pos + playCounterSize, playCounterSize, popularity.playCounter);
764 const auto &
value(*values.front());
767 throw InvalidDataException();
769 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(
value.
dataSize()));
772 }
catch (
const ConversionException &) {
776 argsToString(
"Assigned value(s) \"",
DiagMessage::formatList(valuesAsString),
"\" can not be converted appropriately."), context);
777 }
catch (
const ConversionException &) {
778 diag.emplace_back(
DiagLevel::Critical,
"Assigned value(s) can not be converted appropriately.", context);
780 throw InvalidDataException();
784 if (version >= 3 && m_frame.isCompressed()) {
785 auto compressedSize = compressBound(m_decompressedSize);
786 auto compressedData = make_unique<char[]>(compressedSize);
787 switch (compress(
reinterpret_cast<Bytef *
>(compressedData.get()),
reinterpret_cast<uLongf *
>(&compressedSize),
788 reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
790 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The source buffer was too small.", context);
791 throw InvalidDataException();
793 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The destination buffer was too small.", context);
794 throw InvalidDataException();
798 diag.emplace_back(
DiagLevel::Critical,
"Compressed size exceeds maximum data size.", context);
799 throw InvalidDataException();
801 m_data.swap(compressedData);
802 m_dataSize =
static_cast<std::uint32_t
>(compressedSize);
804 m_dataSize = m_decompressedSize;
809 m_requiredSize = m_dataSize;
815 m_requiredSize += 10;
817 if (m_frame.hasGroupInformation()) {
821 if (version >= 3 && m_frame.isCompressed()) {
837 writer.writeUInt24BE(m_frameId);
838 writer.writeUInt24BE(m_dataSize);
840 writer.writeUInt32BE(m_frameId);
841 if (m_version >= 4) {
842 writer.writeSynchsafeUInt32BE(m_dataSize);
844 writer.writeUInt32BE(m_dataSize);
846 writer.writeUInt16BE(m_frame.
flag());
848 writer.writeByte(m_frame.
group());
851 if (m_version >= 4) {
852 writer.writeSynchsafeUInt32BE(m_decompressedSize);
854 writer.writeUInt32BE(m_decompressedSize);
858 writer.write(m_data.get(), m_dataSize);
869 switch (textEncodingByte) {
870 case Id3v2TextEncodingBytes::Ascii:
872 case Id3v2TextEncodingBytes::Utf16WithBom:
874 case Id3v2TextEncodingBytes::Utf16BigEndianWithoutBom:
876 case Id3v2TextEncodingBytes::Utf8:
890 switch (textEncoding) {
892 return Id3v2TextEncodingBytes::Ascii;
894 return Id3v2TextEncodingBytes::Utf8;
896 return Id3v2TextEncodingBytes::Utf16WithBom;
898 return Id3v2TextEncodingBytes::Utf16WithBom;
921 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
926 if ((bufferSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
928 diag.emplace_back(
DiagLevel::Critical,
"Denoted character set is Latin-1 but an UTF-8 BOM is present - assuming UTF-8.",
934 const char *pos = get<0>(res);
935 for (; *pos != 0x00; ++pos) {
936 if (pos < get<2>(res)) {
946 get<2>(res) = pos + 1;
951 if (bufferSize >= 2) {
952 switch (LE::toInt<std::uint16_t>(buffer)) {
956 "Denoted character set is UTF-16 Big Endian but UTF-16 Little Endian BOM is present - assuming UTF-16 LE.",
967 const std::uint16_t *pos =
reinterpret_cast<const std::uint16_t *
>(get<0>(res));
968 for (; *pos != 0x0000; ++pos) {
969 if (pos <
reinterpret_cast<const std::uint16_t *
>(get<2>(res))) {
979 get<2>(res) =
reinterpret_cast<const char *
>(pos + 1);
1020 if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFFFE)) {
1022 }
else if ((maxSize >= 2) && (BE::toInt<std::uint16_t>(buffer) == 0xFEFF)) {
1027 if ((maxSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
1029 diag.emplace_back(
DiagLevel::Warning,
"UTF-8 byte order mark found in text frame.",
"parsing byte order mark of frame " +
idToString());
1043 static const string context(
"parsing ID3v2.2 picture frame");
1048 const char *end = buffer + maxSize;
1050 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
1051 auto substr =
parseSubstring(buffer + 5,
static_cast<size_t>(end - 5 - buffer), dataEncoding,
true, diag);
1052 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1053 if (get<2>(substr) >= end) {
1054 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1069 static const string context(
"parsing ID3v2.3 picture frame");
1070 const char *end = buffer + maxSize;
1073 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true, diag);
1074 if (get<1>(substr)) {
1075 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
1077 if (get<2>(substr) >= end) {
1078 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (type info, description and actual data are missing).", context);
1081 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
1082 if (++get<2>(substr) >= end) {
1083 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (description and actual data are missing).", context);
1086 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
true, diag);
1087 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1088 if (get<2>(substr) >= end) {
1089 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1103 static const string context(
"parsing comment/unsynchronized lyrics frame");
1104 const char *end = buffer +
dataSize;
1114 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1115 if (get<2>(substr) > end) {
1116 diag.emplace_back(
DiagLevel::Critical,
"Comment frame is incomplete (description not terminated?).", context);
1119 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
false, diag);
1131 LE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1134 BE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1145 unique_ptr<
char[]> &buffer, std::uint32_t &bufferSize,
const TagValue &picture, std::uint8_t typeInfo,
Diagnostics &diag)
1149 StringData convertedDescription;
1150 string::size_type descriptionSize = picture.
description().find(
1152 if (descriptionSize == string::npos) {
1158 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1159 descriptionSize = convertedDescription.second;
1164 const auto requiredBufferSize = 1 + 3 + 1 + descriptionSize
1167 if (requiredBufferSize > numeric_limits<std::uint32_t>::max()) {
1168 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making legacy picture frame");
1171 buffer = make_unique<char[]>(bufferSize =
static_cast<std::uint32_t
>(requiredBufferSize));
1172 char *offset = buffer.get();
1178 const char *imageFormat;
1179 if (picture.
mimeType() ==
"image/jpeg") {
1180 imageFormat =
"JPG";
1181 }
else if (picture.
mimeType() ==
"image/png") {
1182 imageFormat =
"PNG";
1183 }
else if (picture.
mimeType() ==
"image/gif") {
1184 imageFormat =
"GIF";
1185 }
else if (picture.
mimeType() ==
"-->") {
1186 imageFormat = picture.
mimeType().data();
1188 imageFormat =
"UND";
1190 std::memcpy(++offset, imageFormat, 3);
1193 *(offset += 3) =
static_cast<char>(
typeInfo);
1196 offset +=
makeBom(offset + 1, descriptionEncoding);
1197 if (convertedDescription.first) {
1198 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1200 picture.
description().copy(++offset, descriptionSize);
1202 *(offset += descriptionSize) = 0x00;
1224 StringData convertedDescription;
1225 string::size_type descriptionSize = picture.
description().find(
1227 if (descriptionSize == string::npos) {
1233 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1234 descriptionSize = convertedDescription.second;
1237 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
1238 if (mimeTypeSize == string::npos) {
1239 mimeTypeSize = picture.
mimeType().length();
1244 const auto requiredBufferSize = 1 + mimeTypeSize + 1 + 1 + descriptionSize
1247 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1248 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making picture frame");
1251 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1252 char *offset = buffer.get();
1258 picture.
mimeType().copy(++offset, mimeTypeSize);
1260 *(offset += mimeTypeSize) = 0x00;
1262 *(++offset) =
static_cast<char>(
typeInfo);
1265 offset +=
makeBom(offset + 1, descriptionEncoding);
1266 if (convertedDescription.first) {
1267 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1269 picture.
description().copy(++offset, descriptionSize);
1271 *(offset += descriptionSize) = 0x00;
1285 static const string context(
"making comment frame");
1289 if (!comment.description().empty() && encoding != comment.descriptionEncoding()) {
1290 diag.emplace_back(
DiagLevel::Critical,
"Data encoding and description encoding aren't equal.", context);
1294 if (language.length() > 3) {
1295 diag.emplace_back(
DiagLevel::Critical,
"The language must be 3 bytes long (ISO-639-2).", context);
1298 StringData convertedDescription;
1299 string::size_type descriptionSize = comment.description().find(
1301 if (descriptionSize == string::npos) {
1302 descriptionSize = comment.description().size();
1307 convertedDescription = convertUtf8ToUtf16LE(comment.description().data(), descriptionSize);
1308 descriptionSize = convertedDescription.second;
1313 const auto data = comment.toString(encoding);
1314 const auto requiredBufferSize = 1 + 3 + descriptionSize + data.size()
1316 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1320 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1321 char *offset = buffer.get();
1327 for (
unsigned int i = 0; i < 3; ++i) {
1328 *(++offset) = (language.length() > i) ? language[i] : 0x00;
1332 offset +=
makeBom(offset + 1, encoding);
1333 if (convertedDescription.first) {
1334 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1336 comment.description().copy(++offset, descriptionSize);
1338 offset += descriptionSize;
1345 offset +=
makeBom(offset + 1, encoding);
1346 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 isUrlFrame(std::uint32_t id)
Returns an indication whether the specified id is a URL 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.