Browse Source

Merge branch 'skin-bones' of https://github.com/EsotericSoftware/spine-runtimes into skin-bones

badlogic 6 years ago
parent
commit
995cfa0d0a

+ 1 - 2
spine-csharp/src/Attachments/MeshAttachment.cs

@@ -164,8 +164,7 @@ namespace Spine {
 			copy.g = g;
 			copy.g = g;
 			copy.b = b;
 			copy.b = b;
 			copy.a = a;
 			copy.a = a;
-			copy.deformAttachment = deformAttachment;
-
+			
 			CopyTo(copy);
 			CopyTo(copy);
 			copy.regionUVs = new float[regionUVs.Length];
 			copy.regionUVs = new float[regionUVs.Length];
 			Array.Copy(regionUVs, 0, copy.regionUVs, 0, regionUVs.Length);
 			Array.Copy(regionUVs, 0, copy.regionUVs, 0, regionUVs.Length);

+ 1 - 0
spine-csharp/src/Attachments/VertexAttachment.cs

@@ -150,6 +150,7 @@ namespace Spine {
 				attachment.vertices = null;
 				attachment.vertices = null;
 			
 			
 			attachment.worldVerticesLength = worldVerticesLength;
 			attachment.worldVerticesLength = worldVerticesLength;
+			attachment.deformAttachment = deformAttachment;
 		}
 		}
 	}
 	}
 }
 }

+ 3 - 2
spine-csharp/src/Skin.cs

@@ -83,9 +83,10 @@ namespace Spine {
 
 
 			foreach (SkinEntry entry in skin.attachments.Keys) {
 			foreach (SkinEntry entry in skin.attachments.Keys) {
 				if (entry.Attachment is MeshAttachment)
 				if (entry.Attachment is MeshAttachment)
-					SetAttachment(entry.SlotIndex, entry.Name, ((MeshAttachment)entry.Attachment).NewLinkedMesh());
+					SetAttachment(entry.SlotIndex, entry.Name,
+						entry.Attachment != null ? ((MeshAttachment)entry.Attachment).NewLinkedMesh() : null);
 				else
 				else
-					SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment.Copy());
+					SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment != null ? entry.Attachment.Copy() : null);
 			}
 			}
 		}
 		}
 
 

+ 128 - 93
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -40,6 +40,7 @@ import com.badlogic.gdx.utils.DataInput;
 import com.badlogic.gdx.utils.FloatArray;
 import com.badlogic.gdx.utils.FloatArray;
 import com.badlogic.gdx.utils.IntArray;
 import com.badlogic.gdx.utils.IntArray;
 import com.badlogic.gdx.utils.SerializationException;
 import com.badlogic.gdx.utils.SerializationException;
+
 import com.esotericsoftware.spine.Animation.AttachmentTimeline;
 import com.esotericsoftware.spine.Animation.AttachmentTimeline;
 import com.esotericsoftware.spine.Animation.ColorTimeline;
 import com.esotericsoftware.spine.Animation.ColorTimeline;
 import com.esotericsoftware.spine.Animation.CurveTimeline;
 import com.esotericsoftware.spine.Animation.CurveTimeline;
@@ -132,43 +133,7 @@ public class SkeletonBinary {
 		SkeletonData skeletonData = new SkeletonData();
 		SkeletonData skeletonData = new SkeletonData();
 		skeletonData.name = file.nameWithoutExtension();
 		skeletonData.name = file.nameWithoutExtension();
 
 
-		DataInput input = new DataInput(file.read(512)) {
-			private char[] chars = new char[32];
-
-			public String readString () throws IOException {
-				int byteCount = readInt(true);
-				switch (byteCount) {
-				case 0:
-					return null;
-				case 1:
-					return "";
-				}
-				byteCount--;
-				if (chars.length < byteCount) chars = new char[byteCount];
-				char[] chars = this.chars;
-				int charCount = 0;
-				for (int i = 0; i < byteCount;) {
-					int b = read();
-					switch (b >> 4) {
-					case -1:
-						throw new EOFException();
-					case 12:
-					case 13:
-						chars[charCount++] = (char)((b & 0x1F) << 6 | read() & 0x3F);
-						i += 2;
-						break;
-					case 14:
-						chars[charCount++] = (char)((b & 0x0F) << 12 | (read() & 0x3F) << 6 | read() & 0x3F);
-						i += 3;
-						break;
-					default:
-						chars[charCount++] = (char)b;
-						i++;
-					}
-				}
-				return new String(chars, 0, charCount);
-			}
-		};
+		SkeletonInput input = new SkeletonInput(file);
 		try {
 		try {
 			skeletonData.hash = input.readString();
 			skeletonData.hash = input.readString();
 			if (skeletonData.hash.isEmpty()) skeletonData.hash = null;
 			if (skeletonData.hash.isEmpty()) skeletonData.hash = null;
@@ -180,7 +145,6 @@ public class SkeletonBinary {
 			skeletonData.height = input.readFloat();
 			skeletonData.height = input.readFloat();
 
 
 			boolean nonessential = input.readBoolean();
 			boolean nonessential = input.readBoolean();
-
 			if (nonessential) {
 			if (nonessential) {
 				skeletonData.fps = input.readFloat();
 				skeletonData.fps = input.readFloat();
 
 
@@ -191,8 +155,18 @@ public class SkeletonBinary {
 				if (skeletonData.audioPath.isEmpty()) skeletonData.audioPath = null;
 				if (skeletonData.audioPath.isEmpty()) skeletonData.audioPath = null;
 			}
 			}
 
 
+			int n;
+			Object[] o;
+
+			// Strings.
+			input.strings = new Array(n = input.readInt(true));
+			o = input.strings.setSize(n);
+			for (int i = 0; i < n; i++)
+				o[i] = input.readString();
+
 			// Bones.
 			// Bones.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
+			o = skeletonData.bones.setSize(n = input.readInt(true));
+			for (int i = 0; i < n; i++) {
 				String name = input.readString();
 				String name = input.readString();
 				BoneData parent = i == 0 ? null : skeletonData.bones.get(input.readInt(true));
 				BoneData parent = i == 0 ? null : skeletonData.bones.get(input.readInt(true));
 				BoneData data = new BoneData(i, name, parent);
 				BoneData data = new BoneData(i, name, parent);
@@ -207,11 +181,12 @@ public class SkeletonBinary {
 				data.transformMode = TransformMode.values[input.readInt(true)];
 				data.transformMode = TransformMode.values[input.readInt(true)];
 				data.skinRequired = input.readBoolean();
 				data.skinRequired = input.readBoolean();
 				if (nonessential) Color.rgba8888ToColor(data.color, input.readInt());
 				if (nonessential) Color.rgba8888ToColor(data.color, input.readInt());
-				skeletonData.bones.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// Slots.
 			// Slots.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
+			o = skeletonData.slots.setSize(n = input.readInt(true));
+			for (int i = 0; i < n; i++) {
 				String slotName = input.readString();
 				String slotName = input.readString();
 				BoneData boneData = skeletonData.bones.get(input.readInt(true));
 				BoneData boneData = skeletonData.bones.get(input.readInt(true));
 				SlotData data = new SlotData(i, slotName, boneData);
 				SlotData data = new SlotData(i, slotName, boneData);
@@ -220,34 +195,38 @@ public class SkeletonBinary {
 				int darkColor = input.readInt();
 				int darkColor = input.readInt();
 				if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor);
 				if (darkColor != -1) Color.rgb888ToColor(data.darkColor = new Color(), darkColor);
 
 
-				data.attachmentName = input.readString();
+				data.attachmentName = input.readStringRef();
 				data.blendMode = BlendMode.values[input.readInt(true)];
 				data.blendMode = BlendMode.values[input.readInt(true)];
-				skeletonData.slots.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// IK constraints.
 			// IK constraints.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
+			o = skeletonData.ikConstraints.setSize(n = input.readInt(true));
+			for (int i = 0, nn; i < n; i++) {
 				IkConstraintData data = new IkConstraintData(input.readString());
 				IkConstraintData data = new IkConstraintData(input.readString());
 				data.order = input.readInt(true);
 				data.order = input.readInt(true);
 				data.skinRequired = input.readBoolean();
 				data.skinRequired = input.readBoolean();
-				for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
-					data.bones.add(skeletonData.bones.get(input.readInt(true)));
+				Object[] bones = data.bones.setSize(nn = input.readInt(true));
+				for (int ii = 0; ii < nn; ii++)
+					bones[ii] = skeletonData.bones.get(input.readInt(true));
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.mix = input.readFloat();
 				data.mix = input.readFloat();
 				data.bendDirection = input.readByte();
 				data.bendDirection = input.readByte();
 				data.compress = input.readBoolean();
 				data.compress = input.readBoolean();
 				data.stretch = input.readBoolean();
 				data.stretch = input.readBoolean();
 				data.uniform = input.readBoolean();
 				data.uniform = input.readBoolean();
-				skeletonData.ikConstraints.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// Transform constraints.
 			// Transform constraints.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
+			o = skeletonData.transformConstraints.setSize(n = input.readInt(true));
+			for (int i = 0, nn; i < n; i++) {
 				TransformConstraintData data = new TransformConstraintData(input.readString());
 				TransformConstraintData data = new TransformConstraintData(input.readString());
 				data.order = input.readInt(true);
 				data.order = input.readInt(true);
 				data.skinRequired = input.readBoolean();
 				data.skinRequired = input.readBoolean();
-				for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
-					data.bones.add(skeletonData.bones.get(input.readInt(true)));
+				Object[] bones = data.bones.setSize(nn = input.readInt(true));
+				for (int ii = 0; ii < nn; ii++)
+					bones[ii] = skeletonData.bones.get(input.readInt(true));
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.local = input.readBoolean();
 				data.local = input.readBoolean();
 				data.relative = input.readBoolean();
 				data.relative = input.readBoolean();
@@ -261,16 +240,18 @@ public class SkeletonBinary {
 				data.translateMix = input.readFloat();
 				data.translateMix = input.readFloat();
 				data.scaleMix = input.readFloat();
 				data.scaleMix = input.readFloat();
 				data.shearMix = input.readFloat();
 				data.shearMix = input.readFloat();
-				skeletonData.transformConstraints.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// Path constraints.
 			// Path constraints.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
+			o = skeletonData.pathConstraints.setSize(n = input.readInt(true));
+			for (int i = 0, nn; i < n; i++) {
 				PathConstraintData data = new PathConstraintData(input.readString());
 				PathConstraintData data = new PathConstraintData(input.readString());
 				data.order = input.readInt(true);
 				data.order = input.readInt(true);
 				data.skinRequired = input.readBoolean();
 				data.skinRequired = input.readBoolean();
-				for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
-					data.bones.add(skeletonData.bones.get(input.readInt(true)));
+				Object[] bones = data.bones.setSize(nn = input.readInt(true));
+				for (int ii = 0; ii < nn; ii++)
+					bones[ii] = skeletonData.bones.get(input.readInt(true));
 				data.target = skeletonData.slots.get(input.readInt(true));
 				data.target = skeletonData.slots.get(input.readInt(true));
 				data.positionMode = PositionMode.values[input.readInt(true)];
 				data.positionMode = PositionMode.values[input.readInt(true)];
 				data.spacingMode = SpacingMode.values[input.readInt(true)];
 				data.spacingMode = SpacingMode.values[input.readInt(true)];
@@ -282,7 +263,7 @@ public class SkeletonBinary {
 				if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
 				if (data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed) data.spacing *= scale;
 				data.rotateMix = input.readFloat();
 				data.rotateMix = input.readFloat();
 				data.translateMix = input.readFloat();
 				data.translateMix = input.readFloat();
-				skeletonData.pathConstraints.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// Default skin.
 			// Default skin.
@@ -293,11 +274,16 @@ public class SkeletonBinary {
 			}
 			}
 
 
 			// Skins.
 			// Skins.
-			for (int i = 0, n = input.readInt(true); i < n; i++)
-				skeletonData.skins.add(readSkin(input, skeletonData, false, nonessential));
+			{
+				int i = skeletonData.skins.size;
+				o = skeletonData.skins.setSize(n = i + input.readInt(true));
+				for (; i < n; i++)
+					o[i] = readSkin(input, skeletonData, false, nonessential);
+			}
 
 
 			// Linked meshes.
 			// Linked meshes.
-			for (int i = 0, n = linkedMeshes.size; i < n; i++) {
+			n = linkedMeshes.size;
+			for (int i = 0; i < n; i++) {
 				LinkedMesh linkedMesh = linkedMeshes.get(i);
 				LinkedMesh linkedMesh = linkedMeshes.get(i);
 				Skin skin = linkedMesh.skin == null ? skeletonData.getDefaultSkin() : skeletonData.findSkin(linkedMesh.skin);
 				Skin skin = linkedMesh.skin == null ? skeletonData.getDefaultSkin() : skeletonData.findSkin(linkedMesh.skin);
 				if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin);
 				if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin);
@@ -310,8 +296,9 @@ public class SkeletonBinary {
 			linkedMeshes.clear();
 			linkedMeshes.clear();
 
 
 			// Events.
 			// Events.
-			for (int i = 0, n = input.readInt(true); i < n; i++) {
-				EventData data = new EventData(input.readString());
+			o = skeletonData.events.setSize(n = input.readInt(true));
+			for (int i = 0; i < n; i++) {
+				EventData data = new EventData(input.readStringRef());
 				data.intValue = input.readInt(false);
 				data.intValue = input.readInt(false);
 				data.floatValue = input.readFloat();
 				data.floatValue = input.readFloat();
 				data.stringValue = input.readString();
 				data.stringValue = input.readString();
@@ -320,12 +307,13 @@ public class SkeletonBinary {
 					data.volume = input.readFloat();
 					data.volume = input.readFloat();
 					data.balance = input.readFloat();
 					data.balance = input.readFloat();
 				}
 				}
-				skeletonData.events.add(data);
+				o[i] = data;
 			}
 			}
 
 
 			// Animations.
 			// Animations.
-			for (int i = 0, n = input.readInt(true); i < n; i++)
-				readAnimation(input, input.readString(), skeletonData);
+			o = skeletonData.animations.setSize(n = input.readInt(true));
+			for (int i = 0; i < n; i++)
+				o[i] = readAnimation(input, input.readString(), skeletonData);
 
 
 		} catch (IOException ex) {
 		} catch (IOException ex) {
 			throw new SerializationException("Error reading skeleton file.", ex);
 			throw new SerializationException("Error reading skeleton file.", ex);
@@ -335,33 +323,32 @@ public class SkeletonBinary {
 			} catch (IOException ignored) {
 			} catch (IOException ignored) {
 			}
 			}
 		}
 		}
-
-		skeletonData.bones.shrink();
-		skeletonData.slots.shrink();
-		skeletonData.skins.shrink();
-		skeletonData.events.shrink();
-		skeletonData.animations.shrink();
-		skeletonData.ikConstraints.shrink();
 		return skeletonData;
 		return skeletonData;
 	}
 	}
 
 
-	private Skin readSkin (DataInput input, SkeletonData skeletonData, boolean defaultSkin, boolean nonessential)
+	private Skin readSkin (SkeletonInput input, SkeletonData skeletonData, boolean defaultSkin, boolean nonessential)
 		throws IOException {
 		throws IOException {
-		Skin skin = new Skin(defaultSkin ? "default" : input.readString());
+
+		Skin skin = new Skin(defaultSkin ? "default" : input.readStringRef());
+
 		if (!defaultSkin) {
 		if (!defaultSkin) {
-			for (int i = 0, n = input.readInt(true); i < n; i++)
-				skin.bones.add(skeletonData.bones.get(input.readInt(true)));
+			Object[] bones = skeletonData.bones.setSize(input.readInt(true));
+			for (int i = 0, n = skeletonData.bones.size; i < n; i++)
+				bones[i] = skeletonData.bones.get(input.readInt(true));
+
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 				skin.constraints.add(skeletonData.ikConstraints.get(input.readInt(true)));
 				skin.constraints.add(skeletonData.ikConstraints.get(input.readInt(true)));
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 				skin.constraints.add(skeletonData.transformConstraints.get(input.readInt(true)));
 				skin.constraints.add(skeletonData.transformConstraints.get(input.readInt(true)));
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 			for (int i = 0, n = input.readInt(true); i < n; i++)
 				skin.constraints.add(skeletonData.pathConstraints.get(input.readInt(true)));
 				skin.constraints.add(skeletonData.pathConstraints.get(input.readInt(true)));
+			skin.constraints.shrink();
 		}
 		}
+
 		for (int i = 0, n = input.readInt(true); i < n; i++) {
 		for (int i = 0, n = input.readInt(true); i < n; i++) {
 			int slotIndex = input.readInt(true);
 			int slotIndex = input.readInt(true);
 			for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
 			for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
-				String name = input.readString();
+				String name = input.readStringRef();
 				Attachment attachment = readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
 				Attachment attachment = readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
 				if (attachment != null) skin.setAttachment(slotIndex, name, attachment);
 				if (attachment != null) skin.setAttachment(slotIndex, name, attachment);
 			}
 			}
@@ -369,17 +356,17 @@ public class SkeletonBinary {
 		return skin;
 		return skin;
 	}
 	}
 
 
-	private Attachment readAttachment (DataInput input, SkeletonData skeletonData, Skin skin, int slotIndex, String attachmentName,
-		boolean nonessential) throws IOException {
+	private Attachment readAttachment (SkeletonInput input, SkeletonData skeletonData, Skin skin, int slotIndex,
+		String attachmentName, boolean nonessential) throws IOException {
 		float scale = this.scale;
 		float scale = this.scale;
 
 
-		String name = input.readString();
+		String name = input.readStringRef();
 		if (name == null) name = attachmentName;
 		if (name == null) name = attachmentName;
 
 
 		AttachmentType type = AttachmentType.values[input.readByte()];
 		AttachmentType type = AttachmentType.values[input.readByte()];
 		switch (type) {
 		switch (type) {
 		case region: {
 		case region: {
-			String path = input.readString();
+			String path = input.readStringRef();
 			float rotation = input.readFloat();
 			float rotation = input.readFloat();
 			float x = input.readFloat();
 			float x = input.readFloat();
 			float y = input.readFloat();
 			float y = input.readFloat();
@@ -418,7 +405,7 @@ public class SkeletonBinary {
 			return box;
 			return box;
 		}
 		}
 		case mesh: {
 		case mesh: {
-			String path = input.readString();
+			String path = input.readStringRef();
 			int color = input.readInt();
 			int color = input.readInt();
 			int vertexCount = input.readInt(true);
 			int vertexCount = input.readInt(true);
 			float[] uvs = readFloatArray(input, vertexCount << 1, 1);
 			float[] uvs = readFloatArray(input, vertexCount << 1, 1);
@@ -453,10 +440,10 @@ public class SkeletonBinary {
 			return mesh;
 			return mesh;
 		}
 		}
 		case linkedmesh: {
 		case linkedmesh: {
-			String path = input.readString();
+			String path = input.readStringRef();
 			int color = input.readInt();
 			int color = input.readInt();
-			String skinName = input.readString();
-			String parent = input.readString();
+			String skinName = input.readStringRef();
+			String parent = input.readStringRef();
 			boolean inheritDeform = input.readBoolean();
 			boolean inheritDeform = input.readBoolean();
 			float width = 0, height = 0;
 			float width = 0, height = 0;
 			if (nonessential) {
 			if (nonessential) {
@@ -530,7 +517,7 @@ public class SkeletonBinary {
 		return null;
 		return null;
 	}
 	}
 
 
-	private Vertices readVertices (DataInput input, int vertexCount) throws IOException {
+	private Vertices readVertices (SkeletonInput input, int vertexCount) throws IOException {
 		int verticesLength = vertexCount << 1;
 		int verticesLength = vertexCount << 1;
 		Vertices vertices = new Vertices();
 		Vertices vertices = new Vertices();
 		if (!input.readBoolean()) {
 		if (!input.readBoolean()) {
@@ -554,7 +541,7 @@ public class SkeletonBinary {
 		return vertices;
 		return vertices;
 	}
 	}
 
 
-	private float[] readFloatArray (DataInput input, int n, float scale) throws IOException {
+	private float[] readFloatArray (SkeletonInput input, int n, float scale) throws IOException {
 		float[] array = new float[n];
 		float[] array = new float[n];
 		if (scale == 1) {
 		if (scale == 1) {
 			for (int i = 0; i < n; i++)
 			for (int i = 0; i < n; i++)
@@ -566,7 +553,7 @@ public class SkeletonBinary {
 		return array;
 		return array;
 	}
 	}
 
 
-	private short[] readShortArray (DataInput input) throws IOException {
+	private short[] readShortArray (SkeletonInput input) throws IOException {
 		int n = input.readInt(true);
 		int n = input.readInt(true);
 		short[] array = new short[n];
 		short[] array = new short[n];
 		for (int i = 0; i < n; i++)
 		for (int i = 0; i < n; i++)
@@ -574,8 +561,8 @@ public class SkeletonBinary {
 		return array;
 		return array;
 	}
 	}
 
 
-	private void readAnimation (DataInput input, String name, SkeletonData skeletonData) {
-		Array<Timeline> timelines = new Array();
+	private Animation readAnimation (SkeletonInput input, String name, SkeletonData skeletonData) {
+		Array<Timeline> timelines = new Array(32);
 		float scale = this.scale;
 		float scale = this.scale;
 		float duration = 0;
 		float duration = 0;
 
 
@@ -591,7 +578,7 @@ public class SkeletonBinary {
 						AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
 						AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
 						timeline.slotIndex = slotIndex;
 						timeline.slotIndex = slotIndex;
 						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
 						for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
-							timeline.setFrame(frameIndex, input.readFloat(), input.readString());
+							timeline.setFrame(frameIndex, input.readFloat(), input.readStringRef());
 						timelines.add(timeline);
 						timelines.add(timeline);
 						duration = Math.max(duration, timeline.getFrames()[frameCount - 1]);
 						duration = Math.max(duration, timeline.getFrames()[frameCount - 1]);
 						break;
 						break;
@@ -752,7 +739,7 @@ public class SkeletonBinary {
 				for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
 				for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
 					int slotIndex = input.readInt(true);
 					int slotIndex = input.readInt(true);
 					for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
 					for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
-						VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, input.readString());
+						VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, input.readStringRef());
 						boolean weighted = attachment.getBones() != null;
 						boolean weighted = attachment.getBones() != null;
 						float[] vertices = attachment.getVertices();
 						float[] vertices = attachment.getVertices();
 						int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
 						int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length;
@@ -852,11 +839,10 @@ public class SkeletonBinary {
 		}
 		}
 
 
 		timelines.shrink();
 		timelines.shrink();
-		skeletonData.animations.add(new Animation(name, timelines, duration));
-
+		return new Animation(name, timelines, duration);
 	}
 	}
 
 
-	private void readCurve (DataInput input, int frameIndex, CurveTimeline timeline) throws IOException {
+	private void readCurve (SkeletonInput input, int frameIndex, CurveTimeline timeline) throws IOException {
 		switch (input.readByte()) {
 		switch (input.readByte()) {
 		case CURVE_STEPPED:
 		case CURVE_STEPPED:
 			timeline.setStepped(frameIndex);
 			timeline.setStepped(frameIndex);
@@ -875,4 +861,53 @@ public class SkeletonBinary {
 		int[] bones;
 		int[] bones;
 		float[] vertices;
 		float[] vertices;
 	}
 	}
+
+	static class SkeletonInput extends DataInput {
+		private char[] chars = new char[32];
+		Array<String> strings;
+
+		public SkeletonInput (FileHandle file) {
+			super(file.read(512));
+		}
+
+		/** @return May be null. */
+		public String readStringRef () throws IOException {
+			int index = readInt(true);
+			return index == 0 ? null : strings.get(index - 1);
+		}
+
+		public String readString () throws IOException {
+			int byteCount = readInt(true);
+			switch (byteCount) {
+			case 0:
+				return null;
+			case 1:
+				return "";
+			}
+			byteCount--;
+			if (chars.length < byteCount) chars = new char[byteCount];
+			char[] chars = this.chars;
+			int charCount = 0;
+			for (int i = 0; i < byteCount;) {
+				int b = read();
+				switch (b >> 4) {
+				case -1:
+					throw new EOFException();
+				case 12:
+				case 13:
+					chars[charCount++] = (char)((b & 0x1F) << 6 | read() & 0x3F);
+					i += 2;
+					break;
+				case 14:
+					chars[charCount++] = (char)((b & 0x0F) << 12 | (read() & 0x3F) << 6 | read() & 0x3F);
+					i += 3;
+					break;
+				default:
+					chars[charCount++] = (char)b;
+					i++;
+				}
+			}
+			return new String(chars, 0, charCount);
+		}
+	}
 }
 }

+ 7 - 11
spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java

@@ -29,18 +29,9 @@
 
 
 package com.esotericsoftware.spine;
 package com.esotericsoftware.spine;
 
 
-import static com.badlogic.gdx.math.Interpolation.linear;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.delay;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeIn;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeOut;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.moveBy;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.parallel;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.removeActor;
-import static com.badlogic.gdx.scenes.scene2d.actions.Actions.sequence;
+import static com.badlogic.gdx.math.Interpolation.*;
+import static com.badlogic.gdx.scenes.scene2d.actions.Actions.*;
 
 
-import java.awt.FileDialog;
-import java.awt.Frame;
-import java.awt.Toolkit;
 import java.io.File;
 import java.io.File;
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.lang.Thread.UncaughtExceptionHandler;
 
 
@@ -88,11 +79,16 @@ import com.badlogic.gdx.utils.Align;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.StringBuilder;
 import com.badlogic.gdx.utils.StringBuilder;
 import com.badlogic.gdx.utils.viewport.ScreenViewport;
 import com.badlogic.gdx.utils.viewport.ScreenViewport;
+
 import com.esotericsoftware.spine.Animation.MixBlend;
 import com.esotericsoftware.spine.Animation.MixBlend;
 import com.esotericsoftware.spine.AnimationState.AnimationStateAdapter;
 import com.esotericsoftware.spine.AnimationState.AnimationStateAdapter;
 import com.esotericsoftware.spine.AnimationState.TrackEntry;
 import com.esotericsoftware.spine.AnimationState.TrackEntry;
 import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
 import com.esotericsoftware.spine.utils.TwoColorPolygonBatch;
 
 
+import java.awt.FileDialog;
+import java.awt.Frame;
+import java.awt.Toolkit;
+
 public class SkeletonViewer extends ApplicationAdapter {
 public class SkeletonViewer extends ApplicationAdapter {
 	static final float checkModifiedInterval = 0.250f;
 	static final float checkModifiedInterval = 0.250f;
 	static final float reloadDelay = 1;
 	static final float reloadDelay = 1;