822 lines
34 KiB
C#
822 lines
34 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using SpineRuntime38;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
using SpineRuntime38.Attachments;
|
|
|
|
namespace SpineViewer.Spine.Implementations.SkeletonConverter
|
|
{
|
|
[SkeletonConverterImplementation(Version.V38)]
|
|
class SkeletonConverter38 : SpineViewer.Spine.SkeletonConverter
|
|
{
|
|
private SkeletonReader reader = null;
|
|
private JsonObject root = null;
|
|
|
|
private bool nonessential = false;
|
|
private List<JsonObject> idx2Event = [];
|
|
|
|
protected override JsonObject ReadBinary(string binPath)
|
|
{
|
|
using var input = File.OpenRead(binPath);
|
|
reader = new(input);
|
|
|
|
var result = root = [];
|
|
root["skeleton"] = ReadSkeleton();
|
|
ReadStrings();
|
|
root["bones"] = ReadBones();
|
|
root["slots"] = ReadSlots();
|
|
root["ik"] = ReadIK();
|
|
root["transform"] = ReadTransform();
|
|
root["path"] = ReadPath();
|
|
root["skins"] = ReadSkins();
|
|
root["events"] = ReadEvents();
|
|
root["animations"] = ReadAnimations();
|
|
|
|
reader = null;
|
|
nonessential = false;
|
|
root = null;
|
|
|
|
return result;
|
|
}
|
|
|
|
private JsonObject ReadSkeleton()
|
|
{
|
|
JsonObject skeleton = [];
|
|
skeleton["hash"] = reader.ReadString();
|
|
skeleton["spine"] = reader.ReadString();
|
|
skeleton["x"] = reader.ReadFloat();
|
|
skeleton["y"] = reader.ReadFloat();
|
|
skeleton["width"] = reader.ReadFloat();
|
|
skeleton["height"] = reader.ReadFloat();
|
|
nonessential = reader.ReadBoolean();
|
|
if (nonessential)
|
|
{
|
|
skeleton["fps"] = reader.ReadFloat();
|
|
skeleton["images"] = reader.ReadString();
|
|
skeleton["audio"] = reader.ReadString();
|
|
}
|
|
return skeleton;
|
|
}
|
|
|
|
private void ReadStrings()
|
|
{
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
reader.StringTable.Add(reader.ReadString());
|
|
}
|
|
|
|
private JsonArray ReadBones()
|
|
{
|
|
JsonArray bones = [];
|
|
for (int i = 0, n = reader.ReadVarInt(); i < n; i++)
|
|
{
|
|
JsonObject data = [];
|
|
data["name"] = reader.ReadString();
|
|
if (i > 0) data["parent"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
data["rotation"] = reader.ReadFloat();
|
|
data["x"] = reader.ReadFloat();
|
|
data["y"] = reader.ReadFloat();
|
|
data["scaleX"] = reader.ReadFloat();
|
|
data["scaleY"] = reader.ReadFloat();
|
|
data["shearX"] = reader.ReadFloat();
|
|
data["shearY"] = reader.ReadFloat();
|
|
data["length"] = reader.ReadFloat();
|
|
data["transform"] = SkeletonBinary.TransformModeValues[reader.ReadVarInt()].ToString();
|
|
data["skin"] = reader.ReadBoolean();
|
|
if (nonessential) reader.ReadInt();
|
|
bones.Add(data);
|
|
}
|
|
return bones;
|
|
}
|
|
|
|
private JsonArray ReadSlots()
|
|
{
|
|
JsonArray bones = root["bones"].AsArray();
|
|
JsonArray slots = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
data["name"] = reader.ReadString();
|
|
data["bone"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
data["color"] = reader.ReadInt().ToString("x8"); // 0xrrggbbaa -> rrggbbaa
|
|
int dark = reader.ReadInt();
|
|
if (dark != -1) data["dark"] = dark.ToString("x6"); // 0x00rrggbb -> rrggbb
|
|
data["attachment"] = reader.ReadStringRef();
|
|
data["blend"] = ((BlendMode)reader.ReadVarInt()).ToString();
|
|
slots.Add(data);
|
|
}
|
|
return slots;
|
|
}
|
|
|
|
private JsonArray ReadIK()
|
|
{
|
|
JsonArray bones = root["bones"].AsArray();
|
|
JsonArray ik = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
data["name"] = reader.ReadString();
|
|
data["order"] = reader.ReadVarInt();
|
|
data["skin"] = reader.ReadBoolean();
|
|
data["bones"] = ReadNames(bones);
|
|
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
data["mix"] = reader.ReadFloat();
|
|
data["softness"] = reader.ReadFloat();
|
|
data["bendPositive"] = reader.ReadSByte() > 0;
|
|
data["compress"] = reader.ReadBoolean();
|
|
data["stretch"] = reader.ReadBoolean();
|
|
data["uniform"] = reader.ReadBoolean();
|
|
ik.Add(data);
|
|
}
|
|
return ik;
|
|
}
|
|
|
|
private JsonArray ReadTransform()
|
|
{
|
|
JsonArray bones = root["bones"].AsArray();
|
|
JsonArray transform = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
data["name"] = reader.ReadString();
|
|
data["order"] = reader.ReadVarInt();
|
|
data["skin"] = reader.ReadBoolean();
|
|
data["bones"] = ReadNames(bones);
|
|
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
data["local"] = reader.ReadBoolean();
|
|
data["relative"] = reader.ReadBoolean();
|
|
data["rotation"] = reader.ReadFloat();
|
|
data["x"] = reader.ReadFloat();
|
|
data["y"] = reader.ReadFloat();
|
|
data["scaleX"] = reader.ReadFloat();
|
|
data["scaleY"] = reader.ReadFloat();
|
|
data["shearY"] = reader.ReadFloat();
|
|
data["rotateMix"] = reader.ReadFloat();
|
|
data["translateMix"] = reader.ReadFloat();
|
|
data["scaleMix"] = reader.ReadFloat();
|
|
data["shearMix"] = reader.ReadFloat();
|
|
transform.Add(data);
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
private JsonArray ReadPath()
|
|
{
|
|
JsonArray bones = root["bones"].AsArray();
|
|
JsonArray path = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
data["name"] = reader.ReadString();
|
|
data["order"] = reader.ReadVarInt();
|
|
data["skin"] = reader.ReadBoolean();
|
|
data["bones"] = ReadNames(bones);
|
|
data["target"] = bones[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
data["positionMode"] = ((PositionMode)reader.ReadVarInt()).ToString();
|
|
data["spacingMode"] = ((SpacingMode)reader.ReadVarInt()).ToString();
|
|
data["rotateMode"] = ((RotateMode)reader.ReadVarInt()).ToString();
|
|
data["rotation"] = reader.ReadFloat();
|
|
data["position"] = reader.ReadFloat();
|
|
data["spacing"] = reader.ReadFloat();
|
|
data["rotateMix"] = reader.ReadFloat();
|
|
data["translateMix"] = reader.ReadFloat();
|
|
path.Add(data);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
private JsonArray ReadSkins()
|
|
{
|
|
JsonArray skins = [];
|
|
|
|
// default skin
|
|
if (ReadSkin(true) is JsonObject data)
|
|
skins.Add(data);
|
|
|
|
// other skins
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
skins.Add(ReadSkin());
|
|
|
|
return skins;
|
|
}
|
|
|
|
private JsonObject? ReadSkin(bool isDefault = false)
|
|
{
|
|
JsonObject skin = [];
|
|
int slotCount;
|
|
if (isDefault)
|
|
{
|
|
// 这里固定有一个给 default 的 count 值, 算是占位符, 如果是 0 则表示没有 default 的 skin
|
|
skin["name"] = "default";
|
|
slotCount = reader.ReadVarInt();
|
|
if (slotCount <= 0) return null;
|
|
}
|
|
else
|
|
{
|
|
skin["name"] = reader.ReadStringRef();
|
|
skin["bones"] = ReadNames(root["bones"].AsArray());
|
|
skin["ik"] = ReadNames(root["ik"].AsArray());
|
|
skin["transform"] = ReadNames(root["transform"].AsArray()); ;
|
|
skin["path"] = ReadNames(root["path"].AsArray()); ;
|
|
slotCount = reader.ReadVarInt();
|
|
}
|
|
|
|
JsonArray slots = root["slots"].AsArray();
|
|
JsonObject skinAttachments = [];
|
|
while (slotCount-- > 0)
|
|
{
|
|
JsonObject slotAttachments = [];
|
|
skinAttachments[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = slotAttachments;
|
|
for (int attachmentCount = reader.ReadVarInt(); attachmentCount > 0; attachmentCount--)
|
|
{
|
|
var attachmentKey = reader.ReadStringRef();
|
|
slotAttachments[attachmentKey] = ReadAttachment(attachmentKey);
|
|
}
|
|
}
|
|
skin["attachments"] = skinAttachments;
|
|
|
|
return skin;
|
|
}
|
|
|
|
private JsonObject ReadAttachment(string keyName)
|
|
{
|
|
JsonArray slots = root["slots"].AsArray();
|
|
JsonObject attachment = [];
|
|
int vertexCount;
|
|
string path;
|
|
|
|
string name = reader.ReadStringRef() ?? keyName;
|
|
var type = (AttachmentType)reader.ReadByte();
|
|
attachment["name"] = name;
|
|
attachment["type"] = type.ToString();
|
|
switch (type)
|
|
{
|
|
case AttachmentType.Region:
|
|
path = reader.ReadStringRef();
|
|
if (path is not null) attachment["path"] = path;
|
|
attachment["rotation"] = reader.ReadFloat();
|
|
attachment["x"] = reader.ReadFloat();
|
|
attachment["y"] = reader.ReadFloat();
|
|
attachment["scaleX"] = reader.ReadFloat();
|
|
attachment["scaleY"] = reader.ReadFloat();
|
|
attachment["width"] = reader.ReadFloat();
|
|
attachment["height"] = reader.ReadFloat();
|
|
attachment["color"] = reader.ReadInt().ToString("x8");
|
|
break;
|
|
case AttachmentType.Boundingbox:
|
|
vertexCount = reader.ReadVarInt();
|
|
attachment["vertexCount"] = vertexCount;
|
|
attachment["vertices"] = ReadVertices(vertexCount);
|
|
if (nonessential) reader.ReadInt();
|
|
break;
|
|
case AttachmentType.Mesh:
|
|
path = reader.ReadStringRef();
|
|
if (path is not null) attachment["path"] = path;
|
|
attachment["color"] = reader.ReadInt().ToString("x8");
|
|
vertexCount = reader.ReadVarInt();
|
|
attachment["uvs"] = ReadFloatArray(vertexCount << 1); // vertexCount = uvs.Length
|
|
attachment["triangles"] = ReadShortArray();
|
|
attachment["vertices"] = ReadVertices(vertexCount);
|
|
attachment["hull"] = reader.ReadVarInt();
|
|
if (nonessential)
|
|
{
|
|
attachment["edges"] = ReadShortArray();
|
|
attachment["width"] = reader.ReadFloat();
|
|
attachment["height"] = reader.ReadFloat();
|
|
}
|
|
break;
|
|
case AttachmentType.Linkedmesh:
|
|
path = reader.ReadStringRef();
|
|
if (path is not null) attachment["path"] = path;
|
|
attachment["color"] = reader.ReadInt().ToString("x8");
|
|
attachment["skin"] = reader.ReadStringRef();
|
|
attachment["parent"] = reader.ReadStringRef();
|
|
attachment["deform"] = reader.ReadBoolean();
|
|
if (nonessential)
|
|
{
|
|
attachment["width"] = reader.ReadFloat();
|
|
attachment["height"] = reader.ReadFloat();
|
|
}
|
|
// 补充缺失的必需 key
|
|
attachment["uvs"] = new JsonArray();
|
|
attachment["triangles"] = new JsonArray();
|
|
attachment["vertices"] = new JsonArray();
|
|
break;
|
|
case AttachmentType.Path:
|
|
attachment["closed"] = reader.ReadBoolean();
|
|
attachment["constantSpeed"] = reader.ReadBoolean();
|
|
vertexCount = reader.ReadVarInt();
|
|
attachment["vertexCount"] = vertexCount;
|
|
attachment["vertices"] = ReadVertices(vertexCount);
|
|
attachment["lengths"] = ReadFloatArray(vertexCount / 3);
|
|
if (nonessential) reader.ReadInt();
|
|
break;
|
|
case AttachmentType.Point:
|
|
attachment["rotation"] = reader.ReadFloat();
|
|
attachment["x"] = reader.ReadFloat();
|
|
attachment["y"] = reader.ReadFloat();
|
|
if (nonessential) reader.ReadInt();
|
|
break;
|
|
case AttachmentType.Clipping:
|
|
attachment["end"] = slots[reader.ReadVarInt()]["name"].GetValue<string>();
|
|
vertexCount = reader.ReadVarInt();
|
|
attachment["vertexCount"] = vertexCount;
|
|
attachment["vertices"] = ReadVertices(vertexCount);
|
|
if (nonessential) reader.ReadInt();
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Invalid attachment type: {type}");
|
|
}
|
|
return attachment;
|
|
}
|
|
|
|
private JsonObject ReadEvents()
|
|
{
|
|
idx2Event.Clear();
|
|
JsonObject events = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
var name = reader.ReadStringRef();
|
|
events[name] = data;
|
|
data["name"] = name; // 额外增加的, 方便后面查找
|
|
data["int"] = reader.ReadVarInt(false);
|
|
data["float"] = reader.ReadFloat();
|
|
data["string"] = reader.ReadString();
|
|
if (reader.ReadString() is string audio)
|
|
{
|
|
data["audio"] = audio;
|
|
data["volume"] = reader.ReadFloat();
|
|
data["balance"] = reader.ReadFloat();
|
|
}
|
|
idx2Event.Add(data);
|
|
}
|
|
return events;
|
|
}
|
|
|
|
private JsonObject ReadAnimations()
|
|
{
|
|
JsonObject animations = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
{
|
|
JsonObject data = [];
|
|
animations[reader.ReadString()] = data;
|
|
if (ReadSlotTimelines() is JsonObject slots) data["slots"] = slots;
|
|
if (ReadBoneTimelines() is JsonObject bones) data["bones"] = bones;
|
|
if (ReadIKTimelines() is JsonObject ik) data["ik"] = ik;
|
|
if (ReadTransformTimelines() is JsonObject transform) data["transform"] = transform;
|
|
if (ReadPathTimelines() is JsonObject path) data["path"] = path;
|
|
if (ReadDeformTimelines() is JsonObject deform) data["deform"] = deform;
|
|
if (ReadDrawOrderTimelines() is JsonArray draworder) data["drawOrder"] = draworder;
|
|
if (ReadEventTimelines() is JsonArray events) data["events"] = events;
|
|
}
|
|
return animations;
|
|
}
|
|
|
|
private JsonObject? ReadSlotTimelines()
|
|
{
|
|
JsonArray slots = root["slots"].AsArray();
|
|
JsonObject slotTimelines = [];
|
|
|
|
for (int slotCount = reader.ReadVarInt(); slotCount > 0; slotCount--)
|
|
{
|
|
JsonObject timeline = [];
|
|
slotTimelines[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
|
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
var type = reader.ReadByte();
|
|
var frameCount = reader.ReadVarInt();
|
|
switch (type)
|
|
{
|
|
case SkeletonBinary.SLOT_ATTACHMENT:
|
|
timeline["attachment"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
frames.Add(new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["name"] = reader.ReadStringRef(),
|
|
});
|
|
}
|
|
break;
|
|
case SkeletonBinary.SLOT_COLOR:
|
|
timeline["color"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["color"] = reader.ReadInt().ToString("x8"),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
case SkeletonBinary.SLOT_TWO_COLOR:
|
|
timeline["twoColor"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["light"] = reader.ReadInt().ToString("x8"),
|
|
["dark"] = reader.ReadInt().ToString("x6"),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Invalid slot timeline type: {type}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return slotTimelines.Count > 0 ? slotTimelines : null;
|
|
}
|
|
|
|
private JsonObject? ReadBoneTimelines()
|
|
{
|
|
JsonArray bones = root["bones"].AsArray();
|
|
JsonObject boneTimelines = [];
|
|
|
|
for (int boneCount = reader.ReadVarInt(); boneCount > 0; boneCount--)
|
|
{
|
|
JsonObject timeline = [];
|
|
boneTimelines[bones[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
|
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
var type = reader.ReadByte();
|
|
var frameCount = reader.ReadVarInt();
|
|
switch (type)
|
|
{
|
|
case SkeletonBinary.BONE_ROTATE:
|
|
timeline["rotate"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["angle"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
case SkeletonBinary.BONE_TRANSLATE:
|
|
timeline["translate"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["x"] = reader.ReadFloat(),
|
|
["y"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
case SkeletonBinary.BONE_SCALE:
|
|
timeline["scale"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["x"] = reader.ReadFloat(),
|
|
["y"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
case SkeletonBinary.BONE_SHEAR:
|
|
timeline["shear"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["x"] = reader.ReadFloat(),
|
|
["y"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Invalid bone timeline type: {type}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return boneTimelines.Count > 0 ? boneTimelines : null;
|
|
}
|
|
|
|
private JsonObject? ReadIKTimelines()
|
|
{
|
|
JsonArray ik = root["ik"].AsArray();
|
|
JsonObject ikTimelines = [];
|
|
|
|
for (int ikCount = reader.ReadVarInt(); ikCount > 0; ikCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
ikTimelines[ik[reader.ReadVarInt()]["name"].GetValue<string>()] = frames;
|
|
for (int frameCount = reader.ReadVarInt(); frameCount > 0; frameCount--)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["mix"] = reader.ReadFloat(),
|
|
["softness"] = reader.ReadFloat(),
|
|
["bendPositive"] = reader.ReadSByte() > 0,
|
|
["compress"] = reader.ReadBoolean(),
|
|
["stretch"] = reader.ReadBoolean(),
|
|
};
|
|
if (frameCount > 1) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
}
|
|
|
|
return ikTimelines.Count > 0 ? ikTimelines : null;
|
|
}
|
|
|
|
private JsonObject? ReadTransformTimelines()
|
|
{
|
|
JsonArray transform = root["transform"].AsArray();
|
|
JsonObject transformTimelines = [];
|
|
|
|
for (int transformCount = reader.ReadVarInt(); transformCount > 0; transformCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
transformTimelines[transform[reader.ReadVarInt()]["name"].GetValue<string>()] = frames;
|
|
for (int frameCount = reader.ReadVarInt(); frameCount > 0; frameCount--)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["rotateMix"] = reader.ReadFloat(),
|
|
["translateMix"] = reader.ReadFloat(),
|
|
["scaleMix"] = reader.ReadFloat(),
|
|
["shearMix"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 1) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
}
|
|
|
|
return transformTimelines.Count > 0 ? transformTimelines : null;
|
|
}
|
|
|
|
private JsonObject? ReadPathTimelines()
|
|
{
|
|
JsonArray path = root["path"].AsArray();
|
|
JsonObject pathTimelines = [];
|
|
|
|
for (int pathCount = reader.ReadVarInt(); pathCount > 0; pathCount--)
|
|
{
|
|
JsonObject timeline = [];
|
|
pathTimelines[path[reader.ReadVarInt()]["name"].GetValue<string>()] = timeline;
|
|
for (int timelineCount = reader.ReadVarInt(); timelineCount > 0; timelineCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
var type = reader.ReadByte();
|
|
var frameCount = reader.ReadVarInt();
|
|
switch (type)
|
|
{
|
|
case SkeletonBinary.PATH_POSITION:
|
|
timeline["position"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
frames.Add(new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["position"] = reader.ReadFloat(),
|
|
});
|
|
}
|
|
break;
|
|
case SkeletonBinary.PATH_SPACING:
|
|
timeline["spacing"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["spacing"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
case SkeletonBinary.PATH_MIX:
|
|
timeline["mix"] = frames;
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
["rotateMix"] = reader.ReadFloat(),
|
|
["translateMix"] = reader.ReadFloat(),
|
|
};
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Invalid path timeline type: {type}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return pathTimelines.Count > 0 ? pathTimelines : null;
|
|
}
|
|
|
|
private JsonObject? ReadDeformTimelines()
|
|
{
|
|
JsonArray slots = root["slots"].AsArray();
|
|
JsonArray skins = root["skins"].AsArray();
|
|
JsonObject deformTimelines = [];
|
|
|
|
for (int skinCount = reader.ReadVarInt(); skinCount > 0; skinCount--)
|
|
{
|
|
JsonObject skinValue = [];
|
|
deformTimelines[skins[reader.ReadVarInt()]["name"].GetValue<string>()] = skinValue;
|
|
for (int slotCount = reader.ReadVarInt(); slotCount > 0; slotCount--)
|
|
{
|
|
JsonObject slotValue = [];
|
|
skinValue[slots[reader.ReadVarInt()]["name"].GetValue<string>()] = slotValue;
|
|
for (int attachmentCount = reader.ReadVarInt(); attachmentCount > 0; attachmentCount--)
|
|
{
|
|
JsonArray frames = [];
|
|
slotValue[reader.ReadStringRef()] = frames;
|
|
var frameCount = reader.ReadVarInt();
|
|
while (frameCount-- > 0)
|
|
{
|
|
var o = new JsonObject()
|
|
{
|
|
["time"] = reader.ReadFloat(),
|
|
};
|
|
var end = reader.ReadVarInt();
|
|
if (end > 0)
|
|
{
|
|
var start = reader.ReadVarInt();
|
|
o["offset"] = start;
|
|
o["vertices"] = ReadFloatArray(end - start);
|
|
}
|
|
if (frameCount > 0) ReadCurve(o);
|
|
frames.Add(o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return deformTimelines.Count > 0 ? deformTimelines : null;
|
|
}
|
|
|
|
private JsonArray? ReadDrawOrderTimelines()
|
|
{
|
|
JsonArray slots = root["slots"].AsArray();
|
|
JsonArray drawOrderTimelines = [];
|
|
|
|
for (int drawOrderCount = reader.ReadVarInt(); drawOrderCount > 0; drawOrderCount--)
|
|
{
|
|
JsonObject data = new()
|
|
{
|
|
["time"] = reader.ReadFloat()
|
|
};
|
|
JsonArray offsets = [];
|
|
data["offsets"] = offsets;
|
|
for (int offsetCount = reader.ReadVarInt(); offsetCount > 0; offsetCount--)
|
|
{
|
|
offsets.Add(new JsonObject()
|
|
{
|
|
["slot"] = slots[reader.ReadVarInt()]["name"].GetValue<string>(),
|
|
["offset"] = reader.ReadVarInt(),
|
|
});
|
|
}
|
|
drawOrderTimelines.Add(data);
|
|
}
|
|
|
|
return drawOrderTimelines.Count > 0 ? drawOrderTimelines : null;
|
|
}
|
|
|
|
private JsonArray? ReadEventTimelines()
|
|
{
|
|
JsonArray eventTimelines = [];
|
|
for (int eventCount = reader.ReadVarInt(); eventCount > 0; eventCount--)
|
|
{
|
|
JsonObject data = [];
|
|
data["time"] = reader.ReadFloat();
|
|
JsonObject eventData = idx2Event[reader.ReadVarInt()].AsObject();
|
|
data["name"] = eventData["name"].GetValue<string>();
|
|
data["int"] = reader.ReadVarInt();
|
|
data["float"] = reader.ReadFloat();
|
|
data["string"] = reader.ReadBoolean() ? reader.ReadString() : eventData["string"].GetValue<string>();
|
|
if (eventData.ContainsKey("audio"))
|
|
{
|
|
data["volume"] = eventData["volume"].GetValue<string>();
|
|
data["balance"] = eventData["balance"].GetValue<string>();
|
|
}
|
|
eventTimelines.Add(data);
|
|
}
|
|
|
|
return eventTimelines.Count > 0 ? eventTimelines : null;
|
|
}
|
|
|
|
private JsonArray ReadNames(JsonArray array)
|
|
{
|
|
JsonArray names = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
names.Add(array[reader.ReadVarInt()]["name"].GetValue<string>());
|
|
return names;
|
|
}
|
|
|
|
private JsonArray ReadFloatArray(int n)
|
|
{
|
|
JsonArray array = [];
|
|
while (n-- > 0)
|
|
array.Add(reader.ReadFloat());
|
|
return array;
|
|
}
|
|
|
|
private JsonArray ReadShortArray()
|
|
{
|
|
JsonArray array = [];
|
|
for (int n = reader.ReadVarInt(); n > 0; n--)
|
|
array.Add((reader.ReadByte() << 8) | reader.ReadByte());
|
|
return array;
|
|
}
|
|
|
|
private JsonArray ReadVertices(int vertexCount)
|
|
{
|
|
JsonArray vertices = [];
|
|
if (!reader.ReadBoolean())
|
|
return ReadFloatArray(vertexCount << 1);
|
|
|
|
for (int i = 0; i < vertexCount; i++)
|
|
{
|
|
int bonesCount = reader.ReadVarInt();
|
|
vertices.Add(bonesCount);
|
|
for (int j = 0; j < bonesCount; j++)
|
|
{
|
|
vertices.Add(reader.ReadVarInt());
|
|
vertices.Add(reader.ReadFloat());
|
|
vertices.Add(reader.ReadFloat());
|
|
vertices.Add(reader.ReadFloat());
|
|
}
|
|
}
|
|
return vertices;
|
|
}
|
|
|
|
private void ReadCurve(JsonObject obj)
|
|
{
|
|
var type = reader.ReadByte();
|
|
switch (type)
|
|
{
|
|
case SkeletonBinary.CURVE_LINEAR:
|
|
obj["curve"] = 1 / 3f;
|
|
obj["c2"] = 1 / 3f;
|
|
obj["c3"] = 2 / 3f;
|
|
obj["c4"] = 2 / 3f;
|
|
break;
|
|
case SkeletonBinary.CURVE_STEPPED:
|
|
obj["curve"] = "stepped";
|
|
break;
|
|
case SkeletonBinary.CURVE_BEZIER:
|
|
obj["curve"] = reader.ReadFloat();
|
|
obj["c2"] = reader.ReadFloat();
|
|
obj["c3"] = reader.ReadFloat();
|
|
obj["c4"] = reader.ReadFloat();
|
|
break;
|
|
default:
|
|
throw new ArgumentException($"Invalid curve type: {type}"); ;
|
|
}
|
|
}
|
|
|
|
protected override void WriteBinary(JsonObject root, string binPath)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
//public void WriteFloatArray(float[] array)
|
|
//{
|
|
// foreach (var i in array)
|
|
// writer.WriteFloat(i);
|
|
//}
|
|
|
|
//public void WriteShortArray(int[] array)
|
|
//{
|
|
// foreach (var i in array)
|
|
// {
|
|
// writer.WriteByte((byte)(i >> 8));
|
|
// writer.WriteByte((byte)i);
|
|
// }
|
|
//}
|
|
}
|
|
}
|