4 #include "../diagnostics.h"
5 #include "../exceptions.h"
7 #include <c++utilities/conversion/conversionexception.h>
8 #include <c++utilities/conversion/stringbuilder.h>
11 #include <initializer_list>
32 return TagType::Id3v1Tag;
35 std::string_view Id3v1Tag::typeName()
const
50 return encoding == TagTextEncoding::Latin1;
61 CPP_UTILITIES_UNUSED(diag)
63 stream.read(buffer, 128);
64 if (buffer[0] != 0x54 || buffer[1] != 0x41 || buffer[2] != 0x47) {
68 readValue(m_title, 30, buffer + 3);
69 readValue(m_artist, 30, buffer + 33);
70 readValue(m_album, 30, buffer + 63);
71 readValue(m_year, 4, buffer + 93);
72 const auto is11 = buffer[125] == 0;
74 readValue(m_comment, 28, buffer + 97);
77 readValue(m_comment, 30, buffer + 97);
80 readValue(m_comment, is11 ? 28 : 30, buffer + 97);
82 m_trackPos.assignPosition(
PositionInSet(*
reinterpret_cast<char *
>(buffer + 126), 0));
84 m_genre.assignStandardGenreIndex(*
reinterpret_cast<unsigned char *
>(buffer + 127));
96 static const string context(
"making ID3v1 tag");
101 stream.write(buffer, 3);
104 writeValue(m_title, 30, buffer, stream, diag);
105 writeValue(m_artist, 30, buffer, stream, diag);
106 writeValue(m_album, 30, buffer, stream, diag);
107 writeValue(m_year, 4, buffer, stream, diag);
108 writeValue(m_comment, 28, buffer, stream, diag);
116 if (!m_trackPos.isEmpty()) {
118 const auto position(m_trackPos.toPositionInSet().position());
119 if (position < 0x00 || position > 0xFF) {
120 throw ConversionException();
122 buffer[1] =
static_cast<char>(position);
123 }
catch (
const ConversionException &) {
125 DiagLevel::Warning,
"Track position field can not be set because given value can not be converted appropriately.", context);
131 const auto genreIndex(m_genre.toStandardGenreIndex());
132 if (genreIndex < 0x00 || genreIndex > 0xFF) {
133 throw ConversionException();
135 buffer[2] =
static_cast<char>(genreIndex);
136 }
catch (
const ConversionException &) {
137 diag.emplace_back(DiagLevel::Warning,
138 "Genre field can not be set because given value can not be converted to a standard genre number supported by ID3v1.", context);
141 stream.write(buffer, 3);
154 case KnownField::RecordDate:
163 return TagValue::empty();
179 case KnownField::RecordDate:
199 return setValue(field, value);
206 return !m_title.isEmpty();
208 return !m_artist.isEmpty();
210 return !m_album.isEmpty();
211 return !m_year.isEmpty();
213 return !m_comment.isEmpty();
215 return !m_trackPos.isEmpty();
217 return !m_genre.isEmpty();
223 void Id3v1Tag::removeAllFields()
225 m_title.clearDataAndMetadata();
226 m_artist.clearDataAndMetadata();
227 m_album.clearDataAndMetadata();
228 m_year.clearDataAndMetadata();
229 m_comment.clearDataAndMetadata();
230 m_trackPos.clearDataAndMetadata();
231 m_genre.clearDataAndMetadata();
234 std::size_t Id3v1Tag::fieldCount()
const
236 auto count = std::size_t(0);
237 for (
const auto &value : std::initializer_list<const TagValue *>{ &m_title, &m_artist, &m_album, &m_year, &m_comment, &m_trackPos, &m_genre }) {
238 if (!value->isEmpty()) {
251 case KnownField::RecordDate:
261 void Id3v1Tag::ensureTextValuesAreProperlyEncoded()
263 for (
auto *value : initializer_list<TagValue *>{ &m_title, &m_artist, &m_album, &m_year, &m_comment, &m_trackPos, &m_genre }) {
265 switch (value->dataEncoding()) {
266 case TagTextEncoding::Latin1:
268 case TagTextEncoding::Unspecified:
279 void Id3v1Tag::readValue(
TagValue &value,
size_t maxLength,
const char *buffer)
281 const char *end = buffer + maxLength - 1;
282 while ((*end == 0x0 || *end ==
' ') && end >= buffer) {
289 if (maxLength >= 3 && BE::toUInt24(buffer) == 0x00EFBBBF) {
292 value.
assignData(buffer, maxLength, TagDataType::Text, TagTextEncoding::Latin1);
299 void Id3v1Tag::writeValue(
const TagValue &value,
size_t length,
char *buffer, ostream &targetStream, Diagnostics &diag)
302 memset(buffer, 0, length);
305 string valueAsString;
307 valueAsString = value.toString();
308 }
catch (
const ConversionException &) {
310 DiagLevel::Warning,
"Field can not be set because given value can not be converted appropriately.",
"making ID3v1 tag field");
314 auto *valueStart = buffer;
315 auto valueLength = length;
316 switch (value.dataEncoding()) {
317 case TagTextEncoding::Latin1:
318 case TagTextEncoding::Unspecified:
322 for (
const auto c : valueAsString) {
323 if ((c & 0x80) == 0) {
326 buffer[0] =
static_cast<char>(0xEF);
327 buffer[1] =
static_cast<char>(0xBB);
328 buffer[2] =
static_cast<char>(0xBF);
335 diag.emplace_back(DiagLevel::Warning,
"The used encoding is unlikely to be supported by other software.",
"making ID3v1 tag field");
339 if (valueAsString.size() > length) {
341 DiagLevel::Warning, argsToString(
"Value has been truncated. Max. ", length,
" characters supported."),
"making ID3v1 tag field");
343 valueAsString.copy(valueStart, valueLength);
345 targetStream.write(buffer,
static_cast<streamsize
>(length));
The Diagnostics class is a container for DiagMessage.
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 TagValue class wraps values of different types.
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
Contains all classes and functions of the TagInfo library.
KnownField
Specifies the field.
TagTextEncoding
Specifies the text encoding.
TagType
Specifies the tag type.