Tag Parser  7.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
vorbiscomment.cpp
Go to the documentation of this file.
1 #include "./vorbiscomment.h"
2 #include "./vorbiscommentids.h"
3 
4 #include "../ogg/oggiterator.h"
5 
6 #include "../diagnostics.h"
7 #include "../exceptions.h"
8 
9 #include <c++utilities/io/binaryreader.h>
10 #include <c++utilities/io/binarywriter.h>
11 #include <c++utilities/io/copy.h>
12 
13 #include <map>
14 #include <memory>
15 
16 using namespace std;
17 using namespace IoUtilities;
18 using namespace ConversionUtilities;
19 
20 namespace TagParser {
21 
27 const TagValue &VorbisComment::value(KnownField field) const
28 {
29  switch (field) {
30  case KnownField::Vendor:
31  return vendor();
32  default:
34  }
35 }
36 
37 bool VorbisComment::setValue(KnownField field, const TagValue &value)
38 {
39  switch (field) {
40  case KnownField::Vendor:
41  setVendor(value);
42  return true;
43  default:
44  return FieldMapBasedTag<VorbisComment>::setValue(field, value);
45  }
46 }
47 
48 VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField field) const
49 {
50  using namespace VorbisCommentIds;
51  switch (field) {
52  case KnownField::Album:
53  return album();
54  case KnownField::Artist:
55  return artist();
57  return comment();
58  case KnownField::Cover:
59  return cover();
60  case KnownField::Year:
61  return date();
62  case KnownField::Title:
63  return title();
64  case KnownField::Genre:
65  return genre();
67  return trackNumber();
69  return diskNumber();
70  case KnownField::PartNumber:
71  return partNumber();
73  return composer();
75  return encoder();
76  case KnownField::EncoderSettings:
77  return encoderSettings();
79  return description();
81  return label();
83  return performer();
84  case KnownField::Language:
85  return language();
87  return lyricist();
88  default:
89  return string();
90  }
91 }
92 
93 KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) const
94 {
95  using namespace VorbisCommentIds;
96  static const map<string, KnownField> fieldMap({ { album(), KnownField::Album }, { artist(), KnownField::Artist },
99  { partNumber(), KnownField::PartNumber }, { composer(), KnownField::Composer }, { encoder(), KnownField::Encoder },
100  { encoderSettings(), KnownField::EncoderSettings }, { description(), KnownField::Description }, { label(), KnownField::RecordLabel },
102  try {
103  return fieldMap.at(id);
104  } catch (out_of_range &) {
105  return KnownField::Invalid;
106  }
107 }
108 
112 template <class StreamType> void VorbisComment::internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
113 {
114  // prepare parsing
115  static const string context("parsing Vorbis comment");
116  uint64 startOffset = static_cast<uint64>(stream.tellg());
117  try {
118  // read signature: 0x3 + "vorbis"
119  char sig[8];
120  bool skipSignature = flags & VorbisCommentFlags::NoSignature;
121  if (!skipSignature) {
122  CHECK_MAX_SIZE(7);
123  stream.read(sig, 7);
124  skipSignature = (ConversionUtilities::BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
125  }
126  if (skipSignature) {
127  // read vendor (length prefixed string)
128  {
129  CHECK_MAX_SIZE(4);
130  stream.read(sig, 4);
131  const auto vendorSize = LE::toUInt32(sig);
132  if (vendorSize <= maxSize) {
133  auto buff = make_unique<char[]>(vendorSize);
134  stream.read(buff.get(), vendorSize);
135  m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
136  // TODO: Is the vendor string actually UTF-8 (like the field values)?
137  } else {
138  diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
139  throw TruncatedDataException();
140  }
141  maxSize -= vendorSize;
142  }
143  // read field count
144  CHECK_MAX_SIZE(4);
145  stream.read(sig, 4);
146  uint32 fieldCount = LE::toUInt32(sig);
147  for (uint32 i = 0; i < fieldCount; ++i) {
148  // read fields
149  VorbisCommentField field;
150  try {
151  field.parse(stream, maxSize, diag);
152  fields().emplace(field.id(), move(field));
153  } catch (const TruncatedDataException &) {
154  throw;
155  } catch (const Failure &) {
156  // nothing to do here since notifications will be added anyways
157  }
158  }
159  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
160  stream.ignore(); // skip framing byte
161  }
162  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
163  } else {
164  diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
165  throw InvalidDataException();
166  }
167  } catch (const TruncatedDataException &) {
168  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
169  diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
170  throw;
171  }
172 }
173 
181 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
182 {
183  internalParse(iterator, iterator.streamSize(), flags, diag);
184 }
185 
193 void VorbisComment::parse(istream &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
194 {
195  internalParse(stream, maxSize, flags, diag);
196 }
197 
205 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
206 {
207  // prepare making
208  static const string context("making Vorbis comment");
209  string vendor;
210  try {
211  m_vendor.toString(vendor);
212  } catch (const ConversionException &) {
213  diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
214  }
215  BinaryWriter writer(&stream);
216  if (!(flags & VorbisCommentFlags::NoSignature)) {
217  // write signature
218  static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
219  stream.write(sig, sizeof(sig));
220  }
221  // write vendor
222  writer.writeUInt32LE(vendor.size());
223  writer.writeString(vendor);
224  // write field count later
225  const auto fieldCountOffset = stream.tellp();
226  writer.writeUInt32LE(0);
227  // write fields
228  uint32 fieldsWritten = 0;
229  for (auto i : fields()) {
230  VorbisCommentField &field = i.second;
231  if (!field.value().isEmpty()) {
232  try {
233  if (field.make(writer, flags, diag)) {
234  ++fieldsWritten;
235  }
236  } catch (const Failure &) {
237  }
238  }
239  }
240  // write field count
241  const auto framingByteOffset = stream.tellp();
242  stream.seekp(fieldCountOffset);
243  writer.writeUInt32LE(fieldsWritten);
244  stream.seekp(framingByteOffset);
245  // write framing byte
246  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
247  stream.put(0x01);
248  }
249 }
250 
251 } // namespace TagParser
TAG_PARSER_EXPORT const char * composer()
Definition: matroskatagid.h:93
TAG_PARSER_EXPORT const char * language()
FieldMapBasedTagTraits< VorbisComment >::FieldType::IdentifierType IdentifierType
Definition: fieldbasedtag.h:36
TAG_PARSER_EXPORT const char * trackNumber()
TAG_PARSER_EXPORT const char * description()
uint64 streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
Definition: oggiterator.h:114
TAG_PARSER_EXPORT const char * diskNumber()
TAG_PARSER_EXPORT const char * lyricist()
TAG_PARSER_EXPORT const char * encoderSettings()
TAG_PARSER_EXPORT const char * genre()
TAG_PARSER_EXPORT const char * date()
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments...
STL namespace.
bool make(IoUtilities::BinaryWriter &writer, VorbisCommentFlags flags, Diagnostics &diag)
Writes the field to a stream using the specified writer.
KnownField
Specifies the field.
Definition: tag.h:40
TAG_PARSER_EXPORT const char * comment()
TAG_PARSER_EXPORT const char * partNumber()
Definition: matroskatagid.h:30
bool isEmpty() const
Returns an indication whether an value is assigned.
Definition: tagvalue.h:367
TAG_PARSER_EXPORT const char * cover()
Contains utility classes helping to read and write streams.
TAG_PARSER_EXPORT const char * artist()
Definition: matroskatagid.h:77
TAG_PARSER_EXPORT const char * encoder()
TAG_PARSER_EXPORT const char * album()
Definition: matroskatagid.h:81
TAG_PARSER_EXPORT const char * performer()
TAG_PARSER_EXPORT const char * title()
Definition: matroskatagid.h:39
TagValue & value()
Returns the value of the current TagField.
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Definition: exceptions.h:63