using System; using System.Collections.Generic; using System.IO; namespace Spine { public class SkeletonJson { public SkeletonJson(params Atlas[] atlasArray) : this(new AtlasAttachmentLoader(atlasArray)) { } public SkeletonJson(AttachmentLoader attachmentLoader) { if (attachmentLoader == null) { throw new ArgumentNullException("attachmentLoader cannot be null."); } this.attachmentLoader = attachmentLoader; this.Scale = 1f; } public float Scale { get; set; } public SkeletonData ReadSkeletonData(string path) { SkeletonData result; using (StreamReader streamReader = new StreamReader(path)) { SkeletonData skeletonData = this.ReadSkeletonData(streamReader); skeletonData.name = Path.GetFileNameWithoutExtension(path); result = skeletonData; } return result; } public SkeletonData ReadSkeletonData(TextReader reader) { if (reader == null) { throw new ArgumentNullException("reader cannot be null."); } SkeletonData skeletonData = new SkeletonData(); Dictionary dictionary = Json.Deserialize(reader) as Dictionary; if (dictionary == null) { throw new Exception("Invalid JSON."); } if (dictionary.ContainsKey("skeleton")) { Dictionary dictionary2 = (Dictionary)dictionary["skeleton"]; skeletonData.hash = (string)dictionary2["hash"]; skeletonData.version = (string)dictionary2["spine"]; skeletonData.width = this.GetFloat(dictionary2, "width", 0f); skeletonData.height = this.GetFloat(dictionary2, "height", 0f); } foreach (object obj in ((List)dictionary["bones"])) { Dictionary dictionary3 = (Dictionary)obj; BoneData boneData = null; if (dictionary3.ContainsKey("parent")) { boneData = skeletonData.FindBone((string)dictionary3["parent"]); if (boneData == null) { throw new Exception("Parent bone not found: " + dictionary3["parent"]); } } BoneData boneData2 = new BoneData((string)dictionary3["name"], boneData); boneData2.length = this.GetFloat(dictionary3, "length", 0f) * this.Scale; boneData2.x = this.GetFloat(dictionary3, "x", 0f) * this.Scale; boneData2.y = this.GetFloat(dictionary3, "y", 0f) * this.Scale; boneData2.rotation = this.GetFloat(dictionary3, "rotation", 0f); boneData2.scaleX = this.GetFloat(dictionary3, "scaleX", 1f); boneData2.scaleY = this.GetFloat(dictionary3, "scaleY", 1f); boneData2.flipX = this.GetBoolean(dictionary3, "flipX", false); boneData2.flipY = this.GetBoolean(dictionary3, "flipY", false); boneData2.inheritScale = this.GetBoolean(dictionary3, "inheritScale", true); boneData2.inheritRotation = this.GetBoolean(dictionary3, "inheritRotation", true); skeletonData.bones.Add(boneData2); } if (dictionary.ContainsKey("ik")) { foreach (object obj2 in ((List)dictionary["ik"])) { Dictionary dictionary4 = (Dictionary)obj2; IkConstraintData ikConstraintData = new IkConstraintData((string)dictionary4["name"]); foreach (object obj3 in ((List)dictionary4["bones"])) { string text = (string)obj3; BoneData boneData3 = skeletonData.FindBone(text); if (boneData3 == null) { throw new Exception("IK bone not found: " + text); } ikConstraintData.bones.Add(boneData3); } string text2 = (string)dictionary4["target"]; ikConstraintData.target = skeletonData.FindBone(text2); if (ikConstraintData.target == null) { throw new Exception("Target bone not found: " + text2); } ikConstraintData.bendDirection = ((!this.GetBoolean(dictionary4, "bendPositive", true)) ? -1 : 1); ikConstraintData.mix = this.GetFloat(dictionary4, "mix", 1f); skeletonData.ikConstraints.Add(ikConstraintData); } } if (dictionary.ContainsKey("slots")) { foreach (object obj4 in ((List)dictionary["slots"])) { Dictionary dictionary5 = (Dictionary)obj4; string name = (string)dictionary5["name"]; string text3 = (string)dictionary5["bone"]; BoneData boneData4 = skeletonData.FindBone(text3); if (boneData4 == null) { throw new Exception("Slot bone not found: " + text3); } SlotData slotData = new SlotData(name, boneData4); if (dictionary5.ContainsKey("color")) { string hexString = (string)dictionary5["color"]; slotData.r = this.ToColor(hexString, 0); slotData.g = this.ToColor(hexString, 1); slotData.b = this.ToColor(hexString, 2); slotData.a = this.ToColor(hexString, 3); } if (dictionary5.ContainsKey("attachment")) { slotData.attachmentName = (string)dictionary5["attachment"]; } if (dictionary5.ContainsKey("blend")) { slotData.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (string)dictionary5["blend"], false); } else { slotData.blendMode = BlendMode.normal; } skeletonData.slots.Add(slotData); } } if (dictionary.ContainsKey("skins")) { foreach (KeyValuePair keyValuePair in ((Dictionary)dictionary["skins"])) { Skin skin = new Skin(keyValuePair.Key); foreach (KeyValuePair keyValuePair2 in ((Dictionary)keyValuePair.Value)) { int slotIndex = skeletonData.FindSlotIndex(keyValuePair2.Key); foreach (KeyValuePair keyValuePair3 in ((Dictionary)keyValuePair2.Value)) { Attachment attachment = this.ReadAttachment(skin, keyValuePair3.Key, (Dictionary)keyValuePair3.Value); if (attachment != null) { skin.AddAttachment(slotIndex, keyValuePair3.Key, attachment); } } } skeletonData.skins.Add(skin); if (skin.name == "default") { skeletonData.defaultSkin = skin; } } } if (dictionary.ContainsKey("events")) { foreach (KeyValuePair keyValuePair4 in ((Dictionary)dictionary["events"])) { Dictionary map = (Dictionary)keyValuePair4.Value; EventData eventData = new EventData(keyValuePair4.Key); eventData.Int = this.GetInt(map, "int", 0); eventData.Float = this.GetFloat(map, "float", 0f); eventData.String = this.GetString(map, "string", null); skeletonData.events.Add(eventData); } } if (dictionary.ContainsKey("animations")) { foreach (KeyValuePair keyValuePair5 in ((Dictionary)dictionary["animations"])) { this.ReadAnimation(keyValuePair5.Key, (Dictionary)keyValuePair5.Value, skeletonData); } } skeletonData.bones.TrimExcess(); skeletonData.slots.TrimExcess(); skeletonData.skins.TrimExcess(); skeletonData.events.TrimExcess(); skeletonData.animations.TrimExcess(); skeletonData.ikConstraints.TrimExcess(); return skeletonData; } private Attachment ReadAttachment(Skin skin, string name, Dictionary map) { if (map.ContainsKey("name")) { name = (string)map["name"]; } AttachmentType attachmentType = AttachmentType.region; if (map.ContainsKey("type")) { attachmentType = (AttachmentType)Enum.Parse(typeof(AttachmentType), (string)map["type"], false); } string path = name; if (map.ContainsKey("path")) { path = (string)map["path"]; } switch (attachmentType) { case AttachmentType.region: { RegionAttachment regionAttachment = this.attachmentLoader.NewRegionAttachment(skin, name, path); if (regionAttachment == null) { return null; } regionAttachment.Path = path; regionAttachment.x = this.GetFloat(map, "x", 0f) * this.Scale; regionAttachment.y = this.GetFloat(map, "y", 0f) * this.Scale; regionAttachment.scaleX = this.GetFloat(map, "scaleX", 1f); regionAttachment.scaleY = this.GetFloat(map, "scaleY", 1f); regionAttachment.rotation = this.GetFloat(map, "rotation", 0f); regionAttachment.width = this.GetFloat(map, "width", 32f) * this.Scale; regionAttachment.height = this.GetFloat(map, "height", 32f) * this.Scale; regionAttachment.UpdateOffset(); if (map.ContainsKey("color")) { string hexString = (string)map["color"]; regionAttachment.r = this.ToColor(hexString, 0); regionAttachment.g = this.ToColor(hexString, 1); regionAttachment.b = this.ToColor(hexString, 2); regionAttachment.a = this.ToColor(hexString, 3); } return regionAttachment; } case AttachmentType.boundingbox: { BoundingBoxAttachment boundingBoxAttachment = this.attachmentLoader.NewBoundingBoxAttachment(skin, name); if (boundingBoxAttachment == null) { return null; } boundingBoxAttachment.vertices = this.GetFloatArray(map, "vertices", this.Scale); return boundingBoxAttachment; } case AttachmentType.mesh: { MeshAttachment meshAttachment = this.attachmentLoader.NewMeshAttachment(skin, name, path); if (meshAttachment == null) { return null; } meshAttachment.Path = path; meshAttachment.vertices = this.GetFloatArray(map, "vertices", this.Scale); meshAttachment.triangles = this.GetIntArray(map, "triangles"); meshAttachment.regionUVs = this.GetFloatArray(map, "uvs", 1f); meshAttachment.UpdateUVs(); if (map.ContainsKey("color")) { string hexString2 = (string)map["color"]; meshAttachment.r = this.ToColor(hexString2, 0); meshAttachment.g = this.ToColor(hexString2, 1); meshAttachment.b = this.ToColor(hexString2, 2); meshAttachment.a = this.ToColor(hexString2, 3); } meshAttachment.HullLength = this.GetInt(map, "hull", 0) * 2; if (map.ContainsKey("edges")) { meshAttachment.Edges = this.GetIntArray(map, "edges"); } meshAttachment.Width = (float)this.GetInt(map, "width", 0) * this.Scale; meshAttachment.Height = (float)this.GetInt(map, "height", 0) * this.Scale; return meshAttachment; } case AttachmentType.skinnedmesh: { SkinnedMeshAttachment skinnedMeshAttachment = this.attachmentLoader.NewSkinnedMeshAttachment(skin, name, path); if (skinnedMeshAttachment == null) { return null; } skinnedMeshAttachment.Path = path; float[] floatArray = this.GetFloatArray(map, "uvs", 1f); float[] floatArray2 = this.GetFloatArray(map, "vertices", 1f); List list = new List(floatArray.Length * 3 * 3); List list2 = new List(floatArray.Length * 3); float scale = this.Scale; int i = 0; int num = floatArray2.Length; while (i < num) { int num2 = (int)floatArray2[i++]; list2.Add(num2); int num3 = i + num2 * 4; while (i < num3) { list2.Add((int)floatArray2[i]); list.Add(floatArray2[i + 1] * scale); list.Add(floatArray2[i + 2] * scale); list.Add(floatArray2[i + 3]); i += 4; } } skinnedMeshAttachment.bones = list2.ToArray(); skinnedMeshAttachment.weights = list.ToArray(); skinnedMeshAttachment.triangles = this.GetIntArray(map, "triangles"); skinnedMeshAttachment.regionUVs = floatArray; skinnedMeshAttachment.UpdateUVs(); if (map.ContainsKey("color")) { string hexString3 = (string)map["color"]; skinnedMeshAttachment.r = this.ToColor(hexString3, 0); skinnedMeshAttachment.g = this.ToColor(hexString3, 1); skinnedMeshAttachment.b = this.ToColor(hexString3, 2); skinnedMeshAttachment.a = this.ToColor(hexString3, 3); } skinnedMeshAttachment.HullLength = this.GetInt(map, "hull", 0) * 2; if (map.ContainsKey("edges")) { skinnedMeshAttachment.Edges = this.GetIntArray(map, "edges"); } skinnedMeshAttachment.Width = (float)this.GetInt(map, "width", 0) * this.Scale; skinnedMeshAttachment.Height = (float)this.GetInt(map, "height", 0) * this.Scale; return skinnedMeshAttachment; } default: return null; } } private float[] GetFloatArray(Dictionary map, string name, float scale) { List list = (List)map[name]; float[] array = new float[list.Count]; if (scale == 1f) { int i = 0; int count = list.Count; while (i < count) { array[i] = (float)list[i]; i++; } } else { int j = 0; int count2 = list.Count; while (j < count2) { array[j] = (float)list[j] * scale; j++; } } return array; } private int[] GetIntArray(Dictionary map, string name) { List list = (List)map[name]; int[] array = new int[list.Count]; int i = 0; int count = list.Count; while (i < count) { array[i] = (int)((float)list[i]); i++; } return array; } private float GetFloat(Dictionary map, string name, float defaultValue) { if (!map.ContainsKey(name)) { return defaultValue; } return (float)map[name]; } private int GetInt(Dictionary map, string name, int defaultValue) { if (!map.ContainsKey(name)) { return defaultValue; } return (int)((float)map[name]); } private bool GetBoolean(Dictionary map, string name, bool defaultValue) { if (!map.ContainsKey(name)) { return defaultValue; } return (bool)map[name]; } private string GetString(Dictionary map, string name, string defaultValue) { if (!map.ContainsKey(name)) { return defaultValue; } return (string)map[name]; } private float ToColor(string hexString, int colorIndex) { if (hexString.Length != 8) { throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString); } return (float)Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / 255f; } private void ReadAnimation(string name, Dictionary map, SkeletonData skeletonData) { List list = new List(); float num = 0f; float scale = this.Scale; if (map.ContainsKey("slots")) { foreach (KeyValuePair keyValuePair in ((Dictionary)map["slots"])) { string key = keyValuePair.Key; int slotIndex = skeletonData.FindSlotIndex(key); Dictionary dictionary = (Dictionary)keyValuePair.Value; foreach (KeyValuePair keyValuePair2 in dictionary) { List list2 = (List)keyValuePair2.Value; string key2 = keyValuePair2.Key; if (key2 == "color") { ColorTimeline colorTimeline = new ColorTimeline(list2.Count); colorTimeline.slotIndex = slotIndex; int num2 = 0; foreach (object obj in list2) { Dictionary dictionary2 = (Dictionary)obj; float time = (float)dictionary2["time"]; string hexString = (string)dictionary2["color"]; colorTimeline.SetFrame(num2, time, this.ToColor(hexString, 0), this.ToColor(hexString, 1), this.ToColor(hexString, 2), this.ToColor(hexString, 3)); this.ReadCurve(colorTimeline, num2, dictionary2); num2++; } list.Add(colorTimeline); num = Math.Max(num, colorTimeline.frames[colorTimeline.FrameCount * 5 - 5]); } else { if (!(key2 == "attachment")) { throw new Exception(string.Concat(new string[] { "Invalid timeline type for a slot: ", key2, " (", key, ")" })); } AttachmentTimeline attachmentTimeline = new AttachmentTimeline(list2.Count); attachmentTimeline.slotIndex = slotIndex; int num3 = 0; foreach (object obj2 in list2) { Dictionary dictionary3 = (Dictionary)obj2; float time2 = (float)dictionary3["time"]; attachmentTimeline.SetFrame(num3++, time2, (string)dictionary3["name"]); } list.Add(attachmentTimeline); num = Math.Max(num, attachmentTimeline.frames[attachmentTimeline.FrameCount - 1]); } } } } if (map.ContainsKey("bones")) { foreach (KeyValuePair keyValuePair3 in ((Dictionary)map["bones"])) { string key3 = keyValuePair3.Key; int num4 = skeletonData.FindBoneIndex(key3); if (num4 == -1) { throw new Exception("Bone not found: " + key3); } Dictionary dictionary4 = (Dictionary)keyValuePair3.Value; foreach (KeyValuePair keyValuePair4 in dictionary4) { List list3 = (List)keyValuePair4.Value; string key4 = keyValuePair4.Key; if (key4 == "rotate") { RotateTimeline rotateTimeline = new RotateTimeline(list3.Count); rotateTimeline.boneIndex = num4; int num5 = 0; foreach (object obj3 in list3) { Dictionary dictionary5 = (Dictionary)obj3; float time3 = (float)dictionary5["time"]; rotateTimeline.SetFrame(num5, time3, (float)dictionary5["angle"]); this.ReadCurve(rotateTimeline, num5, dictionary5); num5++; } list.Add(rotateTimeline); num = Math.Max(num, rotateTimeline.frames[rotateTimeline.FrameCount * 2 - 2]); } else if (key4 == "translate" || key4 == "scale") { float num6 = 1f; TranslateTimeline translateTimeline; if (key4 == "scale") { translateTimeline = new ScaleTimeline(list3.Count); } else { translateTimeline = new TranslateTimeline(list3.Count); num6 = scale; } translateTimeline.boneIndex = num4; int num7 = 0; foreach (object obj4 in list3) { Dictionary dictionary6 = (Dictionary)obj4; float time4 = (float)dictionary6["time"]; float num8 = (!dictionary6.ContainsKey("x")) ? 0f : ((float)dictionary6["x"]); float num9 = (!dictionary6.ContainsKey("y")) ? 0f : ((float)dictionary6["y"]); translateTimeline.SetFrame(num7, time4, num8 * num6, num9 * num6); this.ReadCurve(translateTimeline, num7, dictionary6); num7++; } list.Add(translateTimeline); num = Math.Max(num, translateTimeline.frames[translateTimeline.FrameCount * 3 - 3]); } else { if (!(key4 == "flipX") && !(key4 == "flipY")) { throw new Exception(string.Concat(new string[] { "Invalid timeline type for a bone: ", key4, " (", key3, ")" })); } bool flag = key4 == "flipX"; FlipXTimeline flipXTimeline = (!flag) ? new FlipYTimeline(list3.Count) : new FlipXTimeline(list3.Count); flipXTimeline.boneIndex = num4; string key5 = (!flag) ? "y" : "x"; int num10 = 0; foreach (object obj5 in list3) { Dictionary dictionary7 = (Dictionary)obj5; float time5 = (float)dictionary7["time"]; flipXTimeline.SetFrame(num10, time5, dictionary7.ContainsKey(key5) && (bool)dictionary7[key5]); num10++; } list.Add(flipXTimeline); num = Math.Max(num, flipXTimeline.frames[flipXTimeline.FrameCount * 2 - 2]); } } } } if (map.ContainsKey("ik")) { foreach (KeyValuePair keyValuePair5 in ((Dictionary)map["ik"])) { IkConstraintData item = skeletonData.FindIkConstraint(keyValuePair5.Key); List list4 = (List)keyValuePair5.Value; IkConstraintTimeline ikConstraintTimeline = new IkConstraintTimeline(list4.Count); ikConstraintTimeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(item); int num11 = 0; foreach (object obj6 in list4) { Dictionary dictionary8 = (Dictionary)obj6; float time6 = (float)dictionary8["time"]; float mix = (!dictionary8.ContainsKey("mix")) ? 1f : ((float)dictionary8["mix"]); bool flag2 = !dictionary8.ContainsKey("bendPositive") || (bool)dictionary8["bendPositive"]; ikConstraintTimeline.SetFrame(num11, time6, mix, (!flag2) ? -1 : 1); this.ReadCurve(ikConstraintTimeline, num11, dictionary8); num11++; } list.Add(ikConstraintTimeline); num = Math.Max(num, ikConstraintTimeline.frames[ikConstraintTimeline.FrameCount * 3 - 3]); } } if (map.ContainsKey("ffd")) { foreach (KeyValuePair keyValuePair6 in ((Dictionary)map["ffd"])) { Skin skin = skeletonData.FindSkin(keyValuePair6.Key); foreach (KeyValuePair keyValuePair7 in ((Dictionary)keyValuePair6.Value)) { int slotIndex2 = skeletonData.FindSlotIndex(keyValuePair7.Key); foreach (KeyValuePair keyValuePair8 in ((Dictionary)keyValuePair7.Value)) { List list5 = (List)keyValuePair8.Value; FFDTimeline ffdtimeline = new FFDTimeline(list5.Count); Attachment attachment = skin.GetAttachment(slotIndex2, keyValuePair8.Key); if (attachment == null) { throw new Exception("FFD attachment not found: " + keyValuePair8.Key); } ffdtimeline.slotIndex = slotIndex2; ffdtimeline.attachment = attachment; int num12; if (attachment is MeshAttachment) { num12 = ((MeshAttachment)attachment).vertices.Length; } else { num12 = ((SkinnedMeshAttachment)attachment).Weights.Length / 3 * 2; } int num13 = 0; foreach (object obj7 in list5) { Dictionary dictionary9 = (Dictionary)obj7; float[] array; if (!dictionary9.ContainsKey("vertices")) { if (attachment is MeshAttachment) { array = ((MeshAttachment)attachment).vertices; } else { array = new float[num12]; } } else { List list6 = (List)dictionary9["vertices"]; array = new float[num12]; int @int = this.GetInt(dictionary9, "offset", 0); if (scale == 1f) { int i = 0; int count = list6.Count; while (i < count) { array[i + @int] = (float)list6[i]; i++; } } else { int j = 0; int count2 = list6.Count; while (j < count2) { array[j + @int] = (float)list6[j] * scale; j++; } } if (attachment is MeshAttachment) { float[] vertices = ((MeshAttachment)attachment).vertices; for (int k = 0; k < num12; k++) { array[k] += vertices[k]; } } } ffdtimeline.SetFrame(num13, (float)dictionary9["time"], array); this.ReadCurve(ffdtimeline, num13, dictionary9); num13++; } list.Add(ffdtimeline); num = Math.Max(num, ffdtimeline.frames[ffdtimeline.FrameCount - 1]); } } } } if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) { List list7 = (List)map[(!map.ContainsKey("drawOrder")) ? "draworder" : "drawOrder"]; DrawOrderTimeline drawOrderTimeline = new DrawOrderTimeline(list7.Count); int count3 = skeletonData.slots.Count; int num14 = 0; foreach (object obj8 in list7) { Dictionary dictionary10 = (Dictionary)obj8; int[] array2 = null; if (dictionary10.ContainsKey("offsets")) { array2 = new int[count3]; for (int l = count3 - 1; l >= 0; l--) { array2[l] = -1; } List list8 = (List)dictionary10["offsets"]; int[] array3 = new int[count3 - list8.Count]; int m = 0; int num15 = 0; foreach (object obj9 in list8) { Dictionary dictionary11 = (Dictionary)obj9; int num16 = skeletonData.FindSlotIndex((string)dictionary11["slot"]); if (num16 == -1) { throw new Exception("Slot not found: " + dictionary11["slot"]); } while (m != num16) { array3[num15++] = m++; } int num17 = m + (int)((float)dictionary11["offset"]); array2[num17] = m++; } while (m < count3) { array3[num15++] = m++; } for (int n = count3 - 1; n >= 0; n--) { if (array2[n] == -1) { array2[n] = array3[--num15]; } } } drawOrderTimeline.SetFrame(num14++, (float)dictionary10["time"], array2); } list.Add(drawOrderTimeline); num = Math.Max(num, drawOrderTimeline.frames[drawOrderTimeline.FrameCount - 1]); } if (map.ContainsKey("events")) { List list9 = (List)map["events"]; EventTimeline eventTimeline = new EventTimeline(list9.Count); int num18 = 0; foreach (object obj10 in list9) { Dictionary dictionary12 = (Dictionary)obj10; EventData eventData = skeletonData.FindEvent((string)dictionary12["name"]); if (eventData == null) { throw new Exception("Event not found: " + dictionary12["name"]); } Event @event = new Event(eventData); @event.Int = this.GetInt(dictionary12, "int", eventData.Int); @event.Float = this.GetFloat(dictionary12, "float", eventData.Float); @event.String = this.GetString(dictionary12, "string", eventData.String); eventTimeline.SetFrame(num18++, (float)dictionary12["time"], @event); } list.Add(eventTimeline); num = Math.Max(num, eventTimeline.frames[eventTimeline.FrameCount - 1]); } list.TrimExcess(); skeletonData.animations.Add(new Animation(name, list, num)); } private void ReadCurve(CurveTimeline timeline, int frameIndex, Dictionary valueMap) { if (!valueMap.ContainsKey("curve")) { return; } object obj = valueMap["curve"]; if (obj.Equals("stepped")) { timeline.SetStepped(frameIndex); } else if (obj is List) { List list = (List)obj; timeline.SetCurve(frameIndex, (float)list[0], (float)list[1], (float)list[2], (float)list[3]); } } private AttachmentLoader attachmentLoader; } }