浏览代码

[c] 4.0 porting, complete but with bugs.

badlogic 4 年之前
父节点
当前提交
e6a14ff532

+ 1 - 0
spine-c/spine-c/include/spine/Animation.h

@@ -117,6 +117,7 @@ struct spTimeline {
 	spPropertyId propertyIds[SP_MAX_PROPERTY_IDS];
 	int propertyIdsCount;
 	spFloatArray *frames;
+	int frameCount;
 	int frameEntries;
 };
 

+ 6 - 0
spine-c/spine-c/include/spine/SkeletonData.h

@@ -95,10 +95,16 @@ SP_API spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, co
 
 SP_API spIkConstraintData* spSkeletonData_findIkConstraint (const spSkeletonData* self, const char* constraintName);
 
+SP_API int spSkeletonData_findIkConstraintIndex (const spSkeletonData* self, const char* constraintName);
+
 SP_API spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkeletonData* self, const char* constraintName);
 
+SP_API int spSkeletonData_findTransformConstraintIndex (const spSkeletonData* self, const char* constraintName);
+
 SP_API spPathConstraintData* spSkeletonData_findPathConstraint (const spSkeletonData* self, const char* constraintName);
 
+SP_API int spSkeletonData_findPathConstraintIndex (const spSkeletonData* self, const char* constraintName);
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 0
spine-c/spine-c/include/spine/spine.h

@@ -31,6 +31,7 @@
 #define SPINE_SPINE_H_
 
 #include <spine/dll.h>
+#include <spine/Array.h>
 #include <spine/Animation.h>
 #include <spine/AnimationState.h>
 #include <spine/AnimationStateData.h>

+ 5 - 2
spine-c/spine-c/src/spine/Animation.c

@@ -39,7 +39,9 @@ spAnimation* spAnimation_create (const char* name, spTimelineArray* timelines, f
     int i, n;
 	spAnimation* self = NEW(spAnimation);
 	MALLOC_STR(self->name, name);
-	self->timelines = timelines;
+	self->timelines = timelines != NULL ? timelines : spTimelineArray_create(1);
+	timelines = self->timelines;
+	self->timelineIds = spPropertyIdArray_create(16);
 	for (i = 0, n = timelines->size; i < n; i++) {
 	    spPropertyIdArray_addAllValues(self->timelineIds, timelines->items[i]->propertyIds, 0, timelines->items[i]->propertyIdsCount);
 	}
@@ -110,6 +112,7 @@ void _spTimeline_init (spTimeline* self,
 ) {
     int i, n;
     self->frames = spFloatArray_create(frameCount * frameEntries);
+    self->frameCount = frameCount;
     self->frameEntries = frameEntries;
 	self->vtable.dispose = dispose;
 	self->vtable.apply = apply;
@@ -136,7 +139,7 @@ void spTimeline_setBezier(spTimeline* self, int bezier, int frame, float value,
 }
 
 int spTimeline_getFrameCount (const spTimeline* self) {
-    return self->frames->size / self->frameEntries;
+    return self->frameCount / self->frameEntries;
 }
 
 float spTimeline_getDuration (const spTimeline* self) {

+ 9 - 0
spine-c/spine-c/src/spine/Json.c

@@ -444,6 +444,15 @@ Json *Json_getItem (Json *object, const char* string) {
 	return c;
 }
 
+Json *Json_getItemAtIndex(Json *object, int childIndex) {
+	Json *current = object->child;
+	while (current != NULL && childIndex > 0) {
+		childIndex--;
+		current = current->next;
+	}
+	return current;
+}
+
 const char* Json_getString (Json* object, const char* name, const char* defaultValue) {
 	object = Json_getItem(object, name);
 	if (object) return object->valueString;

+ 1 - 0
spine-c/spine-c/src/spine/Json.h

@@ -69,6 +69,7 @@ void Json_dispose (Json* json);
 
 /* Get item "string" from object. Case insensitive. */
 Json* Json_getItem (Json* json, const char* string);
+Json *Json_getItemAtIndex(Json *object, int childIndex);
 const char* Json_getString (Json* json, const char* name, const char* defaultValue);
 float Json_getFloat (Json* json, const char* name, float defaultValue);
 int Json_getInt (Json* json, const char* name, int defaultValue);

+ 3 - 4
spine-c/spine-c/src/spine/SkeletonBinary.c

@@ -1139,7 +1139,7 @@ spSkeletonData* spSkeletonBinary_readSkeletonDataFile (spSkeletonBinary* self, c
 spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const unsigned char* binary,
 		const int length) {
 	int i, n, ii, nonessential;
-    char buffer[16];
+    char buffer[32];
     int lowHash, highHash;
 	spSkeletonData* skeletonData;
 	_spSkeletonBinary* internal = SUB_CAST(_spSkeletonBinary, self);
@@ -1155,9 +1155,8 @@ spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const
 	skeletonData = spSkeletonData_create();
 	lowHash = readInt(input);
 	highHash = readInt(input);
-	sprintf(buffer, "%x", highHash);
-	sprintf(buffer + 8, "%x", lowHash);
-	buffer[15] = 0;
+	sprintf(buffer, "%x%x", highHash, lowHash);
+	buffer[31] = 0;
 	skeletonData->hash = strdup(buffer);
 
 	skeletonData->version = readString(input);

+ 21 - 0
spine-c/spine-c/src/spine/SkeletonData.c

@@ -136,6 +136,13 @@ spIkConstraintData* spSkeletonData_findIkConstraint (const spSkeletonData* self,
 	return 0;
 }
 
+int spSkeletonData_findIkConstraintIndex (const spSkeletonData* self, const char* constraintName) {
+    int i;
+    for (i = 0; i < self->ikConstraintsCount; ++i)
+        if (strcmp(self->ikConstraints[i]->name, constraintName) == 0) return i;
+    return -1;
+}
+
 spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkeletonData* self, const char* constraintName) {
 	int i;
 	for (i = 0; i < self->transformConstraintsCount; ++i)
@@ -143,9 +150,23 @@ spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkele
 	return 0;
 }
 
+int spSkeletonData_findTransformConstraintIndex (const spSkeletonData* self, const char* constraintName) {
+    int i;
+    for (i = 0; i < self->transformConstraintsCount; ++i)
+        if (strcmp(self->transformConstraints[i]->name, constraintName) == 0) return i;
+    return -1;
+}
+
 spPathConstraintData* spSkeletonData_findPathConstraint (const spSkeletonData* self, const char* constraintName) {
 	int i;
 	for (i = 0; i < self->pathConstraintsCount; ++i)
 		if (strcmp(self->pathConstraints[i]->name, constraintName) == 0) return self->pathConstraints[i];
 	return 0;
 }
+
+int spSkeletonData_findPathConstraintIndex (const spSkeletonData* self, const char* constraintName) {
+    int i;
+    for (i = 0; i < self->pathConstraintsCount; ++i)
+        if (strcmp(self->pathConstraints[i]->name, constraintName) == 0) return i;
+    return -1;
+}

+ 495 - 273
spine-c/spine-c/src/spine/SkeletonJson.c

@@ -104,20 +104,86 @@ static float toColor (const char* value, int index) {
 	return color / (float)255;
 }
 
-static void readCurve (Json* frame, spCurveTimeline* timeline, int frameIndex) {
-	Json* curve = Json_getItem(frame, "curve");
-	if (!curve) return;
-	if (curve->type == Json_String && strcmp(curve->valueString, "stepped") == 0)
-		spCurveTimeline_setStepped(timeline, frameIndex);
-	else {
-		float c1 = Json_getFloat(frame, "curve", 0);
-		float c2 = Json_getFloat(frame, "c2", 0);
-		float c3 = Json_getFloat(frame, "c3", 1);
-		float c4 = Json_getFloat(frame, "c4", 1);
-		spCurveTimeline_setCurve(timeline, frameIndex, c1, c2, c3, c4);
-	}
+static void toColor2(spColor *color, const char *value, int /*bool*/ hasAlpha) {
+    color->r = toColor(value, 0);
+    color->g = toColor(value, 1);
+    color->b = toColor(value, 2);
+    if (hasAlpha) color->a = toColor(value, 3);
+}
+
+static void setBezier (spCurveTimeline *timeline, int frame, int value, int bezier, float time1, float value1, float cx1, float cy1,
+                              float cx2, float cy2, float time2, float value2) {
+    spTimeline_setBezier(SUPER(timeline), bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+}
+
+static int readCurve (Json *curve, spCurveTimeline *timeline, int bezier, int frame, int value, float time1, float time2,
+                             float value1, float value2, float scale) {
+    if (curve->type == Json_String && strcmp(curve->valueString, "stepped") == 0) {
+        spCurveTimeline_setStepped(timeline, frame);
+    } else {
+        float cx1, cy1, cx2, cy2;
+        curve = Json_getItemAtIndex(curve, value << 2);
+        cx1 = curve->valueFloat;
+        curve = curve->next;
+        cy1 = curve->valueFloat * scale;
+        curve = curve->next;
+        cx2 = curve->valueFloat;
+        curve = curve->next;
+        cy2 = curve->valueFloat * scale;
+        setBezier(timeline, frame, value, bezier++, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+    }
+    return bezier;
+}
+
+static spTimeline *readTimeline (Json *keyMap, spCurveTimeline1 *timeline, float defaultValue, float scale) {
+    float time = Json_getFloat(keyMap, "time", 0);
+    float value = Json_getFloat(keyMap, "value", defaultValue) * scale;
+    int frame, bezier = 0;
+    for (frame = 0;; frame++) {
+        Json *nextMap, *curve;
+        float time2, value2;
+        spCurveTimeline1_setFrame(timeline, frame, time, value);
+        nextMap = keyMap->next;
+        if (nextMap == NULL) break;
+        time2 = Json_getFloat(nextMap, "time", 0);
+        value2 = Json_getFloat(nextMap, "value", defaultValue) * scale;
+        curve = Json_getItem(keyMap, "curve");
+        if (curve != NULL) bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
+        time = time2;
+        value = value2;
+        keyMap = nextMap;
+    }
+    return SUPER(timeline);
+}
+
+static spTimeline *readTimeline2 (Json *keyMap, spCurveTimeline2 *timeline, const char *name1, const char *name2, float defaultValue, float scale) {
+    float time = Json_getFloat(keyMap, "time", 0);
+    float value1 = Json_getFloat(keyMap, name1, defaultValue) * scale;
+    float value2 = Json_getFloat(keyMap, name2, defaultValue) * scale;
+    int frame, bezier = 0;
+    for (frame = 0;; frame++) {
+        Json *nextMap, *curve;
+        float time2, nvalue1, nvalue2;
+        spCurveTimeline2_setFrame(timeline, frame, time, value1, value2);
+        nextMap = keyMap->next;
+        if (nextMap == NULL) break;
+        time2 = Json_getFloat(nextMap, "time", 0);
+        nvalue1 = Json_getFloat(nextMap, name1, defaultValue) * scale;
+        nvalue2 = Json_getFloat(nextMap, name2, defaultValue) * scale;
+        curve = Json_getItem(keyMap, "curve");
+        if (curve != NULL) {
+            bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
+            bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
+        }
+        time = time2;
+        value1 = nvalue1;
+        value2 = nvalue2;
+        keyMap = nextMap;
+    }
+    return SUPER(timeline);
 }
 
+
 static void _spSkeletonJson_addLinkedMesh (spSkeletonJson* self, spMeshAttachment* mesh, const char* skin, int slotIndex,
 	const char* parent, int inheritDeform
 ) {
@@ -142,273 +208,417 @@ static void _spSkeletonJson_addLinkedMesh (spSkeletonJson* self, spMeshAttachmen
 	linkedMesh->inheritDeform = inheritDeform;
 }
 
+static void cleanUpTimelines(spTimelineArray *timelines) {
+    int i, n;
+    for (i = 0, n = timelines->size; i < n; i++) {
+        spTimeline_dispose(timelines->items[i]);
+    }
+    spTimelineArray_dispose(timelines);
+}
+
 static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) {
-	int frameIndex;
-	spAnimation* animation;
-	Json* valueMap;
-	int timelinesCount = 0;
+    spTimelineArray *timelines = spTimelineArray_create(8);
 
+    float scale = self->scale, duration;
 	Json* bones = Json_getItem(root, "bones");
 	Json* slots = Json_getItem(root, "slots");
 	Json* ik = Json_getItem(root, "ik");
 	Json* transform = Json_getItem(root, "transform");
-	Json* path = Json_getItem(root, "path");
+	Json* paths = Json_getItem(root, "path");
 	Json* deformJson = Json_getItem(root, "deform");
 	Json* drawOrderJson = Json_getItem(root, "drawOrder");
 	Json* events = Json_getItem(root, "events");
-	Json *boneMap, *slotMap, *constraintMap;
-	if (!drawOrderJson) drawOrderJson = Json_getItem(root, "draworder");
-
-	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next)
-		timelinesCount += boneMap->size;
-	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next)
-		timelinesCount += slotMap->size;
-	timelinesCount += ik ? ik->size : 0;
-	timelinesCount += transform ? transform->size : 0;
-	for (constraintMap = path ? path->child : 0; constraintMap; constraintMap = constraintMap->next)
-		timelinesCount += constraintMap->size;
-	for (constraintMap = deformJson ? deformJson->child : 0; constraintMap; constraintMap = constraintMap->next)
-		for (slotMap = constraintMap->child; slotMap; slotMap = slotMap->next)
-			timelinesCount += slotMap->size;
-	if (drawOrderJson) ++timelinesCount;
-	if (events) ++timelinesCount;
-
-	animation = spAnimation_create(root->name, timelinesCount);
-	animation->timelinesCount = 0;
+	Json *boneMap, *slotMap, *constraintMap, *keyMap, *nextMap, *curve, *timelineMap;
+	int frame, bezier, i, n;
+	spColor color, color2, newColor, newColor2;
 
 	/* Slot timelines. */
-	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) {
-		Json *timelineMap;
-
-		int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
-		if (slotIndex == -1) {
-			spAnimation_dispose(animation);
-			_spSkeletonJson_setError(self, root, "Slot not found: ", slotMap->name);
-			return 0;
-		}
-
-		for (timelineMap = slotMap->child; timelineMap; timelineMap = timelineMap->next) {
-			if (strcmp(timelineMap->name, "attachment") == 0) {
-				spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineMap->size);
-				timeline->slotIndex = slotIndex;
-
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					Json* name = Json_getItem(valueMap, "name");
-					spAttachmentTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0),
-						name->type == Json_NULL ? 0 : name->valueString);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[timelineMap->size - 1]);
-
-			} else if (strcmp(timelineMap->name, "color") == 0) {
-				spColorTimeline *timeline = spColorTimeline_create(timelineMap->size);
-				timeline->slotIndex = slotIndex;
-
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					const char* s = Json_getString(valueMap, "color", 0);
-					spColorTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1),
-						toColor(s, 2), toColor(s, 3));
-					readCurve(valueMap, SUPER(timeline), frameIndex);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * COLOR_ENTRIES]);
-
-			} else if (strcmp(timelineMap->name, "twoColor") == 0) {
-				spTwoColorTimeline *timeline = spTwoColorTimeline_create(timelineMap->size);
-				timeline->slotIndex = slotIndex;
-
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					const char* s = Json_getString(valueMap, "light", 0);
-					const char* ds = Json_getString(valueMap, "dark", 0);
-					spTwoColorTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2),
-						toColor(s, 3), toColor(ds, 0), toColor(ds, 1), toColor(ds, 2));
-					readCurve(valueMap, SUPER(timeline), frameIndex);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * TWOCOLOR_ENTRIES]);
+    for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) {
+        int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
+        if (slotIndex == -1) {
+            cleanUpTimelines(timelines);
+            _spSkeletonJson_setError(self, NULL, "Slot not found: ", slotMap->name);
+            return NULL;
+        }
 
-			} else {
-				spAnimation_dispose(animation);
-				_spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineMap->name);
-				return 0;
-			}
-		}
-	}
+        for (timelineMap = slotMap->child; timelineMap; timelineMap = timelineMap->next) {
+            if (strcmp(timelineMap->name, "attachment") == 0) {
+                int frameCount = timelineMap->size;
+                spAttachmentTimeline *timeline = spAttachmentTimeline_create(frameCount, slotIndex);
+                for (keyMap = timelineMap->child, frame = 0; keyMap; keyMap = keyMap->next, ++frame) {
+                    spAttachmentTimeline_setFrame(timeline, frame, Json_getFloat(keyMap, "time", 0), Json_getItem(keyMap, "name")->valueString);
+                }
+                spTimelineArray_add(timelines, SUPER(timeline));
+
+            } else if (strcmp(timelineMap->name, "rgba") == 0) {
+                float time;
+                int frameCount = timelineMap->size;
+                int bezierCount = frameCount << 2;
+                spRGBATimeline *timeline = spRGBATimeline_create(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->child;
+                time = Json_getFloat(keyMap, "time", 0);
+                toColor2(&color, Json_getString(keyMap, "color", 0), 1);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    float time2;
+                    spRGBATimeline_setFrame(timeline, frame, time, color.r, color.g, color.b, color.a);
+                    nextMap = keyMap->next;
+                    if (!nextMap) break;
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    toColor2(&newColor, Json_getString(nextMap, "color", 0), 1);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    keyMap = nextMap;
+                }
+                spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+            } else if (strcmp(timelineMap->name, "rgb") == 0) {
+                float time;
+                int frameCount = timelineMap->size;
+                int bezierCount = frameCount * 3;
+                spRGBTimeline *timeline = spRGBTimeline_create(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->child;
+                time = Json_getFloat(keyMap, "time", 0);
+                toColor2(&color, Json_getString(keyMap, "color", 0), 1);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    float time2;
+                    spRGBTimeline_setFrame(timeline, frame, time, color.r, color.g, color.b);
+                    nextMap = keyMap->next;
+                    if (!nextMap) break;
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    toColor2(&newColor, Json_getString(nextMap, "color", 0), 1);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    keyMap = nextMap;
+                }
+                spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+            } else if (strcmp(timelineMap->name, "alpha") == 0) {
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(spAlphaTimeline_create(timelineMap->size, timelineMap->size, slotIndex)), 0, 1));
+            } else if (strcmp(timelineMap->name, "rgba2") == 0) {
+                float time;
+                int frameCount = timelineMap->size;
+                int bezierCount = frameCount * 7;
+                spRGBA2Timeline *timeline = spRGBA2Timeline_create(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->child;
+                time = Json_getFloat(keyMap, "time", 0);
+                toColor2(&color, Json_getString(keyMap, "light", 0), 1);
+                toColor2(&color2, Json_getString(keyMap, "dark", 0), 0);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    float time2;
+                    spRGBA2Timeline_setFrame(timeline, frame, time, color.r, color.g, color.b, color.a, color2.g, color2.g, color2.b);
+                    nextMap = keyMap->next;
+                    if (!nextMap) break;
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    toColor2(&newColor, Json_getString(nextMap, "light", 0), 1);
+                    toColor2(&newColor2, Json_getString(nextMap, "dark", 0), 0);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 3, time, time2, color.a, newColor.a, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 4, time, time2, color2.r, newColor2.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 5, time, time2, color2.g, newColor2.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 6, time, time2, color2.b, newColor2.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    color2 = newColor2;
+                    keyMap = nextMap;
+                }
+                spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+            } else if (strcmp(timelineMap->name, "rgb2") == 0) {
+                float time;
+                int frameCount = timelineMap->size;
+                int bezierCount = frameCount * 7;
+                spRGBA2Timeline *timeline = spRGBA2Timeline_create(frameCount, bezierCount, slotIndex);
+                keyMap = timelineMap->child;
+                time = Json_getFloat(keyMap, "time", 0);
+                toColor2(&color, Json_getString(keyMap, "light", 0), 0);
+                toColor2(&color2, Json_getString(keyMap, "dark", 0), 0);
+
+                for (frame = 0, bezier = 0;;++frame) {
+                    float time2;
+                    spRGBA2Timeline_setFrame(timeline, frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b);
+                    nextMap = keyMap->next;
+                    if (!nextMap) break;
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    toColor2(&newColor, Json_getString(nextMap, "light", 0), 0);
+                    toColor2(&newColor2, Json_getString(nextMap, "dark", 0), 0);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, color.r, newColor.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, color.g, newColor.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, color.b, newColor.b, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 3, time, time2, color2.r, newColor2.r, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 4, time, time2, color2.g, newColor2.g, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 5, time, time2, color2.b, newColor2.b, 1);
+                    }
+                    time = time2;
+                    color = newColor;
+                    color2 = newColor2;
+                    keyMap = nextMap;
+                }
+                spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+            } else {
+                cleanUpTimelines(timelines);
+                _spSkeletonJson_setError(self, NULL, "Invalid timeline type for a slot: ", timelineMap->name);
+                return NULL;
+            }
+        }
+    }
 
 	/* Bone timelines. */
-	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) {
-		Json *timelineMap;
+    for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) {
 
-		int boneIndex = spSkeletonData_findBoneIndex(skeletonData, boneMap->name);
-		if (boneIndex == -1) {
-			spAnimation_dispose(animation);
-			_spSkeletonJson_setError(self, root, "Bone not found: ", boneMap->name);
-			return 0;
-		}
-
-		for (timelineMap = boneMap->child; timelineMap; timelineMap = timelineMap->next) {
-			if (strcmp(timelineMap->name, "rotate") == 0) {
-				spRotateTimeline *timeline = spRotateTimeline_create(timelineMap->size);
-				timeline->boneIndex = boneIndex;
-
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					spRotateTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, "angle", 0));
-					readCurve(valueMap, SUPER(timeline), frameIndex);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * ROTATE_ENTRIES]);
-
-			} else {
-				int isScale = strcmp(timelineMap->name, "scale") == 0;
-				int isTranslate = strcmp(timelineMap->name, "translate") == 0;
-				int isShear = strcmp(timelineMap->name, "shear") == 0;
-				if (isScale || isTranslate || isShear) {
-					float defaultValue = 0;
-					float timelineScale = isTranslate ? self->scale: 1;
-					spTranslateTimeline *timeline = 0;
-					if (isScale) {
-						timeline = spScaleTimeline_create(timelineMap->size);
-						defaultValue = 1;
-					}
-					else if (isTranslate) timeline = spTranslateTimeline_create(timelineMap->size);
-					else if (isShear) timeline = spShearTimeline_create(timelineMap->size);
-					timeline->boneIndex = boneIndex;
-
-					for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-						spTranslateTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0),
-							Json_getFloat(valueMap, "x", defaultValue) * timelineScale,
-							Json_getFloat(valueMap, "y", defaultValue) * timelineScale);
-						readCurve(valueMap, SUPER(timeline), frameIndex);
-					}
-					animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-					animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * TRANSLATE_ENTRIES]);
+        int boneIndex = spSkeletonData_findBoneIndex(skeletonData, boneMap->name);
+        if (boneIndex == -1) {
+            cleanUpTimelines(timelines);
+            _spSkeletonJson_setError(self, NULL, "Bone not found: ", boneMap->name);
+            return NULL;
+        }
 
-				} else {
-					spAnimation_dispose(animation);
-					_spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineMap->name);
-					return 0;
-				}
-			}
-		}
-	}
+        for (timelineMap = boneMap->child; timelineMap; timelineMap = timelineMap->next) {
+            if (timelineMap->size == 0) continue;
+
+            if (strcmp(timelineMap->name, "rotate") == 0) {
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(spRotateTimeline_create(timelineMap->size, timelineMap->size, boneIndex)), 0, 1));
+            } else if (strcmp(timelineMap->name, "translate") == 0) {
+                spTranslateTimeline *timeline = spTranslateTimeline_create(timelineMap->size, timelineMap->size << 1, boneIndex);
+                spTimelineArray_add(timelines, readTimeline2(timelineMap->child, SUPER(timeline), "x", "y", 0, scale));
+            } else if (strcmp(timelineMap->name, "translatex") == 0) {
+                spTranslateXTimeline *timeline = spTranslateXTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 0, scale));
+            } else if (strcmp(timelineMap->name, "translatey") == 0) {
+                spTranslateYTimeline *timeline = spTranslateYTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 0, scale));
+            } else if (strcmp(timelineMap->name, "scale") == 0) {
+                spScaleTimeline *timeline = spScaleTimeline_create(timelineMap->size, timelineMap->size << 1, boneIndex);
+                spTimelineArray_add(timelines, readTimeline2(timelineMap->child, SUPER(timeline), "x", "y", 1, 1));
+            } else if (strcmp(timelineMap->name, "scalex") == 0) {
+                spScaleXTimeline *timeline = spScaleXTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 1, 1));
+            } else if (strcmp(timelineMap->name, "scaley") == 0) {
+                spScaleYTimeline *timeline = spScaleYTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 1, 1));
+            } else if (strcmp(timelineMap->name, "shear") == 0) {
+                spShearTimeline *timeline = spShearTimeline_create(timelineMap->size, timelineMap->size << 1, boneIndex);
+                spTimelineArray_add(timelines, readTimeline2(timelineMap->child, SUPER(timeline), "x", "y", 0, 1));
+            } else if (strcmp(timelineMap->name, "shearx") == 0) {
+                spShearXTimeline *timeline = spShearXTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 0, 1));
+            } else if (strcmp(timelineMap->name, "sheary") == 0) {
+                spShearYTimeline *timeline = spShearYTimeline_create(timelineMap->size, timelineMap->size, boneIndex);
+                spTimelineArray_add(timelines, readTimeline(timelineMap->child, SUPER(timeline), 0, 1));
+            } else {
+                cleanUpTimelines(timelines);
+                _spSkeletonJson_setError(self, NULL, "Invalid timeline type for a bone: ", timelineMap->name);
+                return NULL;
+            }
+        }
+    }
 
 	/* IK constraint timelines. */
-	for (constraintMap = ik ? ik->child : 0; constraintMap; constraintMap = constraintMap->next) {
-		spIkConstraintData* constraint = spSkeletonData_findIkConstraint(skeletonData, constraintMap->name);
-		spIkConstraintTimeline* timeline = spIkConstraintTimeline_create(constraintMap->size);
-		for (frameIndex = 0; frameIndex < skeletonData->ikConstraintsCount; ++frameIndex) {
-			if (constraint == skeletonData->ikConstraints[frameIndex]) {
-				timeline->ikConstraintIndex = frameIndex;
-				break;
-			}
-		}
-		for (valueMap = constraintMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-			spIkConstraintTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, "mix", 1), Json_getFloat(valueMap, "softness", 0) * self->scale,
-					Json_getInt(valueMap, "bendPositive", 1) ? 1 : -1, Json_getInt(valueMap, "compress", 0) ? 1 : 0, Json_getInt(valueMap, "stretch", 0) ? 1 : 0);
-			readCurve(valueMap, SUPER(timeline), frameIndex);
-		}
-		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-		animation->duration = MAX(animation->duration, timeline->frames[(constraintMap->size - 1) * IKCONSTRAINT_ENTRIES]);
-	}
+    for (constraintMap = ik ? ik->child : 0; constraintMap; constraintMap = constraintMap->next) {
+        spIkConstraintData *constraint;
+        spIkConstraintTimeline *timeline;
+        int constraintIndex;
+        float time, mix, softness;
+        keyMap = constraintMap->child;
+        if (keyMap == NULL) continue;
+
+        constraint = spSkeletonData_findIkConstraint(skeletonData, constraintMap->name);
+        constraintIndex = spSkeletonData_findIkConstraintIndex(skeletonData, constraint->name);
+        timeline = spIkConstraintTimeline_create(constraintMap->size, constraintMap->size << 1, constraintIndex);
+
+        time = Json_getFloat(keyMap, "time", 0);
+        mix = Json_getFloat(keyMap, "mix", 1);
+        softness = Json_getFloat(keyMap, "softness", 0) * scale;
+
+        for (frame = 0, bezier = 0;; frame++) {
+            float time2, mix2, softness2;
+            int bendDirection = Json_getInt(keyMap, "bendPositive", 1) ? 1 : -1;
+            spIkConstraintTimeline_setFrame(timeline, frame, time, mix, softness, bendDirection, Json_getInt(keyMap, "compress", 0) ? 1 : 0, Json_getInt(keyMap, "stretch", 0) ? 1 : 0);
+            nextMap = keyMap->next;
+            if (!nextMap) break;
+
+            time2 = Json_getFloat(nextMap, "time", 0);
+            mix2 = Json_getFloat(nextMap, "mix", 1);
+            softness2 = Json_getFloat(nextMap, "softness", 0) * scale;
+            curve = Json_getItem(keyMap, "curve");
+            if (curve) {
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, mix, mix2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, softness, softness2, scale);
+            }
+
+            time = time2;
+            mix = mix2;
+            softness = softness2;
+            keyMap = nextMap;
+        }
+
+        spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+    }
 
 	/* Transform constraint timelines. */
-	for (constraintMap = transform ? transform->child : 0; constraintMap; constraintMap = constraintMap->next) {
-		spTransformConstraintData* constraint = spSkeletonData_findTransformConstraint(skeletonData, constraintMap->name);
-		spTransformConstraintTimeline* timeline = spTransformConstraintTimeline_create(constraintMap->size);
-		for (frameIndex = 0; frameIndex < skeletonData->transformConstraintsCount; ++frameIndex) {
-			if (constraint == skeletonData->transformConstraints[frameIndex]) {
-				timeline->transformConstraintIndex = frameIndex;
-				break;
-			}
-		}
-		for (valueMap = constraintMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-			spTransformConstraintTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, "rotateMix", 1),
-					Json_getFloat(valueMap, "translateMix", 1), Json_getFloat(valueMap, "scaleMix", 1), Json_getFloat(valueMap, "shearMix", 1));
-			readCurve(valueMap, SUPER(timeline), frameIndex);
-		}
-		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-		animation->duration = MAX(animation->duration, timeline->frames[(constraintMap->size - 1) * TRANSFORMCONSTRAINT_ENTRIES]);
-	}
+    for (constraintMap = transform ? transform->child : 0; constraintMap; constraintMap = constraintMap->next) {
+        spTransformConstraintData *constraint;
+        spTransformConstraintTimeline *timeline;
+        int constraintIndex;
+        float time, mixRotate, mixShearY, mixX, mixY, mixScaleX, mixScaleY;
+        keyMap = constraintMap->child;
+        if (keyMap == NULL) continue;
+
+        constraint = spSkeletonData_findTransformConstraint(skeletonData, constraintMap->name);
+        constraintIndex = spSkeletonData_findTransformConstraintIndex(skeletonData, constraint->name);
+        timeline = spTransformConstraintTimeline_create(constraintMap->size, constraintMap->size << 2, constraintIndex);
+
+        time = Json_getFloat(keyMap, "time", 0);
+        mixRotate = Json_getFloat(keyMap, "mixRotate", 1);
+        mixShearY = Json_getFloat(keyMap, "mixShearY", 1);
+        mixX = Json_getFloat(keyMap, "mixX", 1);
+        mixY = Json_getFloat(keyMap, "mixY", mixX);
+        mixScaleX = Json_getFloat(keyMap, "mixScaleX", 1);
+        mixScaleY = Json_getFloat(keyMap, "mixScaleY", mixScaleX);
+
+        for (frame = 0, bezier = 0;; frame++) {
+            float time2, mixRotate2, mixShearY2, mixX2, mixY2, mixScaleX2, mixScaleY2;
+            spTransformConstraintTimeline_setFrame(timeline, frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+            nextMap = keyMap->next;
+            if (!nextMap) break;
+
+            time2 = Json_getFloat(nextMap, "time", 0);
+            mixRotate2 = Json_getFloat(nextMap, "mixRotate", 1);
+            mixShearY2 = Json_getFloat(nextMap, "mixShearY", 1);
+            mixX2 = Json_getFloat(nextMap, "mixX", 1);
+            mixY2 = Json_getFloat(nextMap, "mixY", mixX2);
+            mixScaleX2 = Json_getFloat(nextMap, "mixScaleX", 1);
+            mixScaleY2 = Json_getFloat(nextMap, "mixScaleY", mixScaleX2);
+            curve = Json_getItem(keyMap, "curve");
+            if (curve) {
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, mixX, mixX2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, mixY, mixY2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+                bezier = readCurve(curve, SUPER(timeline), bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+            }
+
+            time = time2;
+            mixRotate = mixRotate2;
+            mixX = mixX2;
+            mixY = mixY2;
+            mixScaleX = mixScaleX2;
+            mixScaleY = mixScaleY2;
+            mixScaleX = mixScaleX2;
+            keyMap = nextMap;
+        }
 
-	/** Path constraint timelines. */
-	for(constraintMap = path ? path->child : 0; constraintMap; constraintMap = constraintMap->next ) {
-		int constraintIndex, i;
-		Json* timelineMap;
-
-		spPathConstraintData* data = spSkeletonData_findPathConstraint(skeletonData, constraintMap->name);
-		if (!data) {
-			spAnimation_dispose(animation);
-			_spSkeletonJson_setError(self, root, "Path constraint not found: ", constraintMap->name);
-			return 0;
-		}
-		for (i = 0; i < skeletonData->pathConstraintsCount; i++) {
-			if (skeletonData->pathConstraints[i] == data) {
-				constraintIndex = i;
-				break;
-			}
-		}
+        spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+    }
 
-		for (timelineMap = constraintMap->child; timelineMap; timelineMap = timelineMap->next) {
-			const char* timelineName = timelineMap->name;
-			if (strcmp(timelineName, "position") == 0 || strcmp(timelineName, "spacing") == 0) {
-				spPathConstraintPositionTimeline* timeline;
-				float timelineScale = 1;
-				if (strcmp(timelineName, "spacing") == 0) {
-					timeline = (spPathConstraintPositionTimeline*)spPathConstraintSpacingTimeline_create(timelineMap->size);
-					if (data->spacingMode == SP_SPACING_MODE_LENGTH || data->spacingMode == SP_SPACING_MODE_FIXED) timelineScale = self->scale;
-				} else {
-					timeline = spPathConstraintPositionTimeline_create(timelineMap->size);
-					if (data->positionMode == SP_POSITION_MODE_FIXED) timelineScale = self->scale;
-				}
-				timeline->pathConstraintIndex = constraintIndex;
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					spPathConstraintPositionTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), Json_getFloat(valueMap, timelineName, 0) * timelineScale);
-					readCurve(valueMap, SUPER(timeline), frameIndex);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * PATHCONSTRAINTPOSITION_ENTRIES]);
-			} else if (strcmp(timelineName, "mix") == 0) {
-				spPathConstraintMixTimeline* timeline = spPathConstraintMixTimeline_create(timelineMap->size);
-				timeline->pathConstraintIndex = constraintIndex;
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-					spPathConstraintMixTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0),
-						Json_getFloat(valueMap, "rotateMix", 1), Json_getFloat(valueMap, "translateMix", 1));
-					readCurve(valueMap, SUPER(timeline), frameIndex);
-				}
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[(timelineMap->size - 1) * PATHCONSTRAINTMIX_ENTRIES]);
-			}
-		}
-	}
+	/** Path constraint timelines. */
+    for (constraintMap = paths ? paths->child : 0; constraintMap; constraintMap = constraintMap->next) {
+        spPathConstraintData *data = spSkeletonData_findPathConstraint(skeletonData, constraintMap->name);
+        int index;
+        if (!data) {
+            cleanUpTimelines(timelines);
+            _spSkeletonJson_setError(self, NULL, "Path constraint not found: ", constraintMap->name);
+            return NULL;
+        }
+        index = spSkeletonData_findPathConstraintIndex(skeletonData, data->name);
+        for (timelineMap = constraintMap->child; timelineMap; timelineMap = timelineMap->next) {
+            const char *timelineName;
+            keyMap = timelineMap->child;
+            if (keyMap == NULL) continue;
+            timelineName = timelineMap->name;
+            if (strcmp(timelineName, "position") == 0 ) {
+                spPathConstraintPositionTimeline *timeline = spPathConstraintPositionTimeline_create(timelineMap->size, timelineMap->size, index);
+                spTimelineArray_add(timelines, readTimeline(keyMap, SUPER(timeline), 0, data->positionMode == SP_POSITION_MODE_FIXED ? scale : 1));
+            } else if (strcmp(timelineName, "spacing") == 0) {
+                spCurveTimeline1 *timeline = SUPER(spPathConstraintSpacingTimeline_create(timelineMap->size, timelineMap->size, index));
+                spTimelineArray_add(timelines, readTimeline(keyMap, timeline, 0,
+                                           data->spacingMode == SP_SPACING_MODE_LENGTH || data->spacingMode == SP_SPACING_MODE_FIXED ? scale : 1));
+            } else if (strcmp(timelineName, "mix") == 0) {
+                spPathConstraintMixTimeline *timeline = spPathConstraintMixTimeline_create(timelineMap->size, timelineMap->size * 3, index);
+                float time = Json_getFloat(keyMap, "time", 0);
+                float mixRotate = Json_getFloat(keyMap, "mixRotate", 1);
+                float mixX = Json_getFloat(keyMap, "mixX", 1);
+                float mixY = Json_getFloat(keyMap, "mixY", mixX);
+                for (frame = 0, bezier = 0;; frame++) {
+                    float time2, mixRotate2, mixX2, mixY2;
+                    spPathConstraintMixTimeline_setFrame(timeline, frame, time, mixRotate, mixX, mixY);
+                    nextMap = keyMap->next;
+                    if (nextMap == NULL) {
+                        break;
+                    }
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    mixRotate2 = Json_getFloat(nextMap, "mixRotate", 1);
+                    mixX2 = Json_getFloat(nextMap, "mixX", 1);
+                    mixY2 = Json_getFloat(nextMap, "mixY", mixX2);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve != NULL) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 1, time, time2, mixX, mixX2, 1);
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 2, time, time2, mixY, mixY2, 1);
+                    }
+                    time = time2;
+                    mixRotate = mixRotate2;
+                    mixX = mixX2;
+                    mixY = mixY2;
+                    keyMap = nextMap;
+                }
+                spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
+            }
+        }
+    }
 
 	/* Deform timelines. */
 	for (constraintMap = deformJson ? deformJson->child : 0; constraintMap; constraintMap = constraintMap->next) {
 		spSkin* skin = spSkeletonData_findSkin(skeletonData, constraintMap->name);
 		for (slotMap = constraintMap->child; slotMap; slotMap = slotMap->next) {
 			int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
-			Json* timelineMap;
+
 			for (timelineMap = slotMap->child; timelineMap; timelineMap = timelineMap->next) {
 				float* tempDeform;
-				spDeformTimeline *timeline;
-				int weighted, deformLength;
-
-				spVertexAttachment* attachment = SUB_CAST(spVertexAttachment, spSkin_getAttachment(skin, slotIndex, timelineMap->name));
+                spVertexAttachment* attachment;
+                int weighted, deformLength;
+                spDeformTimeline *timeline;
+                float time;
+				keyMap = timelineMap->child;
+				if (keyMap == NULL) continue;
+
+				attachment = SUB_CAST(spVertexAttachment, spSkin_getAttachment(skin, slotIndex, timelineMap->name));
 				if (!attachment) {
-					spAnimation_dispose(animation);
+					cleanUpTimelines(timelines);
 					_spSkeletonJson_setError(self, 0, "Attachment not found: ", timelineMap->name);
 					return 0;
 				}
 				weighted = attachment->bones != 0;
 				deformLength = weighted ? attachment->verticesCount / 3 * 2 : attachment->verticesCount;
 				tempDeform = MALLOC(float, deformLength);
+				timeline = spDeformTimeline_create(timelineMap->size, deformLength, timelineMap->size, slotIndex, attachment);
 
-				timeline = spDeformTimeline_create(timelineMap->size, deformLength);
-				timeline->slotIndex = slotIndex;
-				timeline->attachment = SUPER(attachment);
-
-				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
+				time = Json_getFloat(keyMap, "time", 0);
+                for (frame = 0, bezier = 0;; frame++) {
+                    Json *vertices = Json_getItem(keyMap, "vertices");
 					float* deform;
-					Json* vertices = Json_getItem(valueMap, "vertices");
+					float time2;
+
 					if (!vertices) {
 						if (weighted) {
 							deform = tempDeform;
@@ -416,7 +626,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 						} else
 							deform = attachment->vertices;
 					} else {
-						int v, start = Json_getInt(valueMap, "offset", 0);
+						int v, start = Json_getInt(keyMap, "offset", 0);
 						Json* vertex;
 						deform = tempDeform;
 						memset(deform, 0, sizeof(float) * start);
@@ -434,13 +644,20 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 								deform[v] += verticesValues[v];
 						}
 					}
-					spDeformTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), deform);
-					readCurve(valueMap, SUPER(timeline), frameIndex);
+					spDeformTimeline_setFrame(timeline, frame, time, deform);
+                    nextMap = keyMap->next;
+                    if (!nextMap) break;
+                    time2 = Json_getFloat(nextMap, "time", 0);
+                    curve = Json_getItem(keyMap, "curve");
+                    if (curve) {
+                        bezier = readCurve(curve, SUPER(timeline), bezier, frame, 0, time, time2, 0, 1, 1);
+                    }
+                    time = time2;
+                    keyMap = nextMap;
 				}
 				FREE(tempDeform);
 
-				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-				animation->duration = MAX(animation->duration, timeline->frames[timelineMap->size - 1]);
+				spTimelineArray_add(timelines, SUPER(SUPER(timeline)));
 			}
 		}
 	}
@@ -448,10 +665,10 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 	/* Draw order timeline. */
 	if (drawOrderJson) {
 		spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrderJson->size, skeletonData->slotsCount);
-		for (valueMap = drawOrderJson->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-			int ii;
+        for (keyMap = drawOrderJson->child, frame = 0; keyMap; keyMap = keyMap->next, ++frame) {
+            int ii;
 			int* drawOrder = 0;
-			Json* offsets = Json_getItem(valueMap, "offsets");
+			Json* offsets = Json_getItem(keyMap, "offsets");
 			if (offsets) {
 				Json* offsetMap;
 				int* unchanged = MALLOC(int, skeletonData->slotsCount - offsets->size);
@@ -464,7 +681,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 				for (offsetMap = offsets->child; offsetMap; offsetMap = offsetMap->next) {
 					int slotIndex = spSkeletonData_findSlotIndex(skeletonData, Json_getString(offsetMap, "slot", 0));
 					if (slotIndex == -1) {
-						spAnimation_dispose(animation);
+						cleanUpTimelines(timelines);
 						_spSkeletonJson_setError(self, 0, "Slot not found: ", Json_getString(offsetMap, "slot", 0));
 						return 0;
 					}
@@ -483,41 +700,44 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 					if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
 				FREE(unchanged);
 			}
-			spDrawOrderTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), drawOrder);
+			spDrawOrderTimeline_setFrame(timeline, frame, Json_getFloat(keyMap, "time", 0), drawOrder);
 			FREE(drawOrder);
 		}
-		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-		animation->duration = MAX(animation->duration, timeline->frames[drawOrderJson->size - 1]);
+
+        spTimelineArray_add(timelines, SUPER(timeline));
 	}
 
 	/* Event timeline. */
 	if (events) {
 		spEventTimeline* timeline = spEventTimeline_create(events->size);
-		for (valueMap = events->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
-			spEvent* event;
+        for (keyMap = events->child, frame = 0; keyMap; keyMap = keyMap->next, ++frame) {
+            spEvent* event;
 			const char* stringValue;
-			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(valueMap, "name", 0));
+			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(keyMap, "name", 0));
 			if (!eventData) {
-				spAnimation_dispose(animation);
-				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(valueMap, "name", 0));
+				cleanUpTimelines(timelines);
+				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(keyMap, "name", 0));
 				return 0;
 			}
-			event = spEvent_create(Json_getFloat(valueMap, "time", 0), eventData);
-			event->intValue = Json_getInt(valueMap, "int", eventData->intValue);
-			event->floatValue = Json_getFloat(valueMap, "float", eventData->floatValue);
-			stringValue = Json_getString(valueMap, "string", eventData->stringValue);
+			event = spEvent_create(Json_getFloat(keyMap, "time", 0), eventData);
+			event->intValue = Json_getInt(keyMap, "int", eventData->intValue);
+			event->floatValue = Json_getFloat(keyMap, "float", eventData->floatValue);
+			stringValue = Json_getString(keyMap, "string", eventData->stringValue);
 			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
 			if (eventData->audioPath) {
-				event->volume = Json_getFloat(valueMap, "volume", 1);
-				event->balance = Json_getFloat(valueMap, "volume", 0);
+				event->volume = Json_getFloat(keyMap, "volume", 1);
+				event->balance = Json_getFloat(keyMap, "volume", 0);
 			}
-			spEventTimeline_setFrame(timeline, frameIndex, event);
+			spEventTimeline_setFrame(timeline, frame, event);
 		}
-		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
-		animation->duration = MAX(animation->duration, timeline->frames[events->size - 1]);
+		spTimelineArray_add(timelines, SUPER(timeline));
 	}
 
-	return animation;
+    duration = 0;
+	for (i = 0, n = timelines->size; i < n; i++) {
+	    duration = MAX(duration, spTimeline_getDuration(timelines->items[i]));
+	}
+	return spAnimation_create(root->name, timelines, duration);
 }
 
 static void _readVertices (spSkeletonJson* self, Json* attachmentMap, spVertexAttachment* attachment, int verticesLength) {
@@ -802,10 +1022,11 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 			data->offsetScaleY = Json_getFloat(constraintMap, "scaleY", 0);
 			data->offsetShearY = Json_getFloat(constraintMap, "shearY", 0);
 
-			data->rotateMix = Json_getFloat(constraintMap, "rotateMix", 1);
-			data->translateMix = Json_getFloat(constraintMap, "translateMix", 1);
-			data->scaleMix = Json_getFloat(constraintMap, "scaleMix", 1);
-			data->shearMix = Json_getFloat(constraintMap, "shearMix", 1);
+            data->mixX = Json_getFloat(constraintMap, "mixX", 1);
+            data->mixY = Json_getFloat(constraintMap, "mixY", data->mixX);
+            data->mixScaleX = Json_getFloat(constraintMap, "mixScaleX", 1);
+            data->mixScaleY = Json_getFloat(constraintMap, "mixScaleY", data->mixScaleX);
+            data->mixShearY = Json_getFloat(constraintMap, "mixShearY", 1);
 
 			skeletonData->transformConstraints[i] = data;
 		}
@@ -864,8 +1085,9 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 			if (data->positionMode == SP_POSITION_MODE_FIXED) data->position *= self->scale;
 			data->spacing = Json_getFloat(constraintMap, "spacing", 0);
 			if (data->spacingMode == SP_SPACING_MODE_LENGTH || data->spacingMode == SP_SPACING_MODE_FIXED) data->spacing *= self->scale;
-			data->rotateMix = Json_getFloat(constraintMap, "rotateMix", 1);
-			data->translateMix = Json_getFloat(constraintMap, "translateMix", 1);
+            data->mixRotate = Json_getFloat(constraintMap, "mixRotate", 1);
+            data->mixX = Json_getFloat(constraintMap, "mixX", 1);
+            data->mixY = Json_getFloat(constraintMap, "mixY", 1);
 
 			skeletonData->pathConstraints[i] = data;
 		}

+ 50 - 1
spine-sfml/c/src/spine/spine-sfml.cpp

@@ -45,7 +45,56 @@ sf::BlendMode additivePma = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::One
 sf::BlendMode multiplyPma = sf::BlendMode(sf::BlendMode::DstColor, sf::BlendMode::OneMinusSrcAlpha);
 sf::BlendMode screenPma = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcColor);
 
-_SP_ARRAY_IMPLEMENT_TYPE(spColorArray, spColor)
+spColorArray *spColorArray_create(int initialCapacity) {
+    spColorArray *array = ((spColorArray *) _spCalloc(1, sizeof(spColorArray), "_file_name_", 48));
+    array->size = 0;
+    array->capacity = initialCapacity;
+    array->items = ((spColor *) _spCalloc(initialCapacity, sizeof(spColor), "_file_name_", 48));
+    return array;
+}
+void spColorArray_dispose(spColorArray *self) {
+    _spFree((void *) self->items);
+    _spFree((void *) self);
+}
+void spColorArray_clear(spColorArray *self) { self->size = 0; }
+spColorArray *spColorArray_setSize(spColorArray *self, int newSize) {
+    self->size = newSize;
+    if (self->capacity < newSize) {
+        self->capacity = ((8) > ((int) (self->size * 1.75f)) ? (8) : ((int) (self->size * 1.75f)));
+        self->items = ((spColor *) _spRealloc(self->items, sizeof(spColor) * (self->capacity)));
+    }
+    return self;
+}
+void spColorArray_ensureCapacity(spColorArray *self, int newCapacity) {
+    if (self->capacity >= newCapacity)return;
+    self->capacity = newCapacity;
+    self->items = ((spColor *) _spRealloc(self->items, sizeof(spColor) * (self->capacity)));
+}
+void spColorArray_add(spColorArray *self, spColor value) {
+    if (self->size == self->capacity) {
+        self->capacity = ((8) > ((int) (self->size * 1.75f)) ? (8) : ((int) (self->size * 1.75f)));
+        self->items = ((spColor *) _spRealloc(self->items, sizeof(spColor) * (self->capacity)));
+    }
+    self->items[self->size++] = value;
+}
+void spColorArray_addAll(spColorArray *self, spColorArray *other) {
+    int i = 0;
+    for (; i < other->size; i++) { spColorArray_add(self, other->items[i]); }
+}
+void spColorArray_addAllValues(spColorArray *self, spColor *values, int offset, int count) {
+    int i = offset, n = offset + count;
+    for (; i < n; i++) { spColorArray_add(self, values[i]); }
+}
+void spColorArray_removeAt(spColorArray *self, int index) {
+    self->size--;
+    memmove(self->items + index, self->items + index + 1, sizeof(spColor) * (self->size - index));
+}
+
+spColor spColorArray_pop(spColorArray *self) {
+    spColor item = self->items[--self->size];
+    return item;
+}
+spColor spColorArray_peek(spColorArray *self) { return self->items[self->size - 1]; }
 
 void _spAtlasPage_createTexture (spAtlasPage* self, const char* path){
 	Texture* texture = new Texture();