Tag Parser  7.0.3
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
id3v1tag.cpp
Go to the documentation of this file.
1 #include "./id3v1tag.h"
2 #include "./id3genres.h"
3 
4 #include "../diagnostics.h"
5 #include "../exceptions.h"
6 
7 #include <c++utilities/conversion/conversionexception.h>
8 
9 #include <cstring>
10 
11 using namespace std;
12 using namespace ConversionUtilities;
13 
14 namespace TagParser {
15 
24 Id3v1Tag::Id3v1Tag()
25 {
26 }
27 
28 TagType Id3v1Tag::type() const
29 {
30  return TagType::Id3v1Tag;
31 }
32 
33 const char *Id3v1Tag::typeName() const
34 {
35  return tagName;
36 }
37 
38 bool Id3v1Tag::canEncodingBeUsed(TagTextEncoding encoding) const
39 {
40  return Tag::canEncodingBeUsed(encoding);
41 }
42 
51 void Id3v1Tag::parse(std::istream &stream, Diagnostics &diag)
52 {
53  VAR_UNUSED(diag);
54  char buffer[128];
55  stream.read(buffer, 128);
56  if (buffer[0] != 0x54 || buffer[1] != 0x41 || buffer[2] != 0x47) {
57  throw NoDataFoundException();
58  }
59  m_size = 128;
60  readValue(m_title, 30, buffer + 3);
61  readValue(m_artist, 30, buffer + 33);
62  readValue(m_album, 30, buffer + 63);
63  readValue(m_year, 4, buffer + 93);
64  if (buffer[125] == 0) {
65  readValue(m_comment, 28, buffer + 97);
66  m_version = "1.1";
67  } else {
68  readValue(m_comment, 30, buffer + 97);
69  m_version = "1.0";
70  }
71  readValue(m_comment, buffer[125] == 0 ? 28 : 30, buffer + 97);
72  if (buffer[125] == 0) {
73  m_trackPos.assignPosition(PositionInSet(*reinterpret_cast<char *>(buffer + 126), 0));
74  }
75  m_genre.assignStandardGenreIndex(*reinterpret_cast<unsigned char *>(buffer + 127));
76 }
77 
85 void Id3v1Tag::make(ostream &stream, Diagnostics &diag)
86 {
87  static const string context("making ID3v1 tag");
88  char buffer[30];
89  buffer[0] = 0x54;
90  buffer[1] = 0x41;
91  buffer[2] = 0x47;
92  stream.write(buffer, 3);
93  // write text fields
94  writeValue(m_title, 30, buffer, stream, diag);
95  writeValue(m_artist, 30, buffer, stream, diag);
96  writeValue(m_album, 30, buffer, stream, diag);
97  writeValue(m_year, 4, buffer, stream, diag);
98  writeValue(m_comment, 28, buffer, stream, diag);
99  // write numeric fields
100  buffer[0] = 0x0; // empty byte
101  buffer[1] = 0x0; // track nr
102  buffer[2] = 0x0; // genre
103  // track
104  if (!m_trackPos.isEmpty() && m_trackPos.type() == TagDataType::PositionInSet) {
105  try {
106  buffer[1] = m_trackPos.toPositionInSet().position();
107  } catch (const ConversionException &) {
108  diag.emplace_back(
109  DiagLevel::Warning, "Track position field can not be set because given value can not be converted appropriately.", context);
110  }
111  }
112  // genre
113  try {
114  buffer[2] = m_genre.toStandardGenreIndex();
115  } catch (const ConversionException &) {
116  diag.emplace_back(DiagLevel::Warning, "Genre field can not be set because given value can not be converted appropriately.", context);
117  }
118  stream.write(buffer, 3);
119  stream.flush();
120 }
121 
122 const TagValue &Id3v1Tag::value(KnownField field) const
123 {
124  switch (field) {
125  case KnownField::Title:
126  return m_title;
127  case KnownField::Artist:
128  return m_artist;
129  case KnownField::Album:
130  return m_album;
131  case KnownField::Year:
132  return m_year;
133  case KnownField::Comment:
134  return m_comment;
136  return m_trackPos;
137  case KnownField::Genre:
138  return m_genre;
139  default:
140  return TagValue::empty();
141  }
142 }
143 
144 bool Id3v1Tag::setValue(KnownField field, const TagValue &value)
145 {
146  switch (field) {
147  case KnownField::Title:
148  m_title = value;
149  break;
150  case KnownField::Artist:
151  m_artist = value;
152  break;
153  case KnownField::Album:
154  m_album = value;
155  break;
156  case KnownField::Year:
157  m_year = value;
158  break;
159  case KnownField::Comment:
160  m_comment = value;
161  break;
163  m_trackPos = value;
164  break;
165  case KnownField::Genre:
166  m_genre = value;
167  break;
168  default:
169  return false;
170  }
171  return true;
172 }
173 
174 bool Id3v1Tag::setValueConsideringTypeInfo(KnownField field, const TagValue &value, const string &)
175 {
176  return setValue(field, value);
177 }
178 
179 bool Id3v1Tag::hasField(KnownField field) const
180 {
181  switch (field) {
182  case KnownField::Title:
183  return !m_title.isEmpty();
184  case KnownField::Artist:
185  return !m_artist.isEmpty();
186  case KnownField::Album:
187  return !m_album.isEmpty();
188  case KnownField::Year:
189  return !m_year.isEmpty();
190  case KnownField::Comment:
191  return !m_comment.isEmpty();
193  return !m_trackPos.isEmpty();
194  case KnownField::Genre:
195  return !m_genre.isEmpty();
196  default:
197  return false;
198  }
199 }
200 
201 void Id3v1Tag::removeAllFields()
202 {
203  m_title.clearDataAndMetadata();
204  m_artist.clearDataAndMetadata();
205  m_album.clearDataAndMetadata();
206  m_year.clearDataAndMetadata();
207  m_comment.clearDataAndMetadata();
208  m_trackPos.clearDataAndMetadata();
209  m_genre.clearDataAndMetadata();
210 }
211 
212 unsigned int Id3v1Tag::fieldCount() const
213 {
214  unsigned int count = 0;
215  for (const auto &value : { m_title, m_artist, m_album, m_year, m_comment, m_trackPos, m_genre }) {
216  if (!value.isEmpty()) {
217  ++count;
218  }
219  }
220  return count;
221 }
222 
223 bool Id3v1Tag::supportsField(KnownField field) const
224 {
225  switch (field) {
226  case KnownField::Title:
227  case KnownField::Artist:
228  case KnownField::Album:
229  case KnownField::Year:
230  case KnownField::Comment:
232  case KnownField::Genre:
233  return true;
234  default:
235  return false;
236  }
237 }
238 
239 void Id3v1Tag::ensureTextValuesAreProperlyEncoded()
240 {
241  m_title.convertDataEncodingForTag(this);
242  m_artist.convertDataEncodingForTag(this);
243  m_album.convertDataEncodingForTag(this);
244  m_year.convertDataEncodingForTag(this);
245  m_comment.convertDataEncodingForTag(this);
246  m_trackPos.convertDataEncodingForTag(this);
247  m_genre.convertDataEncodingForTag(this);
248 }
249 
253 void Id3v1Tag::readValue(TagValue &value, size_t maxLength, const char *buffer)
254 {
255  const char *end = buffer + maxLength - 1;
256  while ((*end == 0x0 || *end == ' ') && end >= buffer) {
257  --end;
258  --maxLength;
259  }
260  value.assignData(buffer, maxLength, TagDataType::Text, TagTextEncoding::Latin1);
261 }
262 
266 void Id3v1Tag::writeValue(const TagValue &value, size_t length, char *buffer, ostream &targetStream, Diagnostics &diag)
267 {
268  memset(buffer, 0, length);
269  try {
270  value.toString().copy(buffer, length);
271  } catch (const ConversionException &) {
272  diag.emplace_back(
273  DiagLevel::Warning, "Field can not be set because given value can not be converted appropriately.", "making ID3v1 tag field");
274  }
275  targetStream.write(buffer, length);
276 }
277 
278 } // namespace TagParser
STL namespace.
KnownField
Specifies the field.
Definition: tag.h:40
void assignData(const char *data, size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
Assigns a copy of the given data.
Definition: tagvalue.cpp:620
TagType
Specifies the tag type.
Definition: tag.h:20
TagTextEncoding
Specifies the text encoding.
Definition: tagvalue.h:22