Tag Parser  7.0.3
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, CaseInsensitiveStringComparer> 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  const auto knownField(fieldMap.find(id));
103  return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
104 }
105 
109 template <class StreamType> void VorbisComment::internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
110 {
111  // prepare parsing
112  static const string context("parsing Vorbis comment");
113  uint64 startOffset = static_cast<uint64>(stream.tellg());
114  try {
115  // read signature: 0x3 + "vorbis"
116  char sig[8];
117  bool skipSignature = flags & VorbisCommentFlags::NoSignature;
118  if (!skipSignature) {
119  CHECK_MAX_SIZE(7);
120  stream.read(sig, 7);
121  skipSignature = (ConversionUtilities::BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
122  }
123  if (skipSignature) {
124  // read vendor (length prefixed string)
125  {
126  CHECK_MAX_SIZE(4);
127  stream.read(sig, 4);
128  const auto vendorSize = LE::toUInt32(sig);
129  if (vendorSize <= maxSize) {
130  auto buff = make_unique<char[]>(vendorSize);
131  stream.read(buff.get(), vendorSize);
132  m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
133  // TODO: Is the vendor string actually UTF-8 (like the field values)?
134  } else {
135  diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
136  throw TruncatedDataException();
137  }
138  maxSize -= vendorSize;
139  }
140  // read field count
141  CHECK_MAX_SIZE(4);
142  stream.read(sig, 4);
143  uint32 fieldCount = LE::toUInt32(sig);
144  for (uint32 i = 0; i < fieldCount; ++i) {
145  // read fields
146  VorbisCommentField field;
147  try {
148  field.parse(stream, maxSize, diag);
149  fields().emplace(field.id(), move(field));
150  } catch (const TruncatedDataException &) {
151  throw;
152  } catch (const Failure &) {
153  // nothing to do here since notifications will be added anyways
154  }
155  }
156  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
157  stream.ignore(); // skip framing byte
158  }
159  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
160  } else {
161  diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
162  throw InvalidDataException();
163  }
164  } catch (const TruncatedDataException &) {
165  m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
166  diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
167  throw;
168  }
169 }
170 
178 void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
179 {
180  internalParse(iterator, iterator.streamSize(), flags, diag);
181 }
182 
190 void VorbisComment::parse(istream &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
191 {
192  internalParse(stream, maxSize, flags, diag);
193 }
194 
202 void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
203 {
204  // prepare making
205  static const string context("making Vorbis comment");
206  string vendor;
207  try {
208  m_vendor.toString(vendor);
209  } catch (const ConversionException &) {
210  diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
211  }
212  BinaryWriter writer(&stream);
213  if (!(flags & VorbisCommentFlags::NoSignature)) {
214  // write signature
215  static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
216  stream.write(sig, sizeof(sig));
217  }
218  // write vendor
219  writer.writeUInt32LE(vendor.size());
220  writer.writeString(vendor);
221  // write field count later
222  const auto fieldCountOffset = stream.tellp();
223  writer.writeUInt32LE(0);
224  // write fields
225  uint32 fieldsWritten = 0;
226  for (auto i : fields()) {
227  VorbisCommentField &field = i.second;
228  if (!field.value().isEmpty()) {
229  try {
230  if (field.make(writer, flags, diag)) {
231  ++fieldsWritten;
232  }
233  } catch (const Failure &) {
234  }
235  }
236  }
237  // write field count
238  const auto framingByteOffset = stream.tellp();
239  stream.seekp(fieldCountOffset);
240  writer.writeUInt32LE(fieldsWritten);
241  stream.seekp(framingByteOffset);
242  // write framing byte
243  if (!(flags & VorbisCommentFlags::NoFramingByte)) {
244  stream.put(0x01);
245  }
246 }
247 
248 } // 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