9#include <c++utilities/conversion/stringbuilder.h>
10#include <c++utilities/io/binaryreader.h>
11#include <c++utilities/io/binarywriter.h>
12#include <c++utilities/io/copy.h>
53 return std::string(album());
55 return std::string(artist());
57 return std::string(comment());
59 return std::string(cover());
61 return std::string(date());
63 return std::string(title());
65 return std::string(genre());
67 return std::string(trackNumber());
69 return std::string(diskNumber());
71 return std::string(partNumber());
73 return std::string(composer());
75 return std::string(encoder());
77 return std::string(encodedBy());
79 return std::string(encoderSettings());
81 return std::string(description());
83 return std::string(grouping());
85 return std::string(label());
87 return std::string(performer());
89 return std::string(language());
91 return std::string(lyricist());
93 return std::string(lyrics());
95 return std::string(albumArtist());
97 return std::string(conductor());
99 return std::string(copyright());
101 return std::string(license());
103 return std::string(director());
105 return std::string(isrc());
107 return std::string(rating());
109 return std::string(bpm());
111 return std::string(publisher());
113 return std::string(publisherWebpage());
115 return std::string(website());
117 return std::string(arranger());
119 return std::string();
127 static const std::map<std::string_view, KnownField, CaseInsensitiveStringComparer> fieldMap({
168void VorbisComment::extendPositionInSetField(std::string_view field, std::string_view totalField,
const std::string &diagContext,
Diagnostics &diag)
170 auto totalValues = std::vector<std::int32_t>();
171 auto fieldsIter =
fields().equal_range(std::string(totalField));
172 auto fieldsDist = std::distance(fieldsIter.first, fieldsIter.second);
176 totalValues.reserve(
static_cast<std::size_t
>(fieldsDist));
177 for (; fieldsIter.first != fieldsIter.second;) {
179 totalValues.emplace_back(fieldsIter.first->second.value().toInteger());
180 fields().erase(fieldsIter.first++);
181 }
catch (
const ConversionException &e) {
182 diag.emplace_back(
DiagLevel::Warning, argsToString(
"Unable to parse \"", totalField,
"\" as integer: ", e.what()), diagContext);
183 totalValues.emplace_back(0);
188 auto totalIter = totalValues.begin(), totalEnd = totalValues.end();
189 for (fieldsIter =
fields().equal_range(std::string(field)); fieldsIter.first != fieldsIter.second && totalIter != totalEnd;
190 ++fieldsIter.first, ++totalIter) {
191 auto &v = fieldsIter.first->second.value();
193 auto p = v.toPositionInSet();
194 if (p.total() && p.total() != *totalIter) {
196 argsToString(
"The \"", totalField,
"\" field value (", *totalIter,
") does not match \"", field,
"\" field value (", p.total(),
197 "). Discarding the former in favor of the latter."),
200 p.setTotal(*totalIter);
203 }
catch (
const ConversionException &e) {
205 argsToString(
"Unable to parse \"", field,
"\" as position in set for incorporating \"", totalField,
"\": ", e.what()), diagContext);
208 if (totalIter != totalEnd) {
210 DiagLevel::Warning, argsToString(
"Vorbis Comment contains more \"", totalField,
"\" fields than \"", field,
"\" fields."), diagContext);
212 for (; totalIter != totalEnd; ++totalIter) {
221void VorbisComment::convertTotalFields(
const std::string &diagContext,
Diagnostics &diag)
231template <
class StreamType>
232void VorbisComment::internalParse(StreamType &stream, std::uint64_t maxSize,
VorbisCommentFlags flags, std::uint64_t &padding,
Diagnostics &diag)
235 static const string context(
"parsing Vorbis comment");
236 const auto startOffset =
static_cast<std::uint64_t
>(stream.tellg());
241 if (!skipSignature) {
244 skipSignature = (BE::toInt<std::uint64_t>(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
251 const auto vendorSize = LE::toUInt32(sig);
252 if (vendorSize <= maxSize) {
253 auto buff = make_unique<char[]>(vendorSize);
254 stream.read(buff.get(), vendorSize);
261 maxSize -= vendorSize;
267 for (std::uint32_t i = 0; i <
fieldCount; ++i) {
269 VorbisCommentField field;
271 field.parse(stream, maxSize, diag);
272 fields().emplace(field.id(), std::move(field));
273 }
catch (
const TruncatedDataException &) {
275 }
catch (
const Failure &) {
282 m_size =
static_cast<std::uint64_t
>(stream.tellg()) - startOffset;
288 const auto [first, end] =
fields().equal_range(yearFieldId);
289 for (
auto i = first; i != end; ++i) {
290 fields().emplace(dateFieldId, std::move(i->second));
292 fields().erase(first, end);
298 }
catch (
const TruncatedDataException &) {
299 m_size =
static_cast<std::uint64_t
>(stream.tellg()) - startOffset;
305 if constexpr (std::is_same_v<std::decay_t<StreamType>, OggIterator>) {
306 auto bytesRemaining = std::uint64_t();
308 bytesRemaining = stream.remainingBytesInCurrentSegment();
309 if (stream.currentPage().isLastSegmentUnconcluded()) {
310 stream.nextSegment();
312 bytesRemaining += stream.remainingBytesInCurrentSegment();
316 if (bytesRemaining) {
317 diag.emplace_back(
DiagLevel::Information, argsToString(bytesRemaining,
" bytes left in last segment."), context);
318 padding += bytesRemaining;
323 convertTotalFields(context, diag);
336 auto padding = std::uint64_t();
337 internalParse(iterator, iterator.
streamSize(), flags, padding, diag);
349 internalParse(iterator, iterator.
streamSize(), flags, padding, diag);
361 auto padding = std::uint64_t();
362 internalParse(stream, maxSize, flags, padding, diag);
372 if (field.
make(writer, flags, diag)) {
375 }
catch (
const Failure &) {
391 static const string context(
"making Vorbis comment");
394 m_vendor.toString(
vendor);
395 }
catch (
const ConversionException &) {
396 diag.emplace_back(
DiagLevel::Warning,
"Can not convert the assigned vendor to string.", context);
398 BinaryWriter writer(&stream);
401 static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
402 stream.write(sig,
sizeof(sig));
405 writer.writeUInt32LE(
static_cast<std::uint32_t
>(
vendor.size()));
406 writer.writeString(
vendor);
408 const auto fieldCountOffset = stream.tellp();
409 writer.writeUInt32LE(0);
411 std::uint32_t fieldsWritten = 0;
413 for (
auto &i :
fields()) {
414 if (i.first != coverId) {
415 fieldsWritten += makeField(i.second, writer, flags, diag);
418 if (
const auto cover =
fields().find(coverId); cover !=
fields().end()) {
419 fieldsWritten += makeField(cover->second, writer, flags, diag);
422 const auto framingByteOffset = stream.tellp();
423 stream.seekp(fieldCountOffset);
424 writer.writeUInt32LE(fieldsWritten);
425 stream.seekp(framingByteOffset);
The Diagnostics class is a container for DiagMessage.
bool setValue(const IdentifierType &id, const TagValue &value)
Assigns the given value to the field with the specified id.
typename FieldMapBasedTagTraits< VorbisComment >::FieldType::IdentifierType IdentifierType
const TagValue & value(const IdentifierType &id) const
Returns the value of the field with the specified id.
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
KnownField knownField(const IdentifierType &id) const
InvalidDataException() noexcept
Constructs a new exception.
The OggIterator class helps iterating through all segments of an Ogg bitstream.
std::uint64_t streamSize() const
Returns the stream size (which has been specified when constructing the iterator).
constexpr PositionInSet(std::int32_t position=0, std::int32_t total=0)
Constructs a new Position in set of the specified element position and total element count.
TagValue & value()
Returns the value of the current TagField.
The TagValue class wraps values of different types.
bool isEmpty() const
Returns whether no or an empty value is assigned.
TagValue()
Constructs an empty TagValue.
virtual std::size_t fieldCount() const =0
Returns the number of present fields.
TruncatedDataException() noexcept
Constructs a new exception.
#define CHECK_MAX_SIZE(sizeDenotation)
Throws TruncatedDataException() if the specified sizeDenotation exceeds maxSize; otherwise maxSize is...
Contains all classes and functions of the TagInfo library.
KnownField
Specifies the field.
VorbisCommentFlags
The VorbisCommentFlags enum specifies flags which controls parsing and making of Vorbis comments.