Initial attachments support

This commit is contained in:
complexlogic
2023-10-18 17:58:56 -07:00
committed by Urs Fleisch
parent 6342f00e8b
commit 6d019a894c
30 changed files with 983 additions and 158 deletions

View File

@ -2,40 +2,42 @@
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskasimpletag.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "tstring.h"
#include "tutils.h"
#include "tbytevector.h"
#define GREEN_TEXT(s) "" s ""
#define PRINT_PRETTY(label, value) printf("" GREEN_TEXT(label) ": %s\n", value)
#define PRINT_PRETTY(label, value) printf(" " GREEN_TEXT(label) ": %s\n", value)
int main(int argc, char *argv[])
{
if (argc != 2) {
if(argc != 2) {
printf("Usage: matroskareader FILE\n");
return 1;
}
TagLib::Matroska::File file(argv[1]);
if (!file.isValid()) {
if(!file.isValid()) {
printf("File is not valid\n");
return 1;
}
auto tag = dynamic_cast<TagLib::Matroska::Tag*>(file.tag());
if (!tag) {
if(!tag) {
printf("File has no tag\n");
return 0;
}
const TagLib::Matroska::SimpleTagsList &list = tag->simpleTagsList();
printf("Found %i tags:\n\n", list.size());
printf("Found %u tag(s):\n", list.size());
for (TagLib::Matroska::SimpleTag *t : list) {
for(TagLib::Matroska::SimpleTag *t : list) {
PRINT_PRETTY("Tag Name", t->name().toCString(true));
TagLib::Matroska::SimpleTagString *tString = nullptr;
TagLib::Matroska::SimpleTagBinary *tBinary = nullptr;
if ((tString = dynamic_cast<TagLib::Matroska::SimpleTagString*>(t)))
if((tString = dynamic_cast<TagLib::Matroska::SimpleTagString*>(t)))
PRINT_PRETTY("Tag Value", tString->value().toCString(true));
else if ((tBinary = dynamic_cast<TagLib::Matroska::SimpleTagBinary*>(t)))
else if((tBinary = dynamic_cast<TagLib::Matroska::SimpleTagBinary*>(t)))
PRINT_PRETTY("Tag Value",
TagLib::Utils::formatString("Binary with size %i", tBinary->value().size()).toCString(false)
);
@ -50,5 +52,26 @@ int main(int argc, char *argv[])
printf("\n");
}
TagLib::Matroska::Attachments *attachments = file.attachments();
if(attachments) {
const TagLib::Matroska::Attachments::AttachedFileList &list = attachments->attachedFileList();
printf("Found %u attachment(s)\n", list.size());
for(auto attachedFile : list) {
PRINT_PRETTY("Filename", attachedFile->fileName().toCString(true));
const TagLib::String &description = attachedFile->description();
PRINT_PRETTY("Description", !description.isEmpty() ? description.toCString(true) : "None");
const TagLib::String &mediaType = attachedFile->mediaType();
PRINT_PRETTY("Media Type", !mediaType.isEmpty() ? mediaType.toCString(false) : "None");
PRINT_PRETTY("Data Size",
TagLib::Utils::formatString("%u byte(s)",attachedFile->data().size()).toCString(false)
);
PRINT_PRETTY("UID",
TagLib::Utils::formatString("%llu",attachedFile->uid()).toCString(false)
);
}
}
else
printf("File has no attachments\n");
return 0;
}

View File

@ -2,17 +2,20 @@
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskasimpletag.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "tfilestream.h"
#include "tstring.h"
#include "tutils.h"
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: matroskawriter FILE\n");
if(argc != 3) {
printf("Usage: matroskawriter FILE ARTWORK\n");
return 1;
}
TagLib::Matroska::File file(argv[1]);
if (!file.isValid()) {
if(!file.isValid()) {
printf("File is not valid\n");
return 1;
}
@ -31,6 +34,19 @@ int main(int argc, char *argv[])
simpleTag->setTargetTypeValue(TagLib::Matroska::SimpleTag::TargetTypeValue::Album);
simpleTag->setValue("Test Value 2");
tag->addSimpleTag(simpleTag);
tag->setTitle("Test title");
tag->setArtist("Test artist");
tag->setYear(1969);
TagLib::FileStream image(argv[2]);
auto data = image.readBlock(image.length());
auto attachments = file.attachments(true);
auto attachedFile = new TagLib::Matroska::AttachedFile();
attachedFile->setFileName("cover.jpg");
attachedFile->setMediaType("image/jpeg");
attachedFile->setData(data);
//attachedFile->setUID(5081000385627515072ull);
attachments->addAttachedFile(attachedFile);
file.save();

View File

@ -92,9 +92,12 @@ set(tag_PUBLIC_HDRS
toolkit/tpropertymap.h
toolkit/tdebuglistener.h
toolkit/tversionnumber.h
matroska/matroskaattachedfile.h
matroska/matroskaattachments.h
matroska/matroskafile.h
matroska/matroskatag.h
matroska/matroskasimpletag.h
matroska/matroskaelement.h
mpeg/mpegfile.h
mpeg/mpegproperties.h
mpeg/mpegheader.h
@ -227,8 +230,10 @@ if(WITH_SHORTEN)
endif()
set(tag_PRIVATE_HDRS
matroska/ebml/ebmlbinaryelement.h
matroska/ebml/ebmlelement.h
matroska/ebml/ebmlmasterelement.h
matroska/ebml/ebmlmkattachments.h
matroska/ebml/ebmlmksegment.h
matroska/ebml/ebmlmktags.h
matroska/ebml/ebmlstringelement.h
@ -239,14 +244,19 @@ set(tag_PRIVATE_HDRS
set(tag_HDRS ${tag_PUBLIC_HDRS} ${tag_PRIVATE_HDRS})
set(matroska_SRCS
matroska/matroskaattachedfile.cpp
matroska/matroskaattachments.cpp
matroska/matroskaelement.cpp
matroska/matroskafile.cpp
matroska/matroskasimpletag.cpp
matroska/matroskatag.cpp
)
set(ebml_SRCS
matroska/ebml/ebmlbinaryelement.cpp
matroska/ebml/ebmlelement.cpp
matroska/ebml/ebmlmasterelement.cpp
matroska/ebml/ebmlmkattachments.cpp
matroska/ebml/ebmlmksegment.cpp
matroska/ebml/ebmlmktags.cpp
matroska/ebml/ebmlstringelement.cpp

View File

@ -0,0 +1,46 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlbinaryelement.h"
#include "tfile.h"
#include "tbytevector.h"
#include "tdebug.h"
#include <string>
using namespace TagLib;
bool EBML::BinaryElement::read(TagLib::File &file)
{
value = file.readBlock(dataSize);
if(value.size() != dataSize) {
debug("Failed to read binary element");
return false;
}
return true;
}
ByteVector EBML::BinaryElement::render()
{
ByteVector buffer = renderId();
dataSize = value.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(value);
return buffer;
}

View File

@ -0,0 +1,54 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLBINARYELEMENT_H
#define TAGLIB_EBMLBINARYELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <cstdint>
#include "ebmlelement.h"
#include "ebmlutils.h"
#include "tbytevector.h"
namespace TagLib {
class File;
namespace EBML {
class BinaryElement : public Element
{
public:
BinaryElement(Id id, int sizeLength, offset_t dataSize)
: Element(id, sizeLength, dataSize)
{}
BinaryElement(Id id)
: Element(id, 0, 0)
{}
const ByteVector& getValue() const { return value; }
void setValue(const ByteVector &value) { this->value = value; }
bool read(File &file) override;
ByteVector render() override;
private:
ByteVector value;
};
}
}
#endif
#endif

View File

@ -20,8 +20,10 @@
#include "ebmlelement.h"
#include "ebmlmasterelement.h"
#include "ebmlbinaryelement.h"
#include "ebmlmksegment.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlutils.h"
@ -36,15 +38,16 @@ using namespace TagLib;
EBML::Element* EBML::Element::factory(File &file)
{
// Get the element ID
offset_t offset = file.tell();
Id id = readId(file);
if (!id) {
if(!id) {
debug("Failed to parse EMBL ElementID");
return nullptr;
}
// Get the size length and data length
const auto& [sizeLength, dataSize] = readVINT<offset_t>(file);
if (!sizeLength)
if(!sizeLength)
return nullptr;
// Return the subclass
@ -53,26 +56,37 @@ EBML::Element* EBML::Element::factory(File &file)
return new Element(id, sizeLength, dataSize);
case ElementIDs::MkSegment:
return new MkSegment(sizeLength, dataSize);
return new MkSegment(sizeLength, dataSize, offset);
case ElementIDs::MkTags:
return new MkTags(sizeLength, dataSize);
return new MkTags(sizeLength, dataSize, offset);
case ElementIDs::MkAttachments:
return new MkAttachments(sizeLength, dataSize, offset);
case ElementIDs::MkTag:
case ElementIDs::MkTagTargets:
case ElementIDs::MkSimpleTag:
return new MasterElement(id, sizeLength, dataSize);
case ElementIDs::MkAttachedFile:
return new MasterElement(id, sizeLength, dataSize, offset);
case ElementIDs::MkTagName:
case ElementIDs::MkTagString:
case ElementIDs::MkAttachedFileName:
case ElementIDs::MkAttachedFileDescription:
return new UTF8StringElement(id, sizeLength, dataSize);
case ElementIDs::MkTagLanguage:
case ElementIDs::MkAttachedFileMediaType:
return new Latin1StringElement(id, sizeLength, dataSize);
case ElementIDs::MkTagTargetTypeValue:
case ElementIDs::MkAttachedFileUID:
return new UIntElement(id, sizeLength, dataSize);
case ElementIDs::MkAttachedFileData:
return new BinaryElement(id, sizeLength, dataSize);
default:
return new Element(id, sizeLength, dataSize);
}
@ -83,16 +97,16 @@ EBML::Element* EBML::Element::factory(File &file)
EBML::Element::Id EBML::Element::readId(File &file)
{
auto buffer = file.readBlock(1);
if (buffer.size() != 1) {
if(buffer.size() != 1) {
debug("Failed to read VINT size");
return 0;
}
unsigned int nb_bytes = VINTSizeLength<4>(*buffer.begin());
if (!nb_bytes)
if(!nb_bytes)
return 0;
if (nb_bytes > 1)
if(nb_bytes > 1)
buffer.append(file.readBlock(nb_bytes - 1));
if (buffer.size() != nb_bytes) {
if(buffer.size() != nb_bytes) {
debug("Failed to read VINT data");
return 0;
}

View File

@ -24,7 +24,6 @@
#include "tfile.h"
#include "tutils.h"
//#include "ebmlutils.h"
#include "taglib.h"
namespace TagLib {
@ -37,7 +36,6 @@ namespace TagLib {
: id(id), sizeLength(sizeLength), dataSize(dataSize)
{}
virtual ~Element() = default;
virtual bool isMaster() const { return false; }
virtual bool read(File &file) {
skipData(file);
return true;
@ -59,22 +57,29 @@ namespace TagLib {
};
namespace ElementIDs {
inline constexpr Element::Id EBMLHeader = 0x1A45DFA3;
inline constexpr Element::Id MkSegment = 0x18538067;
inline constexpr Element::Id MkTags = 0x1254C367;
inline constexpr Element::Id MkTag = 0x7373;
inline constexpr Element::Id MkTagTargets = 0x63C0;
inline constexpr Element::Id MkTagTargetTypeValue = 0x68CA;
inline constexpr Element::Id MkSimpleTag = 0x67C8;
inline constexpr Element::Id MkTagName = 0x45A3;
inline constexpr Element::Id MkTagLanguage = 0x447A;
inline constexpr Element::Id MkTagString = 0x4487;
inline constexpr Element::Id MkTagsTagLanguage = 0x447A;
inline constexpr Element::Id MkTagsLanguageDefault = 0x4484;
inline constexpr Element::Id EBMLHeader = 0x1A45DFA3;
inline constexpr Element::Id MkSegment = 0x18538067;
inline constexpr Element::Id MkTags = 0x1254C367;
inline constexpr Element::Id MkTag = 0x7373;
inline constexpr Element::Id MkTagTargets = 0x63C0;
inline constexpr Element::Id MkTagTargetTypeValue = 0x68CA;
inline constexpr Element::Id MkSimpleTag = 0x67C8;
inline constexpr Element::Id MkTagName = 0x45A3;
inline constexpr Element::Id MkTagLanguage = 0x447A;
inline constexpr Element::Id MkTagString = 0x4487;
inline constexpr Element::Id MkTagsTagLanguage = 0x447A;
inline constexpr Element::Id MkTagsLanguageDefault = 0x4484;
inline constexpr Element::Id MkAttachments = 0x1941A469;
inline constexpr Element::Id MkAttachedFile = 0x61A7;
inline constexpr Element::Id MkAttachedFileDescription = 0x467E;
inline constexpr Element::Id MkAttachedFileName = 0x466E;
inline constexpr Element::Id MkAttachedFileMediaType = 0x4660;
inline constexpr Element::Id MkAttachedFileData = 0x465C;
inline constexpr Element::Id MkAttachedFileUID = 0x46AE;
}
}
}
#endif
#endif

View File

@ -30,7 +30,7 @@ using namespace TagLib;
EBML::MasterElement::~MasterElement()
{
for (auto element : elements)
for(auto element : elements)
delete element;
}
@ -38,8 +38,8 @@ bool EBML::MasterElement::read(File &file)
{
offset_t maxOffset = file.tell() + dataSize;
Element *element = nullptr;
while ((element = findNextElement(file, maxOffset))) {
if (!element->read(file))
while((element = findNextElement(file, maxOffset))) {
if(!element->read(file))
return false;
elements.append(element);
}
@ -50,7 +50,7 @@ ByteVector EBML::MasterElement::render()
{
ByteVector buffer = renderId();
ByteVector data;
for (auto element : elements)
for(auto element : elements)
data.append(element->render());
dataSize = data.size();
buffer.append(renderVINT(dataSize, 0));

View File

@ -33,15 +33,16 @@ namespace TagLib {
class MasterElement : public Element
{
public:
MasterElement(Id id, int sizeLength, offset_t dataSize)
: Element(id, sizeLength, dataSize)
MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset)
: Element(id, sizeLength, dataSize), offset(offset)
{}
MasterElement(Id id)
: Element(id, 0, 0)
: Element(id, 0, 0), offset(0)
{}
~MasterElement() override;
virtual bool isMaster() const override { return true; }
virtual bool read(File &file) override;
offset_t getOffset() const { return offset; }
offset_t getSize() const { return headSize() + dataSize; }
bool read(File &file) override;
ByteVector render() override;
void appendElement(Element *element) { elements.append(element); }
List<Element*>::Iterator begin () { return elements.begin(); }
@ -50,6 +51,7 @@ namespace TagLib {
List<Element*>::ConstIterator cend () const { return elements.cend(); }
protected:
offset_t offset;
List<Element*> elements;
};

View File

@ -0,0 +1,77 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmkattachments.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlbinaryelement.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "tdebug.h"
#include "tutils.h"
using namespace TagLib;
Matroska::Attachments* EBML::MkAttachments::parse()
{
auto attachments = new Matroska::Attachments();
attachments->setOffset(offset);
attachments->setSize(getSize());
for(auto element : elements) {
if(element->getId() != ElementIDs::MkAttachedFile)
continue;
const String *filename = nullptr;
const String *description = nullptr;
const String *mediaType = nullptr;
const ByteVector *data = nullptr;
Matroska::AttachedFile::UID uid = 0;
auto attachedFile = static_cast<MasterElement*>(element);
for(auto attachedFileChild : *attachedFile) {
Id id = attachedFileChild->getId();
if(id == ElementIDs::MkAttachedFileName)
filename = &(static_cast<UTF8StringElement*>(attachedFileChild)->getValue());
else if(id == ElementIDs::MkAttachedFileData)
data = &(static_cast<BinaryElement*>(attachedFileChild)->getValue());
else if(id == ElementIDs::MkAttachedFileDescription)
description = &(static_cast<UTF8StringElement*>(attachedFileChild)->getValue());
else if(id == ElementIDs::MkAttachedFileMediaType)
mediaType = &(static_cast<Latin1StringElement*>(attachedFileChild)->getValue());
else if(id == ElementIDs::MkAttachedFileUID)
uid = static_cast<UIntElement*>(attachedFileChild)->getValue();
}
if(!(filename && data))
continue;
auto file = new Matroska::AttachedFile();
file->setFileName(*filename);
file->setData(*data);
if(description)
file->setDescription(*description);
if(mediaType)
file->setMediaType(*mediaType);
if(uid)
file->setUID(uid);
attachments->addAttachedFile(file);
}
return attachments;
}

View File

@ -0,0 +1,49 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlmasterelement.h"
#include "ebmlutils.h"
#include "taglib.h"
#ifndef TAGLIB_EBMLMKATTACHMENTS_H
#define TAGLIB_EBMLMKATTACHMENTS_H
#ifndef DO_NOT_DOCUMENT
namespace TagLib {
namespace Matroska {
class Attachments;
}
namespace EBML {
class MkAttachments : public MasterElement
{
public:
MkAttachments(int sizeLength, offset_t dataSize, offset_t offset)
: MasterElement(ElementIDs::MkAttachments, sizeLength, dataSize, offset)
{}
MkAttachments()
: MasterElement(ElementIDs::MkAttachments, 0, 0, 0)
{}
Matroska::Attachments* parse();
};
}
}
#endif
#endif

View File

@ -20,9 +20,11 @@
#include "ebmlmksegment.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
#include "ebmlutils.h"
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskaattachments.h"
#include "tutils.h"
#include "tbytevector.h"
#include "tdebug.h"
@ -32,26 +34,40 @@ using namespace TagLib;
EBML::MkSegment::~MkSegment()
{
delete tags;
delete attachments;
}
bool EBML::MkSegment::read(File &file)
{
offset_t maxOffset = file.tell() + dataSize;
tags = static_cast<MkTags*>(findElement(file, ElementIDs::MkTags, maxOffset));
if (tags) {
offset_t tagsHeadSize = tags->headSize();
tagsOffset = file.tell() - tagsHeadSize;
tagsOriginalSize = tagsHeadSize + tags->getDataSize();
if (!tags->read(file))
return false;
EBML::Element *element = nullptr;
while((element = findNextElement(file, maxOffset))) {
Id id = element->getId();
if(id == ElementIDs::MkTags) {
tags = static_cast<MkTags*>(element);
if(!tags->read(file))
return false;
}
else if(id == ElementIDs::MkAttachments) {
attachments = static_cast<MkAttachments*>(element);
if(!attachments->read(file))
return false;
}
else {
element->skipData(file);
delete element;
}
}
return true;
}
std::tuple<Matroska::Tag*, offset_t, offset_t> EBML::MkSegment::parseTag()
Matroska::Tag* EBML::MkSegment::parseTag()
{
if (tags)
return {tags->parse(), tagsOffset, tagsOriginalSize};
else
return {nullptr, 0, 0};
return tags ? tags->parse() : nullptr;
}
Matroska::Attachments* EBML::MkSegment::parseAttachments()
{
return attachments ? attachments->parse() : nullptr;
}

View File

@ -29,24 +29,25 @@
namespace TagLib {
namespace Matroska {
class Tag;
class Attachments;
}
namespace EBML {
class Tags;
class MkTags;
class MkAttachments;
class MkSegment : public MasterElement
{
public:
MkSegment(int sizeLength, offset_t dataSize)
: MasterElement(ElementIDs::MkSegment, sizeLength, dataSize)
MkSegment(int sizeLength, offset_t dataSize, offset_t offset)
: MasterElement(ElementIDs::MkSegment, sizeLength, dataSize, offset)
{}
~MkSegment() override;
bool read(File &file) override;
std::tuple<Matroska::Tag*, offset_t, offset_t> parseTag();
Matroska::Tag* parseTag();
Matroska::Attachments* parseAttachments();
private:
MkTags *tags = nullptr;
offset_t tagsOffset = 0;
offset_t tagsOriginalSize = 0;
MkAttachments *attachments = nullptr;
};
}

View File

@ -34,30 +34,32 @@ using namespace TagLib;
Matroska::Tag* EBML::MkTags::parse()
{
auto mTag = new Matroska::Tag();
mTag->setOffset(offset);
mTag->setSize(getSize());
// Loop through each <Tag> element
for (auto tagsChild : elements) {
if (tagsChild->getId() != ElementIDs::MkTag)
for(auto tagsChild : elements) {
if(tagsChild->getId() != ElementIDs::MkTag)
continue;
auto tag = static_cast<MasterElement*>(tagsChild);
List<MasterElement*> simpleTags;
MasterElement *targets = nullptr;
// Identify the <Targets> element and the <SimpleTag> elements
for (auto tagChild : *tag) {
for(auto tagChild : *tag) {
Id tagChildId = tagChild->getId();
if (!targets && tagChildId == ElementIDs::MkTagTargets)
if(!targets && tagChildId == ElementIDs::MkTagTargets)
targets = static_cast<MasterElement*>(tagChild);
else if (tagChildId == ElementIDs::MkSimpleTag)
else if(tagChildId == ElementIDs::MkSimpleTag)
simpleTags.append(static_cast<MasterElement*>(tagChild));
}
// Parse the <Targets> element
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
if (targets) {
for (auto targetsChild : *targets) {
if(targets) {
for(auto targetsChild : *targets) {
Id id = targetsChild->getId();
if (id == ElementIDs::MkTagTargetTypeValue
if(id == ElementIDs::MkTagTargetTypeValue
&& targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None) {
targetTypeValue = static_cast<Matroska::SimpleTag::TargetTypeValue>(
static_cast<UIntElement*>(targetsChild)->getValue()
@ -67,43 +69,43 @@ Matroska::Tag* EBML::MkTags::parse()
}
// Parse each <SimpleTag>
for (auto simpleTag : simpleTags) {
for(auto simpleTag : simpleTags) {
const String *tagName = nullptr;
const String *tagValueString = nullptr;
const ByteVector *tagValueBinary = nullptr;
const String *language = nullptr;
bool defaultLanguageFlag = true;
for (auto simpleTagChild : *simpleTag) {
for(auto simpleTagChild : *simpleTag) {
Id id = simpleTagChild->getId();
if (id == ElementIDs::MkTagName && !tagName)
if(id == ElementIDs::MkTagName && !tagName)
tagName = &(static_cast<UTF8StringElement*>(simpleTagChild)->getValue());
else if (id == ElementIDs::MkTagString && !tagValueString)
else if(id == ElementIDs::MkTagString && !tagValueString)
tagValueString = &(static_cast<UTF8StringElement*>(simpleTagChild)->getValue());
else if (id == ElementIDs::MkTagsTagLanguage && !language)
else if(id == ElementIDs::MkTagsTagLanguage && !language)
language = &(static_cast<Latin1StringElement*>(simpleTagChild)->getValue());
else if (id == ElementIDs::MkTagsLanguageDefault)
else if(id == ElementIDs::MkTagsLanguageDefault)
defaultLanguageFlag = static_cast<UIntElement*>(simpleTagChild)->getValue() ? true : false;
}
if (!tagName || (tagValueString && tagValueBinary) || (!tagValueString && !tagValueBinary))
if(!tagName || (tagValueString && tagValueBinary) || (!tagValueString && !tagValueBinary))
continue;
// Create a Simple Tag object and add it to the Tag object
Matroska::SimpleTag *sTag = nullptr;
if (tagValueString) {
if(tagValueString) {
auto sTagString = new Matroska::SimpleTagString();
sTagString->setTargetTypeValue(targetTypeValue);
sTagString->setValue(*tagValueString);
sTag = sTagString;
}
else if (tagValueBinary) {
else if(tagValueBinary) {
auto sTagBinary = new Matroska::SimpleTagBinary();
sTagBinary->setTargetTypeValue(targetTypeValue);
sTagBinary->setValue(*tagValueBinary);
sTag = sTagBinary;
}
sTag->setName(*tagName);
if (language)
if(language)
sTag->setLanguage(*language);
sTag->setDefaultLanguageFlag(defaultLanguageFlag);
mTag->addSimpleTag(sTag);

View File

@ -35,11 +35,11 @@ namespace TagLib {
class MkTags : public MasterElement
{
public:
MkTags(int sizeLength, offset_t dataSize)
: MasterElement(ElementIDs::MkTags, sizeLength, dataSize)
MkTags(int sizeLength, offset_t dataSize, offset_t offset)
: MasterElement(ElementIDs::MkTags, sizeLength, dataSize, offset)
{}
MkTags()
: MasterElement(ElementIDs::MkTags, 0, 0)
: MasterElement(ElementIDs::MkTags, 0, 0, 0)
{}
//virtual void read(File &file) override;
Matroska::Tag* parse();

View File

@ -31,15 +31,15 @@ template<String::Type t>
bool EBML::StringElement<t>::read(TagLib::File &file)
{
ByteVector buffer = file.readBlock(dataSize);
if (buffer.size() != dataSize) {
if(buffer.size() != dataSize) {
debug("Failed to read string");
return false;
}
// The EBML strings aren't supposed to be null-terminated,
// but we'll check for it and stip the null terminator if found
// but we'll check for it and strip the null terminator if found
int nullByte = buffer.find('\0');
if (nullByte >= 0)
if(nullByte >= 0)
buffer = ByteVector(buffer.data(), nullByte);
value = String(buffer, t);
return true;

View File

@ -29,22 +29,42 @@ using namespace TagLib;
bool EBML::UIntElement::read(TagLib::File &file)
{
ByteVector buffer = file.readBlock(dataSize);
if (buffer.size() != dataSize) {
if(buffer.size() != dataSize) {
debug("Failed to read EBML Uint element");
return false;
}
value = buffer.toLongLong(true);
value = buffer.toULongLong(true);
return true;
}
ByteVector EBML::UIntElement::render()
{
int dataSize = 0;
if(value <= 0xFFull)
dataSize = 1;
else if(value <= 0xFFFFull)
dataSize = 2;
else if(value <= 0xFFFFFFull)
dataSize = 3;
else if(value <= 0xFFFFFFFFull)
dataSize = 4;
else if(value <= 0xFFFFFFFFFFull)
dataSize = 5;
else if(value <= 0xFFFFFFFFFFFFull)
dataSize = 6;
else if(value <= 0xFFFFFFFFFFFFFFull)
dataSize = 7;
else if(value <= 0xFFFFFFFFFFFFFFFFull)
dataSize = 8;
ByteVector buffer = renderId();
dataSize = minSize(value);
//dataSize = minSize(value);
buffer.append(renderVINT(dataSize, 0));
uint64_t value = this->value;
unsigned long long value = this->value;
//debug(Utils::formatString("Writing %llu", value));
static const auto byteOrder = Utils::systemByteOrder();
if (byteOrder == Utils::LittleEndian)
if(byteOrder == Utils::LittleEndian)
value = Utils::byteSwap((unsigned long long) value);
buffer.append(ByteVector((char*) &value + (sizeof(value) - dataSize), dataSize));

View File

@ -38,13 +38,13 @@ namespace TagLib {
UIntElement(Id id)
: UIntElement(id, 0, 0)
{}
unsigned int getValue() const { return value; }
void setValue(unsigned int value) { this->value = value; }
unsigned long long getValue() const { return value; }
void setValue(unsigned long long value) { this->value = value; }
bool read(File &file) override;
ByteVector render() override;
private:
uint64_t value = 0;
unsigned long long value = 0;
//protected:

View File

@ -18,6 +18,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <random>
#include "ebmlutils.h"
#include "ebmlelement.h"
#include "tbytevector.h"
@ -32,9 +33,9 @@ using namespace TagLib;
EBML::Element* EBML::findElement(File &file, EBML::Element::Id id, offset_t maxOffset)
{
Element *element = nullptr;
while (file.tell() < maxOffset) {
while(file.tell() < maxOffset) {
element = Element::factory(file);
if (!element || element->getId() == id)
if(!element || element->getId() == id)
return element;
element->skipData(file);
delete element;
@ -53,15 +54,15 @@ std::pair<int, T> EBML::readVINT(File &file)
{
static_assert(sizeof(T) == 8);
auto buffer = file.readBlock(1);
if (buffer.size() != 1) {
if(buffer.size() != 1) {
debug("Failed to read VINT size");
return {0, 0};
}
unsigned int nb_bytes = VINTSizeLength<8>(*buffer.begin());
if (!nb_bytes)
if(!nb_bytes)
return {0, 0};
if (nb_bytes > 1)
if(nb_bytes > 1)
buffer.append(file.readBlock(nb_bytes - 1));
int bits_to_shift = (sizeof(T) * 8) - (7 * nb_bytes);
offset_t mask = 0xFFFFFFFFFFFFFFFF >> bits_to_shift;
@ -75,11 +76,11 @@ namespace TagLib::EBML {
template<typename T>
std::pair<int, T> EBML::parseVINT(const ByteVector &buffer)
{
if (buffer.isEmpty())
if(buffer.isEmpty())
return {0, 0};
unsigned int numBytes = VINTSizeLength<8>(*buffer.begin());
if (!numBytes)
if(!numBytes)
return {0, 0};
int bits_to_shift = (sizeof(T) * 8) - (7 * numBytes);
@ -96,7 +97,15 @@ ByteVector EBML::renderVINT(uint64_t number, int minSizeLength)
int numBytes = std::max(minSizeLength, minSize(number));
number |= (1ULL << (numBytes * 7));
static const auto byteOrder = Utils::systemByteOrder();
if (byteOrder == Utils::LittleEndian)
if(byteOrder == Utils::LittleEndian)
number = Utils::byteSwap(static_cast<unsigned long long>(number));
return ByteVector((char*) &number + (sizeof(number) - numBytes), numBytes);
}
unsigned long long EBML::randomUID()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<unsigned long long> distribution;
return distribution(generator);
}

View File

@ -52,24 +52,23 @@ namespace TagLib {
constexpr unsigned int VINTSizeLength(uint8_t firstByte)
{
static_assert(maxSizeLength >= 1 && maxSizeLength <= 8);
if (!firstByte) {
if(!firstByte) {
debug("VINT with greater than 8 bytes not allowed");
return 0;
}
uint8_t mask = 0b10000000;
unsigned int numBytes = 1;
while (!(mask & firstByte)) {
while(!(mask & firstByte)) {
numBytes++;
mask >>= 1;
}
if (numBytes > maxSizeLength) {
if(numBytes > maxSizeLength) {
debug(Utils::formatString("VINT size length exceeds %i bytes", maxSizeLength));
return 0;
}
return numBytes;
}
//Id readId(File &file);
template<typename T>
std::pair<int, T> readVINT(File &file);
template<typename T>
@ -77,18 +76,19 @@ namespace TagLib {
Element* findElement(File &file, Element::Id id, offset_t maxLength);
Element* findNextElement(File &file, offset_t maxOffset);
ByteVector renderVINT(uint64_t number, int minSizeLength);
unsigned long long randomUID();
constexpr int minSize(uint64_t data)
{
if (data <= 0x7Fu)
if(data <= 0x7Fu)
return 1;
else if (data <= 0x3FFFu)
else if(data <= 0x3FFFu)
return 2;
else if (data <= 0x1FFFFFu)
else if(data <= 0x1FFFFFu)
return 3;
else if (data <= 0xFFFFFFFu)
else if(data <= 0xFFFFFFFu)
return 4;
else if (data <= 0x7FFFFFFFFu)
else if(data <= 0x7FFFFFFFFu)
return 5;
else
return 0;
@ -96,11 +96,11 @@ namespace TagLib {
constexpr int idSize(Element::Id id)
{
if (id <= 0xFF)
if(id <= 0xFF)
return 1;
else if (id <= 0xFFFF)
else if(id <= 0xFFFF)
return 2;
else if (id <= 0xFFFFFF)
else if(id <= 0xFFFFFF)
return 3;
else
return 4;

View File

@ -0,0 +1,77 @@
#include <memory>
#include "matroskaattachedfile.h"
#include "tstring.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::AttachedFile::AttachedFilePrivate
{
public:
AttachedFilePrivate() {}
~AttachedFilePrivate() = default;
AttachedFilePrivate(const AttachedFilePrivate &) = delete;
AttachedFilePrivate &operator=(const AttachedFilePrivate &) = delete;
String fileName;
String description;
String mediaType;
ByteVector data;
UID uid = 0;
};
Matroska::AttachedFile::AttachedFile()
: d(std::make_unique<AttachedFilePrivate>())
{
}
Matroska::AttachedFile::~AttachedFile() = default;
void Matroska::AttachedFile::setFileName(const String &fileName)
{
d->fileName = fileName;
}
const String& Matroska::AttachedFile::fileName() const
{
return d->fileName;
}
void Matroska::AttachedFile::setDescription(const String &description)
{
d->description = description;
}
const String& Matroska::AttachedFile::description() const
{
return d->description;
}
void Matroska::AttachedFile::setMediaType(const String &mediaType)
{
d->mediaType = mediaType;
}
const String& Matroska::AttachedFile::mediaType() const
{
return d->mediaType;
}
void Matroska::AttachedFile::setData(const ByteVector &data)
{
d->data = data;
}
const ByteVector& Matroska::AttachedFile::data() const
{
return d->data;
}
void Matroska::AttachedFile::setUID(UID uid)
{
d->uid = uid;
}
Matroska::AttachedFile::UID Matroska::AttachedFile::uid() const
{
return d->uid;
}

View File

@ -0,0 +1,57 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAS_MATROSKAATTACHEDFILE_H
#define HAS_MATROSKAATTACHEDFILE_H
#include "taglib_export.h"
namespace TagLib {
class String;
class ByteVector;
namespace Matroska {
class TAGLIB_EXPORT AttachedFile
{
public:
using UID = unsigned long long;
AttachedFile();
~AttachedFile();
void setFileName(const String &fileName);
const String& fileName() const;
void setDescription(const String &description);
const String& description() const;
void setMediaType(const String &mediaType);
const String& mediaType() const;
void setData(const ByteVector &data);
const ByteVector& data() const;
void setUID(UID uid);
UID uid() const;
private:
class AttachedFilePrivate;
std::unique_ptr<AttachedFilePrivate> d;
};
}
}
#endif

View File

@ -0,0 +1,97 @@
#include <memory>
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "ebmlmkattachments.h"
#include "ebmlmasterelement.h"
#include "ebmlstringelement.h"
#include "ebmlbinaryelement.h"
#include "ebmluintelement.h"
#include "tlist.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::Attachments::AttachmentsPrivate
{
public:
AttachmentsPrivate() {}
~AttachmentsPrivate() = default;
AttachmentsPrivate(const AttachmentsPrivate &) = delete;
AttachmentsPrivate &operator=(const AttachmentsPrivate &) = delete;
List<AttachedFile*> files;
};
Matroska::Attachments::Attachments()
: d(std::make_unique<AttachmentsPrivate>())
{
d->files.setAutoDelete(true);
}
Matroska::Attachments::~Attachments() = default;
void Matroska::Attachments::addAttachedFile(AttachedFile *file)
{
d->files.append(file);
}
void Matroska::Attachments::removeAttachedFile(AttachedFile *file)
{
auto it = d->files.find(file);
if(it != d->files.end()) {
delete *it;
d->files.erase(it);
}
}
void Matroska::Attachments::clear()
{
d->files.clear();
}
const Matroska::Attachments::AttachedFileList& Matroska::Attachments::attachedFileList() const
{
return d->files;
}
ByteVector Matroska::Attachments::render()
{
EBML::MkAttachments attachments;
for(const auto attachedFile : d->files) {
auto attachedFileElement = new EBML::MasterElement(EBML::ElementIDs::MkAttachedFile);
// Filename
auto fileNameElement = new EBML::UTF8StringElement(EBML::ElementIDs::MkAttachedFileName);
fileNameElement->setValue(attachedFile->fileName());
attachedFileElement->appendElement(fileNameElement);
// Media/MIME type
auto mediaTypeElement = new EBML::Latin1StringElement(EBML::ElementIDs::MkAttachedFileMediaType);
mediaTypeElement->setValue(attachedFile->mediaType());
attachedFileElement->appendElement(mediaTypeElement);
// Description
const String &description = attachedFile->description();
if(!description.isEmpty()) {
auto descriptionElement = new EBML::UTF8StringElement(EBML::ElementIDs::MkAttachedFileDescription);
descriptionElement->setValue(description);
attachedFileElement->appendElement(descriptionElement);
}
// Data
auto dataElement = new EBML::BinaryElement(EBML::ElementIDs::MkAttachedFileData);
dataElement->setValue(attachedFile->data());
attachedFileElement->appendElement(dataElement);
// UID
auto uidElement = new EBML::UIntElement(EBML::ElementIDs::MkAttachedFileUID);
AttachedFile::UID uid = attachedFile->uid();
if(!uid)
uid = EBML::randomUID();
uidElement->setValue(uid);
attachedFileElement->appendElement(uidElement);
attachments.appendElement(attachedFileElement);
}
return attachments.render();
}

View File

@ -0,0 +1,60 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAS_MATROSKAATTACHMENTS_H
#define HAS_MATROSKAATTACHMENTS_H
#include <memory>
#include "taglib_export.h"
#include "tlist.h"
#include "matroskaelement.h"
namespace TagLib {
namespace EBML {
class MkAttachments;
}
namespace Matroska {
class AttachedFile;
class File;
class TAGLIB_EXPORT Attachments : private Element
{
public:
using AttachedFileList = List<AttachedFile*>;
Attachments();
virtual ~Attachments();
void addAttachedFile(AttachedFile *file);
void removeAttachedFile(AttachedFile *file);
void clear();
const AttachedFileList& attachedFileList() const;
ByteVector render() override;
private:
friend class EBML::MkAttachments;
friend class Matroska::File;
class AttachmentsPrivate;
std::unique_ptr<AttachmentsPrivate> d;
};
}
}
#endif

View File

@ -0,0 +1,44 @@
#include <memory>
#include "matroskaelement.h"
#include "tlist.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::Element::ElementPrivate
{
public:
ElementPrivate() {}
~ElementPrivate() = default;
ElementPrivate(const ElementPrivate &) = delete;
ElementPrivate &operator=(const ElementPrivate &) = delete;
offset_t size = 0;
offset_t offset = 0;
};
Matroska::Element::Element()
: e(std::make_unique<ElementPrivate>())
{
}
Matroska::Element::~Element() = default;
offset_t Matroska::Element::size() const
{
return e->size;
}
offset_t Matroska::Element::offset() const
{
return e->offset;
}
void Matroska::Element::setOffset(offset_t offset)
{
e->offset = offset;
}
void Matroska::Element::setSize(offset_t size)
{
e->size = size;
}

View File

@ -0,0 +1,51 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAS_MATROSKAELEMENT_H
#define HAS_MATROSKAELEMENT_H
#include <memory>
#include "taglib_export.h"
#include "tutils.h"
#include "tbytevector.h"
namespace TagLib {
namespace Matroska {
class TAGLIB_EXPORT Element
{
public:
Element();
virtual ~Element();
virtual ByteVector render() = 0;
offset_t size() const;
offset_t offset() const;
void setOffset(offset_t offset);
void setSize(offset_t size);
private:
class ElementPrivate;
std::unique_ptr<ElementPrivate> e;
};
}
}
#endif

View File

@ -20,6 +20,7 @@
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskaattachments.h"
#include "ebmlutils.h"
#include "ebmlelement.h"
#include "ebmlmksegment.h"
@ -28,6 +29,8 @@
#include "tutils.h"
#include <memory>
#include <algorithm>
#include <vector>
using namespace TagLib;
@ -35,29 +38,27 @@ class Matroska::File::FilePrivate
{
public:
FilePrivate() {}
~FilePrivate()
{
delete tag;
delete attachments;
}
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
Matroska::Tag *tag = nullptr;
offset_t tagsOffset = 0;
offset_t tagsOriginalSize = 0;
Matroska::Attachments *attachments = nullptr;
offset_t segmentSizeOffset = 0;
offset_t segmentSizeLength = 0;
offset_t segmentDataSize = 0;
//Properties *properties = nullptr;
};
Matroska::File::File(FileName file, bool readProperties)
: TagLib::File(file),
d(std::make_unique<FilePrivate>())
{
if (!isOpen()) {
if(!isOpen()) {
debug("Failed to open matroska file");
setValid(false);
return;
@ -68,7 +69,7 @@ Matroska::File::File(IOStream *stream, bool readProperties)
: TagLib::File(stream),
d(std::make_unique<FilePrivate>())
{
if (!isOpen()) {
if(!isOpen()) {
debug("Failed to open matroska file");
setValid(false);
return;
@ -84,22 +85,33 @@ TagLib::Tag* Matroska::File::tag() const
Matroska::Tag* Matroska::File::tag(bool create) const
{
if (d->tag)
if(d->tag)
return d->tag;
else {
if (create)
if(create)
d->tag = new Matroska::Tag();
return d->tag;
}
}
Matroska::Attachments* Matroska::File::attachments(bool create) const
{
if(d->attachments)
return d->attachments;
else {
if(create)
d->attachments = new Attachments();
return d->attachments;
}
}
void Matroska::File::read(bool readProperties)
{
offset_t fileLength = length();
// Find the EBML Header
std::unique_ptr<EBML::Element> head(EBML::Element::factory(*this));
if (!head || head->getId() != EBML::ElementIDs::EBMLHeader) {
if(!head || head->getId() != EBML::ElementIDs::EBMLHeader) {
debug("Failed to find EBML head");
setValid(false);
return;
@ -112,7 +124,7 @@ void Matroska::File::read(bool readProperties)
EBML::findElement(*this, EBML::ElementIDs::MkSegment, fileLength - tell())
)
);
if (!segment) {
if(!segment) {
debug("Failed to find Matroska segment");
setValid(false);
return;
@ -122,32 +134,103 @@ void Matroska::File::read(bool readProperties)
d->segmentDataSize = segment->getDataSize();
// Read the segment into memory from file
if (!segment->read(*this)) {
if(!segment->read(*this)) {
debug("Failed to read segment");
setValid(false);
return;
}
// Parse the tag
const auto& [tag, tagsOffset, tagsOriginalSize] = segment->parseTag();
d->tag = tag;
d->tagsOffset = tagsOffset;
d->tagsOriginalSize = tagsOriginalSize;
d->tag = segment->parseTag();
// Parse the attachments
d->attachments = segment->parseAttachments();
setValid(true);
}
bool Matroska::File::save()
{
if (d->tag) {
std::vector<Element*> renderListExisting;
std::vector<Element*> renderListNew;
offset_t newSegmentDataSize = d->segmentDataSize;
if(d->tag) {
if(d->tag->size())
renderListExisting.push_back(d->tag);
else
renderListNew.push_back(d->tag);
}
if(d->attachments) {
//d->attachments->setOffset(d->tag->offset());
//renderListExisting.push_back(d->attachments);
if(d->attachments->size())
renderListExisting.push_back(d->attachments);
else
renderListNew.push_back(d->attachments);
}
// Render from end to beginning so we don't have to shift
// the file offsets
std::sort(renderListExisting.begin(),
renderListExisting.end(),
[](auto a, auto b) { return a->offset() > b->offset(); }
);
// Overwrite existing elements
for(auto element : renderListExisting) {
offset_t offset = element->offset();
offset_t originalSize = element->size();
ByteVector data = element->render();
insert(data, offset, originalSize);
newSegmentDataSize += (data.size() - originalSize);
}
// Add new elements to the end of file
for(auto element : renderListNew) {
offset_t offset = length();
ByteVector data = element->render();
insert(data, offset, 0);
newSegmentDataSize += data.size();
}
// Write the new segment data size
if(newSegmentDataSize != d->segmentDataSize) {
auto segmentDataSizeBuffer = EBML::renderVINT(newSegmentDataSize, d->segmentSizeLength);
insert(segmentDataSizeBuffer, d->segmentSizeOffset, d->segmentSizeLength);
d->segmentDataSize = newSegmentDataSize;
}
/*
auto&& renderElements [&d->segmentDataSize](Element *element) {
auto offset = element->offset();
if(!offset)
}
if(d->tag) {
ByteVector tag = d->tag->render();
if (!d->tagsOriginalSize) {
d->tagsOffset = d->segmentSizeOffset + d->segmentSizeLength + d->segmentDataSize;
auto tagsOriginalSize = d->tag->size();
auto tagsOffset = d->tag->offset();
if(!tagsOriginalSize) {
tagsOffset = d->segmentSizeOffset + d->segmentSizeLength + d->segmentDataSize;
}
insert(tag, d->tagsOffset, d->tagsOriginalSize);
d->segmentDataSize += (tag.size() - d->tagsOriginalSize);
insert(tag, tagsOffset, tagsOriginalSize);
d->segmentDataSize += (tag.size() - tagsOriginalSize);
auto segmentDataSizeBuffer = EBML::renderVINT(d->segmentDataSize, d->segmentSizeLength);
insert(segmentDataSizeBuffer, d->segmentSizeOffset, d->segmentSizeLength);
}
*/
/*
if(d->attachments) {
ByteVector attachments = d->attachments->render();
}
*/
return true;
}

View File

@ -35,6 +35,7 @@ namespace TagLib {
namespace Matroska {
class Properties;
class Tag;
class Attachments;
class TAGLIB_EXPORT File : public TagLib::File
{
public:
@ -45,6 +46,7 @@ namespace TagLib {
File &operator=(const File &) = delete;
AudioProperties *audioProperties() const override { return nullptr; }
TagLib::Tag *tag() const override;
Attachments* attachments(bool create = false) const;
Matroska::Tag *tag(bool create) const;
bool save() override;
//PropertyMap properties() const override { return PropertyMap(); }

View File

@ -69,8 +69,10 @@ void Matroska::Tag::addSimpleTag(SimpleTag *tag)
void Matroska::Tag::removeSimpleTag(SimpleTag *tag)
{
auto it = d->tags.find(tag);
if (it != d->tags.end())
if(it != d->tags.end()) {
delete *it;
d->tags.erase(it);
}
}
void Matroska::Tag::clearSimpleTags()
@ -161,7 +163,7 @@ String Matroska::Tag::genre() const
unsigned int Matroska::Tag::year() const
{
auto value = getTag("DATE");
if (!value)
if(!value)
return 0;
auto list = value->split("-");
return static_cast<unsigned int>(list.front().toInt());
@ -170,7 +172,7 @@ unsigned int Matroska::Tag::year() const
unsigned int Matroska::Tag::track() const
{
auto value = getTag("TRACKNUMBER");
if (!value)
if(!value)
return 0;
auto list = value->split("-");
return static_cast<unsigned int>(list.front().toInt());
@ -188,7 +190,7 @@ ByteVector Matroska::Tag::render()
targetList.setAutoDelete(true);
// Build target-based list
for (auto tag : d->tags) {
for(auto tag : d->tags) {
auto targetTypeValue = tag->targetTypeValue();
auto it = std::find_if(targetList.begin(),
targetList.end(),
@ -197,7 +199,7 @@ ByteVector Matroska::Tag::render()
return simpleTag->targetTypeValue() == targetTypeValue;
}
);
if (it == targetList.end()) {
if(it == targetList.end()) {
auto list = new List<SimpleTag*>();
list->append(tag);
targetList.append(list);
@ -205,14 +207,14 @@ ByteVector Matroska::Tag::render()
else
(*it)->append(tag);
}
for (auto list : targetList) {
for(auto list : targetList) {
auto frontTag = list->front();
auto targetTypeValue = frontTag->targetTypeValue();
auto tag = new EBML::MasterElement(EBML::ElementIDs::MkTag);
// Build <Tag Targets> element
auto targets = new EBML::MasterElement(EBML::ElementIDs::MkTagTargets);
if (targetTypeValue != Matroska::SimpleTag::TargetTypeValue::None) {
if(targetTypeValue != Matroska::SimpleTag::TargetTypeValue::None) {
auto element = new EBML::UIntElement(EBML::ElementIDs::MkTagTargetTypeValue);
element->setValue(static_cast<unsigned int>(targetTypeValue));
targets->appendElement(element);
@ -220,7 +222,7 @@ ByteVector Matroska::Tag::render()
tag->appendElement(targets);
// Build <Simple Tag> element
for (auto simpleTag : *list) {
for(auto simpleTag : *list) {
auto t = new EBML::MasterElement(EBML::ElementIDs::MkSimpleTag);
auto tagName = new EBML::UTF8StringElement(EBML::ElementIDs::MkTagName);
tagName->setValue(simpleTag->name());
@ -299,7 +301,7 @@ bool Matroska::Tag::setTag(const String &key, const String &value)
// Workaround Clang issue - no lambda capture of structured bindings
const String &name = pair.first;
auto targetTypeValue = pair.second;
if (name.isEmpty())
if(name.isEmpty())
return false;
removeSimpleTags(
[&name, targetTypeValue] (auto t) {
@ -307,7 +309,7 @@ bool Matroska::Tag::setTag(const String &key, const String &value)
&& t->targetTypeValue() == targetTypeValue;
}
);
if (!value.isEmpty()) {
if(!value.isEmpty()) {
auto t = new Matroska::SimpleTagString();
t->setTargetTypeValue(targetTypeValue);
t->setName(name);
@ -323,7 +325,7 @@ const String* Matroska::Tag::getTag(const String &key) const
// Workaround Clang issue - no lambda capture of structured bindings
const String &name = pair.first;
auto targetTypeValue = pair.second;
if (name.isEmpty())
if(name.isEmpty())
return nullptr;
auto tag = dynamic_cast<const Matroska::SimpleTagString*>(
findSimpleTag(
@ -342,7 +344,7 @@ std::pair<String, Matroska::SimpleTag::TargetTypeValue> Matroska::Utils::transla
simpleTagsTranslation.cend(),
[&key](const auto &t) { return key == std::get<0>(t); }
);
if (it != simpleTagsTranslation.end())
if(it != simpleTagsTranslation.end())
return { std::get<1>(*it), std::get<2>(*it) };
else
return { String(), Matroska::SimpleTag::TargetTypeValue::None };
@ -363,8 +365,8 @@ String Matroska::Utils::translateTag(const String &name, Matroska::SimpleTag::Ta
PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
{
PropertyMap unsupportedProperties;
for (const auto& [key, value] : propertyMap) {
if (!setTag(key, value.toString()))
for(const auto& [key, value] : propertyMap) {
if(!setTag(key, value.toString()))
unsupportedProperties[key] = value;
}
return unsupportedProperties;
@ -374,10 +376,10 @@ PropertyMap Matroska::Tag::properties() const
{
PropertyMap properties;
Matroska::SimpleTagString *tStr = nullptr;
for (auto simpleTag : d->tags) {
if ((tStr = dynamic_cast<Matroska::SimpleTagString*>(simpleTag))) {
for(auto simpleTag : d->tags) {
if((tStr = dynamic_cast<Matroska::SimpleTagString*>(simpleTag))) {
String key = Matroska::Utils::translateTag(tStr->name(), tStr->targetTypeValue());
if (!key.isEmpty() && !properties.contains(key))
if(!key.isEmpty() && !properties.contains(key))
properties[key] = tStr->value();
}
}

View File

@ -29,12 +29,17 @@
#include "tstring.h"
#include "tlist.h"
#include "matroskafile.h"
#include "matroskaelement.h"
#include "matroskasimpletag.h"
namespace TagLib {
namespace EBML {
class MkTags;
}
namespace Matroska {
using SimpleTagsList = List<SimpleTag*>;
class TAGLIB_EXPORT Tag : public TagLib::Tag
class TAGLIB_EXPORT Tag : public TagLib::Tag, private Element
{
public:
Tag();
@ -58,6 +63,7 @@ namespace TagLib {
void setYear(unsigned int i) override;
void setTrack(unsigned int i) override;
bool isEmpty() const override;
ByteVector render() override;
PropertyMap properties() const override;
PropertyMap setProperties(const PropertyMap &propertyMap) override;
template <typename T>
@ -65,9 +71,9 @@ namespace TagLib {
{
auto &list = simpleTagsListPrivate();
int numRemoved = 0;
for (auto it = list.begin(); it != list.end();) {
for(auto it = list.begin(); it != list.end();) {
it = std::find_if(it, list.end(), std::forward<T>(p));
if (it != list.end()) {
if(it != list.end()) {
delete *it;
*it = nullptr;
it = list.erase(it);
@ -81,15 +87,16 @@ namespace TagLib {
SimpleTagsList findSimpleTags(T&& p)
{
auto &list = simpleTagsListPrivate();
for (auto it = list.begin(); it != list.end();) {
for(auto it = list.begin(); it != list.end();) {
it = std::find_if(it, list.end(), std::forward<T>(p));
if (it != list.end()) {
if(it != list.end()) {
list.append(*it);
++it;
}
}
return list;
}
template<typename T>
const Matroska::SimpleTag* findSimpleTag(T&& p) const
{
@ -97,6 +104,7 @@ namespace TagLib {
auto it = std::find_if(list.begin(), list.end(), std::forward<T>(p));
return it != list.end() ? *it : nullptr;
}
template <typename T>
Matroska::SimpleTag* findSimpleTag(T&&p)
{
@ -107,7 +115,7 @@ namespace TagLib {
private:
friend class Matroska::File;
ByteVector render();
friend class EBML::MkTags;
SimpleTagsList& simpleTagsListPrivate();
const SimpleTagsList& simpleTagsListPrivate() const;
bool setTag(const String &key, const String &value);