Files
hfut-bishe/client/core/persistence/AnimationBundleStore.cpp
T
2026-05-14 13:30:06 +08:00

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