tagparser/vorbis/vorbiscommentfield.cpp

120 lines
3.9 KiB
C++

#include "vorbiscommentfield.h"
#include "vorbiscommentids.h"
#include "../ogg/oggiterator.h"
#include "../id3/id3v2frame.h"
#include "../exceptions.h"
#include <c++utilities/io/binaryreader.h>
#include <c++utilities/io/binarywriter.h>
#include <c++utilities/conversion/binaryconversion.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/misc/memory.h>
using namespace std;
using namespace IoUtilities;
using namespace ConversionUtilities;
namespace Media {
/*!
* \class Media::VorbisCommentField
* \brief The VorbisCommentField class is used by VorbisComment to store the fields.
*/
/*!
* \brief Constructs a new Vorbis comment field.
*/
VorbisCommentField::VorbisCommentField()
{}
/*!
* \brief Constructs a new Vorbis comment with the specified \a id and \a value.
*/
VorbisCommentField::VorbisCommentField(const identifierType &id, const TagValue &value) :
TagField<VorbisCommentField>(id, value)
{}
/*!
* \brief Parses a field from the stream read using the specified \a reader.
*
* The position of the current character in the input stream is expected to be
* at the beginning of the field to be parsed.
*
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws Media::Failure or a derived exception when a parsing
* error occurs.
*/
void VorbisCommentField::parse(OggIterator &iterator)
{
static const string context("parsing Vorbis comment field");
char buff[4];
iterator.read(buff, 4);
if(uint32 size = LE::toUInt32(buff)) { // read size
// read data
unique_ptr<char[]> data = make_unique<char []>(size);
iterator.read(data.get(), size);
uint32 idSize = 0;
for(const char *i = data.get(), *end = data.get() + size; i != end && (*i) != '='; ++i, ++idSize)
;
// extract id
setId(string(data.get(), idSize));
if(!idSize) {
// empty field ID
addNotification(NotificationType::Critical, "The field ID is empty.", context);
throw InvalidDataException();
} else if(id() == VorbisCommentIds::cover()) {
// extract cover value
vector<char> buffer = decodeBase64(string(data.get() + idSize + 1, size - idSize - 1));
Id3v2FrameHelper helper(id(), *this);
byte type;
helper.parsePicture(buffer.data(), buffer.size(), value(), type);
setTypeInfo(type);
} else if(id().size() + 1 < size) {
// extract other values (as string)
setValue(string(data.get() + idSize + 1, size - idSize - 1));
}
}
}
/*!
* \brief Writes the field to a stream using the specified \a writer.
*
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws Media::Failure or a derived exception when a making
* error occurs.
*/
void VorbisCommentField::make(BinaryWriter &writer)
{
static const string context("making Vorbis comment field");
if(id().empty()) {
addNotification(NotificationType::Critical, "The field ID is empty.", context);
}
// try to convert value to string
try {
string valueString;
if(id() == VorbisCommentIds::cover()) {
// make cover
Id3v2FrameHelper helper(id(), *this);
vector<char> buffer;
helper.makePicture(buffer, value(), isTypeInfoAssigned() ? typeInfo() : 0);
valueString = encodeBase64(buffer);
} else {
// make normal string value
valueString = value().toString();
}
// write size
writer.writeUInt32LE(id().size() + 1 + valueString.size());
writer.writeString(id());
writer.writeChar('=');
writer.writeString(valueString);
} catch(ConversionException &) {
addNotification(NotificationType::Critical, "Assigned value can not be converted appropriately.", context);
throw InvalidDataException();
}
}
}