Эх сурвалжийг харах

Added binary format support for C#.

Also fixed up line endings.
NathanSweet 10 жил өмнө
parent
commit
d0625e06a0

+ 1 - 0
spine-csharp/spine-csharp.csproj

@@ -75,6 +75,7 @@
     <Compile Include="src\Skeleton.cs" />
     <Compile Include="src\SkeletonData.cs" />
     <Compile Include="src\SkeletonJson.cs" />
+    <Compile Include="src\SkeletonBinary.cs" />
     <Compile Include="src\Skin.cs" />
     <Compile Include="src\Slot.cs" />
     <Compile Include="src\SlotData.cs" />

+ 1 - 0
spine-csharp/spine-csharp_xna.csproj

@@ -111,6 +111,7 @@
     <Compile Include="src\EventData.cs" />
     <Compile Include="src\Json.cs" />
     <Compile Include="src\Skeleton.cs" />
+    <Compile Include="src\SkeletonBinary.cs" />
     <Compile Include="src\SkeletonBounds.cs" />
     <Compile Include="src\SkeletonData.cs" />
     <Compile Include="src\SkeletonJson.cs" />

+ 6 - 6
spine-csharp/src/Animation.cs

@@ -365,7 +365,7 @@ namespace Spine {
 		}
 
 		/// <summary>Sets the time and value of the specified keyframe.</summary>
-		public void setFrame (int frameIndex, float time, float r, float g, float b, float a) {
+		public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) {
 			frameIndex *= 5;
 			frames[frameIndex] = time;
 			frames[frameIndex + 1] = r;
@@ -433,7 +433,7 @@ namespace Spine {
 		}
 
 		/// <summary>Sets the time and value of the specified keyframe.</summary>
-		public void setFrame (int frameIndex, float time, String attachmentName) {
+		public void SetFrame (int frameIndex, float time, String attachmentName) {
 			frames[frameIndex] = time;
 			attachmentNames[frameIndex] = attachmentName;
 		}
@@ -469,7 +469,7 @@ namespace Spine {
 		}
 
 		/// <summary>Sets the time and value of the specified keyframe.</summary>
-		public void setFrame (int frameIndex, float time, Event e) {
+		public void SetFrame (int frameIndex, float time, Event e) {
 			frames[frameIndex] = time;
 			events[frameIndex] = e;
 		}
@@ -518,7 +518,7 @@ namespace Spine {
 
 		/// <summary>Sets the time and value of the specified keyframe.</summary>
 		/// <param name="drawOrder">May be null to use bind pose draw order.</param>
-		public void setFrame (int frameIndex, float time, int[] drawOrder) {
+		public void SetFrame (int frameIndex, float time, int[] drawOrder) {
 			frames[frameIndex] = time;
 			drawOrders[frameIndex] = drawOrder;
 		}
@@ -564,7 +564,7 @@ namespace Spine {
 		}
 
 		/// <summary>Sets the time and value of the specified keyframe.</summary>
-		public void setFrame (int frameIndex, float time, float[] vertices) {
+		public void SetFrame (int frameIndex, float time, float[] vertices) {
 			frames[frameIndex] = time;
 			frameVertices[frameIndex] = vertices;
 		}
@@ -641,7 +641,7 @@ namespace Spine {
 		}
 
 		/** Sets the time, mix and bend direction of the specified keyframe. */
-		public void setFrame (int frameIndex, float time, float mix, int bendDirection) {
+		public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
 			frameIndex *= 3;
 			frames[frameIndex] = time;
 			frames[frameIndex + 1] = mix;

+ 663 - 0
spine-csharp/src/SkeletonBinary.cs

@@ -0,0 +1,663 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software (typically granted by licensing Spine), you
+ * may not (a) modify, translate, adapt or otherwise create derivative works,
+ * improvements of the Software or develop new applications using the Software
+ * or (b) remove, delete, alter or obscure any trademarks or any copyright,
+ * trademark, patent or other intellectual property or proprietary rights
+ * notices on or in the Software, including any copy thereof. Redistributions
+ * in binary or source form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+
+#if WINDOWS_STOREAPP
+using System.Threading.Tasks;
+using Windows.Storage;
+#endif
+
+namespace Spine {
+	public class SkeletonBinary {
+		public const int TIMELINE_SCALE = 0;
+		public const int TIMELINE_ROTATE = 1;
+		public const int TIMELINE_TRANSLATE = 2;
+		public const int TIMELINE_ATTACHMENT = 3;
+		public const int TIMELINE_COLOR = 4;
+		public const int TIMELINE_FLIPX = 5;
+		public const int TIMELINE_FLIPY = 6;
+
+		public const int CURVE_LINEAR = 0;
+		public const int CURVE_STEPPED = 1;
+		public const int CURVE_BEZIER = 2;
+
+		private AttachmentLoader attachmentLoader;
+		public float Scale { get; set; }
+		private char[] chars = new char[32];
+		private byte[] buffer = new byte[4];
+
+		public SkeletonBinary (params Atlas[] atlasArray)
+			: this(new AtlasAttachmentLoader(atlasArray)) {
+		}
+
+		public SkeletonBinary (AttachmentLoader attachmentLoader) {
+			if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
+			this.attachmentLoader = attachmentLoader;
+			Scale = 1;
+		}
+
+#if WINDOWS_STOREAPP
+		private async Task<SkeletonData> ReadFile(string path) {
+			var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
+			using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
+				SkeletonData skeletonData = ReadSkeletonData(input);
+				skeletonData.Name = Path.GetFileNameWithoutExtension(path);
+				return skeletonData;
+			}
+		}
+
+		public SkeletonData ReadSkeletonData (String path) {
+			return this.ReadFile(path).Result;
+		}
+#else
+		public SkeletonData ReadSkeletonData (String path) {
+#if WINDOWS_PHONE
+			using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path)))
+			{
+#else
+			using (var input = new BufferedStream(new FileStream(path, FileMode.Open))) {
+#endif
+				SkeletonData skeletonData = ReadSkeletonData(input);
+				skeletonData.name = Path.GetFileNameWithoutExtension(path);
+				return skeletonData;
+			}
+		}
+#endif
+
+		public SkeletonData ReadSkeletonData (BufferedStream input) {
+			if (input == null) throw new ArgumentNullException("input cannot be null.");
+			float scale = Scale;
+
+			var skeletonData = new SkeletonData();
+			skeletonData.hash = ReadString(input);
+			if (skeletonData.hash.Length == 0) skeletonData.hash = null;
+			skeletonData.version = ReadString(input);
+			if (skeletonData.version.Length == 0) skeletonData.version = null;
+			skeletonData.width = ReadFloat(input);
+			skeletonData.height = ReadFloat(input);
+
+			bool nonessential = ReadBoolean(input);
+
+			if (nonessential) {
+				skeletonData.imagesPath = ReadString(input);
+				if (skeletonData.imagesPath.Length == 0) skeletonData.imagesPath = null;
+			}
+
+			// Bones.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				String name = ReadString(input);
+				BoneData parent = null;
+				int parentIndex = ReadInt(input, true) - 1;
+				if (parentIndex != -1) parent = skeletonData.bones[parentIndex];
+				BoneData boneData = new BoneData(name, parent);
+				boneData.x = ReadFloat(input) * scale;
+				boneData.y = ReadFloat(input) * scale;
+				boneData.scaleX = ReadFloat(input);
+				boneData.scaleY = ReadFloat(input);
+				boneData.rotation = ReadFloat(input);
+				boneData.length = ReadFloat(input) * scale;
+				boneData.flipX = ReadBoolean(input);
+				boneData.flipY = ReadBoolean(input);
+				boneData.inheritScale = ReadBoolean(input);
+				boneData.inheritRotation = ReadBoolean(input);
+				if (nonessential) ReadInt(input); // Skip bone color.
+				skeletonData.bones.Add(boneData);
+			}
+
+			// IK constraints.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				IkConstraintData ikConstraintData = new IkConstraintData(ReadString(input));
+				for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++)
+					ikConstraintData.bones.Add(skeletonData.bones[ReadInt(input, true)]);
+				ikConstraintData.target = skeletonData.bones[ReadInt(input, true)];
+				ikConstraintData.mix = ReadFloat(input);
+				ikConstraintData.bendDirection = ReadSByte(input);
+				skeletonData.ikConstraints.Add(ikConstraintData);
+			}
+
+			// Slots.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				String slotName = ReadString(input);
+				BoneData boneData = skeletonData.bones[ReadInt(input, true)];
+				SlotData slotData = new SlotData(slotName, boneData);
+				int color = ReadInt(input);
+				slotData.r = ((color & 0xff000000) >> 24) / 255f;
+				slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
+				slotData.b = ((color & 0x0000ff00) >> 8) / 255f;
+				slotData.a = ((color & 0x000000ff)) / 255f;
+				slotData.attachmentName = ReadString(input);
+				slotData.additiveBlending = ReadBoolean(input);
+				skeletonData.slots.Add(slotData);
+			}
+
+			// Default skin.
+			Skin defaultSkin = ReadSkin(input, "default", nonessential);
+			if (defaultSkin != null) {
+				skeletonData.defaultSkin = defaultSkin;
+				skeletonData.skins.Add(defaultSkin);
+			}
+
+			// Skins.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++)
+				skeletonData.skins.Add(ReadSkin(input, ReadString(input), nonessential));
+
+			// Events.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				EventData eventData = new EventData(ReadString(input));
+				eventData.Int = ReadInt(input, false);
+				eventData.Float = ReadFloat(input);
+				eventData.String = ReadString(input);
+				skeletonData.events.Add(eventData);
+			}
+
+			// Animations.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++)
+				ReadAnimation(ReadString(input), input, skeletonData);
+
+			skeletonData.bones.TrimExcess();
+			skeletonData.slots.TrimExcess();
+			skeletonData.skins.TrimExcess();
+			skeletonData.events.TrimExcess();
+			skeletonData.animations.TrimExcess();
+			skeletonData.ikConstraints.TrimExcess();
+			return skeletonData;
+		}
+
+		/** @return May be null. */
+		private Skin ReadSkin (BufferedStream input, String skinName, bool nonessential) {
+			int slotCount = ReadInt(input, true);
+			if (slotCount == 0) return null;
+			Skin skin = new Skin(skinName);
+			for (int i = 0; i < slotCount; i++) {
+				int slotIndex = ReadInt(input, true);
+				for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
+					String name = ReadString(input);
+					skin.AddAttachment(slotIndex, name, ReadAttachment(input, skin, name, nonessential));
+				}
+			}
+			return skin;
+		}
+
+		private Attachment ReadAttachment (BufferedStream input, Skin skin, String attachmentName, bool nonessential) {
+			float scale = Scale;
+
+			String name = ReadString(input);
+			if (name == null) name = attachmentName;
+
+			switch ((AttachmentType)input.ReadByte()) {
+			case AttachmentType.region: {
+				String path = ReadString(input);
+				if (path == null) path = name;
+				RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
+				if (region == null) return null;
+				region.Path = path;
+				region.x = ReadFloat(input) * scale;
+				region.y = ReadFloat(input) * scale;
+				region.scaleX = ReadFloat(input);
+				region.scaleY = ReadFloat(input);
+				region.rotation = ReadFloat(input);
+				region.width = ReadFloat(input) * scale;
+				region.height = ReadFloat(input) * scale;
+				int color = ReadInt(input);
+				region.r = ((color & 0xff000000) >> 24) / 255f;
+				region.g = ((color & 0x00ff0000) >> 16) / 255f;
+				region.b = ((color & 0x0000ff00) >> 8) / 255f;
+				region.a = ((color & 0x000000ff)) / 255f;
+				region.UpdateOffset();
+				return region;
+			}
+			case AttachmentType.boundingbox: {
+				BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
+				if (box == null) return null;
+				box.vertices = ReadFloatArray(input, scale);
+				return box;
+			}
+			case AttachmentType.mesh: {
+				String path = ReadString(input);
+				if (path == null) path = name;
+				MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
+				if (mesh == null) return null;
+				mesh.Path = path;
+				mesh.regionUVs = ReadFloatArray(input, 1);
+				mesh.triangles = ReadShortArray(input);
+				mesh.vertices = ReadFloatArray(input, scale);
+				mesh.UpdateUVs();
+				int color = ReadInt(input);
+				mesh.r = ((color & 0xff000000) >> 24) / 255f;
+				mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
+				mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
+				mesh.a = ((color & 0x000000ff)) / 255f;
+				mesh.HullLength = ReadInt(input, true) * 2;
+				if (nonessential) {
+					mesh.Edges = ReadIntArray(input);
+					mesh.Width = ReadFloat(input) * scale;
+					mesh.Height = ReadFloat(input) * scale;
+				}
+				return mesh;
+			}
+			case AttachmentType.skinnedmesh: {
+				String path = ReadString(input);
+				if (path == null) path = name;
+				SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
+				if (mesh == null) return null;
+				mesh.Path = path;
+				float[] uvs = ReadFloatArray(input, 1);
+				int[] triangles = ReadShortArray(input);
+
+				int vertexCount = ReadInt(input, true);
+				var weights = new List<float>(uvs.Length * 3 * 3);
+				var bones = new List<int>(uvs.Length * 3);
+				for (int i = 0; i < vertexCount; i++) {
+					int boneCount = (int)ReadFloat(input);
+					bones.Add(boneCount);
+					for (int nn = i + boneCount * 4; i < nn; i += 4) {
+						bones.Add((int)ReadFloat(input));
+						weights.Add(ReadFloat(input) * scale);
+						weights.Add(ReadFloat(input) * scale);
+						weights.Add(ReadFloat(input));
+					}
+				}
+				mesh.bones = bones.ToArray();
+				mesh.weights = weights.ToArray();
+				mesh.triangles = triangles;
+				mesh.regionUVs = uvs;
+				mesh.UpdateUVs();
+				int color = ReadInt(input);
+				mesh.r = ((color & 0xff000000) >> 24) / 255f;
+				mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
+				mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
+				mesh.a = ((color & 0x000000ff)) / 255f;
+				mesh.HullLength = ReadInt(input, true) * 2;
+				if (nonessential) {
+					mesh.Edges = ReadIntArray(input);
+					mesh.Width = ReadFloat(input) * scale;
+					mesh.Height = ReadFloat(input) * scale;
+				}
+				return mesh;
+			}
+			}
+			return null;
+		}
+
+		private float[] ReadFloatArray (BufferedStream input, float scale) {
+			int n = ReadInt(input, true);
+			float[] array = new float[n];
+			if (scale == 1) {
+				for (int i = 0; i < n; i++)
+					array[i] = ReadFloat(input);
+			} else {
+				for (int i = 0; i < n; i++)
+					array[i] = ReadFloat(input) * scale;
+			}
+			return array;
+		}
+
+		private int[] ReadShortArray (BufferedStream input) {
+			int n = ReadInt(input, true);
+			int[] array = new int[n];
+			for (int i = 0; i < n; i++)
+				array[i] = (input.ReadByte() << 8) + input.ReadByte();
+			return array;
+		}
+
+		private int[] ReadIntArray (BufferedStream input) {
+			int n = ReadInt(input, true);
+			int[] array = new int[n];
+			for (int i = 0; i < n; i++)
+				array[i] = ReadInt(input, true);
+			return array;
+		}
+
+		private void ReadAnimation (String name, BufferedStream input, SkeletonData skeletonData) {
+			var timelines = new List<Timeline>();
+			float scale = Scale;
+			float duration = 0;
+	
+			// Slot timelines.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				int slotIndex = ReadInt(input, true);
+				for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
+					int timelineType = input.ReadByte();
+					int frameCount = ReadInt(input, true);
+					switch (timelineType) {
+					case TIMELINE_COLOR: {
+						ColorTimeline timeline = new ColorTimeline(frameCount);
+						timeline.slotIndex = slotIndex;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+							float time = ReadFloat(input);
+							int color = ReadInt(input);
+							float r = ((color & 0xff000000) >> 24) / 255f;
+							float g = ((color & 0x00ff0000) >> 16) / 255f;
+							float b = ((color & 0x0000ff00) >> 8) / 255f;
+							float a = ((color & 0x000000ff)) / 255f;
+							timeline.SetFrame(frameIndex, time, r, g, b, a);
+							if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+						}
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount * 5 - 5]);
+						break;
+					}
+					case TIMELINE_ATTACHMENT: {
+						AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
+						timeline.slotIndex = slotIndex;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
+							timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input));
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount - 1]);
+						break;
+					}
+					}
+				}
+			}
+
+			// Bone timelines.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				int boneIndex = ReadInt(input, true);
+				for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
+					int timelineType = input.ReadByte();
+					int frameCount = ReadInt(input, true);
+					switch (timelineType) {
+					case TIMELINE_ROTATE: {
+						RotateTimeline timeline = new RotateTimeline(frameCount);
+						timeline.boneIndex = boneIndex;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+							timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input));
+							if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+						}
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
+						break;
+					}
+					case TIMELINE_TRANSLATE:
+					case TIMELINE_SCALE: {
+						TranslateTimeline timeline;
+						float timelineScale = 1;
+						if (timelineType == TIMELINE_SCALE)
+							timeline = new ScaleTimeline(frameCount);
+						else {
+							timeline = new TranslateTimeline(frameCount);
+							timelineScale = scale;
+						}
+						timeline.boneIndex = boneIndex;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+							timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale, ReadFloat(input)
+								* timelineScale);
+							if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+						}
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
+						break;
+					}
+					case TIMELINE_FLIPX:
+					case TIMELINE_FLIPY: {
+						FlipXTimeline timeline = timelineType == TIMELINE_FLIPX ? new FlipXTimeline(frameCount) : new FlipYTimeline(
+							frameCount);
+						timeline.boneIndex = boneIndex;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
+							timeline.SetFrame(frameIndex, ReadFloat(input), ReadBoolean(input));
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
+						break;
+					}
+					}
+				}
+			}
+
+			// IK timelines.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				IkConstraintData ikConstraint = skeletonData.ikConstraints[ReadInt(input, true)];
+				int frameCount = ReadInt(input, true);
+				IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
+				timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint);
+				for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+					timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
+					if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+				}
+				timelines.Add(timeline);
+				duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
+			}
+
+			// FFD timelines.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				Skin skin = skeletonData.skins[ReadInt(input, true)];
+				for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
+					int slotIndex = ReadInt(input, true);
+					for (int iii = 0, nnn = ReadInt(input, true); iii < nnn; iii++) {
+						Attachment attachment = skin.GetAttachment(slotIndex, ReadString(input));
+						int frameCount = ReadInt(input, true);
+						FFDTimeline timeline = new FFDTimeline(frameCount);
+						timeline.slotIndex = slotIndex;
+						timeline.attachment = attachment;
+						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+							float time = ReadFloat(input);
+
+							float[] vertices;
+							int vertexCount;
+							if (attachment is MeshAttachment)
+								vertexCount = ((MeshAttachment)attachment).vertices.Length;
+							else
+								vertexCount = ((SkinnedMeshAttachment)attachment).weights.Length / 3 * 2;
+
+							int end = ReadInt(input, true);
+							if (end == 0) {
+								if (attachment is MeshAttachment)
+									vertices = ((MeshAttachment)attachment).vertices;
+								else
+									vertices = new float[vertexCount];
+							} else {
+								vertices = new float[vertexCount];
+								int start = ReadInt(input, true);
+								end += start;
+								if (scale == 1) {
+									for (int v = start; v < end; v++)
+										vertices[v] = ReadFloat(input);
+								} else {
+									for (int v = start; v < end; v++)
+										vertices[v] = ReadFloat(input) * scale;
+								}
+								if (attachment is MeshAttachment) {
+									float[] meshVertices = ((MeshAttachment)attachment).vertices;
+									for (int v = 0, vn = vertices.Length; v < vn; v++)
+										vertices[v] += meshVertices[v];
+								}
+							}
+
+							timeline.SetFrame(frameIndex, time, vertices);
+							if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+						}
+						timelines.Add(timeline);
+						duration = Math.Max(duration, timeline.frames[frameCount - 1]);
+					}
+				}
+			}
+
+			// Draw order timeline.
+			int drawOrderCount = ReadInt(input, true);
+			if (drawOrderCount > 0) {
+				DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount);
+				int slotCount = skeletonData.slots.Count;
+				for (int i = 0; i < drawOrderCount; i++) {
+					int offsetCount = ReadInt(input, true);
+					int[] drawOrder = new int[slotCount];
+					for (int ii = slotCount - 1; ii >= 0; ii--)
+						drawOrder[ii] = -1;
+					int[] unchanged = new int[slotCount - offsetCount];
+					int originalIndex = 0, unchangedIndex = 0;
+					for (int ii = 0; ii < offsetCount; ii++) {
+						int slotIndex = ReadInt(input, true);
+						// Collect unchanged items.
+						while (originalIndex != slotIndex)
+							unchanged[unchangedIndex++] = originalIndex++;
+						// Set changed items.
+						drawOrder[originalIndex + ReadInt(input, true)] = originalIndex++;
+					}
+					// Collect remaining unchanged items.
+					while (originalIndex < slotCount)
+						unchanged[unchangedIndex++] = originalIndex++;
+					// Fill in unchanged items.
+					for (int ii = slotCount - 1; ii >= 0; ii--)
+						if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+					timeline.SetFrame(i, ReadFloat(input), drawOrder);
+				}
+				timelines.Add(timeline);
+				duration = Math.Max(duration, timeline.frames[drawOrderCount - 1]);
+			}
+
+			// Event timeline.
+			int eventCount = ReadInt(input, true);
+			if (eventCount > 0) {
+				EventTimeline timeline = new EventTimeline(eventCount);
+				for (int i = 0; i < eventCount; i++) {
+					float time = ReadFloat(input);
+					EventData eventData = skeletonData.events[ReadInt(input, true)];
+					Event e = new Event(eventData);
+					e.Int = ReadInt(input, false);
+					e.Float = ReadFloat(input);
+					e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;
+					timeline.SetFrame(i, time, e);
+				}
+				timelines.Add(timeline);
+				duration = Math.Max(duration, timeline.frames[eventCount - 1]);
+			}
+
+			timelines.TrimExcess();
+			skeletonData.animations.Add(new Animation(name, timelines, duration));
+		}
+
+		private void ReadCurve (BufferedStream input, int frameIndex, CurveTimeline timeline) {
+			switch (input.ReadByte()) {
+			case CURVE_STEPPED:
+				timeline.SetStepped(frameIndex);
+				break;
+			case CURVE_BEZIER:
+				timeline.SetCurve(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input));
+				break;
+			}
+		}
+
+		private sbyte ReadSByte (BufferedStream input) {
+			int value = input.ReadByte();
+			if (value == -1) throw new EndOfStreamException();
+			return (sbyte)value;
+		}
+
+		private bool ReadBoolean (BufferedStream input) {
+			return input.ReadByte() != 0;
+		}
+
+		private float ReadFloat (BufferedStream input) {
+			buffer[3] = (byte)input.ReadByte();
+			buffer[2] = (byte)input.ReadByte();
+			buffer[1] = (byte)input.ReadByte();
+			buffer[0] = (byte)input.ReadByte();
+			return BitConverter.ToSingle(buffer, 0);
+		}
+
+		private int ReadInt (BufferedStream input) {
+			return (input.ReadByte() << 24) + (input.ReadByte() << 16) + (input.ReadByte() << 8) + input.ReadByte();
+		}
+
+		private int ReadInt (BufferedStream input, bool optimizePositive) {
+			int b = input.ReadByte();
+			int result = b & 0x7F;
+			if ((b & 0x80) != 0) {
+				b = input.ReadByte();
+				result |= (b & 0x7F) << 7;
+				if ((b & 0x80) != 0) {
+					b = input.ReadByte();
+					result |= (b & 0x7F) << 14;
+					if ((b & 0x80) != 0) {
+						b = input.ReadByte();
+						result |= (b & 0x7F) << 21;
+						if ((b & 0x80) != 0) {
+							b = input.ReadByte();
+							result |= (b & 0x7F) << 28;
+						}
+					}
+				}
+			}
+			return optimizePositive ? result : ((result >> 1) ^ -(result & 1));
+		}
+
+		private string ReadString (BufferedStream input) {
+			int charCount = ReadInt(input, true);
+			switch (charCount) {
+			case 0:
+				return null;
+			case 1:
+				return "";
+			}
+			charCount--;
+			char[] chars = this.chars;
+			if (chars.Length < charCount) this.chars = chars = new char[charCount];
+			// Try to read 7 bit ASCII chars.
+			int charIndex = 0;
+			int b = 0;
+			while (charIndex < charCount) {
+				b = input.ReadByte();
+				if (b > 127) break;
+				chars[charIndex++] = (char)b;
+			}
+			// If a char was not ASCII, finish with slow path.
+			if (charIndex < charCount) ReadUtf8_slow(input, charCount, charIndex, b);
+			return new String(chars, 0, charCount);
+		}
+
+		private void ReadUtf8_slow (BufferedStream input, int charCount, int charIndex, int b) {
+			char[] chars = this.chars;
+			while (true) {
+				switch (b >> 4) {
+				case 0:
+				case 1:
+				case 2:
+				case 3:
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+					chars[charIndex] = (char)b;
+					break;
+				case 12:
+				case 13:
+					chars[charIndex] = (char)((b & 0x1F) << 6 | input.ReadByte() & 0x3F);
+					break;
+				case 14:
+					chars[charIndex] = (char)((b & 0x0F) << 12 | (input.ReadByte() & 0x3F) << 6 | input.ReadByte() & 0x3F);
+					break;
+				}
+				if (++charIndex >= charCount) break;
+				b = input.ReadByte() & 0xFF;
+			}
+		}
+	}
+}

+ 1 - 1
spine-csharp/src/SkeletonData.cs

@@ -42,7 +42,7 @@ namespace Spine {
 		internal List<Animation> animations = new List<Animation>();
 		internal List<IkConstraintData> ikConstraints = new List<IkConstraintData>();
 		internal float width, height;
-		internal String version, hash;
+		internal String version, hash, imagesPath;
 
 		public String Name { get { return name; } set { name = value; } }
 		public List<BoneData> Bones { get { return bones; } } // Ordered parents first.

+ 21 - 20
spine-csharp/src/SkeletonJson.cs

@@ -53,25 +53,24 @@ namespace Spine {
 		}
 
 #if WINDOWS_STOREAPP
-        private async Task<SkeletonData> ReadFile(string path) {
-            var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
-            var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
-            using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
-                SkeletonData skeletonData = ReadSkeletonData(reader);
-                skeletonData.Name = Path.GetFileNameWithoutExtension(path);
-                return skeletonData;
-            }
-        }
+		private async Task<SkeletonData> ReadFile(string path) {
+			var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
+			var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
+			using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
+				SkeletonData skeletonData = ReadSkeletonData(reader);
+				skeletonData.Name = Path.GetFileNameWithoutExtension(path);
+				return skeletonData;
+			}
+		}
 
 		public SkeletonData ReadSkeletonData (String path) {
-		    return this.ReadFile(path).Result;
+			return this.ReadFile(path).Result;
 		}
 #else
 		public SkeletonData ReadSkeletonData (String path) {
 #if WINDOWS_PHONE
-            Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
-            using (StreamReader reader = new StreamReader(stream))
-            {
+			Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
+			using (StreamReader reader = new StreamReader(stream)) {
 #else
 			using (StreamReader reader = new StreamReader(path)) {
 #endif
@@ -209,7 +208,9 @@ namespace Spine {
 			skeletonData.bones.TrimExcess();
 			skeletonData.slots.TrimExcess();
 			skeletonData.skins.TrimExcess();
+			skeletonData.events.TrimExcess();
 			skeletonData.animations.TrimExcess();
+			skeletonData.ikConstraints.TrimExcess();
 			return skeletonData;
 		}
 
@@ -369,7 +370,7 @@ namespace Spine {
 			return (String)map[name];
 		}
 
-		public static float ToColor (String hexString, int colorIndex) {
+		private float ToColor (String hexString, int colorIndex) {
 			if (hexString.Length != 8)
 				throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
 			return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
@@ -397,7 +398,7 @@ namespace Spine {
 							foreach (Dictionary<String, Object> valueMap in values) {
 								float time = (float)valueMap["time"];
 								String c = (String)valueMap["color"];
-								timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
+								timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
 								ReadCurve(timeline, frameIndex, valueMap);
 								frameIndex++;
 							}
@@ -411,7 +412,7 @@ namespace Spine {
 							int frameIndex = 0;
 							foreach (Dictionary<String, Object> valueMap in values) {
 								float time = (float)valueMap["time"];
-								timeline.setFrame(frameIndex++, time, (String)valueMap["name"]);
+								timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]);
 							}
 							timelines.Add(timeline);
 							duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
@@ -502,7 +503,7 @@ namespace Spine {
 						float time = (float)valueMap["time"];
 						float mix = valueMap.ContainsKey("mix") ? (float)valueMap["mix"] : 1;
 						bool bendPositive = valueMap.ContainsKey("bendPositive") ? (bool)valueMap["bendPositive"] : true;
-						timeline.setFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
+						timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
 						ReadCurve(timeline, frameIndex, valueMap);
 						frameIndex++;
 					}
@@ -556,7 +557,7 @@ namespace Spine {
 									}
 								}
 
-								timeline.setFrame(frameIndex, (float)valueMap["time"], vertices);
+								timeline.SetFrame(frameIndex, (float)valueMap["time"], vertices);
 								ReadCurve(timeline, frameIndex, valueMap);
 								frameIndex++;
 							}
@@ -598,7 +599,7 @@ namespace Spine {
 						for (int i = slotCount - 1; i >= 0; i--)
 							if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
 					}
-					timeline.setFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder);
+					timeline.SetFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder);
 				}
 				timelines.Add(timeline);
 				duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
@@ -615,7 +616,7 @@ namespace Spine {
 					e.Int = GetInt(eventMap, "int", eventData.Int);
 					e.Float = GetFloat(eventMap, "float", eventData.Float);
 					e.String = GetString(eventMap, "string", eventData.String);
-					timeline.setFrame(frameIndex++, (float)eventMap["time"], e);
+					timeline.SetFrame(frameIndex++, (float)eventMap["time"], e);
 				}
 				timelines.Add(timeline);
 				duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);

BIN
spine-xna/example/data/raptor.skel


+ 17 - 4
spine-xna/example/src/ExampleGame.cs

@@ -76,12 +76,25 @@ namespace Spine {
 			// String name = "spineboy";
 			// String name = "goblins-mesh";
 			String name = "raptor";
+			bool binaryData = true;
 
 			Atlas atlas = new Atlas(assetsFolder + name + ".atlas", new XnaTextureLoader(GraphicsDevice));
-			SkeletonJson json = new SkeletonJson(atlas);
-			if (name == "spineboy") json.Scale = 0.6f;
-			if (name == "raptor") json.Scale = 0.5f;
-			skeleton = new Skeleton(json.ReadSkeletonData(assetsFolder + name + ".json"));
+
+			float scale = 1;
+			if (name == "spineboy") scale = 0.6f;
+			if (name == "raptor") scale = 0.5f;
+
+			SkeletonData skeletonData;
+			if (binaryData) {
+				SkeletonBinary binary = new SkeletonBinary(atlas);
+				binary.Scale = scale;
+				skeletonData = binary.ReadSkeletonData(assetsFolder + name + ".skel");
+			} else {
+				SkeletonJson json = new SkeletonJson(atlas);
+				json.Scale = scale;
+				skeletonData = json.ReadSkeletonData(assetsFolder + name + ".json");
+			}
+			skeleton = new Skeleton(skeletonData);
 			if (name == "goblins-mesh") skeleton.SetSkin("goblin");
 
 			// Define mixing between animations.