163 lines
5.6 KiB
C++
163 lines
5.6 KiB
C++
#include "persistence/AnimationBundleStore.h"
|
|
|
|
#include "persistence/JsonFileAtomic.h"
|
|
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
|
|
namespace core::persistence {
|
|
namespace {
|
|
|
|
QJsonObject keyToJson(const Project::AnimationKey& k, Project::AnimationValueType t) {
|
|
QJsonObject o;
|
|
o.insert("frame", k.frame);
|
|
switch (t) {
|
|
case Project::AnimationValueType::Vec2:
|
|
o.insert("x", k.vec2Value.x());
|
|
o.insert("y", k.vec2Value.y());
|
|
break;
|
|
case Project::AnimationValueType::Double:
|
|
o.insert("value", k.numberValue);
|
|
break;
|
|
case Project::AnimationValueType::Bool:
|
|
o.insert("value", k.boolValue);
|
|
break;
|
|
case Project::AnimationValueType::ImagePath:
|
|
o.insert("value", k.stringValue);
|
|
break;
|
|
case Project::AnimationValueType::PngBytes:
|
|
o.insert("value", QString::fromLatin1(k.bytesValue.toBase64()));
|
|
break;
|
|
}
|
|
return o;
|
|
}
|
|
|
|
bool keyFromJson(const QJsonObject& o, Project::AnimationValueType t, Project::AnimationKey& out) {
|
|
out = Project::AnimationKey{};
|
|
out.frame = o.value("frame").toInt(0);
|
|
switch (t) {
|
|
case Project::AnimationValueType::Vec2:
|
|
out.vec2Value = QPointF(o.value("x").toDouble(0.0), o.value("y").toDouble(0.0));
|
|
break;
|
|
case Project::AnimationValueType::Double:
|
|
out.numberValue = o.value("value").toDouble(0.0);
|
|
break;
|
|
case Project::AnimationValueType::Bool:
|
|
out.boolValue = o.value("value").toBool(true);
|
|
break;
|
|
case Project::AnimationValueType::ImagePath:
|
|
out.stringValue = o.value("value").toString();
|
|
break;
|
|
case Project::AnimationValueType::PngBytes: {
|
|
const QByteArray b64 = o.value("value").toString().toLatin1();
|
|
out.bytesValue = QByteArray::fromBase64(b64);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
QString AnimationBundleStore::bundleRelativeDir(const QString& animationId) {
|
|
if (animationId.isEmpty()) return {};
|
|
return QStringLiteral("assets/animations/%1").arg(animationId);
|
|
}
|
|
|
|
bool AnimationBundleStore::save(const QString& projectDirAbs, const Project::Animation& animation) {
|
|
if (projectDirAbs.isEmpty() || animation.id.isEmpty()) {
|
|
return false;
|
|
}
|
|
const QString relDir = bundleRelativeDir(animation.id);
|
|
if (relDir.isEmpty()) return false;
|
|
const QString absDir = QDir(projectDirAbs).filePath(relDir);
|
|
QDir().mkpath(absDir);
|
|
|
|
QJsonObject root;
|
|
root.insert("id", animation.id);
|
|
root.insert("name", animation.name);
|
|
root.insert("fps", animation.fps);
|
|
root.insert("lengthFrames", animation.lengthFrames);
|
|
root.insert("loop", animation.loop);
|
|
|
|
QJsonArray tracks;
|
|
for (const auto& t : animation.tracks) {
|
|
QJsonObject to;
|
|
to.insert("id", t.id);
|
|
to.insert("targetKind", int(t.targetKind));
|
|
to.insert("targetId", t.targetId);
|
|
to.insert("property", int(t.property));
|
|
to.insert("valueType", int(t.valueType));
|
|
to.insert("interpolation", int(t.interpolation));
|
|
QJsonArray keys;
|
|
for (const auto& k : t.keys) {
|
|
keys.append(keyToJson(k, t.valueType));
|
|
}
|
|
to.insert("keys", keys);
|
|
tracks.append(to);
|
|
}
|
|
root.insert("tracks", tracks);
|
|
|
|
return writeJsonAtomic(QDir(absDir).filePath(QStringLiteral("anim.json")), root, true);
|
|
}
|
|
|
|
bool AnimationBundleStore::load(const QString& projectDirAbs, Project::Animation& inOutAnimation) {
|
|
if (projectDirAbs.isEmpty() || inOutAnimation.id.isEmpty()) {
|
|
return false;
|
|
}
|
|
const QString relDir = inOutAnimation.payloadPath;
|
|
if (relDir.isEmpty()) return false;
|
|
const QString absDir = QDir(projectDirAbs).filePath(relDir);
|
|
if (!QFileInfo(absDir).exists()) return false;
|
|
|
|
QJsonObject root;
|
|
if (!readJsonObject(QDir(absDir).filePath(QStringLiteral("anim.json")), root)) {
|
|
return false;
|
|
}
|
|
const QString id = root.value("id").toString();
|
|
if (id.isEmpty() || id != inOutAnimation.id) {
|
|
return false;
|
|
}
|
|
Project::Animation a = inOutAnimation;
|
|
a.name = root.value("name").toString();
|
|
a.fps = std::max(1, root.value("fps").toInt(30));
|
|
a.lengthFrames = std::max(1, root.value("lengthFrames").toInt(Project::kClipFixedFrames));
|
|
a.loop = root.value("loop").toBool(true);
|
|
|
|
a.tracks.clear();
|
|
const QJsonValue tracksVal = root.value("tracks");
|
|
if (tracksVal.isArray()) {
|
|
for (const auto& it : tracksVal.toArray()) {
|
|
if (!it.isObject()) continue;
|
|
const QJsonObject to = it.toObject();
|
|
Project::AnimationTrack t;
|
|
t.id = to.value("id").toString();
|
|
t.targetKind = Project::AnimationTargetKind(to.value("targetKind").toInt(0));
|
|
t.targetId = to.value("targetId").toString();
|
|
t.property = Project::AnimationProperty(to.value("property").toInt(0));
|
|
t.valueType = Project::AnimationValueType(to.value("valueType").toInt(0));
|
|
t.interpolation = Project::AnimationInterpolation(to.value("interpolation").toInt(1));
|
|
const QJsonValue keysVal = to.value("keys");
|
|
if (keysVal.isArray()) {
|
|
for (const auto& kv : keysVal.toArray()) {
|
|
if (!kv.isObject()) continue;
|
|
Project::AnimationKey k;
|
|
keyFromJson(kv.toObject(), t.valueType, k);
|
|
t.keys.push_back(k);
|
|
}
|
|
}
|
|
if (!t.targetId.isEmpty()) {
|
|
a.tracks.push_back(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
inOutAnimation = std::move(a);
|
|
return true;
|
|
}
|
|
|
|
} // namespace core::persistence
|
|
|