serializer: ensure backwards compatibility

This commit is contained in:
itsmattkc
2023-02-26 15:42:54 -08:00
parent 46d15396a6
commit 7ec8a4dd7e
31 changed files with 777 additions and 417 deletions
+31 -51
View File
@@ -58,7 +58,7 @@ void ProjectSerializer::Destroy()
instances_.clear();
}
ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QString &filename, const QString &type)
ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QString &filename, LoadType load_type)
{
QFile project_file(filename);
@@ -76,7 +76,7 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin
reader.reset(new QXmlStreamReader(&project_file));
}
Result inner_result = Load(project, reader.get(), type);
Result inner_result = Load(project, reader.get(), load_type);
project_file.close();
@@ -89,14 +89,14 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin
r.SetDetails(reader->errorString());
return r;
} else {
return kSuccess;
return inner_result;
}
} else {
return kFileError;
}
}
ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamReader *reader, const QString &type)
ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamReader *reader, LoadType load_type)
{
// Determine project version
uint version = 0;
@@ -106,24 +106,26 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe
if (reader->name() == QStringLiteral("olive")
|| reader->name() == QStringLiteral("project")) { // 0.1 projects only
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("version")) {
version = reader->readElementText().toUInt();
} else if (reader->name() == QStringLiteral("url")) {
project->SetSavedURL(reader->readElementText());
// HACK for 0.1 projects
if (version == 190219) {
res = LoadWithSerializerVersion(version, project, reader);
}
} else if (reader->name() == type) {
// Found our data
res = LoadWithSerializerVersion(version, project, reader);
} else {
reader->skipCurrentElement();
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("version")) { // 230220+ projects
version = attr.value().toUInt();
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("version")) { // projects <= 220403
version = reader->readElementText().toUInt();
} else if (reader->name() == QStringLiteral("url")) {
if (project) {
project->SetSavedURL(reader->readElementText());
} else {
reader->skipCurrentElement();
}
} else {
// Handle any other value with the serializer
res = LoadWithSerializerVersion(version, project, reader, load_type);
}
}
} else {
reader->skipCurrentElement();
}
@@ -132,7 +134,7 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, QXmlStreamRe
return res;
}
ProjectSerializer::Result ProjectSerializer::Paste(const QString &type)
ProjectSerializer::Result ProjectSerializer::Paste(LoadType load_type)
{
QString clipboard = Core::PasteStringFromClipboard();
if (clipboard.isEmpty()) {
@@ -141,28 +143,10 @@ ProjectSerializer::Result ProjectSerializer::Paste(const QString &type)
QXmlStreamReader reader(clipboard);
Project temp;
ProjectSerializer::Result res = ProjectSerializer::Load(&temp, &reader, type);
if (res.code() != ProjectSerializer::kSuccess) {
return res;
}
QVector<Node*> pasted_nodes;
foreach (Node *n, temp.nodes()) {
if (!temp.default_nodes().contains(n)) {
// Move nodes out of Project
n->setParent(nullptr);
pasted_nodes.append(n);
}
}
res.SetLoadedNodes(pasted_nodes);
return res;
return ProjectSerializer::Load(nullptr, &reader, load_type);
}
ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QString &type, bool compress)
ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, bool compress)
{
QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename());
@@ -172,7 +156,7 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS
QByteArray b;
QXmlStreamWriter writer(&b);
Result inner_result = Save(&writer, data, type);
Result inner_result = Save(&writer, data);
if (writer.hasError()) {
Result r(kXmlError);
@@ -207,7 +191,7 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS
}
}
ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data, const QString &type)
ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, const SaveData &data)
{
writer->setAutoFormatting(true);
@@ -221,18 +205,14 @@ ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, cons
// Version is stored in YYMMDD from whenever the project format was last changed
// Allows easy integer math for checking project versions.
writer->writeTextElement(QStringLiteral("version"), QString::number(serializer->Version()));
writer->writeAttribute(QStringLiteral("version"), QString::number(serializer->Version()));
if (!data.GetFilename().isEmpty()) {
writer->writeTextElement("url", data.GetFilename());
}
writer->writeStartElement(type);
serializer->Save(writer, data, nullptr);
writer->writeEndElement(); // [type]
writer->writeEndElement(); // olive
writer->writeEndDocument();
@@ -244,12 +224,12 @@ ProjectSerializer::Result ProjectSerializer::Save(QXmlStreamWriter *writer, cons
return kSuccess;
}
ProjectSerializer::Result ProjectSerializer::Copy(const SaveData &data, const QString &type)
ProjectSerializer::Result ProjectSerializer::Copy(const SaveData &data)
{
QString copy_str;
QXmlStreamWriter writer(&copy_str);
ProjectSerializer::Result res = ProjectSerializer::Save(&writer, data, type);
ProjectSerializer::Result res = ProjectSerializer::Save(&writer, data);
if (res == kSuccess) {
Core::CopyStringToClipboard(copy_str);
@@ -269,7 +249,7 @@ bool ProjectSerializer::IsCancelled() const
return false;
}
ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader)
ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader, LoadType load_type)
{
// Failed to find version in file
if (version == 0) {
@@ -291,7 +271,7 @@ ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint vers
}
if (serializer) {
LoadData ld = serializer->Load(project, reader, nullptr);
LoadData ld = serializer->Load(project, reader, load_type, nullptr);
Result r(kSuccess);
if (reader->hasError()) {
r = Result(kXmlError);
+35 -23
View File
@@ -38,6 +38,14 @@ namespace olive {
class ProjectSerializer
{
public:
enum LoadType
{
kProject,
kOnlyNodes,
kOnlyMarkers,
kOnlyKeyframes
};
ProjectSerializer() = default;
virtual ~ProjectSerializer(){}
@@ -69,6 +77,10 @@ public:
SerializedKeyframes keyframes;
MainWindowLayoutInfo layout;
QVector<Node*> nodes;
};
class Result
@@ -91,10 +103,6 @@ public:
void SetLoadData(const LoadData &p) { load_data_ = p; }
const QVector<Node*> &GetLoadedNodes() const { return loaded_nodes_; }
void SetLoadedNodes(const QVector<Node*> &n) { loaded_nodes_ = n; }
private:
ResultCode code_;
@@ -102,28 +110,28 @@ public:
LoadData load_data_;
QVector<Node*> loaded_nodes_;
};
class SaveData
{
public:
SaveData(Project *project = nullptr, const QString &filename = QString())
SaveData(LoadType type, Project *project = nullptr, const QString &filename = QString())
{
type_ = type;
project_ = project;
filename_ = filename;
}
Project *GetProject() const
{
return project_;
}
Project *GetProject() const { return project_; }
void SetProject(Project *p) { project_ = p; }
const QString &GetFilename() const
{
return filename_;
}
const QString &GetFilename() const { return filename_; }
void SetFilename(const QString &s) { filename_ = s; }
LoadType type() const { return type_; }
const MainWindowLayoutInfo &GetLayout() const { return layout_; }
void SetLayout(const MainWindowLayoutInfo &layout) { layout_ = layout; }
const QVector<Node*> &GetOnlySerializeNodes() const { return only_serialize_nodes_; }
void SetOnlySerializeNodes(const QVector<Node*> &only) { only_serialize_nodes_ = only; }
@@ -139,10 +147,14 @@ public:
void SetProperties(const SerializedProperties &p) { properties_ = p; }
private:
LoadType type_;
Project *project_;
QString filename_;
MainWindowLayoutInfo layout_;
QVector<Node*> only_serialize_nodes_;
SerializedProperties properties_;
@@ -157,18 +169,18 @@ public:
static void Destroy();
static Result Load(Project *project, const QString &filename, const QString &type);
static Result Load(Project *project, QXmlStreamReader *read_device, const QString &type);
static Result Paste(const QString &type);
static Result Load(Project *project, const QString &filename, LoadType load_type);
static Result Load(Project *project, QXmlStreamReader *read_device, LoadType load_type);
static Result Paste(LoadType load_type);
static Result Save(const SaveData &data, const QString &type, bool compress);
static Result Save(QXmlStreamWriter *write_device, const SaveData &data, const QString &type);
static Result Copy(const SaveData &data, const QString &type);
static Result Save(const SaveData &data, bool compress);
static Result Save(QXmlStreamWriter *write_device, const SaveData &data);
static Result Copy(const SaveData &data);
static bool CheckCompressedID(QFile *file);
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const = 0;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const = 0;
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const {}
@@ -177,7 +189,7 @@ protected:
bool IsCancelled() const;
private:
static Result LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader);
static Result LoadWithSerializerVersion(uint version, Project *project, QXmlStreamReader *reader, LoadType load_type);
static QVector<ProjectSerializer*> instances_;
@@ -22,7 +22,7 @@
namespace olive {
ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer::LoadData ProjectSerializer190219::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
return LoadData();
}
@@ -31,7 +31,7 @@ public:
ProjectSerializer190219() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual uint Version() const override
{
@@ -26,7 +26,7 @@
namespace olive {
ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer210528::LoadData ProjectSerializer210528::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
XMLNodeData xml_node_data;
@@ -31,7 +31,7 @@ public:
ProjectSerializer210528() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual uint Version() const override
{
@@ -26,7 +26,7 @@
namespace olive {
ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer210907::LoadData ProjectSerializer210907::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
XMLNodeData xml_node_data;
@@ -31,7 +31,7 @@ public:
ProjectSerializer210907() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual uint Version() const override
{
@@ -26,7 +26,7 @@
namespace olive {
ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer211228::LoadData ProjectSerializer211228::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
QMap<quintptr, QMap<QString, QString> > properties;
QMap<quintptr, QMap<quintptr, Node::Position> > positions;
@@ -31,7 +31,7 @@ public:
ProjectSerializer211228() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual uint Version() const override
{
+229 -206
View File
@@ -26,7 +26,7 @@
namespace olive {
ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
QMap<quintptr, QMap<QString, QString> > properties;
QMap<quintptr, QMap<quintptr, Node::Position> > positions;
@@ -34,253 +34,275 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
LoadData load_data;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("uuid")) {
if ((load_type == kProject && reader->name() == QStringLiteral("project"))
|| (load_type == kOnlyNodes && (reader->name() == QStringLiteral("nodes") || reader->name() == QStringLiteral("timeline")))
|| (load_type == kOnlyKeyframes && reader->name() == QStringLiteral("keyframes"))
|| (load_type == kOnlyMarkers && reader->name() == QStringLiteral("markers"))) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("layout")) {
project->SetUuid(QUuid::fromString(reader->readElementText()));
// Since the main window's functions have to occur in the GUI thread (and we're likely
// loading in a secondary thread), we load all necessary data into a separate struct so we
// can continue loading and queue it with the main window so it can handle the data
// appropriately in its own thread.
} else if (reader->name() == QStringLiteral("nodes")) {
load_data.layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data.node_ptrs);
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
bool is_root = false;
bool is_cm = false;
bool is_settings = false;
QString id;
} else if (reader->name() == QStringLiteral("uuid")) {
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
id = attr.value().toString();
} else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) {
is_root = true;
} else if (attr.name() == QStringLiteral("cm") && attr.value() == QStringLiteral("1")) {
is_cm = true;
} else if (attr.name() == QStringLiteral("settings") && attr.value() == QStringLiteral("1")) {
is_settings = true;
}
}
}
if (id.isEmpty()) {
qWarning() << "Failed to load node with empty ID";
reader->skipCurrentElement();
} else {
Node* node;
if (is_root) {
node = project->root();
} else if (is_cm) {
node = project->color_manager();
} else if (is_settings) {
node = project->settings();
} else {
node = NodeFactory::CreateFromID(id);
}
if (!node) {
qWarning() << "Failed to find node with ID" << id;
reader->skipCurrentElement();
} else {
// Disable cache while node is being loaded (we'll re-enable it later)
node->SetCachesEnabled(false);
LoadNode(node, xml_node_data, reader);
node->setParent(project);
}
}
if (project) {
project->SetUuid(QUuid::fromString(reader->readElementText()));
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("keyframes")) {
} else if (reader->name() == QStringLiteral("nodes")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
QString node_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
node_id = attr.value().toString();
break;
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
bool is_root = false;
bool is_cm = false;
bool is_settings = false;
QString id;
Node *n = nullptr;
if (!node_id.isEmpty()) {
n = NodeFactory::CreateFromID(node_id);
}
if (!n) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
QString input_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
input_id = attr.value().toString();
break;
}
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
id = attr.value().toString();
} else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) {
is_root = true;
} else if (attr.name() == QStringLiteral("cm") && attr.value() == QStringLiteral("1")) {
is_cm = true;
} else if (attr.name() == QStringLiteral("settings") && attr.value() == QStringLiteral("1")) {
is_settings = true;
}
}
}
if (input_id.isEmpty()) {
reader->skipCurrentElement();
if (id.isEmpty()) {
qWarning() << "Failed to load node with empty ID";
reader->skipCurrentElement();
} else {
Node* node;
if (is_root) {
node = project->root();
} else if (is_cm) {
node = project->color_manager();
} else if (is_settings) {
node = project->settings();
} else {
node = NodeFactory::CreateFromID(id);
}
if (!node) {
qWarning() << "Failed to find node with ID" << id;
reader->skipCurrentElement();
} else {
// Disable cache while node is being loaded (we'll re-enable it later)
node->SetCachesEnabled(false);
LoadNode(node, xml_node_data, reader);
if (project) {
node->setParent(project);
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("element")) {
QString element_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
element_id = attr.value().toString();
break;
}
}
load_data.nodes.append(node);
}
}
}
} else {
reader->skipCurrentElement();
}
}
if (element_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("track")) {
QString track_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
track_id = attr.value().toString();
break;
}
}
} else if (reader->name() == QStringLiteral("keyframes")) {
if (track_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
NodeKeyframe *key = new NodeKeyframe();
key->set_input(input_id);
key->set_element(element_id.toInt());
key->set_track(track_id.toInt());
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
QString node_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
node_id = attr.value().toString();
break;
}
}
LoadKeyframe(reader, key, n->GetInputDataType(input_id));
Node *n = nullptr;
if (!node_id.isEmpty()) {
n = NodeFactory::CreateFromID(node_id);
}
load_data.keyframes[node_id].append(key);
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
if (!n) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
QString input_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
input_id = attr.value().toString();
break;
}
}
if (input_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("element")) {
QString element_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
element_id = attr.value().toString();
break;
}
}
if (element_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("track")) {
QString track_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
track_id = attr.value().toString();
break;
}
}
if (track_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
NodeKeyframe *key = new NodeKeyframe();
key->set_input(input_id);
key->set_element(element_id.toInt());
key->set_track(track_id.toInt());
LoadKeyframe(reader, key, n->GetInputDataType(input_id));
load_data.keyframes[node_id].append(key);
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
delete n;
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("markers")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("marker")) {
TimelineMarker *marker = new TimelineMarker();
LoadMarker(reader, marker);
load_data.markers.push_back(marker);
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("positions")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("context")) {
quintptr context_ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
context_ptr = attr.value().toULongLong();
break;
}
}
if (context_ptr) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr node_ptr;
Node::Position node_pos;
if (LoadPosition(reader, &node_ptr, &node_pos)) {
if (node_ptr) {
positions[context_ptr].insert(node_ptr, node_pos);
} else {
qWarning() << "Failed to find pointer for node position";
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
} else {
reader->skipCurrentElement();
}
} else {
qWarning() << "Attempted to load context with no pointer";
reader->skipCurrentElement();
}
} else {
reader->skipCurrentElement();
}
delete n;
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("markers")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("marker")) {
TimelineMarker *marker = new TimelineMarker();
LoadMarker(reader, marker);
load_data.markers.push_back(marker);
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("properties")) {
} else if (reader->name() == QStringLiteral("positions")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr ptr = 0;
while (XMLReadNextStartElement(reader)) {
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
ptr = attr.value().toULongLong();
if (reader->name() == QStringLiteral("context")) {
quintptr context_ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
context_ptr = attr.value().toULongLong();
break;
}
}
if (context_ptr) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr node_ptr;
Node::Position node_pos;
if (LoadPosition(reader, &node_ptr, &node_pos)) {
if (node_ptr) {
positions[context_ptr].insert(node_ptr, node_pos);
} else {
qWarning() << "Failed to find pointer for node position";
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
// Only attribute we're looking for right now
break;
}
}
if (ptr) {
QMap<QString, QString> properties_for_node;
while (XMLReadNextStartElement(reader)) {
properties_for_node.insert(reader->name().toString(), reader->readElementText());
}
properties.insert(ptr, properties_for_node);
}
} else {
qWarning() << "Attempted to load context with no pointer";
reader->skipCurrentElement();
}
} else {
reader->skipCurrentElement();
}
} else {
// Skip this
reader->skipCurrentElement();
}
} else if (reader->name() == QStringLiteral("properties")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
ptr = attr.value().toULongLong();
// Only attribute we're looking for right now
break;
}
}
if (ptr) {
QMap<QString, QString> properties_for_node;
while (XMLReadNextStartElement(reader)) {
properties_for_node.insert(reader->name().toString(), reader->readElementText());
}
properties.insert(ptr, properties_for_node);
}
} else {
reader->skipCurrentElement();
}
}
} else {
// Skip this
reader->skipCurrentElement();
}
}
@@ -309,7 +331,8 @@ ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project
}
// Re-enable caches and resolve tracks
for (Node *n : project->nodes()) {
const QVector<Node*> &nodes = project ? project->nodes() : load_data.nodes;
for (Node *n : nodes) {
n->SetCachesEnabled(true);
if (Track *t = dynamic_cast<Track *>(n)) {
@@ -31,7 +31,7 @@ public:
ProjectSerializer220403() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual uint Version() const override
{
@@ -23,21 +23,349 @@
#include "config/config.h"
#include "node/factory.h"
#include "node/group/group.h"
#include "node/serializeddata.h"
namespace olive {
ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const
{
QMap<quintptr, QMap<QString, QString> > properties;
QMap<quintptr, QMap<quintptr, Node::Position> > positions;
LoadData load_data;
SerializedData project_data;
project->Load(reader);
switch (load_type) {
case kProject:
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("project")) {
project_data = project->Load(reader);
} else if (reader->name() == QStringLiteral("layout")) {
load_data.layout = MainWindowLayoutInfo::fromXml(reader, project_data.node_ptrs);
} else {
reader->skipCurrentElement();
}
}
PostConnect(project->nodes(), &project_data);
break;
}
case kOnlyMarkers:
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("marker")) {
TimelineMarker *marker = new TimelineMarker();
marker->load(reader);
load_data.markers.push_back(marker);
} else {
reader->skipCurrentElement();
}
}
break;
}
case kOnlyKeyframes:
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
QString node_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
node_id = attr.value().toString();
break;
}
}
Node *n = nullptr;
if (!node_id.isEmpty()) {
n = NodeFactory::CreateFromID(node_id);
}
if (!n) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
QString input_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
input_id = attr.value().toString();
break;
}
}
if (input_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("element")) {
QString element_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
element_id = attr.value().toString();
break;
}
}
if (element_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("track")) {
QString track_id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
track_id = attr.value().toString();
break;
}
}
if (track_id.isEmpty()) {
reader->skipCurrentElement();
} else {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("key")) {
NodeKeyframe *key = new NodeKeyframe();
key->set_input(input_id);
key->set_element(element_id.toInt());
key->set_track(track_id.toInt());
key->load(reader, n->GetInputDataType(input_id));
load_data.keyframes[node_id].append(key);
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
} else {
reader->skipCurrentElement();
}
}
}
delete n;
} else {
reader->skipCurrentElement();
}
}
break;
}
case kOnlyNodes:
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
QString id;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
id = attr.value().toString();
}
}
if (id.isEmpty()) {
qWarning() << "Failed to load node with empty ID";
reader->skipCurrentElement();
} else {
Node* node = NodeFactory::CreateFromID(id);
if (!node) {
qWarning() << "Failed to find node with ID" << id;
reader->skipCurrentElement();
} else {
// Disable cache while node is being loaded (we'll re-enable it later)
node->SetCachesEnabled(false);
node->Load(reader, &project_data);
load_data.nodes.append(node);
}
}
} else if (reader->name() == QStringLiteral("properties")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
quintptr ptr = 0;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("ptr")) {
ptr = attr.value().toULongLong();
// Only attribute we're looking for right now
break;
}
}
if (ptr) {
QMap<QString, QString> properties_for_node;
while (XMLReadNextStartElement(reader)) {
properties_for_node.insert(reader->name().toString(), reader->readElementText());
}
properties.insert(ptr, properties_for_node);
}
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
PostConnect(load_data.nodes, &project_data);
// Resolve serialized properties (if any)
for (auto it=properties.cbegin(); it!=properties.cend(); it++) {
Node *node = project_data.node_ptrs.value(it.key());
if (node) {
load_data.properties.insert(node, it.value());
}
}
break;
}
}
return load_data;
}
void ProjectSerializer230220::Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const
{
if (!data.GetOnlySerializeMarkers().empty()) {
writer->writeStartElement(QStringLiteral("markers"));
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
for (auto it=data.GetOnlySerializeMarkers().cbegin(); it!=data.GetOnlySerializeMarkers().cend(); it++) {
TimelineMarker *marker = *it;
writer->writeStartElement(QStringLiteral("marker"));
marker->save(writer);
writer->writeEndElement(); // marker
}
writer->writeEndElement(); // markers
} else if (!data.GetOnlySerializeKeyframes().empty()) {
writer->writeStartElement(QStringLiteral("keyframes"));
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
// Organize keyframes into node+input
QHash<QString, QHash<QString, QMap<int, QMap<int, QVector<NodeKeyframe*> > > > > organized;
for (auto it=data.GetOnlySerializeKeyframes().cbegin(); it!=data.GetOnlySerializeKeyframes().cend(); it++) {
NodeKeyframe *key = *it;
organized[key->parent()->id()][key->input()][key->element()][key->track()].append(key);
}
for (auto it=organized.cbegin(); it!=organized.cend(); it++) {
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("id"), it.key());
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
writer->writeStartElement(QStringLiteral("input"));
writer->writeAttribute(QStringLiteral("id"), jt.key());
for (auto kt=jt.value().cbegin(); kt!=jt.value().cend(); kt++) {
writer->writeStartElement(QStringLiteral("element"));
writer->writeAttribute(QStringLiteral("id"), QString::number(kt.key()));
for (auto lt=kt.value().cbegin(); lt!=kt.value().cend(); lt++) {
const QVector<NodeKeyframe *> &keys = lt.value();
writer->writeStartElement(QStringLiteral("track"));
writer->writeAttribute(QStringLiteral("id"), QString::number(lt.key()));
for (NodeKeyframe *key : keys) {
writer->writeStartElement(QStringLiteral("key"));
key->save(writer, key->parent()->GetInputDataType(key->input()));
writer->writeEndElement(); // key
}
writer->writeEndElement(); // track
}
writer->writeEndElement(); // element
}
writer->writeEndElement(); // input
}
writer->writeEndElement(); // node;
}
writer->writeEndElement(); // keyframes
} else if (!data.GetOnlySerializeNodes().empty()) {
writer->writeStartElement(QStringLiteral("nodes"));
writer->writeAttribute(QStringLiteral("version"), QString::number(1));
for (Node *n : data.GetOnlySerializeNodes()) {
writer->writeStartElement(QStringLiteral("node"));
n->Save(writer);
writer->writeEndElement();
}
if (!data.GetProperties().empty()) {
writer->writeStartElement(QStringLiteral("properties"));
for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) {
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
writer->writeTextElement(jt.key(), jt.value());
}
writer->writeEndElement(); // node
}
writer->writeEndElement(); // properties
}
writer->writeEndElement(); // nodes
} else if (Project *project = data.GetProject()) {
writer->writeStartElement(QStringLiteral("project"));
project->Save(writer);
writer->writeEndElement();
writer->writeStartElement(QStringLiteral("layout"));
data.GetLayout().toXml(writer);
writer->writeEndElement();
} else {
qCritical() << "ProjectSerializer provided nothing to save";
}
}
void ProjectSerializer230220::PostConnect(const QVector<Node *> &nodes, SerializedData *project_data) const
{
for (auto it = nodes.cbegin(); it != nodes.cend(); it++){
Node *n = *it;
n->PostLoadEvent(project_data);
n->SetCachesEnabled(true);
}
foreach (const SerializedData::SerializedConnection& con, project_data->desired_connections) {
if (Node *out = project_data->node_ptrs.value(con.output_node)) {
Node::ConnectEdge(out, con.input);
}
}
foreach (const SerializedData::BlockLink& l, project_data->block_links) {
Node *a = l.block;
Node *b = project_data->node_ptrs.value(l.link);
Node::Link(a, b);
}
}
}
@@ -31,7 +31,7 @@ public:
ProjectSerializer230220() = default;
protected:
virtual LoadData Load(Project *project, QXmlStreamReader *reader, void *reserved) const override;
virtual LoadData Load(Project *project, QXmlStreamReader *reader, LoadType load_type, void *reserved) const override;
virtual void Save(QXmlStreamWriter *writer, const SaveData &data, void *reserved) const override;
@@ -40,6 +40,9 @@ protected:
return 230220;
}
private:
void PostConnect(const QVector<Node*> &nodes, SerializedData *project_data) const;
};
}