2015-09-06 19:57:33 +02:00
|
|
|
#include "./vorbiscomment.h"
|
|
|
|
#include "./vorbiscommentids.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-09-06 19:57:33 +02:00
|
|
|
#include "../ogg/oggiterator.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2018-03-05 17:49:29 +01:00
|
|
|
#include "../diagnostics.h"
|
2018-03-07 01:17:50 +01:00
|
|
|
#include "../exceptions.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
#include <c++utilities/io/binaryreader.h>
|
|
|
|
#include <c++utilities/io/binarywriter.h>
|
|
|
|
#include <c++utilities/io/copy.h>
|
|
|
|
|
|
|
|
#include <map>
|
2017-02-05 21:02:40 +01:00
|
|
|
#include <memory>
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace IoUtilities;
|
|
|
|
using namespace ConversionUtilities;
|
|
|
|
|
2018-03-06 23:09:15 +01:00
|
|
|
namespace TagParser {
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
2018-06-03 20:38:32 +02:00
|
|
|
* \class TagParser::VorbisComment
|
|
|
|
* \brief Implementation of TagParser::Tag for Vorbis comments.
|
2015-04-22 19:22:01 +02:00
|
|
|
*/
|
|
|
|
|
2015-07-27 23:13:03 +02:00
|
|
|
const TagValue &VorbisComment::value(KnownField field) const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (field) {
|
2015-07-27 23:13:03 +02:00
|
|
|
case KnownField::Vendor:
|
|
|
|
return vendor();
|
|
|
|
default:
|
2017-03-07 00:02:59 +01:00
|
|
|
return FieldMapBasedTag<VorbisComment>::value(field);
|
2015-07-27 23:13:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VorbisComment::setValue(KnownField field, const TagValue &value)
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (field) {
|
2015-07-27 23:13:03 +02:00
|
|
|
case KnownField::Vendor:
|
|
|
|
setVendor(value);
|
|
|
|
return true;
|
|
|
|
default:
|
2017-03-07 00:02:59 +01:00
|
|
|
return FieldMapBasedTag<VorbisComment>::setValue(field, value);
|
2015-07-27 23:13:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-07 17:16:17 +01:00
|
|
|
VorbisComment::IdentifierType VorbisComment::internallyGetFieldId(KnownField field) const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
using namespace VorbisCommentIds;
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (field) {
|
|
|
|
case KnownField::Album:
|
|
|
|
return album();
|
|
|
|
case KnownField::Artist:
|
|
|
|
return artist();
|
|
|
|
case KnownField::Comment:
|
|
|
|
return comment();
|
|
|
|
case KnownField::Cover:
|
|
|
|
return cover();
|
|
|
|
case KnownField::Year:
|
|
|
|
return date();
|
|
|
|
case KnownField::Title:
|
|
|
|
return title();
|
|
|
|
case KnownField::Genre:
|
|
|
|
return genre();
|
|
|
|
case KnownField::TrackPosition:
|
|
|
|
return trackNumber();
|
|
|
|
case KnownField::DiskPosition:
|
|
|
|
return diskNumber();
|
|
|
|
case KnownField::PartNumber:
|
|
|
|
return partNumber();
|
|
|
|
case KnownField::Composer:
|
|
|
|
return composer();
|
|
|
|
case KnownField::Encoder:
|
|
|
|
return encoder();
|
|
|
|
case KnownField::EncoderSettings:
|
|
|
|
return encoderSettings();
|
|
|
|
case KnownField::Description:
|
|
|
|
return description();
|
|
|
|
case KnownField::RecordLabel:
|
|
|
|
return label();
|
|
|
|
case KnownField::Performers:
|
|
|
|
return performer();
|
|
|
|
case KnownField::Language:
|
|
|
|
return language();
|
|
|
|
case KnownField::Lyricist:
|
|
|
|
return lyricist();
|
|
|
|
default:
|
|
|
|
return string();
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-07 17:16:17 +01:00
|
|
|
KnownField VorbisComment::internallyGetKnownField(const IdentifierType &id) const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
using namespace VorbisCommentIds;
|
2018-05-13 20:41:26 +02:00
|
|
|
static const map<string, KnownField, CaseInsensitiveStringComparer> fieldMap({ { album(), KnownField::Album }, { artist(), KnownField::Artist },
|
2018-03-07 01:17:50 +01:00
|
|
|
{ comment(), KnownField::Comment }, { cover(), KnownField::Cover }, { date(), KnownField::Year }, { title(), KnownField::Title },
|
|
|
|
{ genre(), KnownField::Genre }, { trackNumber(), KnownField::TrackPosition }, { diskNumber(), KnownField::DiskPosition },
|
|
|
|
{ partNumber(), KnownField::PartNumber }, { composer(), KnownField::Composer }, { encoder(), KnownField::Encoder },
|
|
|
|
{ encoderSettings(), KnownField::EncoderSettings }, { description(), KnownField::Description }, { label(), KnownField::RecordLabel },
|
|
|
|
{ performer(), KnownField::Performers }, { lyricist(), KnownField::Lyricist } });
|
2018-05-13 20:41:26 +02:00
|
|
|
const auto knownField(fieldMap.find(id));
|
|
|
|
return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2016-05-16 20:56:53 +02:00
|
|
|
* \brief Internal implementation for parsing.
|
2015-04-22 19:22:01 +02:00
|
|
|
*/
|
2018-03-07 01:17:50 +01:00
|
|
|
template <class StreamType> void VorbisComment::internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
// prepare parsing
|
|
|
|
static const string context("parsing Vorbis comment");
|
2016-05-16 20:56:53 +02:00
|
|
|
uint64 startOffset = static_cast<uint64>(stream.tellg());
|
2015-04-22 19:22:01 +02:00
|
|
|
try {
|
|
|
|
// read signature: 0x3 + "vorbis"
|
|
|
|
char sig[8];
|
2016-05-14 00:24:01 +02:00
|
|
|
bool skipSignature = flags & VorbisCommentFlags::NoSignature;
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!skipSignature) {
|
2016-05-16 20:56:53 +02:00
|
|
|
CHECK_MAX_SIZE(7);
|
|
|
|
stream.read(sig, 7);
|
2016-01-17 19:32:58 +01:00
|
|
|
skipSignature = (ConversionUtilities::BE::toUInt64(sig) & 0xffffffffffffff00u) == 0x03766F7262697300u;
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
if (skipSignature) {
|
2015-04-22 19:22:01 +02:00
|
|
|
// read vendor (length prefixed string)
|
|
|
|
{
|
2016-05-16 20:56:53 +02:00
|
|
|
CHECK_MAX_SIZE(4);
|
|
|
|
stream.read(sig, 4);
|
2016-05-14 00:24:01 +02:00
|
|
|
const auto vendorSize = LE::toUInt32(sig);
|
2018-03-07 01:17:50 +01:00
|
|
|
if (vendorSize <= maxSize) {
|
|
|
|
auto buff = make_unique<char[]>(vendorSize);
|
2016-05-16 20:56:53 +02:00
|
|
|
stream.read(buff.get(), vendorSize);
|
2016-05-14 00:24:01 +02:00
|
|
|
m_vendor.assignData(move(buff), vendorSize, TagDataType::Text, TagTextEncoding::Utf8);
|
|
|
|
// TODO: Is the vendor string actually UTF-8 (like the field values)?
|
2016-01-17 19:32:58 +01:00
|
|
|
} else {
|
2018-03-05 17:49:29 +01:00
|
|
|
diag.emplace_back(DiagLevel::Critical, "Vendor information is truncated.", context);
|
2016-01-17 19:32:58 +01:00
|
|
|
throw TruncatedDataException();
|
|
|
|
}
|
2016-05-16 20:56:53 +02:00
|
|
|
maxSize -= vendorSize;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
// read field count
|
2016-05-16 20:56:53 +02:00
|
|
|
CHECK_MAX_SIZE(4);
|
|
|
|
stream.read(sig, 4);
|
2015-04-22 19:22:01 +02:00
|
|
|
uint32 fieldCount = LE::toUInt32(sig);
|
2018-03-07 01:17:50 +01:00
|
|
|
for (uint32 i = 0; i < fieldCount; ++i) {
|
2015-04-22 19:22:01 +02:00
|
|
|
// read fields
|
2018-03-11 22:27:12 +01:00
|
|
|
VorbisCommentField field;
|
2015-04-22 19:22:01 +02:00
|
|
|
try {
|
2018-03-05 17:49:29 +01:00
|
|
|
field.parse(stream, maxSize, diag);
|
2018-03-11 22:27:12 +01:00
|
|
|
fields().emplace(field.id(), move(field));
|
2018-03-07 01:17:50 +01:00
|
|
|
} catch (const TruncatedDataException &) {
|
2015-04-22 19:22:01 +02:00
|
|
|
throw;
|
2018-03-07 01:17:50 +01:00
|
|
|
} catch (const Failure &) {
|
2015-04-22 19:22:01 +02:00
|
|
|
// nothing to do here since notifications will be added anyways
|
|
|
|
}
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!(flags & VorbisCommentFlags::NoFramingByte)) {
|
2016-05-16 20:56:53 +02:00
|
|
|
stream.ignore(); // skip framing byte
|
2016-05-14 00:24:01 +02:00
|
|
|
}
|
2016-05-16 20:56:53 +02:00
|
|
|
m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
|
2015-04-22 19:22:01 +02:00
|
|
|
} else {
|
2018-03-05 17:49:29 +01:00
|
|
|
diag.emplace_back(DiagLevel::Critical, "Signature is invalid.", context);
|
2015-04-22 19:22:01 +02:00
|
|
|
throw InvalidDataException();
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
} catch (const TruncatedDataException &) {
|
2016-05-16 20:56:53 +02:00
|
|
|
m_size = static_cast<uint32>(static_cast<uint64>(stream.tellg()) - startOffset);
|
2018-03-05 17:49:29 +01:00
|
|
|
diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
|
2015-04-22 19:22:01 +02:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-16 20:56:53 +02:00
|
|
|
/*!
|
|
|
|
* \brief Parses tag information using the specified OGG \a iterator.
|
|
|
|
*
|
|
|
|
* \throws Throws std::ios_base::failure when an IO error occurs.
|
2018-06-03 20:38:32 +02:00
|
|
|
* \throws Throws TagParser::Failure or a derived exception when a parsing
|
2016-05-16 20:56:53 +02:00
|
|
|
* error occurs.
|
|
|
|
*/
|
2018-03-05 17:49:29 +01:00
|
|
|
void VorbisComment::parse(OggIterator &iterator, VorbisCommentFlags flags, Diagnostics &diag)
|
2016-05-16 20:56:53 +02:00
|
|
|
{
|
2018-03-05 17:49:29 +01:00
|
|
|
internalParse(iterator, iterator.streamSize(), flags, diag);
|
2016-05-16 20:56:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Parses tag information using the specified OGG \a iterator.
|
|
|
|
*
|
|
|
|
* \throws Throws std::ios_base::failure when an IO error occurs.
|
2018-06-03 20:38:32 +02:00
|
|
|
* \throws Throws TagParser::Failure or a derived exception when a parsing
|
2016-05-16 20:56:53 +02:00
|
|
|
* error occurs.
|
|
|
|
*/
|
2018-03-05 17:49:29 +01:00
|
|
|
void VorbisComment::parse(istream &stream, uint64 maxSize, VorbisCommentFlags flags, Diagnostics &diag)
|
2016-05-16 20:56:53 +02:00
|
|
|
{
|
2018-03-05 17:49:29 +01:00
|
|
|
internalParse(stream, maxSize, flags, diag);
|
2016-05-16 20:56:53 +02:00
|
|
|
}
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
/*!
|
|
|
|
* \brief Writes tag information to the specified \a stream.
|
|
|
|
*
|
|
|
|
* \throws Throws std::ios_base::failure when an IO error occurs.
|
2018-06-03 20:38:32 +02:00
|
|
|
* \throws Throws TagParser::Failure or a derived exception when a making
|
2015-04-22 19:22:01 +02:00
|
|
|
* error occurs.
|
|
|
|
*/
|
2018-03-05 17:49:29 +01:00
|
|
|
void VorbisComment::make(std::ostream &stream, VorbisCommentFlags flags, Diagnostics &diag)
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
// prepare making
|
|
|
|
static const string context("making Vorbis comment");
|
2015-07-27 23:13:03 +02:00
|
|
|
string vendor;
|
|
|
|
try {
|
|
|
|
m_vendor.toString(vendor);
|
2018-03-07 01:17:50 +01:00
|
|
|
} catch (const ConversionException &) {
|
2018-03-05 17:49:29 +01:00
|
|
|
diag.emplace_back(DiagLevel::Warning, "Can not convert the assigned vendor to string.", context);
|
2015-07-27 23:13:03 +02:00
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
BinaryWriter writer(&stream);
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!(flags & VorbisCommentFlags::NoSignature)) {
|
2016-01-17 19:32:58 +01:00
|
|
|
// write signature
|
2018-03-07 01:17:50 +01:00
|
|
|
static const char sig[7] = { 0x03, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73 };
|
2016-01-17 19:32:58 +01:00
|
|
|
stream.write(sig, sizeof(sig));
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
// write vendor
|
2015-07-27 23:13:03 +02:00
|
|
|
writer.writeUInt32LE(vendor.size());
|
|
|
|
writer.writeString(vendor);
|
2016-05-16 20:56:53 +02:00
|
|
|
// write field count later
|
|
|
|
const auto fieldCountOffset = stream.tellp();
|
|
|
|
writer.writeUInt32LE(0);
|
2015-04-22 19:22:01 +02:00
|
|
|
// write fields
|
2016-05-16 20:56:53 +02:00
|
|
|
uint32 fieldsWritten = 0;
|
2018-03-07 01:17:50 +01:00
|
|
|
for (auto i : fields()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
VorbisCommentField &field = i.second;
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!field.value().isEmpty()) {
|
2015-08-16 23:39:42 +02:00
|
|
|
try {
|
2018-03-07 01:17:50 +01:00
|
|
|
if (field.make(writer, flags, diag)) {
|
2016-05-16 20:56:53 +02:00
|
|
|
++fieldsWritten;
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
} catch (const Failure &) {
|
2015-08-16 23:39:42 +02:00
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
}
|
2016-05-16 20:56:53 +02:00
|
|
|
// write field count
|
|
|
|
const auto framingByteOffset = stream.tellp();
|
|
|
|
stream.seekp(fieldCountOffset);
|
|
|
|
writer.writeUInt32LE(fieldsWritten);
|
|
|
|
stream.seekp(framingByteOffset);
|
2015-04-22 19:22:01 +02:00
|
|
|
// write framing byte
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!(flags & VorbisCommentFlags::NoFramingByte)) {
|
2016-05-14 00:24:01 +02:00
|
|
|
stream.put(0x01);
|
|
|
|
}
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
} // namespace TagParser
|