5#include "../diagnostics.h"
6#include "../exceptions.h"
9#include <c++utilities/conversion/stringbuilder.h>
10#include <c++utilities/conversion/stringconversion.h>
25namespace Id3v2TextEncodingBytes {
26enum Id3v2TextEncodingByte : std::uint8_t { Ascii, Utf16WithBom, Utf16BigEndianWithoutBom,
Utf8 };
69template <
class stringtype>
static int parseGenreIndex(
const stringtype &denotation)
72 for (
auto c : denotation) {
83 if (c >=
'0' && c <=
'9') {
96 if (c >=
'0' && c <=
'9') {
97 index = index * 10 + c -
'0';
110static std::string stringFromSubstring(std::tuple<const char *, std::size_t, const char *> substr)
112 return std::string(std::get<0>(substr), std::get<1>(substr));
118static std::u16string wideStringFromSubstring(std::tuple<const char *, std::size_t, const char *> substr,
TagTextEncoding encoding)
120 std::u16string res(
reinterpret_cast<u16string::const_pointer
>(std::get<0>(substr)), std::get<1>(substr) / 2);
128static std::uint64_t readPlayCounter(
const char *begin,
const char *end,
const std::string &context, Diagnostics &diag)
130 auto res = std::uint64_t();
132 if (end - begin > 8) {
133 diag.emplace_back(
DiagLevel::Critical,
"Play counter is bigger than eight bytes and therefore not supported.", context);
136 for (
auto shift = 0; pos >= begin; shift += 8, --pos) {
137 res +=
static_cast<std::uint64_t
>(
static_cast<std::uint8_t
>(*pos)) << shift;
154 static const string defaultContext(
"parsing ID3v2 frame");
161 setId(reader.readUInt24BE());
162 if (
id() & 0xFFFF0000u) {
171 context =
"parsing " %
idToString() +
" frame";
174 m_dataSize = reader.readUInt24BE();
175 m_totalSize = m_dataSize + 6;
176 if (m_totalSize > maximalSize) {
177 diag.emplace_back(
DiagLevel::Warning,
"The frame is truncated and will be ignored.", context);
188 setId(reader.readUInt32BE());
189 if (
id() & 0xFF000000u) {
198 context =
"parsing " %
idToString() +
" frame";
201 m_dataSize =
version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
202 m_totalSize = m_dataSize + 10;
203 if (m_totalSize > maximalSize) {
204 diag.emplace_back(
DiagLevel::Warning,
"The frame is truncated and will be ignored.", context);
209 m_flag = reader.readUInt16BE();
221 argsToString(
"The frame is only supported in ID3v2.4 and newer but the tag's version is ID3v2.",
version,
'.'), context);
224 argsToString(
"The frame is only supported in ID3v2.3 and older but the tag's version is ID3v2.",
version,
'.'), context);
228 if (m_dataSize <= 0) {
234 unique_ptr<char[]> buffer;
238 uLongf decompressedSize =
version >= 4 ? reader.readSynchsafeUInt32BE() : reader.readUInt32BE();
239 if (decompressedSize < m_dataSize) {
240 diag.emplace_back(
DiagLevel::Critical,
"The decompressed size is smaller than the compressed size.", context);
243 const auto bufferCompressed = make_unique<char[]>(m_dataSize);
244 reader.read(bufferCompressed.get(), m_dataSize);
245 buffer = make_unique<char[]>(decompressedSize);
247 uncompress(
reinterpret_cast<Bytef *
>(buffer.get()), &decompressedSize,
reinterpret_cast<Bytef *
>(bufferCompressed.get()), m_dataSize)) {
249 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The source buffer was too small.", context);
252 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The destination buffer was too small.", context);
255 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The input data was corrupted or incomplete.", context);
264 diag.emplace_back(
DiagLevel::Critical,
"The decompressed data exceeds the maximum supported frame size.", context);
267 m_dataSize =
static_cast<std::uint32_t
>(decompressedSize);
269 buffer = make_unique<char[]>(m_dataSize);
270 reader.read(buffer.get(), m_dataSize);
279 const char *currentOffset = buffer.get() + 1;
280 for (
size_t currentIndex = 1; currentIndex < m_dataSize;) {
282 const auto substr(
parseSubstring(currentOffset, m_dataSize - currentIndex, dataEncoding,
false, diag));
285 if (!get<1>(substr)) {
286 if (currentIndex == 1) {
289 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
290 currentOffset = get<2>(substr);
296 if (this->
value().isEmpty()) {
297 return &this->
value();
299 m_additionalValues.emplace_back();
300 return &m_additionalValues.back();
313 }
catch (
const ConversionException &) {
314 diag.emplace_back(
DiagLevel::Warning,
"The value of track/disk position frame is not numeric and will be ignored.", context);
320 const auto milliseconds = [&] {
322 const auto parsedStringRef =
parseSubstring(buffer.get() + 1, m_dataSize - 1, dataEncoding,
false, diag);
324 ? convertUtf16BEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef))
325 : convertUtf16LEToUtf8(get<0>(parsedStringRef), get<1>(parsedStringRef));
326 return string(convertedStringData.first.get(), convertedStringData.second);
328 return stringFromSubstring(substr);
332 }
catch (
const ConversionException &) {
333 diag.emplace_back(
DiagLevel::Warning,
"The value of the length frame is not numeric and will be ignored.", context);
338 const auto genreIndex = [&] {
340 return parseGenreIndex(wideStringFromSubstring(substr, dataEncoding));
342 return parseGenreIndex(stringFromSubstring(substr));
345 if (genreIndex != -1) {
357 currentIndex =
static_cast<size_t>(get<2>(substr) - buffer.get());
358 currentOffset = get<2>(substr);
362 if (
version < 4 && !m_additionalValues.empty()) {
364 DiagLevel::Warning,
"Multiple strings found though the tag is pre-ID3v2.4. " + ignoreAdditionalValuesDiagMsg(), context);
392 auto substr =
parseSubstring(buffer.get(), m_dataSize, userEncoding,
true, diag);
393 auto end = buffer.get() + m_dataSize;
394 if (std::get<1>(substr)) {
395 popularity.user.assign(std::get<0>(substr), std::get<1>(substr));
397 auto ratingPos = std::get<2>(substr);
398 if (ratingPos >= end) {
399 diag.emplace_back(
DiagLevel::Critical,
"Popularimeter frame is incomplete (rating is missing).", context);
402 popularity.rating =
static_cast<std::uint8_t
>(*ratingPos);
403 popularity.playCounter = readPlayCounter(ratingPos + 1, end, context, diag);
443void Id3v2Frame::internallyClearValue()
446 m_additionalValues.clear();
452void Id3v2Frame::internallyClearFurtherData()
465std::string Id3v2Frame::ignoreAdditionalValuesDiagMsg()
const
467 if (m_additionalValues.size() == 1) {
468 return argsToString(
"Additional value \"", m_additionalValues.front().toString(
TagTextEncoding::Utf8),
"\" is supposed to be ignored.");
476static std::uint32_t computePlayCounterSize(std::uint64_t
playCounter)
488static void writePlayCounter(
char *last, std::uint32_t playCounterSize, std::uint64_t
playCounter)
506Id3v2FrameMaker::Id3v2FrameMaker(Id3v2Frame &frame, std::uint8_t
version, Diagnostics &diag)
508 , m_frameId(m_frame.id())
511 const string context(
"making " % m_frame.idToString() +
" frame");
514 if (m_frame.isEncrypted()) {
515 diag.emplace_back(
DiagLevel::Critical,
"Cannot make an encrypted frame (isn't supported by this tagging library).", context);
516 throw InvalidDataException();
518 if (m_frame.hasPaddingReached()) {
519 diag.emplace_back(
DiagLevel::Critical,
"Cannot make a frame which is marked as padding.", context);
520 throw InvalidDataException();
522 if (
version < 3 && m_frame.isCompressed()) {
523 diag.emplace_back(
DiagLevel::Warning,
"Compression is not supported by the version of ID3v2 and won't be applied.", context);
525 if (
version < 3 && (m_frame.flag() || m_frame.group())) {
527 "The existing flag and group information is not supported by the version of ID3v2 and will be ignored/discarted.", context);
531 vector<const TagValue *> values;
532 values.reserve(1 + frame.additionalValues().size());
533 if (!frame.value().isEmpty()) {
534 values.emplace_back(&frame.value());
536 for (
const auto &
value : frame.additionalValues()) {
538 values.emplace_back(&
value);
543 if (values.empty()) {
544 throw NoDataProvidedException();
549 if (values.size() != 1) {
551 diag.emplace_back(
DiagLevel::Critical,
"Multiple values are not supported for non-text-frames.", context);
552 throw InvalidDataException();
555 DiagLevel::Warning,
"Multiple strings assigned to pre-ID3v2.4 text frame. " + frame.ignoreAdditionalValuesDiagMsg(), context);
565 "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.",
567 throw InvalidDataException();
575 "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.",
577 throw InvalidDataException();
585 argsToString(
"The frame is only supported in ID3v2.4 and newer but version of the tag being written is ID3v2.",
version,
586 ". The frame is written nevertheless but other tools might not be able to deal with it."),
590 argsToString(
"The frame is only supported in ID3v2.3 and older but version of the tag being written is ID3v2.",
version,
591 ". The frame is written nevertheless but other tools might not be able to deal with it."),
599 vector<string> substrings;
600 substrings.reserve(1 + frame.additionalValues().size());
607 for (
const auto *
const value : values) {
616 }
catch (
const ConversionException &) {
618 argsToString(
"The track/disk number \"", substrings.back(),
"\" is not of the expected form, eg. \"4/10\"."), context);
625 for (
const auto *
const value : values) {
629 throw InvalidDataException();
631 substrings.emplace_back(numberToString(
static_cast<std::uint64_t
>(
duration.totalMilliseconds())));
637 for (
const auto *
const value : values) {
663 for (
const auto *
const value : values) {
678 const auto byteOrderMark = [&] {
681 return string({
'\xFF',
'\xFE' });
683 return string({
'\xFE',
'\xFF' });
688 const auto concatenatedSubstrings = joinStrings(substrings,
string(),
false, byteOrderMark,
string(terminationLength,
'\0'));
691 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(1 + concatenatedSubstrings.size()));
693 concatenatedSubstrings.copy(&m_data[1], concatenatedSubstrings.size());
697 m_frame.makePicture(m_data, m_decompressedSize, *values.front(), m_frame.isTypeInfoAssigned() ? m_frame.typeInfo() : 0,
version, diag);
703 m_frame.makeComment(m_data, m_decompressedSize, *values.front(),
version, diag);
710 }
catch (
const ConversionException &) {
712 argsToString(
"The play counter \"", values.front()->toDisplayString(),
"\" is not an unsigned integer."), context);
714 m_decompressedSize = computePlayCounterSize(
playCounter);
715 m_data = make_unique<char[]>(m_decompressedSize);
716 writePlayCounter(m_data.get() + m_decompressedSize - 1, m_decompressedSize,
playCounter);
722 popularity = values.front()->toPopularity();
723 }
catch (
const ConversionException &) {
726 "The popularity \"", values.front()->toDisplayString(),
"\" is not of the expected form, eg. \"user|rating|counter\"."),
730 if (popularity.rating > 0xFF) {
731 popularity.rating = 0xFF;
732 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 255."), context);
733 }
else if (popularity.rating < 0x00) {
734 popularity.rating = 0x00;
735 diag.emplace_back(
DiagLevel::Warning, argsToString(
"The rating has been clamped to 0."), context);
738 m_decompressedSize =
static_cast<std::uint32_t
>(popularity.user.size() + 2);
739 const auto playCounterSize = computePlayCounterSize(popularity.playCounter);
740 m_decompressedSize += playCounterSize;
742 m_data = make_unique<char[]>(m_decompressedSize);
743 auto pos = popularity.user.size() + 1;
744 std::memcpy(m_data.get(), popularity.user.data(), pos);
745 m_data[pos] =
static_cast<char>(popularity.rating);
746 writePlayCounter(m_data.get() + pos + playCounterSize, playCounterSize, popularity.playCounter);
750 const auto &
value(*values.front());
753 throw InvalidDataException();
755 m_data = make_unique<char[]>(m_decompressedSize =
static_cast<std::uint32_t
>(
value.
dataSize()));
758 }
catch (
const ConversionException &) {
762 argsToString(
"Assigned value(s) \"",
DiagMessage::formatList(valuesAsString),
"\" can not be converted appropriately."), context);
763 }
catch (
const ConversionException &) {
764 diag.emplace_back(
DiagLevel::Critical,
"Assigned value(s) can not be converted appropriately.", context);
766 throw InvalidDataException();
770 if (
version >= 3 && m_frame.isCompressed()) {
771 auto compressedSize = compressBound(m_decompressedSize);
772 auto compressedData = make_unique<char[]>(compressedSize);
773 switch (compress(
reinterpret_cast<Bytef *
>(compressedData.get()),
reinterpret_cast<uLongf *
>(&compressedSize),
774 reinterpret_cast<Bytef *
>(m_data.get()), m_decompressedSize)) {
776 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The source buffer was too small.", context);
777 throw InvalidDataException();
779 diag.emplace_back(
DiagLevel::Critical,
"Decompressing failed. The destination buffer was too small.", context);
780 throw InvalidDataException();
784 diag.emplace_back(
DiagLevel::Critical,
"Compressed size exceeds maximum data size.", context);
785 throw InvalidDataException();
787 m_data.swap(compressedData);
788 m_dataSize =
static_cast<std::uint32_t
>(compressedSize);
790 m_dataSize = m_decompressedSize;
795 m_requiredSize = m_dataSize;
801 m_requiredSize += 10;
803 if (m_frame.hasGroupInformation()) {
807 if (
version >= 3 && m_frame.isCompressed()) {
823 writer.writeUInt24BE(m_frameId);
824 writer.writeUInt24BE(m_dataSize);
826 writer.writeUInt32BE(m_frameId);
827 if (m_version >= 4) {
828 writer.writeSynchsafeUInt32BE(m_dataSize);
830 writer.writeUInt32BE(m_dataSize);
832 writer.writeUInt16BE(m_frame.
flag());
834 writer.writeByte(m_frame.
group());
837 if (m_version >= 4) {
838 writer.writeSynchsafeUInt32BE(m_decompressedSize);
840 writer.writeUInt32BE(m_decompressedSize);
844 writer.write(m_data.get(), m_dataSize);
855 switch (textEncodingByte) {
856 case Id3v2TextEncodingBytes::Ascii:
858 case Id3v2TextEncodingBytes::Utf16WithBom:
860 case Id3v2TextEncodingBytes::Utf16BigEndianWithoutBom:
876 switch (textEncoding) {
878 return Id3v2TextEncodingBytes::Ascii;
882 return Id3v2TextEncodingBytes::Utf16WithBom;
884 return Id3v2TextEncodingBytes::Utf16WithBom;
907 tuple<const char *, size_t, const char *> res(buffer, 0, buffer + bufferSize);
912 if ((bufferSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
914 diag.emplace_back(
DiagLevel::Critical,
"Denoted character set is Latin-1 but an UTF-8 BOM is present - assuming UTF-8.",
920 const char *pos = get<0>(res);
921 for (; *pos != 0x00; ++pos) {
922 if (pos < get<2>(res)) {
932 get<2>(res) = pos + 1;
937 if (bufferSize >= 2) {
938 switch (LE::toUInt16(buffer)) {
942 "Denoted character set is UTF-16 Big Endian but UTF-16 Little Endian BOM is present - assuming UTF-16 LE.",
953 const std::uint16_t *pos =
reinterpret_cast<const std::uint16_t *
>(get<0>(res));
954 for (; *pos != 0x0000; ++pos) {
955 if (pos <
reinterpret_cast<const std::uint16_t *
>(get<2>(res))) {
965 get<2>(res) =
reinterpret_cast<const char *
>(pos + 1);
1006 if ((maxSize >= 2) && (BE::toUInt16(buffer) == 0xFFFE)) {
1008 }
else if ((maxSize >= 2) && (BE::toUInt16(buffer) == 0xFEFF)) {
1013 if ((maxSize >= 3) && (BE::toUInt24(buffer) == 0x00EFBBBF)) {
1015 diag.emplace_back(
DiagLevel::Warning,
"UTF-8 byte order mark found in text frame.",
"parsing byte order mark of frame " +
idToString());
1029 static const string context(
"parsing ID3v2.2 picture frame");
1034 const char *end = buffer + maxSize;
1036 typeInfo =
static_cast<unsigned char>(*(buffer + 4));
1037 auto substr =
parseSubstring(buffer + 5,
static_cast<size_t>(end - 5 - buffer), dataEncoding,
true, diag);
1038 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1039 if (get<2>(substr) >= end) {
1040 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1055 static const string context(
"parsing ID3v2.3 picture frame");
1056 const char *end = buffer + maxSize;
1059 auto substr =
parseSubstring(buffer + 1, maxSize - 1, mimeTypeEncoding,
true, diag);
1060 if (get<1>(substr)) {
1061 tagValue.
setMimeType(
string(get<0>(substr), get<1>(substr)));
1063 if (get<2>(substr) >= end) {
1064 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (type info, description and actual data are missing).", context);
1067 typeInfo =
static_cast<unsigned char>(*get<2>(substr));
1068 if (++get<2>(substr) >= end) {
1069 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (description and actual data are missing).", context);
1072 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
true, diag);
1073 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1074 if (get<2>(substr) >= end) {
1075 diag.emplace_back(
DiagLevel::Critical,
"Picture frame is incomplete (actual data is missing).", context);
1089 static const string context(
"parsing comment/unsynchronized lyrics frame");
1090 const char *end = buffer +
dataSize;
1100 tagValue.
setDescription(
string(get<0>(substr), get<1>(substr)), dataEncoding);
1101 if (get<2>(substr) > end) {
1102 diag.emplace_back(
DiagLevel::Critical,
"Comment frame is incomplete (description not terminated?).", context);
1105 substr =
parseSubstring(get<2>(substr),
static_cast<size_t>(end - get<2>(substr)), dataEncoding,
false, diag);
1117 LE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1120 BE::getBytes(
static_cast<std::uint16_t
>(0xFEFF), buffer);
1131 unique_ptr<
char[]> &buffer, std::uint32_t &bufferSize,
const TagValue &picture, std::uint8_t typeInfo,
Diagnostics &diag)
1135 StringData convertedDescription;
1136 string::size_type descriptionSize = picture.
description().find(
1138 if (descriptionSize == string::npos) {
1144 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1145 descriptionSize = convertedDescription.second;
1150 const auto requiredBufferSize = 1 + 3 + 1 + descriptionSize
1153 if (requiredBufferSize > numeric_limits<std::uint32_t>::max()) {
1154 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making legacy picture frame");
1157 buffer = make_unique<char[]>(bufferSize =
static_cast<std::uint32_t
>(requiredBufferSize));
1158 char *offset = buffer.get();
1164 const char *imageFormat;
1165 if (picture.
mimeType() ==
"image/jpeg") {
1166 imageFormat =
"JPG";
1167 }
else if (picture.
mimeType() ==
"image/png") {
1168 imageFormat =
"PNG";
1169 }
else if (picture.
mimeType() ==
"image/gif") {
1170 imageFormat =
"GIF";
1171 }
else if (picture.
mimeType() ==
"-->") {
1172 imageFormat = picture.
mimeType().data();
1174 imageFormat =
"UND";
1176 strncpy(++offset, imageFormat, 3);
1179 *(offset += 3) =
static_cast<char>(
typeInfo);
1182 offset +=
makeBom(offset + 1, descriptionEncoding);
1183 if (convertedDescription.first) {
1184 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1186 picture.
description().copy(++offset, descriptionSize);
1188 *(offset += descriptionSize) = 0x00;
1210 StringData convertedDescription;
1211 string::size_type descriptionSize = picture.
description().find(
1213 if (descriptionSize == string::npos) {
1219 convertedDescription = convertUtf8ToUtf16LE(picture.
description().data(), descriptionSize);
1220 descriptionSize = convertedDescription.second;
1223 string::size_type mimeTypeSize = picture.
mimeType().find(
'\0');
1224 if (mimeTypeSize == string::npos) {
1225 mimeTypeSize = picture.
mimeType().length();
1230 const auto requiredBufferSize = 1 + mimeTypeSize + 1 + 1 + descriptionSize
1233 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1234 diag.emplace_back(
DiagLevel::Critical,
"Required size exceeds maximum.",
"making picture frame");
1237 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1238 char *offset = buffer.get();
1244 picture.
mimeType().copy(++offset, mimeTypeSize);
1246 *(offset += mimeTypeSize) = 0x00;
1248 *(++offset) =
static_cast<char>(
typeInfo);
1251 offset +=
makeBom(offset + 1, descriptionEncoding);
1252 if (convertedDescription.first) {
1253 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1255 picture.
description().copy(++offset, descriptionSize);
1257 *(offset += descriptionSize) = 0x00;
1271 static const string context(
"making comment frame");
1275 if (!
comment.description().empty() && encoding !=
comment.descriptionEncoding()) {
1276 diag.emplace_back(
DiagLevel::Critical,
"Data encoding and description encoding aren't equal.", context);
1281 diag.emplace_back(
DiagLevel::Critical,
"The language must be 3 bytes long (ISO-639-2).", context);
1284 StringData convertedDescription;
1285 string::size_type descriptionSize =
comment.description().find(
1287 if (descriptionSize == string::npos) {
1288 descriptionSize =
comment.description().size();
1293 convertedDescription = convertUtf8ToUtf16LE(
comment.description().data(), descriptionSize);
1294 descriptionSize = convertedDescription.second;
1299 const auto data =
comment.toString(encoding);
1300 const auto requiredBufferSize = 1 + 3 + descriptionSize + data.size()
1302 if (requiredBufferSize > numeric_limits<uint32_t>::max()) {
1306 buffer = make_unique<char[]>(bufferSize =
static_cast<uint32_t
>(requiredBufferSize));
1307 char *offset = buffer.get();
1313 for (
unsigned int i = 0; i < 3; ++i) {
1318 offset +=
makeBom(offset + 1, encoding);
1319 if (convertedDescription.first) {
1320 copy(convertedDescription.first.get(), convertedDescription.first.get() + descriptionSize, ++offset);
1322 comment.description().copy(++offset, descriptionSize);
1324 offset += descriptionSize;
1331 offset +=
makeBom(offset + 1, encoding);
1332 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 PositionInSet class describes the position of an element in a set which consists of a certain num...
The TagField class is used by FieldMapBasedTag to store the fields.
void setTypeInfo(const TypeInfoType &typeInfo)
Sets the type info of the current TagField.
const TypeInfoType & typeInfo() const
Returns the type info of the current TagField.
IdentifierType & id()
Returns the id of the current TagField.
void setId(const IdentifierType &id)
Sets the id of the current Tag Field.
std::string idToString() const
Returns the id of the current TagField as string.
TagValue & value()
Returns the value of the current TagField.
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 comment()
constexpr TAG_PARSER_EXPORT std::string_view language()
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.
TagType scale
Specifies the scale used for rating by the tag defining that scale.