瀏覽代碼

Initial Commit, nothing special going on here yet!

Stephen Gowen 8 年之前
父節點
當前提交
4e542e2931
共有 83 個文件被更改,包括 14696 次插入0 次删除
  1. 27 0
      spine-cpp/LICENSE
  2. 40 0
      spine-cpp/README.md
  3. 588 0
      spine-cpp/spine-cpp/include/spine/Animation.h
  4. 185 0
      spine-cpp/spine-cpp/include/spine/AnimationState.h
  5. 77 0
      spine-cpp/spine-cpp/include/spine/AnimationStateData.h
  6. 133 0
      spine-cpp/spine-cpp/include/spine/Array.h
  7. 174 0
      spine-cpp/spine-cpp/include/spine/Atlas.h
  8. 58 0
      spine-cpp/spine-cpp/include/spine/AtlasAttachmentLoader.h
  9. 83 0
      spine-cpp/spine-cpp/include/spine/Attachment.h
  10. 79 0
      spine-cpp/spine-cpp/include/spine/AttachmentLoader.h
  11. 127 0
      spine-cpp/spine-cpp/include/spine/Bone.h
  12. 85 0
      spine-cpp/spine-cpp/include/spine/BoneData.h
  13. 59 0
      spine-cpp/spine-cpp/include/spine/BoundingBoxAttachment.h
  14. 61 0
      spine-cpp/spine-cpp/include/spine/ClippingAttachment.h
  15. 78 0
      spine-cpp/spine-cpp/include/spine/Color.h
  16. 72 0
      spine-cpp/spine-cpp/include/spine/Event.h
  17. 69 0
      spine-cpp/spine-cpp/include/spine/EventData.h
  18. 87 0
      spine-cpp/spine-cpp/include/spine/IkConstraint.h
  19. 76 0
      spine-cpp/spine-cpp/include/spine/IkConstraintData.h
  20. 91 0
      spine-cpp/spine-cpp/include/spine/MeshAttachment.h
  21. 63 0
      spine-cpp/spine-cpp/include/spine/PathAttachment.h
  22. 113 0
      spine-cpp/spine-cpp/include/spine/PathConstraint.h
  23. 97 0
      spine-cpp/spine-cpp/include/spine/PathConstraintData.h
  24. 65 0
      spine-cpp/spine-cpp/include/spine/PointAttachment.h
  25. 75 0
      spine-cpp/spine-cpp/include/spine/RegionAttachment.h
  26. 174 0
      spine-cpp/spine-cpp/include/spine/Skeleton.h
  27. 72 0
      spine-cpp/spine-cpp/include/spine/SkeletonBinary.h
  28. 113 0
      spine-cpp/spine-cpp/include/spine/SkeletonBounds.h
  29. 68 0
      spine-cpp/spine-cpp/include/spine/SkeletonClipping.h
  30. 117 0
      spine-cpp/spine-cpp/include/spine/SkeletonData.h
  31. 73 0
      spine-cpp/spine-cpp/include/spine/SkeletonJson.h
  32. 95 0
      spine-cpp/spine-cpp/include/spine/Skin.h
  33. 93 0
      spine-cpp/spine-cpp/include/spine/Slot.h
  34. 90 0
      spine-cpp/spine-cpp/include/spine/SlotData.h
  35. 81 0
      spine-cpp/spine-cpp/include/spine/TransformConstraint.h
  36. 87 0
      spine-cpp/spine-cpp/include/spine/TransformConstraintData.h
  37. 63 0
      spine-cpp/spine-cpp/include/spine/Triangulator.h
  38. 68 0
      spine-cpp/spine-cpp/include/spine/VertexAttachment.h
  39. 85 0
      spine-cpp/spine-cpp/include/spine/VertexEffect.h
  40. 48 0
      spine-cpp/spine-cpp/include/spine/dll.h
  41. 318 0
      spine-cpp/spine-cpp/include/spine/extension.h
  42. 63 0
      spine-cpp/spine-cpp/include/spine/spine.h
  43. 1529 0
      spine-cpp/spine-cpp/src/spine/Animation.c
  44. 932 0
      spine-cpp/spine-cpp/src/spine/AnimationState.c
  45. 151 0
      spine-cpp/spine-cpp/src/spine/AnimationStateData.c
  46. 39 0
      spine-cpp/spine-cpp/src/spine/Array.c
  47. 359 0
      spine-cpp/spine-cpp/src/spine/Atlas.c
  48. 100 0
      spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.c
  49. 57 0
      spine-cpp/spine-cpp/src/spine/Attachment.c
  50. 98 0
      spine-cpp/spine-cpp/src/spine/AttachmentLoader.c
  51. 304 0
      spine-cpp/spine-cpp/src/spine/Bone.c
  52. 48 0
      spine-cpp/spine-cpp/src/spine/BoneData.c
  53. 47 0
      spine-cpp/spine-cpp/src/spine/BoundingBoxAttachment.c
  54. 48 0
      spine-cpp/spine-cpp/src/spine/ClippingAttachment.c
  55. 84 0
      spine-cpp/spine-cpp/src/spine/Color.c
  56. 44 0
      spine-cpp/spine-cpp/src/spine/Event.c
  57. 44 0
      spine-cpp/spine-cpp/src/spine/EventData.c
  58. 208 0
      spine-cpp/spine-cpp/src/spine/IkConstraint.c
  59. 46 0
      spine-cpp/spine-cpp/src/spine/IkConstraintData.c
  60. 426 0
      spine-cpp/spine-cpp/src/spine/Json.c
  61. 83 0
      spine-cpp/spine-cpp/src/spine/Json.h
  62. 100 0
      spine-cpp/spine-cpp/src/spine/MeshAttachment.c
  63. 48 0
      spine-cpp/spine-cpp/src/spine/PathAttachment.c
  64. 467 0
      spine-cpp/spine-cpp/src/spine/PathConstraint.c
  65. 44 0
      spine-cpp/spine-cpp/src/spine/PathConstraintData.c
  66. 61 0
      spine-cpp/spine-cpp/src/spine/PointAttachment.c
  67. 130 0
      spine-cpp/spine-cpp/src/spine/RegionAttachment.c
  68. 583 0
      spine-cpp/spine-cpp/src/spine/Skeleton.c
  69. 1095 0
      spine-cpp/spine-cpp/src/spine/SkeletonBinary.c
  70. 205 0
      spine-cpp/spine-cpp/src/spine/SkeletonBounds.c
  71. 313 0
      spine-cpp/spine-cpp/src/spine/SkeletonClipping.c
  72. 147 0
      spine-cpp/spine-cpp/src/spine/SkeletonData.c
  73. 1119 0
      spine-cpp/spine-cpp/src/spine/SkeletonJson.c
  74. 106 0
      spine-cpp/spine-cpp/src/spine/Skin.c
  75. 82 0
      spine-cpp/spine-cpp/src/spine/Slot.c
  76. 56 0
      spine-cpp/spine-cpp/src/spine/SlotData.c
  77. 273 0
      spine-cpp/spine-cpp/src/spine/TransformConstraint.c
  78. 44 0
      spine-cpp/spine-cpp/src/spine/TransformConstraintData.c
  79. 361 0
      spine-cpp/spine-cpp/src/spine/Triangulator.c
  80. 114 0
      spine-cpp/spine-cpp/src/spine/VertexAttachment.c
  81. 98 0
      spine-cpp/spine-cpp/src/spine/VertexEffect.c
  82. 128 0
      spine-cpp/spine-cpp/src/spine/extension.c
  83. 105 0
      spine-cpp/spine-cpp/src/spine/kvec.h

+ 27 - 0
spine-cpp/LICENSE

@@ -0,0 +1,27 @@
+Spine Runtimes Software License v2.5
+
+Copyright (c) 2013-2016, Esoteric Software
+All rights reserved.
+
+You are granted a perpetual, non-exclusive, non-sublicensable, and
+non-transferable license to use, install, execute, and perform the Spine
+Runtimes software and derivative works solely for personal or internal
+use. Without the written permission of Esoteric Software (see Section 2 of
+the Spine Software License Agreement), you may not (a) modify, translate,
+adapt, or develop new applications using the Spine Runtimes or otherwise
+create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+USE, DATA, OR PROFITS) 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.

+ 40 - 0
spine-cpp/README.md

@@ -0,0 +1,40 @@
+# spine-cpp
+
+The spine-cpp runtime provides basic functionality to load and manipulate [Spine](http://esotericsoftware.com) skeletal animation data using C++. It does not perform rendering but can be extended to enable Spine animations for other projects that utilize C++. Note, this library uses C++03 for maximum portability and therefore does not take advantage of any C++11 or newer features such as std::unique_ptr.
+
+## Licensing
+
+This Spine Runtime may only be used for personal or internal use, typically to evaluate Spine before purchasing. If you would like to incorporate a Spine Runtime into your applications, distribute software containing a Spine Runtime, or modify a Spine Runtime, then you will need a valid [Spine license](https://esotericsoftware.com/spine-purchase). Please see the [Spine Runtimes Software License](https://github.com/EsotericSoftware/spine-runtimes/blob/master/LICENSE) for detailed information.
+
+The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes.
+
+## Spine version
+
+spine-cpp works with data exported from Spine 3.6.xx.
+
+spine-cpp supports all Spine features.
+
+## Setup
+
+1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/3.6.zip).
+1. Open the `spine-c.sln` Visual C++ 2010 Express project file. For other IDEs, you will need to create a new project and import the source.
+
+Alternatively, the contents of the `spine-c/spine-c/src` and `spine-c/spine-c/include` directories can be copied into your project. Be sure your header search is configured to find the contents of the `spine-c/spine-c/include` directory. Note that the includes use `spine/Xxx.h`, so the `spine` directory cannot be omitted when copying the files.
+
+If `SPINE_SHORT_NAMES` is defined, the `sp` prefix for all structs and functions is optional. Only use this if the spine-c names won't cause a conflict.
+
+## Extension
+
+Extending spine-c requires implementing three methods:
+
+- `_spAtlasPage_createTexture` Loads a texture and stores it and its size in the `void* rendererObject`, `width` and `height` fields of an `spAtlasPage` struct.
+- `_spAtlasPage_disposeTexture` Disposes of a texture loaded with `_spAtlasPage_createTexture`.
+- `_spUtil_readFile` Reads a file. If this doesn't need to be customized, `_readFile` is provided which reads a file using `fopen`.
+
+With these implemented, the spine-c API can then be used to load Spine animation data. Rendering is done by enumerating the slots for a skeleton and rendering the attachment for each slot. Each attachment has a `rendererObject` field that is set when the attachment is loaded.
+
+For example, `AtlasAttachmentLoader` is typically used to load attachments when using a Spine texture atlas. When `AtlasAttachmentLoader` loads a `RegionAttachment`, the attachment's `void* rendererObject` is set to an `AtlasRegion`. Rendering code can then obtain the `AtlasRegion` from the attachment, get the `AtlasPage` it belongs to, and get the page's `void* rendererObject`. This is the renderer specific texture object set by `_spAtlasPage_createTexture`. Attachment loading can be [customized](http://esotericsoftware.com/spine-using-runtimes/#attachmentloader) if not using `AtlasAttachmentLoader` or to provider different renderer specific data.
+
+[spine-sfml](https://github.com/EsotericSoftware/spine-runtimes/blob/master/spine-sfml/src/spine/spine-sfml.cpp#L39) serves as a simple example of extending spine-c.
+
+spine-c uses an OOP style of programming where each "class" is made up of a struct and a number of functions prefixed with the struct name. More detals about how this works are available in [extension.h](https://github.com/EsotericSoftware/spine-runtimes/blob/master/spine-c/spine-c/include/spine/extension.h#L2). This mechanism allows you to provide your own implementations for `spAttachmentLoader`, `spAttachment` and `spTimeline`, if necessary.

+ 588 - 0
spine-cpp/spine-cpp/include/spine/Animation.h

@@ -0,0 +1,588 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATION_H_
+#define SPINE_ANIMATION_H_
+
+#include <spine/dll.h>
+#include <spine/Event.h>
+#include <spine/Attachment.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spTimeline spTimeline;
+struct spSkeleton;
+
+typedef struct spAnimation {
+	const char* const name;
+	float duration;
+
+	int timelinesCount;
+	spTimeline** timelines;
+
+#ifdef __cplusplus
+	spAnimation() :
+		name(0),
+		duration(0),
+		timelinesCount(0),
+		timelines(0) {
+	}
+#endif
+} spAnimation;
+
+typedef enum {
+	SP_MIX_POSE_SETUP,
+	SP_MIX_POSE_CURRENT,
+	SP_MIX_POSE_CURRENT_LAYERED
+} spMixPose;
+
+typedef enum {
+	SP_MIX_DIRECTION_IN,
+	SP_MIX_DIRECTION_OUT
+} spMixDirection;
+
+SP_API spAnimation* spAnimation_create (const char* name, int timelinesCount);
+SP_API void spAnimation_dispose (spAnimation* self);
+
+/** Poses the skeleton at the specified time for this animation.
+ * @param lastTime The last time the animation was applied.
+ * @param events Any triggered events are added. May be null.*/
+SP_API void spAnimation_apply (const spAnimation* self, struct spSkeleton* skeleton, float lastTime, float time, int loop,
+		spEvent** events, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAnimation Animation;
+#define Animation_create(...) spAnimation_create(__VA_ARGS__)
+#define Animation_dispose(...) spAnimation_dispose(__VA_ARGS__)
+#define Animation_apply(...) spAnimation_apply(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef enum {
+	SP_TIMELINE_ROTATE,
+	SP_TIMELINE_TRANSLATE,
+	SP_TIMELINE_SCALE,
+	SP_TIMELINE_SHEAR,
+	SP_TIMELINE_ATTACHMENT,
+	SP_TIMELINE_COLOR,
+	SP_TIMELINE_DEFORM,
+	SP_TIMELINE_EVENT,
+	SP_TIMELINE_DRAWORDER,
+	SP_TIMELINE_IKCONSTRAINT,
+	SP_TIMELINE_TRANSFORMCONSTRAINT,
+	SP_TIMELINE_PATHCONSTRAINTPOSITION,
+	SP_TIMELINE_PATHCONSTRAINTSPACING,
+	SP_TIMELINE_PATHCONSTRAINTMIX,
+	SP_TIMELINE_TWOCOLOR
+} spTimelineType;
+
+struct spTimeline {
+	const spTimelineType type;
+	const void* const vtable;
+
+#ifdef __cplusplus
+	spTimeline() :
+		type(SP_TIMELINE_SCALE),
+		vtable(0) {
+	}
+#endif
+};
+
+SP_API void spTimeline_dispose (spTimeline* self);
+SP_API void spTimeline_apply (const spTimeline* self, struct spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction);
+SP_API int spTimeline_getPropertyId (const spTimeline* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTimeline Timeline;
+#define TIMELINE_SCALE SP_TIMELINE_SCALE
+#define TIMELINE_ROTATE SP_TIMELINE_ROTATE
+#define TIMELINE_TRANSLATE SP_TIMELINE_TRANSLATE
+#define TIMELINE_COLOR SP_TIMELINE_COLOR
+#define TIMELINE_ATTACHMENT SP_TIMELINE_ATTACHMENT
+#define TIMELINE_EVENT SP_TIMELINE_EVENT
+#define TIMELINE_DRAWORDER SP_TIMELINE_DRAWORDER
+#define Timeline_dispose(...) spTimeline_dispose(__VA_ARGS__)
+#define Timeline_apply(...) spTimeline_apply(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spCurveTimeline {
+	spTimeline super;
+	float* curves; /* type, x, y, ... */
+
+#ifdef __cplusplus
+	spCurveTimeline() :
+		super(),
+		curves(0) {
+	}
+#endif
+} spCurveTimeline;
+
+SP_API void spCurveTimeline_setLinear (spCurveTimeline* self, int frameIndex);
+SP_API void spCurveTimeline_setStepped (spCurveTimeline* self, int frameIndex);
+
+/* Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
+ * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
+ * the difference between the keyframe's values. */
+SP_API void spCurveTimeline_setCurve (spCurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2);
+SP_API float spCurveTimeline_getCurvePercent (const spCurveTimeline* self, int frameIndex, float percent);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spCurveTimeline CurveTimeline;
+#define CurveTimeline_setLinear(...) spCurveTimeline_setLinear(__VA_ARGS__)
+#define CurveTimeline_setStepped(...) spCurveTimeline_setStepped(__VA_ARGS__)
+#define CurveTimeline_setCurve(...) spCurveTimeline_setCurve(__VA_ARGS__)
+#define CurveTimeline_getCurvePercent(...) spCurveTimeline_getCurvePercent(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, angle, ... for rotate. time, x, y, ... for translate and scale. */
+	int boneIndex;
+
+#ifdef __cplusplus
+	spBaseTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		boneIndex(0) {
+	}
+#endif
+} spBaseTimeline;
+
+/**/
+
+static const int ROTATE_PREV_TIME = -2, ROTATE_PREV_ROTATION = -1;
+static const int ROTATE_ROTATION = 1;
+static const int ROTATE_ENTRIES = 2;
+
+typedef struct spBaseTimeline spRotateTimeline;
+
+SP_API spRotateTimeline* spRotateTimeline_create (int framesCount);
+
+SP_API void spRotateTimeline_setFrame (spRotateTimeline* self, int frameIndex, float time, float angle);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spRotateTimeline RotateTimeline;
+#define RotateTimeline_create(...) spRotateTimeline_create(__VA_ARGS__)
+#define RotateTimeline_setFrame(...) spRotateTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int TRANSLATE_ENTRIES = 3;
+
+typedef struct spBaseTimeline spTranslateTimeline;
+
+SP_API spTranslateTimeline* spTranslateTimeline_create (int framesCount);
+
+SP_API void spTranslateTimeline_setFrame (spTranslateTimeline* self, int frameIndex, float time, float x, float y);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTranslateTimeline TranslateTimeline;
+#define TranslateTimeline_create(...) spTranslateTimeline_create(__VA_ARGS__)
+#define TranslateTimeline_setFrame(...) spTranslateTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline spScaleTimeline;
+
+SP_API spScaleTimeline* spScaleTimeline_create (int framesCount);
+
+SP_API void spScaleTimeline_setFrame (spScaleTimeline* self, int frameIndex, float time, float x, float y);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spScaleTimeline ScaleTimeline;
+#define ScaleTimeline_create(...) spScaleTimeline_create(__VA_ARGS__)
+#define ScaleTimeline_setFrame(...) spScaleTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline spShearTimeline;
+
+SP_API spShearTimeline* spShearTimeline_create (int framesCount);
+
+SP_API void spShearTimeline_setFrame (spShearTimeline* self, int frameIndex, float time, float x, float y);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spShearTimeline ShearTimeline;
+#define ShearTimeline_create(...) spShearTimeline_create(__VA_ARGS__)
+#define ShearTimeline_setFrame(...) spShearTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int COLOR_ENTRIES = 5;
+
+typedef struct spColorTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, r, g, b, a, ... */
+	int slotIndex;
+
+#ifdef __cplusplus
+	spColorTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		slotIndex(0) {
+	}
+#endif
+} spColorTimeline;
+
+SP_API spColorTimeline* spColorTimeline_create (int framesCount);
+
+SP_API void spColorTimeline_setFrame (spColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spColorTimeline ColorTimeline;
+#define ColorTimeline_create(...) spColorTimeline_create(__VA_ARGS__)
+#define ColorTimeline_setFrame(...) spColorTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int TWOCOLOR_ENTRIES = 8;
+
+typedef struct spTwoColorTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, r, g, b, a, ... */
+	int slotIndex;
+
+#ifdef __cplusplus
+	spTwoColorTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		slotIndex(0) {
+	}
+#endif
+} spTwoColorTimeline;
+
+SP_API spTwoColorTimeline* spTwoColorTimeline_create (int framesCount);
+
+SP_API void spTwoColorTimeline_setFrame (spTwoColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTwoColorTimeline TwoColorTimeline;
+#define TwoColorTimeline_create(...) spTwoColorTimeline_create(__VA_ARGS__)
+#define TwoColorTimeline_setFrame(...) spTwoColorTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spAttachmentTimeline {
+	spTimeline super;
+	int const framesCount;
+	float* const frames; /* time, ... */
+	int slotIndex;
+	const char** const attachmentNames;
+
+#ifdef __cplusplus
+	spAttachmentTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		slotIndex(0),
+		attachmentNames(0) {
+	}
+#endif
+} spAttachmentTimeline;
+
+SP_API spAttachmentTimeline* spAttachmentTimeline_create (int framesCount);
+
+/* @param attachmentName May be 0. */
+SP_API void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentTimeline AttachmentTimeline;
+#define AttachmentTimeline_create(...) spAttachmentTimeline_create(__VA_ARGS__)
+#define AttachmentTimeline_setFrame(...) spAttachmentTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spEventTimeline {
+	spTimeline super;
+	int const framesCount;
+	float* const frames; /* time, ... */
+	spEvent** const events;
+
+#ifdef __cplusplus
+	spEventTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		events(0) {
+	}
+#endif
+} spEventTimeline;
+
+SP_API spEventTimeline* spEventTimeline_create (int framesCount);
+
+SP_API void spEventTimeline_setFrame (spEventTimeline* self, int frameIndex, spEvent* event);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventTimeline EventTimeline;
+#define EventTimeline_create(...) spEventTimeline_create(__VA_ARGS__)
+#define EventTimeline_setFrame(...) spEventTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spDrawOrderTimeline {
+	spTimeline super;
+	int const framesCount;
+	float* const frames; /* time, ... */
+	const int** const drawOrders;
+	int const slotsCount;
+
+#ifdef __cplusplus
+	spDrawOrderTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		drawOrders(0),
+		slotsCount(0) {
+	}
+#endif
+} spDrawOrderTimeline;
+
+SP_API spDrawOrderTimeline* spDrawOrderTimeline_create (int framesCount, int slotsCount);
+
+SP_API void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, float time, const int* drawOrder);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spDrawOrderTimeline DrawOrderTimeline;
+#define DrawOrderTimeline_create(...) spDrawOrderTimeline_create(__VA_ARGS__)
+#define DrawOrderTimeline_setFrame(...) spDrawOrderTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spDeformTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, ... */
+	int const frameVerticesCount;
+	const float** const frameVertices;
+	int slotIndex;
+	spAttachment* attachment;
+
+#ifdef __cplusplus
+	spDeformTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		frameVerticesCount(0),
+		frameVertices(0),
+		slotIndex(0) {
+	}
+#endif
+} spDeformTimeline;
+
+SP_API spDeformTimeline* spDeformTimeline_create (int framesCount, int frameVerticesCount);
+
+SP_API void spDeformTimeline_setFrame (spDeformTimeline* self, int frameIndex, float time, float* vertices);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spDeformTimeline DeformTimeline;
+#define DeformTimeline_create(...) spDeformTimeline_create(__VA_ARGS__)
+#define DeformTimeline_setFrame(...) spDeformTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int IKCONSTRAINT_ENTRIES = 3;
+
+typedef struct spIkConstraintTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, mix, bendDirection, ... */
+	int ikConstraintIndex;
+
+#ifdef __cplusplus
+	spIkConstraintTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		ikConstraintIndex(0) {
+	}
+#endif
+} spIkConstraintTimeline;
+
+SP_API spIkConstraintTimeline* spIkConstraintTimeline_create (int framesCount);
+
+SP_API void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spIkConstraintTimeline IkConstraintTimeline;
+#define IkConstraintTimeline_create(...) spIkConstraintTimeline_create(__VA_ARGS__)
+#define IkConstraintTimeline_setFrame(...) spIkConstraintTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int TRANSFORMCONSTRAINT_ENTRIES = 5;
+
+typedef struct spTransformConstraintTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */
+	int transformConstraintIndex;
+
+#ifdef __cplusplus
+	spTransformConstraintTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		transformConstraintIndex(0) {
+	}
+#endif
+} spTransformConstraintTimeline;
+
+SP_API spTransformConstraintTimeline* spTransformConstraintTimeline_create (int framesCount);
+
+SP_API void spTransformConstraintTimeline_setFrame (spTransformConstraintTimeline* self, int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTransformConstraintTimeline TransformConstraintTimeline;
+#define TransformConstraintTimeline_create(...) spTransformConstraintTimeline_create(__VA_ARGS__)
+#define TransformConstraintTimeline_setFrame(...) spTransformConstraintTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int PATHCONSTRAINTPOSITION_ENTRIES = 2;
+
+typedef struct spPathConstraintPositionTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */
+	int pathConstraintIndex;
+
+#ifdef __cplusplus
+	spPathConstraintPositionTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		pathConstraintIndex(0) {
+	}
+#endif
+} spPathConstraintPositionTimeline;
+
+SP_API spPathConstraintPositionTimeline* spPathConstraintPositionTimeline_create (int framesCount);
+
+SP_API void spPathConstraintPositionTimeline_setFrame (spPathConstraintPositionTimeline* self, int frameIndex, float time, float value);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathConstraintPositionTimeline PathConstraintPositionTimeline;
+#define PathConstraintPositionTimeline_create(...) spPathConstraintPositionTimeline_create(__VA_ARGS__)
+#define PathConstraintPositionTimeline_setFrame(...) spPathConstraintPositionTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int PATHCONSTRAINTSPACING_ENTRIES = 2;
+
+typedef struct spPathConstraintSpacingTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */
+	int pathConstraintIndex;
+
+#ifdef __cplusplus
+	spPathConstraintSpacingTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		pathConstraintIndex(0) {
+	}
+#endif
+} spPathConstraintSpacingTimeline;
+
+SP_API spPathConstraintSpacingTimeline* spPathConstraintSpacingTimeline_create (int framesCount);
+
+SP_API void spPathConstraintSpacingTimeline_setFrame (spPathConstraintSpacingTimeline* self, int frameIndex, float time, float value);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathConstraintSpacingTimeline PathConstraintSpacingTimeline;
+#define PathConstraintSpacingTimeline_create(...) spPathConstraintSpacingTimeline_create(__VA_ARGS__)
+#define PathConstraintSpacingTimeline_setFrame(...) spPathConstraintSpacingTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+static const int PATHCONSTRAINTMIX_ENTRIES = 3;
+
+typedef struct spPathConstraintMixTimeline {
+	spCurveTimeline super;
+	int const framesCount;
+	float* const frames; /* time, rotate mix, translate mix, scale mix, shear mix, ... */
+	int pathConstraintIndex;
+
+#ifdef __cplusplus
+	spPathConstraintMixTimeline() :
+		super(),
+		framesCount(0),
+		frames(0),
+		pathConstraintIndex(0) {
+	}
+#endif
+} spPathConstraintMixTimeline;
+
+SP_API spPathConstraintMixTimeline* spPathConstraintMixTimeline_create (int framesCount);
+
+SP_API void spPathConstraintMixTimeline_setFrame (spPathConstraintMixTimeline* self, int frameIndex, float time, float rotateMix, float translateMix);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathConstraintMixTimeline PathConstraintMixTimeline;
+#define PathConstraintMixTimeline_create(...) spPathConstraintMixTimeline_create(__VA_ARGS__)
+#define PathConstraintMixTimeline_setFrame(...) spPathConstraintMixTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATION_H_ */

+ 185 - 0
spine-cpp/spine-cpp/include/spine/AnimationState.h

@@ -0,0 +1,185 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATIONSTATE_H_
+#define SPINE_ANIMATIONSTATE_H_
+
+#include <spine/dll.h>
+#include <spine/Animation.h>
+#include <spine/AnimationStateData.h>
+#include <spine/Event.h>
+#include <spine/Array.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	SP_ANIMATION_START, SP_ANIMATION_INTERRUPT, SP_ANIMATION_END, SP_ANIMATION_COMPLETE, SP_ANIMATION_DISPOSE, SP_ANIMATION_EVENT
+} spEventType;
+
+typedef struct spAnimationState spAnimationState;
+typedef struct spTrackEntry spTrackEntry;
+
+typedef void (*spAnimationStateListener) (spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event);
+
+_SP_ARRAY_DECLARE_TYPE(spTrackEntryArray, spTrackEntry*)
+
+struct spTrackEntry {
+	spAnimation* animation;
+	spTrackEntry* next;
+	spTrackEntry* mixingFrom;
+	spAnimationStateListener listener;
+	int trackIndex;
+	int /*boolean*/ loop;
+	float eventThreshold, attachmentThreshold, drawOrderThreshold;
+	float animationStart, animationEnd, animationLast, nextAnimationLast;
+	float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
+	float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
+	spIntArray* timelineData;
+	spTrackEntryArray* timelineDipMix;
+	float* timelinesRotation;
+	int timelinesRotationCount;
+	void* rendererObject;
+	void* userData;
+
+#ifdef __cplusplus
+	spTrackEntry() :
+		animation(0),
+		next(0), mixingFrom(0),
+		listener(0),
+		trackIndex(0),
+		loop(0),
+		eventThreshold(0), attachmentThreshold(0), drawOrderThreshold(0),
+		animationStart(0), animationEnd(0), animationLast(0), nextAnimationLast(0),
+		delay(0), trackTime(0), trackLast(0), nextTrackLast(0), trackEnd(0), timeScale(0),
+		alpha(0), mixTime(0), mixDuration(0), interruptAlpha(0), totalAlpha(0),
+		timelineData(0),
+		timelineDipMix(0),
+		timelinesRotation(0),
+		timelinesRotationCount(0) {
+	}
+#endif
+};
+
+struct spAnimationState {
+	spAnimationStateData* const data;
+
+	int tracksCount;
+	spTrackEntry** tracks;
+
+	spAnimationStateListener listener;
+
+	float timeScale;
+
+	spTrackEntryArray* mixingTo;
+
+	void* rendererObject;
+
+#ifdef __cplusplus
+	spAnimationState() :
+		data(0),
+		tracksCount(0),
+		tracks(0),
+		listener(0),
+		timeScale(0),
+		mixingTo(0),
+		rendererObject(0) {
+	}
+#endif
+};
+
+/* @param data May be 0 for no mixing. */
+SP_API spAnimationState* spAnimationState_create (spAnimationStateData* data);
+SP_API void spAnimationState_dispose (spAnimationState* self);
+
+SP_API void spAnimationState_update (spAnimationState* self, float delta);
+SP_API int /**bool**/ spAnimationState_apply (spAnimationState* self, struct spSkeleton* skeleton);
+
+SP_API void spAnimationState_clearTracks (spAnimationState* self);
+SP_API void spAnimationState_clearTrack (spAnimationState* self, int trackIndex);
+
+/** Set the current animation. Any queued animations are cleared. */
+SP_API spTrackEntry* spAnimationState_setAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop);
+SP_API spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop);
+
+/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix
+ * duration. */
+SP_API spTrackEntry* spAnimationState_addAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop, float delay);
+SP_API spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop,
+		float delay);
+SP_API spTrackEntry* spAnimationState_setEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration);
+SP_API spTrackEntry* spAnimationState_addEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration, float delay);
+SP_API void spAnimationState_setEmptyAnimations(spAnimationState* self, float mixDuration);
+
+SP_API spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex);
+
+SP_API void spAnimationState_clearListenerNotifications(spAnimationState* self);
+
+SP_API float spTrackEntry_getAnimationTime (spTrackEntry* entry);
+
+/** Use this to dispose static memory before your app exits to appease your memory leak detector*/
+SP_API void spAnimationState_disposeStatics ();
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventType EventType;
+#define ANIMATION_START SP_ANIMATION_START
+#define ANIMATION_INTERRUPT SP_ANIMATION_INTERRUPT
+#define ANIMATION_END SP_ANIMATION_END
+#define ANIMATION_COMPLETE SP_ANIMATION_COMPLETE
+#define ANIMATION_DISPOSE SP_ANIMATION_DISPOSE
+#define ANIMATION_EVENT SP_ANIMATION_EVENT
+typedef spAnimationStateListener AnimationStateListener;
+typedef spTrackEntry TrackEntry;
+typedef spAnimationState AnimationState;
+#define AnimationState_create(...) spAnimationState_create(__VA_ARGS__)
+#define AnimationState_dispose(...) spAnimationState_dispose(__VA_ARGS__)
+#define AnimationState_update(...) spAnimationState_update(__VA_ARGS__)
+#define AnimationState_apply(...) spAnimationState_apply(__VA_ARGS__)
+#define AnimationState_clearTracks(...) spAnimationState_clearTracks(__VA_ARGS__)
+#define AnimationState_clearTrack(...) spAnimationState_clearTrack(__VA_ARGS__)
+#define AnimationState_setAnimationByName(...) spAnimationState_setAnimationByName(__VA_ARGS__)
+#define AnimationState_setAnimation(...) spAnimationState_setAnimation(__VA_ARGS__)
+#define AnimationState_addAnimationByName(...) spAnimationState_addAnimationByName(__VA_ARGS__)
+#define AnimationState_addAnimation(...) spAnimationState_addAnimation(__VA_ARGS__)
+#define AnimationState_setEmptyAnimation(...) spAnimatinState_setEmptyAnimation(__VA_ARGS__)
+#define AnimationState_addEmptyAnimation(...) spAnimatinState_addEmptyAnimation(__VA_ARGS__)
+#define AnimationState_setEmptyAnimations(...) spAnimatinState_setEmptyAnimations(__VA_ARGS__)
+#define AnimationState_getCurrent(...) spAnimationState_getCurrent(__VA_ARGS__)
+#define AnimationState_clearListenerNotifications(...) spAnimatinState_clearListenerNotifications(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATIONSTATE_H_ */

+ 77 - 0
spine-cpp/spine-cpp/include/spine/AnimationStateData.h

@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATIONSTATEDATA_H_
+#define SPINE_ANIMATIONSTATEDATA_H_
+
+#include <spine/dll.h>
+#include <spine/Animation.h>
+#include <spine/SkeletonData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spAnimationStateData {
+	spSkeletonData* const skeletonData;
+	float defaultMix;
+	const void* const entries;
+
+#ifdef __cplusplus
+	spAnimationStateData() :
+		skeletonData(0),
+		defaultMix(0),
+		entries(0) {
+	}
+#endif
+} spAnimationStateData;
+
+SP_API spAnimationStateData* spAnimationStateData_create (spSkeletonData* skeletonData);
+SP_API void spAnimationStateData_dispose (spAnimationStateData* self);
+
+SP_API void spAnimationStateData_setMixByName (spAnimationStateData* self, const char* fromName, const char* toName, float duration);
+SP_API void spAnimationStateData_setMix (spAnimationStateData* self, spAnimation* from, spAnimation* to, float duration);
+/* Returns 0 if there is no mixing between the animations. */
+SP_API float spAnimationStateData_getMix (spAnimationStateData* self, spAnimation* from, spAnimation* to);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAnimationStateData AnimationStateData;
+#define AnimationStateData_create(...) spAnimationStateData_create(__VA_ARGS__)
+#define AnimationStateData_dispose(...) spAnimationStateData_dispose(__VA_ARGS__)
+#define AnimationStateData_setMixByName(...) spAnimationStateData_setMixByName(__VA_ARGS__)
+#define AnimationStateData_setMix(...) spAnimationStateData_setMix(__VA_ARGS__)
+#define AnimationStateData_getMix(...) spAnimationStateData_getMix(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATIONSTATEDATA_H_ */

+ 133 - 0
spine-cpp/spine-cpp/include/spine/Array.h

@@ -0,0 +1,133 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ARRAY_H
+#define SPINE_ARRAY_H
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _SP_ARRAY_DECLARE_TYPE(name, itemType) \
+	typedef struct name { int size; int capacity; itemType* items; } name; \
+	SP_API name* name##_create(int initialCapacity); \
+	SP_API void name##_dispose(name* self); \
+	SP_API void name##_clear(name* self); \
+	SP_API name* name##_setSize(name* self, int newSize); \
+	SP_API void name##_ensureCapacity(name* self, int newCapacity); \
+	SP_API void name##_add(name* self, itemType value); \
+	SP_API void name##_addAll(name* self, name* other); \
+	SP_API void name##_addAllValues(name* self, itemType* values, int offset, int count); \
+	SP_API void name##_removeAt(name* self, int index); \
+	SP_API int name##_contains(name* self, itemType value); \
+	SP_API itemType name##_pop(name* self); \
+	SP_API itemType name##_peek(name* self);
+
+#define _SP_ARRAY_IMPLEMENT_TYPE(name, itemType) \
+	name* name##_create(int initialCapacity) { \
+		name* array = CALLOC(name, 1); \
+		array->size = 0; \
+		array->capacity = initialCapacity; \
+		array->items = CALLOC(itemType, initialCapacity); \
+		return array; \
+	} \
+	void name##_dispose(name* self) { \
+		FREE(self->items); \
+		FREE(self); \
+	} \
+	void name##_clear(name* self) { \
+		self->size = 0; \
+	} \
+	name* name##_setSize(name* self, int newSize) { \
+		self->size = newSize; \
+		if (self->capacity < newSize) { \
+			self->capacity = MAX(8, (int)(self->size * 1.75f)); \
+			self->items = REALLOC(self->items, itemType, self->capacity); \
+		} \
+		return self; \
+	} \
+	void name##_ensureCapacity(name* self, int newCapacity) { \
+		if (self->capacity >= newCapacity) return; \
+		self->capacity = newCapacity; \
+		self->items = REALLOC(self->items, itemType, self->capacity); \
+	} \
+	void name##_add(name* self, itemType value) { \
+		if (self->size == self->capacity) { \
+            self->capacity = MAX(8, (int)(self->size * 1.75f)); \
+            self->items = REALLOC(self->items, itemType, self->capacity); \
+		} \
+		self->items[self->size++] = value; \
+	} \
+	void name##_addAll(name* self, name* other) { \
+		int i = 0; \
+		for (; i < other->size; i++) { \
+			name##_add(self, other->items[i]); \
+		} \
+	} \
+	void name##_addAllValues(name* self, itemType* values, int offset, int count) { \
+		int i = offset, n = offset + count; \
+		for (; i < n; i++) { \
+			name##_add(self, values[i]); \
+		} \
+	} \
+	void name##_removeAt(name* self, int index) { \
+		self->size--; \
+		memmove(self->items + index, self->items + index + 1, sizeof(itemType) * (self->size - index)); \
+	} \
+	int name##_contains(name* self, itemType value) { \
+		itemType* items = self->items; \
+		int i, n; \
+		for (i = 0, n = self->size; i < n; i++) { \
+			if (items[i] == value) return -1; \
+		} \
+		return 0; \
+	} \
+	itemType name##_pop(name* self) { \
+		itemType item = self->items[--self->size]; \
+		return item; \
+	} \
+	itemType name##_peek(name* self) { \
+		return self->items[self->size - 1]; \
+	}
+
+_SP_ARRAY_DECLARE_TYPE(spFloatArray, float)
+_SP_ARRAY_DECLARE_TYPE(spIntArray, int)
+_SP_ARRAY_DECLARE_TYPE(spShortArray, short)
+_SP_ARRAY_DECLARE_TYPE(spUnsignedShortArray, unsigned short)
+_SP_ARRAY_DECLARE_TYPE(spArrayFloatArray, spFloatArray*)
+_SP_ARRAY_DECLARE_TYPE(spArrayShortArray, spShortArray*)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ARRAY_H */

+ 174 - 0
spine-cpp/spine-cpp/include/spine/Atlas.h

@@ -0,0 +1,174 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ATLAS_H_
+#define SPINE_ATLAS_H_
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spAtlas spAtlas;
+
+typedef enum {
+	SP_ATLAS_UNKNOWN_FORMAT,
+	SP_ATLAS_ALPHA,
+	SP_ATLAS_INTENSITY,
+	SP_ATLAS_LUMINANCE_ALPHA,
+	SP_ATLAS_RGB565,
+	SP_ATLAS_RGBA4444,
+	SP_ATLAS_RGB888,
+	SP_ATLAS_RGBA8888
+} spAtlasFormat;
+
+typedef enum {
+	SP_ATLAS_UNKNOWN_FILTER,
+	SP_ATLAS_NEAREST,
+	SP_ATLAS_LINEAR,
+	SP_ATLAS_MIPMAP,
+	SP_ATLAS_MIPMAP_NEAREST_NEAREST,
+	SP_ATLAS_MIPMAP_LINEAR_NEAREST,
+	SP_ATLAS_MIPMAP_NEAREST_LINEAR,
+	SP_ATLAS_MIPMAP_LINEAR_LINEAR
+} spAtlasFilter;
+
+typedef enum {
+	SP_ATLAS_MIRROREDREPEAT,
+	SP_ATLAS_CLAMPTOEDGE,
+	SP_ATLAS_REPEAT
+} spAtlasWrap;
+
+typedef struct spAtlasPage spAtlasPage;
+struct spAtlasPage {
+	const spAtlas* atlas;
+	const char* name;
+	spAtlasFormat format;
+	spAtlasFilter minFilter, magFilter;
+	spAtlasWrap uWrap, vWrap;
+
+	void* rendererObject;
+	int width, height;
+
+	spAtlasPage* next;
+};
+
+SP_API spAtlasPage* spAtlasPage_create (spAtlas* atlas, const char* name);
+SP_API void spAtlasPage_dispose (spAtlasPage* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasFormat AtlasFormat;
+#define ATLAS_UNKNOWN_FORMAT SP_ATLAS_UNKNOWN_FORMAT
+#define ATLAS_ALPHA SP_ATLAS_ALPHA
+#define ATLAS_INTENSITY SP_ATLAS_INTENSITY
+#define ATLAS_LUMINANCE_ALPHA SP_ATLAS_LUMINANCE_ALPHA
+#define ATLAS_RGB565 SP_ATLAS_RGB565
+#define ATLAS_RGBA4444 SP_ATLAS_RGBA4444
+#define ATLAS_RGB888 SP_ATLAS_RGB888
+#define ATLAS_RGBA8888 SP_ATLAS_RGBA8888
+typedef spAtlasFilter AtlasFilter;
+#define ATLAS_UNKNOWN_FILTER SP_ATLAS_UNKNOWN_FILTER
+#define ATLAS_NEAREST SP_ATLAS_NEAREST
+#define ATLAS_LINEAR SP_ATLAS_LINEAR
+#define ATLAS_MIPMAP SP_ATLAS_MIPMAP
+#define ATLAS_MIPMAP_NEAREST_NEAREST SP_ATLAS_MIPMAP_NEAREST_NEAREST
+#define ATLAS_MIPMAP_LINEAR_NEAREST SP_ATLAS_MIPMAP_LINEAR_NEAREST
+#define ATLAS_MIPMAP_NEAREST_LINEAR SP_ATLAS_MIPMAP_NEAREST_LINEAR
+#define ATLAS_MIPMAP_LINEAR_LINEAR SP_ATLAS_MIPMAP_LINEAR_LINEAR
+typedef spAtlasWrap AtlasWrap;
+#define ATLAS_MIRROREDREPEAT SP_ATLAS_MIRROREDREPEAT
+#define ATLAS_CLAMPTOEDGE SP_ATLAS_CLAMPTOEDGE
+#define ATLAS_REPEAT SP_ATLAS_REPEAT
+typedef spAtlasPage AtlasPage;
+#define AtlasPage_create(...) spAtlasPage_create(__VA_ARGS__)
+#define AtlasPage_dispose(...) spAtlasPage_dispose(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spAtlasRegion spAtlasRegion;
+struct spAtlasRegion {
+	const char* name;
+	int x, y, width, height;
+	float u, v, u2, v2;
+	int offsetX, offsetY;
+	int originalWidth, originalHeight;
+	int index;
+	int/*bool*/rotate;
+	int/*bool*/flip;
+	int* splits;
+	int* pads;
+
+	spAtlasPage* page;
+
+	spAtlasRegion* next;
+};
+
+SP_API spAtlasRegion* spAtlasRegion_create ();
+SP_API void spAtlasRegion_dispose (spAtlasRegion* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasRegion AtlasRegion;
+#define AtlasRegion_create(...) spAtlasRegion_create(__VA_ARGS__)
+#define AtlasRegion_dispose(...) spAtlasRegion_dispose(__VA_ARGS__)
+#endif
+
+/**/
+
+struct spAtlas {
+	spAtlasPage* pages;
+	spAtlasRegion* regions;
+
+	void* rendererObject;
+};
+
+/* Image files referenced in the atlas file will be prefixed with dir. */
+SP_API spAtlas* spAtlas_create (const char* data, int length, const char* dir, void* rendererObject);
+/* Image files referenced in the atlas file will be prefixed with the directory containing the atlas file. */
+SP_API spAtlas* spAtlas_createFromFile (const char* path, void* rendererObject);
+SP_API void spAtlas_dispose (spAtlas* atlas);
+
+/* Returns 0 if the region was not found. */
+SP_API spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlas Atlas;
+#define Atlas_create(...) spAtlas_create(__VA_ARGS__)
+#define Atlas_createFromFile(...) spAtlas_createFromFile(__VA_ARGS__)
+#define Atlas_dispose(...) spAtlas_dispose(__VA_ARGS__)
+#define Atlas_findRegion(...) spAtlas_findRegion(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATLAS_H_ */

+ 58 - 0
spine-cpp/spine-cpp/include/spine/AtlasAttachmentLoader.h

@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ATLASATTACHMENTLOADER_H_
+#define SPINE_ATLASATTACHMENTLOADER_H_
+
+#include <spine/dll.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/Atlas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spAtlasAttachmentLoader {
+	spAttachmentLoader super;
+	spAtlas* atlas;
+} spAtlasAttachmentLoader;
+
+SP_API spAtlasAttachmentLoader* spAtlasAttachmentLoader_create (spAtlas* atlas);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasAttachmentLoader AtlasAttachmentLoader;
+#define AtlasAttachmentLoader_create(...) spAtlasAttachmentLoader_create(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATLASATTACHMENTLOADER_H_ */

+ 83 - 0
spine-cpp/spine-cpp/include/spine/Attachment.h

@@ -0,0 +1,83 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ATTACHMENT_H_
+#define SPINE_ATTACHMENT_H_
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spAttachmentLoader;
+
+typedef enum {
+	SP_ATTACHMENT_REGION,
+	SP_ATTACHMENT_BOUNDING_BOX,
+	SP_ATTACHMENT_MESH,
+	SP_ATTACHMENT_LINKED_MESH,
+	SP_ATTACHMENT_PATH,
+	SP_ATTACHMENT_POINT,
+	SP_ATTACHMENT_CLIPPING
+} spAttachmentType;
+
+typedef struct spAttachment {
+	const char* const name;
+	const spAttachmentType type;
+	const void* const vtable;
+	struct spAttachmentLoader* attachmentLoader;
+
+#ifdef __cplusplus
+	spAttachment() :
+		name(0),
+		type(SP_ATTACHMENT_REGION),
+		vtable(0) {
+	}
+#endif
+} spAttachment;
+
+void spAttachment_dispose (spAttachment* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentType AttachmentType;
+#define ATTACHMENT_REGION SP_ATTACHMENT_REGION
+#define ATTACHMENT_BOUNDING_BOX SP_ATTACHMENT_BOUNDING_BOX
+#define ATTACHMENT_MESH SP_ATTACHMENT_MESH
+#define ATTACHMENT_LINKED_MESH SP_ATTACHMENT_LINKED_MESH
+typedef spAttachment Attachment;
+#define Attachment_dispose(...) spAttachment_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATTACHMENT_H_ */

+ 79 - 0
spine-cpp/spine-cpp/include/spine/AttachmentLoader.h

@@ -0,0 +1,79 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_ATTACHMENTLOADER_H_
+#define SPINE_ATTACHMENTLOADER_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/Skin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spAttachmentLoader {
+	const char* error1;
+	const char* error2;
+
+	const void* const vtable;
+#ifdef __cplusplus
+	spAttachmentLoader () :
+					error1(0),
+					error2(0),
+					vtable(0) {
+	}
+#endif
+} spAttachmentLoader;
+
+SP_API void spAttachmentLoader_dispose (spAttachmentLoader* self);
+
+/* Called to create each attachment. Returns 0 to not load an attachment. If 0 is returned and _spAttachmentLoader_setError was
+ * called, an error occurred. */
+SP_API spAttachment* spAttachmentLoader_createAttachment (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name,
+		const char* path);
+/* Called after the attachment has been fully configured. */
+SP_API void spAttachmentLoader_configureAttachment (spAttachmentLoader* self, spAttachment* attachment);
+/* Called just before the attachment is disposed. This can release allocations made in spAttachmentLoader_configureAttachment. */
+SP_API void spAttachmentLoader_disposeAttachment (spAttachmentLoader* self, spAttachment* attachment);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentLoader AttachmentLoader;
+#define AttachmentLoader_dispose(...) spAttachmentLoader_dispose(__VA_ARGS__)
+#define AttachmentLoader_createAttachment(...) spAttachmentLoader_createAttachment(__VA_ARGS__)
+#define AttachmentLoader_configureAttachment(...) spAttachmentLoader_configureAttachment(__VA_ARGS__)
+#define AttachmentLoader_disposeAttachment(...) spAttachmentLoader_disposeAttachment(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATTACHMENTLOADER_H_ */

+ 127 - 0
spine-cpp/spine-cpp/include/spine/Bone.h

@@ -0,0 +1,127 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_BONE_H_
+#define SPINE_BONE_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spBone spBone;
+struct spBone {
+	spBoneData* const data;
+	struct spSkeleton* const skeleton;
+	spBone* const parent;
+	int childrenCount;
+	spBone** const children;
+	float x, y, rotation, scaleX, scaleY, shearX, shearY;
+	float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
+	int /*bool*/ appliedValid;
+
+	float const a, b, worldX;
+	float const c, d, worldY;
+
+	int/*bool*/ sorted;
+
+#ifdef __cplusplus
+	spBone() :
+		data(0),
+		skeleton(0),
+		parent(0),
+		childrenCount(0), children(0),
+		x(0), y(0), rotation(0), scaleX(0), scaleY(0),
+		ax(0), ay(0), arotation(0), ascaleX(0), ascaleY(0), ashearX(0), ashearY(0),
+		appliedValid(0),
+
+		a(0), b(0), worldX(0),
+		c(0), d(0), worldY(0),
+
+		sorted(0) {
+	}
+#endif
+};
+
+SP_API void spBone_setYDown (int/*bool*/yDown);
+SP_API int/*bool*/spBone_isYDown ();
+
+/* @param parent May be 0. */
+SP_API spBone* spBone_create (spBoneData* data, struct spSkeleton* skeleton, spBone* parent);
+SP_API void spBone_dispose (spBone* self);
+
+SP_API void spBone_setToSetupPose (spBone* self);
+
+SP_API void spBone_updateWorldTransform (spBone* self);
+SP_API void spBone_updateWorldTransformWith (spBone* self, float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY);
+
+SP_API float spBone_getWorldRotationX (spBone* self);
+SP_API float spBone_getWorldRotationY (spBone* self);
+SP_API float spBone_getWorldScaleX (spBone* self);
+SP_API float spBone_getWorldScaleY (spBone* self);
+
+SP_API void spBone_updateAppliedTransform (spBone* self);
+
+SP_API void spBone_worldToLocal (spBone* self, float worldX, float worldY, float* localX, float* localY);
+SP_API void spBone_localToWorld (spBone* self, float localX, float localY, float* worldX, float* worldY);
+SP_API float spBone_worldToLocalRotation (spBone* self, float worldRotation);
+SP_API float spBone_localToWorldRotation (spBone* self, float localRotation);
+SP_API void spBone_rotateWorld (spBone* self, float degrees);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBone Bone;
+#define Bone_setYDown(...) spBone_setYDown(__VA_ARGS__)
+#define Bone_isYDown() spBone_isYDown()
+#define Bone_create(...) spBone_create(__VA_ARGS__)
+#define Bone_dispose(...) spBone_dispose(__VA_ARGS__)
+#define Bone_setToSetupPose(...) spBone_setToSetupPose(__VA_ARGS__)
+#define Bone_updateWorldTransform(...) spBone_updateWorldTransform(__VA_ARGS__)
+#define Bone_updateWorldTransformWith(...) spBone_updateWorldTransformWith(__VA_ARGS__)
+#define Bone_getWorldRotationX(...) spBone_getWorldRotationX(__VA_ARGS__)
+#define Bone_getWorldRotationY(...) spBone_getWorldRotationY(__VA_ARGS__)
+#define Bone_getWorldScaleX(...) spBone_getWorldScaleX(__VA_ARGS__)
+#define Bone_getWorldScaleY(...) spBone_getWorldScaleY(__VA_ARGS__)
+#define Bone_updateAppliedTransform(...) spBone_updateAppliedTransform(__VA_ARGS__)
+#define Bone_worldToLocal(...) spBone_worldToLocal(__VA_ARGS__)
+#define Bone_localToWorld(...) spBone_localToWorld(__VA_ARGS__)
+#define Bone_worldToLocalRotation(...) spBone_worldToLocalRotation(__VA_ARGS__)
+#define Bone_localToWorldRotation(...) spBone_localToWorldRotation(__VA_ARGS__)
+#define Bone_rotateWorld(...) spBone_rotateWorld(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BONE_H_ */

+ 85 - 0
spine-cpp/spine-cpp/include/spine/BoneData.h

@@ -0,0 +1,85 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_BONEDATA_H_
+#define SPINE_BONEDATA_H_
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	SP_TRANSFORMMODE_NORMAL,
+	SP_TRANSFORMMODE_ONLYTRANSLATION,
+	SP_TRANSFORMMODE_NOROTATIONORREFLECTION,
+	SP_TRANSFORMMODE_NOSCALE,
+	SP_TRANSFORMMODE_NOSCALEORREFLECTION
+} spTransformMode;
+
+typedef struct spBoneData spBoneData;
+struct spBoneData {
+	const int index;
+	const char* const name;
+	spBoneData* const parent;
+	float length;
+	float x, y, rotation, scaleX, scaleY, shearX, shearY;
+	spTransformMode transformMode;
+
+#ifdef __cplusplus
+	spBoneData() :
+		index(0),
+		name(0),
+		parent(0),
+		length(0),
+		x(0), y(0),
+		rotation(0),
+		scaleX(0), scaleY(0),
+		shearX(0), shearY(0),
+		transformMode(SP_TRANSFORMMODE_NORMAL) {
+	}
+#endif
+};
+
+SP_API spBoneData* spBoneData_create (int index, const char* name, spBoneData* parent);
+SP_API void spBoneData_dispose (spBoneData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBoneData BoneData;
+#define BoneData_create(...) spBoneData_create(__VA_ARGS__)
+#define BoneData_dispose(...) spBoneData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BONEDATA_H_ */

+ 59 - 0
spine-cpp/spine-cpp/include/spine/BoundingBoxAttachment.h

@@ -0,0 +1,59 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_BOUNDINGBOXATTACHMENT_H_
+#define SPINE_BOUNDINGBOXATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spBoundingBoxAttachment {
+	spVertexAttachment super;
+} spBoundingBoxAttachment;
+
+SP_API spBoundingBoxAttachment* spBoundingBoxAttachment_create (const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBoundingBoxAttachment BoundingBoxAttachment;
+#define BoundingBoxAttachment_create(...) spBoundingBoxAttachment_create(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BOUNDINGBOXATTACHMENT_H_ */

+ 61 - 0
spine-cpp/spine-cpp/include/spine/ClippingAttachment.h

@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_CLIPPINGATTACHMENT_H_
+#define SPINE_CLIPPINGATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spClippingAttachment {
+	spVertexAttachment super;
+	spSlotData* endSlot;
+} spClippingAttachment;
+
+SP_API void _spClippingAttachment_dispose(spAttachment* self);
+SP_API spClippingAttachment* spClippingAttachment_create (const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spClippingAttachment ClippingAttachment;
+#define ClippingAttachment_create(...) spClippingAttachment_create(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_CLIPPINGATTACHMENT_H_ */

+ 78 - 0
spine-cpp/spine-cpp/include/spine/Color.h

@@ -0,0 +1,78 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_COLOR_H_
+#define SPINE_COLOR_H_
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spColor {
+	float r, g, b, a;
+
+#ifdef __cplusplus
+	spColor() :
+		r(0), g(0), b(0), a(0) {
+	}
+
+	bool operator==(const spColor& rhs) {
+		return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
+	}
+#endif
+} spColor;
+
+/* @param attachmentName May be 0 for no setup pose attachment. */
+SP_API spColor* spColor_create();
+SP_API void spColor_dispose(spColor* self);
+SP_API void spColor_setFromFloats(spColor* color, float r, float g, float b, float a);
+SP_API void spColor_setFromColor(spColor* color, spColor* otherColor);
+SP_API void spColor_addFloats(spColor* color, float r, float g, float b, float a);
+SP_API void spColor_addColor(spColor* color, spColor* otherColor);
+SP_API void spColor_clamp(spColor* color);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spColor color;
+#define Color_create() spColor_create()
+#define Color_dispose(...) spColor_dispose(__VA_ARGS__)
+#define Color_setFromFloats(...) spColor_setFromFloats(__VA_ARGS__)
+#define Color_setFromColor(...) spColor_setFromColor(__VA_ARGS__)
+#define Color_addColor(...) spColor_addColor(__VA_ARGS__)
+#define Color_addFloats(...) spColor_addFloats(__VA_ARGS__)
+#define Color_clamp(...) spColor_clamp(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_COLOR_H_ */

+ 72 - 0
spine-cpp/spine-cpp/include/spine/Event.h

@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_EVENT_H_
+#define SPINE_EVENT_H_
+
+#include <spine/dll.h>
+#include <spine/EventData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spEvent {
+	spEventData* const data;
+	float const time;
+	int intValue;
+	float floatValue;
+	const char* stringValue;
+
+#ifdef __cplusplus
+	spEvent() :
+		data(0),
+		time(0),
+		intValue(0),
+		floatValue(0),
+		stringValue(0) {
+	}
+#endif
+} spEvent;
+
+SP_API spEvent* spEvent_create (float time, spEventData* data);
+SP_API void spEvent_dispose (spEvent* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEvent Event;
+#define Event_create(...) spEvent_create(__VA_ARGS__)
+#define Event_dispose(...) spEvent_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EVENT_H_ */

+ 69 - 0
spine-cpp/spine-cpp/include/spine/EventData.h

@@ -0,0 +1,69 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_EVENTDATA_H_
+#define SPINE_EVENTDATA_H_
+
+#include <spine/dll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spEventData {
+	const char* const name;
+	int intValue;
+	float floatValue;
+	const char* stringValue;
+
+#ifdef __cplusplus
+	spEventData() :
+		name(0),
+		intValue(0),
+		floatValue(0),
+		stringValue(0) {
+	}
+#endif
+} spEventData;
+
+SP_API spEventData* spEventData_create (const char* name);
+SP_API void spEventData_dispose (spEventData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventData EventData;
+#define EventData_create(...) spEventData_create(__VA_ARGS__)
+#define EventData_dispose(...) spEventData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EVENTDATA_H_ */

+ 87 - 0
spine-cpp/spine-cpp/include/spine/IkConstraint.h

@@ -0,0 +1,87 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_IKCONSTRAINT_H_
+#define SPINE_IKCONSTRAINT_H_
+
+#include <spine/dll.h>
+#include <spine/IkConstraintData.h>
+#include <spine/Bone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spIkConstraint {
+	spIkConstraintData* const data;
+
+	int bonesCount;
+	spBone** bones;
+
+	spBone* target;
+	int bendDirection;
+	float mix;
+
+#ifdef __cplusplus
+	spIkConstraint() :
+		data(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		bendDirection(0),
+		mix(0) {
+	}
+#endif
+} spIkConstraint;
+
+SP_API spIkConstraint* spIkConstraint_create (spIkConstraintData* data, const struct spSkeleton* skeleton);
+SP_API void spIkConstraint_dispose (spIkConstraint* self);
+
+SP_API void spIkConstraint_apply (spIkConstraint* self);
+
+SP_API void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, float alpha);
+SP_API void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDirection, float alpha);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spIkConstraint IkConstraint;
+#define IkConstraint_create(...) spIkConstraint_create(__VA_ARGS__)
+#define IkConstraint_dispose(...) spIkConstraint_dispose(__VA_ARGS__)
+#define IkConstraint_apply(...) spIkConstraint_apply(__VA_ARGS__)
+#define IkConstraint_apply1(...) spIkConstraint_apply1(__VA_ARGS__)
+#define IkConstraint_apply2(...) spIkConstraint_apply2(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_IKCONSTRAINT_H_ */

+ 76 - 0
spine-cpp/spine-cpp/include/spine/IkConstraintData.h

@@ -0,0 +1,76 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_IKCONSTRAINTDATA_H_
+#define SPINE_IKCONSTRAINTDATA_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spIkConstraintData {
+	const char* const name;
+	int order;
+	int bonesCount;
+	spBoneData** bones;
+
+	spBoneData* target;
+	int bendDirection;
+	float mix;
+
+#ifdef __cplusplus
+	spIkConstraintData() :
+		name(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		bendDirection(0),
+		mix(0) {
+	}
+#endif
+} spIkConstraintData;
+
+SP_API spIkConstraintData* spIkConstraintData_create (const char* name);
+SP_API void spIkConstraintData_dispose (spIkConstraintData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spIkConstraintData IkConstraintData;
+#define IkConstraintData_create(...) spIkConstraintData_create(__VA_ARGS__)
+#define IkConstraintData_dispose(...) spIkConstraintData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_IKCONSTRAINTDATA_H_ */

+ 91 - 0
spine-cpp/spine-cpp/include/spine/MeshAttachment.h

@@ -0,0 +1,91 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_MESHATTACHMENT_H_
+#define SPINE_MESHATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spMeshAttachment spMeshAttachment;
+struct spMeshAttachment {
+	spVertexAttachment super;
+
+	void* rendererObject;
+	int regionOffsetX, regionOffsetY; /* Pixels stripped from the bottom left, unrotated. */
+	int regionWidth, regionHeight; /* Unrotated, stripped pixel size. */
+	int regionOriginalWidth, regionOriginalHeight; /* Unrotated, unstripped pixel size. */
+	float regionU, regionV, regionU2, regionV2;
+	int/*bool*/regionRotate;
+
+	const char* path;
+
+	float* regionUVs;
+	float* uvs;
+
+	int trianglesCount;
+	unsigned short* triangles;
+
+	spColor color;
+
+	int hullLength;
+
+	spMeshAttachment* const parentMesh;
+	int/*bool*/inheritDeform;
+
+	/* Nonessential. */
+	int edgesCount;
+	int* edges;
+	float width, height;
+};
+
+SP_API spMeshAttachment* spMeshAttachment_create (const char* name);
+SP_API void spMeshAttachment_updateUVs (spMeshAttachment* self);
+SP_API void spMeshAttachment_setParentMesh (spMeshAttachment* self, spMeshAttachment* parentMesh);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spMeshAttachment MeshAttachment;
+#define MeshAttachment_create(...) spMeshAttachment_create(__VA_ARGS__)
+#define MeshAttachment_updateUVs(...) spMeshAttachment_updateUVs(__VA_ARGS__)
+#define MeshAttachment_setParentMesh(...) spMeshAttachment_setParentMesh(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_MESHATTACHMENT_H_ */

+ 63 - 0
spine-cpp/spine-cpp/include/spine/PathAttachment.h

@@ -0,0 +1,63 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_PATHATTACHMENT_H_
+#define SPINE_PATHATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spPathAttachment {
+	spVertexAttachment super;
+	int lengthsLength;
+	float* lengths;
+	int/*bool*/ closed, constantSpeed;
+} spPathAttachment;
+
+SP_API spPathAttachment* spPathAttachment_create (const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathAttachment PathAttachment;
+#define PathAttachment_create(...) spPathAttachment_create(__VA_ARGS__)
+#define PathAttachment_computeWorldVertices(...) spPathAttachment_computeWorldVertices(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_PATHATTACHMENT_H_ */

+ 113 - 0
spine-cpp/spine-cpp/include/spine/PathConstraint.h

@@ -0,0 +1,113 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_PATHCONSTRAINT_H_
+#define SPINE_PATHCONSTRAINT_H_
+
+#include <spine/dll.h>
+#include <spine/PathConstraintData.h>
+#include <spine/Bone.h>
+#include <spine/Slot.h>
+#include "PathAttachment.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spPathConstraint {
+	spPathConstraintData* const data;
+	int bonesCount;
+	spBone** const bones;
+	spSlot* target;
+	float position, spacing, rotateMix, translateMix;
+
+	int spacesCount;
+	float* spaces;
+
+	int positionsCount;
+	float* positions;
+
+	int worldCount;
+	float* world;
+
+	int curvesCount;
+	float* curves;
+
+	int lengthsCount;
+	float* lengths;
+
+	float segments[10];
+
+#ifdef __cplusplus
+	spPathConstraint() :
+		data(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		position(0),
+		spacing(0),
+		rotateMix(0),
+		translateMix(0),
+		spacesCount(0),
+		spaces(0),
+		positionsCount(0),
+		positions(0),
+		worldCount(0),
+		world(0),
+		curvesCount(0),
+		curves(0),
+		lengthsCount(0),
+		lengths(0) {
+	}
+#endif
+} spPathConstraint;
+
+#define SP_PATHCONSTRAINT_
+
+SP_API spPathConstraint* spPathConstraint_create (spPathConstraintData* data, const struct spSkeleton* skeleton);
+SP_API void spPathConstraint_dispose (spPathConstraint* self);
+
+SP_API void spPathConstraint_apply (spPathConstraint* self);
+SP_API float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents, int/*bool*/percentPosition, int/**/percentSpacing);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathConstraint PathConstraint;
+#define PathConstraint_create(...) spPathConstraint_create(__VA_ARGS__)
+#define PathConstraint_dispose(...) spPathConstraint_dispose(__VA_ARGS__)
+#define PathConstraint_apply(...) spPathConstraint_apply(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_PATHCONSTRAINT_H_ */

+ 97 - 0
spine-cpp/spine-cpp/include/spine/PathConstraintData.h

@@ -0,0 +1,97 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_PATHCONSTRAINTDATA_H_
+#define SPINE_PATHCONSTRAINTDATA_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+#include <spine/SlotData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	SP_POSITION_MODE_FIXED, SP_POSITION_MODE_PERCENT
+} spPositionMode;
+
+typedef enum {
+	SP_SPACING_MODE_LENGTH, SP_SPACING_MODE_FIXED, SP_SPACING_MODE_PERCENT
+} spSpacingMode;
+
+typedef enum {
+	SP_ROTATE_MODE_TANGENT, SP_ROTATE_MODE_CHAIN, SP_ROTATE_MODE_CHAIN_SCALE
+} spRotateMode;
+
+typedef struct spPathConstraintData {
+	const char* const name;
+	int order;
+	int bonesCount;
+	spBoneData** const bones;
+	spSlotData* target;
+	spPositionMode positionMode;
+	spSpacingMode spacingMode;
+	spRotateMode rotateMode;
+	float offsetRotation;
+	float position, spacing, rotateMix, translateMix;
+
+#ifdef __cplusplus
+	spPathConstraintData() :
+		name(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		positionMode(SP_POSITION_MODE_FIXED),
+		spacingMode(SP_SPACING_MODE_LENGTH),
+		rotateMode(SP_ROTATE_MODE_TANGENT),
+		offsetRotation(0),
+		position(0),
+		spacing(0),
+		rotateMix(0),
+		translateMix(0) {
+	}
+#endif
+} spPathConstraintData;
+
+SP_API spPathConstraintData* spPathConstraintData_create (const char* name);
+SP_API void spPathConstraintData_dispose (spPathConstraintData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPathConstraintData PathConstraintData;
+#define PathConstraintData_create(...) spPathConstraintData_create(__VA_ARGS__)
+#define PathConstraintData_dispose(...) spPathConstraintData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_PATHCONSTRAINTDATA_H_ */

+ 65 - 0
spine-cpp/spine-cpp/include/spine/PointAttachment.h

@@ -0,0 +1,65 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_POINTATTACHMENT_H_
+#define SPINE_POINTATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spPointAttachment {
+	spVertexAttachment super;
+	float x, y, rotation;
+	spColor color;
+} spPointAttachment;
+
+SP_API spPointAttachment* spPointAttachment_create (const char* name);
+SP_API void spPointAttachment_computeWorldPosition (spPointAttachment* self, spBone* bone, float* x, float* y);
+SP_API float spPointAttachment_computeWorldRotation (spPointAttachment* self, spBone* bone);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPointAttachment PointAttachment;
+#define PointAttachment_create(...) spPointAttachment_create(__VA_ARGS__)
+#define PointAttachment_computeWorldPosition(...) spPointAttachment_computeWorldPosition(__VA_ARGS__)
+#define PointAttachment_computeWorldRotation(...) spPointAttachment_computeWorldRotation(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_POINTATTACHMENT_H_ */

+ 75 - 0
spine-cpp/spine-cpp/include/spine/RegionAttachment.h

@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_REGIONATTACHMENT_H_
+#define SPINE_REGIONATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spRegionAttachment {
+	spAttachment super;
+	const char* path;
+	float x, y, scaleX, scaleY, rotation, width, height;
+	spColor color;
+
+	void* rendererObject;
+	int regionOffsetX, regionOffsetY; /* Pixels stripped from the bottom left, unrotated. */
+	int regionWidth, regionHeight; /* Unrotated, stripped pixel size. */
+	int regionOriginalWidth, regionOriginalHeight; /* Unrotated, unstripped pixel size. */
+
+	float offset[8];
+	float uvs[8];
+} spRegionAttachment;
+
+SP_API spRegionAttachment* spRegionAttachment_create (const char* name);
+SP_API void spRegionAttachment_setUVs (spRegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate);
+SP_API void spRegionAttachment_updateOffset (spRegionAttachment* self);
+SP_API void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, spBone* bone, float* vertices, int offset, int stride);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spRegionAttachment RegionAttachment;
+#define RegionAttachment_create(...) spRegionAttachment_create(__VA_ARGS__)
+#define RegionAttachment_setUVs(...) spRegionAttachment_setUVs(__VA_ARGS__)
+#define RegionAttachment_updateOffset(...) spRegionAttachment_updateOffset(__VA_ARGS__)
+#define RegionAttachment_computeWorldVertices(...) spRegionAttachment_computeWorldVertices(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_REGIONATTACHMENT_H_ */

+ 174 - 0
spine-cpp/spine-cpp/include/spine/Skeleton.h

@@ -0,0 +1,174 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETON_H_
+#define SPINE_SKELETON_H_
+
+#include <spine/dll.h>
+#include <spine/SkeletonData.h>
+#include <spine/Slot.h>
+#include <spine/Skin.h>
+#include <spine/IkConstraint.h>
+#include <spine/TransformConstraint.h>
+#include <spine/PathConstraint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spSkeleton {
+	spSkeletonData* const data;
+
+	int bonesCount;
+	spBone** bones;
+	spBone* const root;
+
+	int slotsCount;
+	spSlot** slots;
+	spSlot** drawOrder;
+
+	int ikConstraintsCount;
+	spIkConstraint** ikConstraints;
+
+	int transformConstraintsCount;
+	spTransformConstraint** transformConstraints;
+
+	int pathConstraintsCount;
+	spPathConstraint** pathConstraints;
+
+	spSkin* const skin;
+	spColor color;
+	float time;
+	int/*bool*/flipX, flipY;
+	float x, y;
+
+#ifdef __cplusplus
+	spSkeleton() :
+		data(0),
+		bonesCount(0),
+		bones(0),
+		root(0),
+		slotsCount(0),
+		slots(0),
+		drawOrder(0),
+
+		ikConstraintsCount(0),
+		ikConstraints(0),
+
+		transformConstraintsCount(0),
+		transformConstraints(0),
+
+		skin(0),
+		color(),
+		time(0),
+		flipX(0),
+		flipY(0),
+		x(0), y(0) {
+	}
+#endif
+} spSkeleton;
+
+SP_API spSkeleton* spSkeleton_create (spSkeletonData* data);
+SP_API void spSkeleton_dispose (spSkeleton* self);
+
+/* Caches information about bones and constraints. Must be called if bones or constraints, or weighted path attachments
+ * are added or removed. */
+SP_API void spSkeleton_updateCache (spSkeleton* self);
+SP_API void spSkeleton_updateWorldTransform (const spSkeleton* self);
+
+/* Sets the bones, constraints, and slots to their setup pose values. */
+SP_API void spSkeleton_setToSetupPose (const spSkeleton* self);
+/* Sets the bones and constraints to their setup pose values. */
+SP_API void spSkeleton_setBonesToSetupPose (const spSkeleton* self);
+SP_API void spSkeleton_setSlotsToSetupPose (const spSkeleton* self);
+
+/* Returns 0 if the bone was not found. */
+SP_API spBone* spSkeleton_findBone (const spSkeleton* self, const char* boneName);
+/* Returns -1 if the bone was not found. */
+SP_API int spSkeleton_findBoneIndex (const spSkeleton* self, const char* boneName);
+
+/* Returns 0 if the slot was not found. */
+SP_API spSlot* spSkeleton_findSlot (const spSkeleton* self, const char* slotName);
+/* Returns -1 if the slot was not found. */
+SP_API int spSkeleton_findSlotIndex (const spSkeleton* self, const char* slotName);
+
+/* Sets the skin used to look up attachments before looking in the SkeletonData defaultSkin. Attachments from the new skin are
+ * attached if the corresponding attachment from the old skin was attached. If there was no old skin, each slot's setup mode
+ * attachment is attached from the new skin.
+ * @param skin May be 0.*/
+SP_API void spSkeleton_setSkin (spSkeleton* self, spSkin* skin);
+/* Returns 0 if the skin was not found. See spSkeleton_setSkin.
+ * @param skinName May be 0. */
+SP_API int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName);
+
+/* Returns 0 if the slot or attachment was not found. */
+SP_API spAttachment* spSkeleton_getAttachmentForSlotName (const spSkeleton* self, const char* slotName, const char* attachmentName);
+/* Returns 0 if the slot or attachment was not found. */
+SP_API spAttachment* spSkeleton_getAttachmentForSlotIndex (const spSkeleton* self, int slotIndex, const char* attachmentName);
+/* Returns 0 if the slot or attachment was not found.
+ * @param attachmentName May be 0. */
+SP_API int spSkeleton_setAttachment (spSkeleton* self, const char* slotName, const char* attachmentName);
+
+/* Returns 0 if the IK constraint was not found. */
+SP_API spIkConstraint* spSkeleton_findIkConstraint (const spSkeleton* self, const char* constraintName);
+
+/* Returns 0 if the transform constraint was not found. */
+SP_API spTransformConstraint* spSkeleton_findTransformConstraint (const spSkeleton* self, const char* constraintName);
+
+/* Returns 0 if the path constraint was not found. */
+SP_API spPathConstraint* spSkeleton_findPathConstraint (const spSkeleton* self, const char* constraintName);
+
+SP_API void spSkeleton_update (spSkeleton* self, float deltaTime);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeleton Skeleton;
+#define Skeleton_create(...) spSkeleton_create(__VA_ARGS__)
+#define Skeleton_dispose(...) spSkeleton_dispose(__VA_ARGS__)
+#define Skeleton_updateWorldTransform(...) spSkeleton_updateWorldTransform(__VA_ARGS__)
+#define Skeleton_setToSetupPose(...) spSkeleton_setToSetupPose(__VA_ARGS__)
+#define Skeleton_setBonesToSetupPose(...) spSkeleton_setBonesToSetupPose(__VA_ARGS__)
+#define Skeleton_setSlotsToSetupPose(...) spSkeleton_setSlotsToSetupPose(__VA_ARGS__)
+#define Skeleton_findBone(...) spSkeleton_findBone(__VA_ARGS__)
+#define Skeleton_findBoneIndex(...) spSkeleton_findBoneIndex(__VA_ARGS__)
+#define Skeleton_findSlot(...) spSkeleton_findSlot(__VA_ARGS__)
+#define Skeleton_findSlotIndex(...) spSkeleton_findSlotIndex(__VA_ARGS__)
+#define Skeleton_setSkin(...) spSkeleton_setSkin(__VA_ARGS__)
+#define Skeleton_setSkinByName(...) spSkeleton_setSkinByName(__VA_ARGS__)
+#define Skeleton_getAttachmentForSlotName(...) spSkeleton_getAttachmentForSlotName(__VA_ARGS__)
+#define Skeleton_getAttachmentForSlotIndex(...) spSkeleton_getAttachmentForSlotIndex(__VA_ARGS__)
+#define Skeleton_setAttachment(...) spSkeleton_setAttachment(__VA_ARGS__)
+#define Skeleton_update(...) spSkeleton_update(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETON_H_*/

+ 72 - 0
spine-cpp/spine-cpp/include/spine/SkeletonBinary.h

@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONBINARY_H_
+#define SPINE_SKELETONBINARY_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/SkeletonData.h>
+#include <spine/Atlas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spAtlasAttachmentLoader;
+
+typedef struct spSkeletonBinary {
+	float scale;
+	spAttachmentLoader* attachmentLoader;
+	const char* const error;
+} spSkeletonBinary;
+
+SP_API spSkeletonBinary* spSkeletonBinary_createWithLoader (spAttachmentLoader* attachmentLoader);
+SP_API spSkeletonBinary* spSkeletonBinary_create (spAtlas* atlas);
+SP_API void spSkeletonBinary_dispose (spSkeletonBinary* self);
+
+SP_API spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const unsigned char* binary, const int length);
+SP_API spSkeletonData* spSkeletonBinary_readSkeletonDataFile (spSkeletonBinary* self, const char* path);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonBinary SkeletonBinary;
+#define SkeletonBinary_createWithLoader(...) spSkeletonBinary_createWithLoader(__VA_ARGS__)
+#define SkeletonBinary_create(...) spSkeletonBinary_create(__VA_ARGS__)
+#define SkeletonBinary_dispose(...) spSkeletonBinary_dispose(__VA_ARGS__)
+#define SkeletonBinary_readSkeletonData(...) spSkeletonBinary_readSkeletonData(__VA_ARGS__)
+#define SkeletonBinary_readSkeletonDataFile(...) spSkeletonBinary_readSkeletonDataFile(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONBINARY_H_ */

+ 113 - 0
spine-cpp/spine-cpp/include/spine/SkeletonBounds.h

@@ -0,0 +1,113 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONBOUNDS_H_
+#define SPINE_SKELETONBOUNDS_H_
+
+#include <spine/dll.h>
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/Skeleton.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spPolygon {
+	float* const vertices;
+	int count;
+	int capacity;
+} spPolygon;
+
+SP_API spPolygon* spPolygon_create (int capacity);
+SP_API void spPolygon_dispose (spPolygon* self);
+
+SP_API int/*bool*/spPolygon_containsPoint (spPolygon* polygon, float x, float y);
+SP_API int/*bool*/spPolygon_intersectsSegment (spPolygon* polygon, float x1, float y1, float x2, float y2);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPolygon Polygon;
+#define Polygon_create(...) spPolygon_create(__VA_ARGS__)
+#define Polygon_dispose(...) spPolygon_dispose(__VA_ARGS__)
+#define Polygon_containsPoint(...) spPolygon_containsPoint(__VA_ARGS__)
+#define Polygon_intersectsSegment(...) spPolygon_intersectsSegment(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spSkeletonBounds {
+	int count;
+	spBoundingBoxAttachment** boundingBoxes;
+	spPolygon** polygons;
+
+	float minX, minY, maxX, maxY;
+} spSkeletonBounds;
+
+SP_API spSkeletonBounds* spSkeletonBounds_create ();
+SP_API void spSkeletonBounds_dispose (spSkeletonBounds* self);
+SP_API void spSkeletonBounds_update (spSkeletonBounds* self, spSkeleton* skeleton, int/*bool*/updateAabb);
+
+/** Returns true if the axis aligned bounding box contains the point. */
+SP_API int/*bool*/spSkeletonBounds_aabbContainsPoint (spSkeletonBounds* self, float x, float y);
+
+/** Returns true if the axis aligned bounding box intersects the line segment. */
+SP_API int/*bool*/spSkeletonBounds_aabbIntersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2);
+
+/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
+SP_API int/*bool*/spSkeletonBounds_aabbIntersectsSkeleton (spSkeletonBounds* self, spSkeletonBounds* bounds);
+
+/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
+ * efficient to only call this method if spSkeletonBounds_aabbContainsPoint returns true. */
+SP_API spBoundingBoxAttachment* spSkeletonBounds_containsPoint (spSkeletonBounds* self, float x, float y);
+
+/** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
+ * more efficient to only call this method if spSkeletonBounds_aabbIntersectsSegment returns true. */
+SP_API spBoundingBoxAttachment* spSkeletonBounds_intersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2);
+
+/** Returns the polygon for the specified bounding box, or null. */
+SP_API spPolygon* spSkeletonBounds_getPolygon (spSkeletonBounds* self, spBoundingBoxAttachment* boundingBox);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonBounds SkeletonBounds;
+#define SkeletonBounds_create(...) spSkeletonBounds_create(__VA_ARGS__)
+#define SkeletonBounds_dispose(...) spSkeletonBounds_dispose(__VA_ARGS__)
+#define SkeletonBounds_update(...) spSkeletonBounds_update(__VA_ARGS__)
+#define SkeletonBounds_aabbContainsPoint(...) spSkeletonBounds_aabbContainsPoint(__VA_ARGS__)
+#define SkeletonBounds_aabbIntersectsSegment(...) spSkeletonBounds_aabbIntersectsSegment(__VA_ARGS__)
+#define SkeletonBounds_aabbIntersectsSkeleton(...) spSkeletonBounds_aabbIntersectsSkeleton(__VA_ARGS__)
+#define SkeletonBounds_containsPoint(...) spSkeletonBounds_containsPoint(__VA_ARGS__)
+#define SkeletonBounds_intersectsSegment(...) spSkeletonBounds_intersectsSegment(__VA_ARGS__)
+#define SkeletonBounds_getPolygon(...) spSkeletonBounds_getPolygon(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONBOUNDS_H_ */

+ 68 - 0
spine-cpp/spine-cpp/include/spine/SkeletonClipping.h

@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONCLIPPING_H
+#define SPINE_SKELETONCLIPPING_H
+
+#include <spine/dll.h>
+#include <spine/Array.h>
+#include <spine/ClippingAttachment.h>
+#include <spine/Slot.h>
+#include <spine/Triangulator.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spSkeletonClipping {
+	spTriangulator* triangulator;
+	spFloatArray* clippingPolygon;
+	spFloatArray* clipOutput;
+	spFloatArray* clippedVertices;
+	spFloatArray* clippedUVs;
+	spUnsignedShortArray* clippedTriangles;
+	spFloatArray* scratch;
+	spClippingAttachment* clipAttachment;
+	spArrayFloatArray* clippingPolygons;
+} spSkeletonClipping;
+
+SP_API spSkeletonClipping* spSkeletonClipping_create();
+SP_API int spSkeletonClipping_clipStart(spSkeletonClipping* self, spSlot* slot, spClippingAttachment* clip);
+SP_API void spSkeletonClipping_clipEnd(spSkeletonClipping* self, spSlot* slot);
+SP_API void spSkeletonClipping_clipEnd2(spSkeletonClipping* self);
+SP_API int /*boolean*/ spSkeletonClipping_isClipping(spSkeletonClipping* self);
+SP_API void spSkeletonClipping_clipTriangles(spSkeletonClipping* self, float* vertices, int verticesLength, unsigned short* triangles, int trianglesLength, float* uvs, int stride);
+SP_API void spSkeletonClipping_dispose(spSkeletonClipping* self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONCLIPPING_H */

+ 117 - 0
spine-cpp/spine-cpp/include/spine/SkeletonData.h

@@ -0,0 +1,117 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONDATA_H_
+#define SPINE_SKELETONDATA_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+#include <spine/SlotData.h>
+#include <spine/Skin.h>
+#include <spine/EventData.h>
+#include <spine/Animation.h>
+#include <spine/IkConstraintData.h>
+#include <spine/TransformConstraintData.h>
+#include <spine/PathConstraintData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spSkeletonData {
+	const char* version;
+	const char* hash;
+	float width, height;
+
+	int bonesCount;
+	spBoneData** bones;
+
+	int slotsCount;
+	spSlotData** slots;
+
+	int skinsCount;
+	spSkin** skins;
+	spSkin* defaultSkin;
+
+	int eventsCount;
+	spEventData** events;
+
+	int animationsCount;
+	spAnimation** animations;
+
+	int ikConstraintsCount;
+	spIkConstraintData** ikConstraints;
+
+	int transformConstraintsCount;
+	spTransformConstraintData** transformConstraints;
+
+	int pathConstraintsCount;
+	spPathConstraintData** pathConstraints;
+} spSkeletonData;
+
+SP_API spSkeletonData* spSkeletonData_create ();
+SP_API void spSkeletonData_dispose (spSkeletonData* self);
+
+SP_API spBoneData* spSkeletonData_findBone (const spSkeletonData* self, const char* boneName);
+SP_API int spSkeletonData_findBoneIndex (const spSkeletonData* self, const char* boneName);
+
+SP_API spSlotData* spSkeletonData_findSlot (const spSkeletonData* self, const char* slotName);
+SP_API int spSkeletonData_findSlotIndex (const spSkeletonData* self, const char* slotName);
+
+SP_API spSkin* spSkeletonData_findSkin (const spSkeletonData* self, const char* skinName);
+
+SP_API spEventData* spSkeletonData_findEvent (const spSkeletonData* self, const char* eventName);
+
+SP_API spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, const char* animationName);
+
+SP_API spIkConstraintData* spSkeletonData_findIkConstraint (const spSkeletonData* self, const char* constraintName);
+
+SP_API spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkeletonData* self, const char* constraintName);
+
+SP_API spPathConstraintData* spSkeletonData_findPathConstraint (const spSkeletonData* self, const char* constraintName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonData SkeletonData;
+#define SkeletonData_create(...) spSkeletonData_create(__VA_ARGS__)
+#define SkeletonData_dispose(...) spSkeletonData_dispose(__VA_ARGS__)
+#define SkeletonData_findBone(...) spSkeletonData_findBone(__VA_ARGS__)
+#define SkeletonData_findBoneIndex(...) spSkeletonData_findBoneIndex(__VA_ARGS__)
+#define SkeletonData_findSlot(...) spSkeletonData_findSlot(__VA_ARGS__)
+#define SkeletonData_findSlotIndex(...) spSkeletonData_findSlotIndex(__VA_ARGS__)
+#define SkeletonData_findSkin(...) spSkeletonData_findSkin(__VA_ARGS__)
+#define SkeletonData_findEvent(...) spSkeletonData_findEvent(__VA_ARGS__)
+#define SkeletonData_findAnimation(...) spSkeletonData_findAnimation(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONDATA_H_ */

+ 73 - 0
spine-cpp/spine-cpp/include/spine/SkeletonJson.h

@@ -0,0 +1,73 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONJSON_H_
+#define SPINE_SKELETONJSON_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/SkeletonData.h>
+#include <spine/Atlas.h>
+#include <spine/Animation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spAtlasAttachmentLoader;
+
+typedef struct spSkeletonJson {
+	float scale;
+	spAttachmentLoader* attachmentLoader;
+	const char* const error;
+} spSkeletonJson;
+
+SP_API spSkeletonJson* spSkeletonJson_createWithLoader (spAttachmentLoader* attachmentLoader);
+SP_API spSkeletonJson* spSkeletonJson_create (spAtlas* atlas);
+SP_API void spSkeletonJson_dispose (spSkeletonJson* self);
+
+SP_API spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const char* json);
+SP_API spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonJson SkeletonJson;
+#define SkeletonJson_createWithLoader(...) spSkeletonJson_createWithLoader(__VA_ARGS__)
+#define SkeletonJson_create(...) spSkeletonJson_create(__VA_ARGS__)
+#define SkeletonJson_dispose(...) spSkeletonJson_dispose(__VA_ARGS__)
+#define SkeletonJson_readSkeletonData(...) spSkeletonJson_readSkeletonData(__VA_ARGS__)
+#define SkeletonJson_readSkeletonDataFile(...) spSkeletonJson_readSkeletonDataFile(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONJSON_H_ */

+ 95 - 0
spine-cpp/spine-cpp/include/spine/Skin.h

@@ -0,0 +1,95 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SKIN_H_
+#define SPINE_SKIN_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spSkin {
+	const char* const name;
+
+#ifdef __cplusplus
+	spSkin() :
+		name(0) {
+	}
+#endif
+} spSkin;
+
+/* Private structs, needed by Skeleton */
+typedef struct _Entry _Entry;
+struct _Entry {
+	int slotIndex;
+	const char* name;
+	spAttachment* attachment;
+	_Entry* next;
+};
+
+typedef struct {
+	spSkin super;
+	_Entry* entries;
+} _spSkin;
+
+SP_API spSkin* spSkin_create (const char* name);
+SP_API void spSkin_dispose (spSkin* self);
+
+/* The Skin owns the attachment. */
+SP_API void spSkin_addAttachment (spSkin* self, int slotIndex, const char* name, spAttachment* attachment);
+/* Returns 0 if the attachment was not found. */
+SP_API spAttachment* spSkin_getAttachment (const spSkin* self, int slotIndex, const char* name);
+
+/* Returns 0 if the slot or attachment was not found. */
+SP_API const char* spSkin_getAttachmentName (const spSkin* self, int slotIndex, int attachmentIndex);
+
+/** Attach each attachment in this skin if the corresponding attachment in oldSkin is currently attached. */
+SP_API void spSkin_attachAll (const spSkin* self, struct spSkeleton* skeleton, const spSkin* oldspSkin);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkin Skin;
+#define Skin_create(...) spSkin_create(__VA_ARGS__)
+#define Skin_dispose(...) spSkin_dispose(__VA_ARGS__)
+#define Skin_addAttachment(...) spSkin_addAttachment(__VA_ARGS__)
+#define Skin_getAttachment(...) spSkin_getAttachment(__VA_ARGS__)
+#define Skin_getAttachmentName(...) spSkin_getAttachmentName(__VA_ARGS__)
+#define Skin_attachAll(...) spSkin_attachAll(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKIN_H_ */

+ 93 - 0
spine-cpp/spine-cpp/include/spine/Slot.h

@@ -0,0 +1,93 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SLOT_H_
+#define SPINE_SLOT_H_
+
+#include <spine/dll.h>
+#include <spine/Bone.h>
+#include <spine/Attachment.h>
+#include <spine/SlotData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spSlot {
+	spSlotData* const data;
+	spBone* const bone;
+	spColor color;
+	spColor* darkColor;
+	spAttachment* const attachment;
+
+	int attachmentVerticesCapacity;
+	int attachmentVerticesCount;
+	float* attachmentVertices;
+
+#ifdef __cplusplus
+	spSlot() :
+		data(0),
+		bone(0),
+		color(),
+		darkColor(0),
+		attachment(0),
+		attachmentVerticesCapacity(0),
+		attachmentVerticesCount(0),
+		attachmentVertices(0) {
+	}
+#endif
+} spSlot;
+
+SP_API spSlot* spSlot_create (spSlotData* data, spBone* bone);
+SP_API void spSlot_dispose (spSlot* self);
+
+/* @param attachment May be 0 to clear the attachment for the slot. */
+SP_API void spSlot_setAttachment (spSlot* self, spAttachment* attachment);
+
+SP_API void spSlot_setAttachmentTime (spSlot* self, float time);
+SP_API float spSlot_getAttachmentTime (const spSlot* self);
+
+SP_API void spSlot_setToSetupPose (spSlot* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSlot Slot;
+#define Slot_create(...) spSlot_create(__VA_ARGS__)
+#define Slot_dispose(...) spSlot_dispose(__VA_ARGS__)
+#define Slot_setAttachment(...) spSlot_setAttachment(__VA_ARGS__)
+#define Slot_setAttachmentTime(...) spSlot_setAttachmentTime(__VA_ARGS__)
+#define Slot_getAttachmentTime(...) spSlot_getAttachmentTime(__VA_ARGS__)
+#define Slot_setToSetupPose(...) spSlot_setToSetupPose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SLOT_H_ */

+ 90 - 0
spine-cpp/spine-cpp/include/spine/SlotData.h

@@ -0,0 +1,90 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SLOTDATA_H_
+#define SPINE_SLOTDATA_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+#include <spine/Color.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	SP_BLEND_MODE_NORMAL, SP_BLEND_MODE_ADDITIVE, SP_BLEND_MODE_MULTIPLY, SP_BLEND_MODE_SCREEN
+} spBlendMode;
+
+typedef struct spSlotData {
+	const int index;
+	const char* const name;
+	const spBoneData* const boneData;
+	const char* attachmentName;
+	spColor color;
+	spColor* darkColor;
+	spBlendMode blendMode;
+
+#ifdef __cplusplus
+	spSlotData() :
+		index(0),
+		name(0),
+		boneData(0),
+		attachmentName(0),
+		color(),
+		darkColor(0),
+		blendMode(SP_BLEND_MODE_NORMAL) {
+	}
+#endif
+} spSlotData;
+
+SP_API spSlotData* spSlotData_create (const int index, const char* name, spBoneData* boneData);
+SP_API void spSlotData_dispose (spSlotData* self);
+
+/* @param attachmentName May be 0 for no setup pose attachment. */
+SP_API void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBlendMode BlendMode;
+#define BLEND_MODE_NORMAL SP_BLEND_MODE_NORMAL
+#define BLEND_MODE_ADDITIVE SP_BLEND_MODE_ADDITIVE
+#define BLEND_MODE_MULTIPLY SP_BLEND_MODE_MULTIPLY
+#define BLEND_MODE_SCREEN SP_BLEND_MODE_SCREEN
+typedef spSlotData SlotData;
+#define SlotData_create(...) spSlotData_create(__VA_ARGS__)
+#define SlotData_dispose(...) spSlotData_dispose(__VA_ARGS__)
+#define SlotData_setAttachmentName(...) spSlotData_setAttachmentName(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SLOTDATA_H_ */

+ 81 - 0
spine-cpp/spine-cpp/include/spine/TransformConstraint.h

@@ -0,0 +1,81 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_TRANSFORMCONSTRAINT_H_
+#define SPINE_TRANSFORMCONSTRAINT_H_
+
+#include <spine/dll.h>
+#include <spine/TransformConstraintData.h>
+#include <spine/Bone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spTransformConstraint {
+	spTransformConstraintData* const data;
+	int bonesCount;
+	spBone** const bones;
+	spBone* target;
+	float rotateMix, translateMix, scaleMix, shearMix;
+
+#ifdef __cplusplus
+	spTransformConstraint() :
+		data(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		rotateMix(0),
+		translateMix(0),
+		scaleMix(0),
+		shearMix(0) {
+	}
+#endif
+} spTransformConstraint;
+
+SP_API spTransformConstraint* spTransformConstraint_create (spTransformConstraintData* data, const struct spSkeleton* skeleton);
+SP_API void spTransformConstraint_dispose (spTransformConstraint* self);
+
+SP_API void spTransformConstraint_apply (spTransformConstraint* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTransformConstraint TransformConstraint;
+#define TransformConstraint_create(...) spTransformConstraint_create(__VA_ARGS__)
+#define TransformConstraint_dispose(...) spTransformConstraint_dispose(__VA_ARGS__)
+#define TransformConstraint_apply(...) spTransformConstraint_apply(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_TRANSFORMCONSTRAINT_H_ */

+ 87 - 0
spine-cpp/spine-cpp/include/spine/TransformConstraintData.h

@@ -0,0 +1,87 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_TRANSFORMCONSTRAINTDATA_H_
+#define SPINE_TRANSFORMCONSTRAINTDATA_H_
+
+#include <spine/dll.h>
+#include <spine/BoneData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spTransformConstraintData {
+	const char* const name;
+	int order;
+	int bonesCount;
+	spBoneData** const bones;
+	spBoneData* target;
+	float rotateMix, translateMix, scaleMix, shearMix;
+	float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
+	int /*boolean*/ relative;
+	int /*boolean*/ local;
+
+#ifdef __cplusplus
+	spTransformConstraintData() :
+		name(0),
+		bonesCount(0),
+		bones(0),
+		target(0),
+		rotateMix(0),
+		translateMix(0),
+		scaleMix(0),
+		shearMix(0),
+		offsetRotation(0),
+		offsetX(0),
+		offsetY(0),
+		offsetScaleX(0),
+		offsetScaleY(0),
+		offsetShearY(0),
+		relative(0),
+		local(0) {
+	}
+#endif
+} spTransformConstraintData;
+
+SP_API spTransformConstraintData* spTransformConstraintData_create (const char* name);
+SP_API void spTransformConstraintData_dispose (spTransformConstraintData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTransformConstraintData TransformConstraintData;
+#define TransformConstraintData_create(...) spTransformConstraintData_create(__VA_ARGS__)
+#define TransformConstraintData_dispose(...) spTransformConstraintData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_TRANSFORMCONSTRAINTDATA_H_ */

+ 63 - 0
spine-cpp/spine-cpp/include/spine/Triangulator.h

@@ -0,0 +1,63 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_TRIANGULATOR_H
+#define SPINE_TRIANGULATOR_H
+
+#include <spine/dll.h>
+#include <spine/Array.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spTriangulator {
+	spArrayFloatArray* convexPolygons;
+	spArrayShortArray* convexPolygonsIndices;
+
+	spShortArray* indicesArray;
+	spIntArray* isConcaveArray;
+	spShortArray* triangles;
+
+	spArrayFloatArray* polygonPool;
+	spArrayShortArray* polygonIndicesPool;
+} spTriangulator;
+
+SP_API spTriangulator* spTriangulator_create();
+SP_API spShortArray* spTriangulator_triangulate(spTriangulator* self, spFloatArray* verticesArray);
+SP_API spArrayFloatArray* spTriangulator_decompose(spTriangulator* self, spFloatArray* verticesArray, spShortArray* triangles);
+SP_API void spTriangulator_dispose(spTriangulator* self);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_TRIANGULATOR_H_ */

+ 68 - 0
spine-cpp/spine-cpp/include/spine/VertexAttachment.h

@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_VERTEXATTACHMENT_H_
+#define SPINE_VERTEXATTACHMENT_H_
+
+#include <spine/dll.h>
+#include <spine/Attachment.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spVertexAttachment spVertexAttachment;
+struct spVertexAttachment {
+	spAttachment super;
+
+	int bonesCount;
+	int* bones;
+
+	int verticesCount;
+	float* vertices;
+
+	int worldVerticesLength;
+
+	int id;
+};
+
+SP_API void spVertexAttachment_computeWorldVertices (spVertexAttachment* self, spSlot* slot, int start, int count, float* worldVertices, int offset, int stride);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spVertexAttachment VertexAttachment;
+#define VertexAttachment_computeWorldVertices(...) spVertexAttachment_computeWorldVertices(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_VERTEXATTACHMENT_H_ */

+ 85 - 0
spine-cpp/spine-cpp/include/spine/VertexEffect.h

@@ -0,0 +1,85 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_VERTEXEFFECT_H_
+#define SPINE_VERTEXEFFECT_H_
+
+#include <spine/dll.h>
+#include <spine/Skeleton.h>
+#include <spine/Color.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spVertexEffect;
+
+typedef void (*spVertexEffectBegin)(struct spVertexEffect *self, spSkeleton *skeleton);
+
+typedef void (*spVertexEffectTransform)(struct spVertexEffect *self, float *x, float *y, float *u, float *v,
+										spColor *light, spColor *dark);
+
+typedef void (*spVertexEffectEnd)(struct spVertexEffect *self);
+
+typedef struct spVertexEffect {
+	spVertexEffectBegin begin;
+	spVertexEffectTransform transform;
+	spVertexEffectEnd end;
+} spVertexEffect;
+
+typedef struct spJitterVertexEffect {
+	spVertexEffect super;
+	float jitterX;
+	float jitterY;
+} spJitterVertexEffect;
+
+typedef struct spSwirlVertexEffect {
+	spVertexEffect super;
+	float centerX;
+	float centerY;
+	float radius;
+	float angle;
+	float worldX;
+	float worldY;
+} spSwirlVertexEffect;
+
+SP_API spJitterVertexEffect *spJitterVertexEffect_create(float jitterX, float jitterY);
+
+SP_API void spJitterVertexEffect_dispose(spJitterVertexEffect *effect);
+
+SP_API spSwirlVertexEffect *spSwirlVertexEffect_create(float radius);
+
+SP_API void spSwirlVertexEffect_dispose(spSwirlVertexEffect *effect);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_VERTEX_EFFECT_H_ */

+ 48 - 0
spine-cpp/spine-cpp/include/spine/dll.h

@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SHAREDLIB_H
+#define SPINE_SHAREDLIB_H
+
+#ifdef _WIN32
+	#define DLLIMPORT __declspec(dllimport)
+	#define DLLEXPORT __declspec(dllexport)
+#else
+	#define DLLIMPORT
+	#define DLLEXPORT
+#endif
+
+#ifdef SPINEPLUGIN_API
+	#define SP_API SPINEPLUGIN_API
+#else
+	#define SP_API
+#endif
+
+#endif /* SPINE_SHAREDLIB_H */

+ 318 - 0
spine-cpp/spine-cpp/include/spine/extension.h

@@ -0,0 +1,318 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+/*
+ Implementation notes:
+
+ - An OOP style is used where each "class" is made up of a struct and a number of functions prefixed with the struct name.
+
+ - struct fields that are const are readonly. Either they are set in a create function and can never be changed, or they can only
+ be changed by calling a function.
+
+ - Inheritance is done using a struct field named "super" as the first field, allowing the struct to be cast to its "super class".
+ This works because a pointer to a struct is guaranteed to be a pointer to the first struct field.
+
+ - Classes intended for inheritance provide init/deinit functions which subclasses must call in their create/dispose functions.
+
+ - Polymorphism is done by a base class providing function pointers in its init function. The public API delegates to these
+ function pointers.
+
+ - Subclasses do not provide a dispose function, instead the base class' dispose function should be used, which will delegate to
+ a dispose function pointer.
+
+ - Classes not designed for inheritance cannot be extended because they may use an internal subclass to hide private data and don't
+ expose function pointers.
+
+ - The public API hides implementation details, such as init/deinit functions. An internal API is exposed by extension.h to allow
+ classes to be extended. Internal functions begin with underscore (_).
+
+ - OOP in C tends to lose type safety. Macros for casting are provided in extension.h to give context for why a cast is being done.
+
+ - If SPINE_SHORT_NAMES is defined, the "sp" prefix for all class names is optional.
+ */
+
+#ifndef SPINE_EXTENSION_H_
+#define SPINE_EXTENSION_H_
+
+/* All allocation uses these. */
+#define MALLOC(TYPE,COUNT) ((TYPE*)_spMalloc(sizeof(TYPE) * (COUNT), __FILE__, __LINE__))
+#define CALLOC(TYPE,COUNT) ((TYPE*)_spCalloc(COUNT, sizeof(TYPE), __FILE__, __LINE__))
+#define REALLOC(PTR,TYPE,COUNT) ((TYPE*)_spRealloc(PTR, sizeof(TYPE) * (COUNT)))
+#define NEW(TYPE) CALLOC(TYPE,1)
+
+/* Gets the direct super class. Type safe. */
+#define SUPER(VALUE) (&VALUE->super)
+
+/* Cast to a super class. Not type safe, use with care. Prefer SUPER() where possible. */
+#define SUPER_CAST(TYPE,VALUE) ((TYPE*)VALUE)
+
+/* Cast to a sub class. Not type safe, use with care. */
+#define SUB_CAST(TYPE,VALUE) ((TYPE*)VALUE)
+
+/* Casts away const. Can be used as an lvalue. Not type safe, use with care. */
+#define CONST_CAST(TYPE,VALUE) (*(TYPE*)&VALUE)
+
+/* Gets the vtable for the specified type. Not type safe, use with care. */
+#define VTABLE(TYPE,VALUE) ((_##TYPE##Vtable*)((TYPE*)VALUE)->vtable)
+
+/* Frees memory. Can be used on const types. */
+#define FREE(VALUE) _spFree((void*)VALUE)
+
+/* Allocates a new char[], assigns it to TO, and copies FROM to it. Can be used on const types. */
+#define MALLOC_STR(TO,FROM) strcpy(CONST_CAST(char*, TO) = (char*)MALLOC(char, strlen(FROM) + 1), FROM)
+
+#define PI 3.1415926535897932385f
+#define PI2 (PI * 2)
+#define DEG_RAD (PI / 180)
+#define RAD_DEG (180 / PI)
+
+#define ABS(A) ((A) < 0? -(A): (A))
+#define SIGNUM(A) ((A) < 0? -1: (A) > 0 ? 1 : 0)
+
+#ifdef __STDC_VERSION__
+#define FMOD(A,B) fmodf(A, B)
+#define ATAN2(A,B) atan2f(A, B)
+#define SIN(A) sinf(A)
+#define COS(A) cosf(A)
+#define SQRT(A) sqrtf(A)
+#define ACOS(A) acosf(A)
+#define POW(A,B) pow(A, B)
+#else
+#define FMOD(A,B) (float)fmod(A, B)
+#define ATAN2(A,B) (float)atan2(A, B)
+#define COS(A) (float)cos(A)
+#define SIN(A) (float)sin(A)
+#define SQRT(A) (float)sqrt(A)
+#define ACOS(A) (float)acos(A)
+#define POW(A,B) (float)pow(A, B)
+#endif
+
+#define SIN_DEG(A) SIN((A) * DEG_RAD)
+#define COS_DEG(A) COS((A) * DEG_RAD)
+#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define UNUSED(x) (void)(x)
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <spine/Skeleton.h>
+#include <spine/Animation.h>
+#include <spine/Atlas.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/VertexAttachment.h>
+#include <spine/RegionAttachment.h>
+#include <spine/MeshAttachment.h>
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/ClippingAttachment.h>
+#include <spine/PathAttachment.h>
+#include <spine/PointAttachment.h>
+#include <spine/AnimationState.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Functions that must be implemented:
+ */
+
+void _spAtlasPage_createTexture (spAtlasPage* self, const char* path);
+void _spAtlasPage_disposeTexture (spAtlasPage* self);
+char* _spUtil_readFile (const char* path, int* length);
+
+#ifdef SPINE_SHORT_NAMES
+#define _AtlasPage_createTexture(...) _spAtlasPage_createTexture(__VA_ARGS__)
+#define _AtlasPage_disposeTexture(...) _spAtlasPage_disposeTexture(__VA_ARGS__)
+#define _Util_readFile(...) _spUtil_readFile(__VA_ARGS__)
+#endif
+
+/*
+ * Internal API available for extension:
+ */
+
+void* _spMalloc (size_t size, const char* file, int line);
+void* _spCalloc (size_t num, size_t size, const char* file, int line);
+void* _spRealloc(void* ptr, size_t size);
+void _spFree (void* ptr);
+float _spRandom ();
+
+void _spSetMalloc (void* (*_malloc) (size_t size));
+void _spSetDebugMalloc (void* (*_malloc) (size_t size, const char* file, int line));
+void _spSetRealloc(void* (*_realloc) (void* ptr, size_t size));
+void _spSetFree (void (*_free) (void* ptr));
+void _spSetRandom(float (*_random) ());
+
+char* _spReadFile (const char* path, int* length);
+
+
+/*
+ * Math utilities
+ */
+float _spMath_random(float min, float max);
+float _spMath_randomTriangular(float min, float max);
+float _spMath_randomTriangularWith(float min, float max, float mode);
+float _spMath_interpolate(float (*apply) (float a), float start, float end, float a);
+float _spMath_pow2_apply(float a);
+float _spMath_pow2out_apply(float a);
+
+/**/
+
+typedef union _spEventQueueItem {
+	int type;
+	spTrackEntry* entry;
+	spEvent* event;
+} _spEventQueueItem;
+
+typedef struct _spAnimationState _spAnimationState;
+
+typedef struct _spEventQueue {
+	_spAnimationState* state;
+	_spEventQueueItem* objects;
+	int objectsCount;
+	int objectsCapacity;
+	int /*boolean*/ drainDisabled;
+
+#ifdef __cplusplus
+	_spEventQueue() :
+		state(0),
+		objects(0),
+		objectsCount(0),
+		objectsCapacity(0),
+		drainDisabled(0) {
+	}
+#endif
+} _spEventQueue;
+
+struct _spAnimationState {
+	spAnimationState super;
+
+	int eventsCount;
+	spEvent** events;
+
+	_spEventQueue* queue;
+
+	int* propertyIDs;
+	int propertyIDsCount;
+	int propertyIDsCapacity;
+
+	int /*boolean*/ animationsChanged;
+
+#ifdef __cplusplus
+	_spAnimationState() :
+		super(),
+		eventsCount(0),
+		events(0),
+		queue(0),
+		propertyIDs(0),
+		propertyIDsCount(0),
+		propertyIDsCapacity(0),
+		animationsChanged(0) {
+	}
+#endif
+};
+
+
+/**/
+
+/* configureAttachment and disposeAttachment may be 0. */
+void _spAttachmentLoader_init (spAttachmentLoader* self,
+	void (*dispose) (spAttachmentLoader* self),
+	spAttachment* (*createAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name,
+		const char* path),
+	void (*configureAttachment) (spAttachmentLoader* self, spAttachment*),
+	void (*disposeAttachment) (spAttachmentLoader* self, spAttachment*)
+);
+void _spAttachmentLoader_deinit (spAttachmentLoader* self);
+/* Can only be called from createAttachment. */
+void _spAttachmentLoader_setError (spAttachmentLoader* self, const char* error1, const char* error2);
+void _spAttachmentLoader_setUnknownTypeError (spAttachmentLoader* self, spAttachmentType type);
+
+#ifdef SPINE_SHORT_NAMES
+#define _AttachmentLoader_init(...) _spAttachmentLoader_init(__VA_ARGS__)
+#define _AttachmentLoader_deinit(...) _spAttachmentLoader_deinit(__VA_ARGS__)
+#define _AttachmentLoader_setError(...) _spAttachmentLoader_setError(__VA_ARGS__)
+#define _AttachmentLoader_setUnknownTypeError(...) _spAttachmentLoader_setUnknownTypeError(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spAttachment_init (spAttachment* self, const char* name, spAttachmentType type,
+void (*dispose) (spAttachment* self));
+void _spAttachment_deinit (spAttachment* self);
+void _spVertexAttachment_init (spVertexAttachment* self);
+void _spVertexAttachment_deinit (spVertexAttachment* self);
+
+#ifdef SPINE_SHORT_NAMES
+#define _Attachment_init(...) _spAttachment_init(__VA_ARGS__)
+#define _Attachment_deinit(...) _spAttachment_deinit(__VA_ARGS__)
+#define _VertexAttachment_deinit(...) _spVertexAttachment_deinit(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spTimeline_init (spTimeline* self, spTimelineType type,
+	void (*dispose) (spTimeline* self),
+	void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction),
+	int (*getPropertyId) (const spTimeline* self));
+void _spTimeline_deinit (spTimeline* self);
+
+#ifdef SPINE_SHORT_NAMES
+#define _Timeline_init(...) _spTimeline_init(__VA_ARGS__)
+#define _Timeline_deinit(...) _spTimeline_deinit(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spCurveTimeline_init (spCurveTimeline* self, spTimelineType type, int framesCount,
+	void (*dispose) (spTimeline* self),
+	void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction),
+	int (*getPropertyId) (const spTimeline* self));
+void _spCurveTimeline_deinit (spCurveTimeline* self);
+int _spCurveTimeline_binarySearch (float *values, int valuesLength, float target, int step);
+
+#ifdef SPINE_SHORT_NAMES
+#define _CurveTimeline_init(...) _spCurveTimeline_init(__VA_ARGS__)
+#define _CurveTimeline_deinit(...) _spCurveTimeline_deinit(__VA_ARGS__)
+#define _CurveTimeline_binarySearch(...) _spCurveTimeline_binarySearch(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EXTENSION_H_ */

+ 63 - 0
spine-cpp/spine-cpp/include/spine/spine.h

@@ -0,0 +1,63 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#ifndef SPINE_SPINE_H_
+#define SPINE_SPINE_H_
+
+#include <spine/dll.h>
+#include <spine/Animation.h>
+#include <spine/AnimationState.h>
+#include <spine/AnimationStateData.h>
+#include <spine/Atlas.h>
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/Attachment.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/Bone.h>
+#include <spine/BoneData.h>
+#include <spine/RegionAttachment.h>
+#include <spine/VertexAttachment.h>
+#include <spine/MeshAttachment.h>
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/ClippingAttachment.h>
+#include <spine/PointAttachment.h>
+#include <spine/Skeleton.h>
+#include <spine/SkeletonBounds.h>
+#include <spine/SkeletonData.h>
+#include <spine/SkeletonBinary.h>
+#include <spine/SkeletonJson.h>
+#include <spine/Skin.h>
+#include <spine/Slot.h>
+#include <spine/SlotData.h>
+#include <spine/SkeletonClipping.h>
+#include <spine/Event.h>
+#include <spine/EventData.h>
+#include <spine/VertexEffect.h>
+
+#endif /* SPINE_SPINE_H_ */

+ 1529 - 0
spine-cpp/spine-cpp/src/spine/Animation.c

@@ -0,0 +1,1529 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Animation.h>
+#include <spine/IkConstraint.h>
+#include <limits.h>
+#include <spine/extension.h>
+
+spAnimation* spAnimation_create (const char* name, int timelinesCount) {
+	spAnimation* self = NEW(spAnimation);
+	MALLOC_STR(self->name, name);
+	self->timelinesCount = timelinesCount;
+	self->timelines = MALLOC(spTimeline*, timelinesCount);
+	return self;
+}
+
+void spAnimation_dispose (spAnimation* self) {
+	int i;
+	for (i = 0; i < self->timelinesCount; ++i)
+		spTimeline_dispose(self->timelines[i]);
+	FREE(self->timelines);
+	FREE(self->name);
+	FREE(self);
+}
+
+void spAnimation_apply (const spAnimation* self, spSkeleton* skeleton, float lastTime, float time, int loop, spEvent** events,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int i, n = self->timelinesCount;
+
+	if (loop && self->duration) {
+		time = FMOD(time, self->duration);
+		if (lastTime > 0) lastTime = FMOD(lastTime, self->duration);
+	}
+
+	for (i = 0; i < n; ++i)
+		spTimeline_apply(self->timelines[i], skeleton, lastTime, time, events, eventsCount, alpha, pose, direction);
+}
+
+/**/
+
+typedef struct _spTimelineVtable {
+	void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+			int* eventsCount, float alpha, spMixPose pose, spMixDirection direction);
+	int (*getPropertyId) (const spTimeline* self);
+	void (*dispose) (spTimeline* self);
+} _spTimelineVtable;
+
+void _spTimeline_init (spTimeline* self, spTimelineType type, /**/
+					   void (*dispose) (spTimeline* self), /**/
+					   void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction),
+					   int (*getPropertyId) (const spTimeline* self)) {
+	CONST_CAST(spTimelineType, self->type) = type;
+	CONST_CAST(_spTimelineVtable*, self->vtable) = NEW(_spTimelineVtable);
+	VTABLE(spTimeline, self)->dispose = dispose;
+	VTABLE(spTimeline, self)->apply = apply;
+	VTABLE(spTimeline, self)->getPropertyId = getPropertyId;
+}
+
+void _spTimeline_deinit (spTimeline* self) {
+	FREE(self->vtable);
+}
+
+void spTimeline_dispose (spTimeline* self) {
+	VTABLE(spTimeline, self)->dispose(self);
+}
+
+void spTimeline_apply (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	VTABLE(spTimeline, self)->apply(self, skeleton, lastTime, time, firedEvents, eventsCount, alpha, pose, direction);
+}
+
+int spTimeline_getPropertyId (const spTimeline* self) {
+	return VTABLE(spTimeline, self)->getPropertyId(self);
+}
+
+/**/
+
+static const float CURVE_LINEAR = 0, CURVE_STEPPED = 1, CURVE_BEZIER = 2;
+static const int BEZIER_SIZE = 10 * 2 - 1;
+
+void _spCurveTimeline_init (spCurveTimeline* self, spTimelineType type, int framesCount, /**/
+		void (*dispose) (spTimeline* self), /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction),
+		int (*getPropertyId)(const spTimeline* self)) {
+	_spTimeline_init(SUPER(self), type, dispose, apply, getPropertyId);
+	self->curves = CALLOC(float, (framesCount - 1) * BEZIER_SIZE);
+}
+
+void _spCurveTimeline_deinit (spCurveTimeline* self) {
+	_spTimeline_deinit(SUPER(self));
+	FREE(self->curves);
+}
+
+void spCurveTimeline_setLinear (spCurveTimeline* self, int frameIndex) {
+	self->curves[frameIndex * BEZIER_SIZE] = CURVE_LINEAR;
+}
+
+void spCurveTimeline_setStepped (spCurveTimeline* self, int frameIndex) {
+	self->curves[frameIndex * BEZIER_SIZE] = CURVE_STEPPED;
+}
+
+void spCurveTimeline_setCurve (spCurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2) {
+	float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
+	float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
+	float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
+	float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
+	float x = dfx, y = dfy;
+
+	int i = frameIndex * BEZIER_SIZE, n = i + BEZIER_SIZE - 1;
+	self->curves[i++] = CURVE_BEZIER;
+
+	for (; i < n; i += 2) {
+		self->curves[i] = x;
+		self->curves[i + 1] = y;
+		dfx += ddfx;
+		dfy += ddfy;
+		ddfx += dddfx;
+		ddfy += dddfy;
+		x += dfx;
+		y += dfy;
+	}
+}
+
+float spCurveTimeline_getCurvePercent (const spCurveTimeline* self, int frameIndex, float percent) {
+	float x, y;
+	int i = frameIndex * BEZIER_SIZE, start, n;
+	float type = self->curves[i];
+	percent = CLAMP(percent, 0, 1);
+	if (type == CURVE_LINEAR) return percent;
+	if (type == CURVE_STEPPED) return 0;
+	i++;
+	x = 0;
+	for (start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) {
+		x = self->curves[i];
+		if (x >= percent) {
+			float prevX, prevY;
+			if (i == start) {
+				prevX = 0;
+				prevY = 0;
+			} else {
+				prevX = self->curves[i - 2];
+				prevY = self->curves[i - 1];
+			}
+			return prevY + (self->curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
+		}
+	}
+	y = self->curves[i - 1];
+	return y + (1 - y) * (percent - x) / (1 - x); /* Last point is 1,1. */
+}
+
+/* @param target After the first and before the last entry. */
+static int binarySearch (float *values, int valuesLength, float target, int step) {
+	int low = 0, current;
+	int high = valuesLength / step - 2;
+	if (high == 0) return step;
+	current = high >> 1;
+	while (1) {
+		if (values[(current + 1) * step] <= target)
+			low = current + 1;
+		else
+			high = current;
+		if (low == high) return (low + 1) * step;
+		current = (low + high) >> 1;
+	}
+	return 0;
+}
+
+int _spCurveTimeline_binarySearch (float *values, int valuesLength, float target, int step) {
+	return binarySearch(values, valuesLength, target, step);
+}
+
+/* @param target After the first and before the last entry. */
+static int binarySearch1 (float *values, int valuesLength, float target) {
+	int low = 0, current;
+	int high = valuesLength - 2;
+	if (high == 0) return 1;
+	current = high >> 1;
+	while (1) {
+		if (values[(current + 1)] <= target)
+			low = current + 1;
+		else
+			high = current;
+		if (low == high) return low + 1;
+		current = (low + high) >> 1;
+	}
+	return 0;
+}
+
+/**/
+
+void _spBaseTimeline_dispose (spTimeline* timeline) {
+	struct spBaseTimeline* self = SUB_CAST(struct spBaseTimeline, timeline);
+	_spCurveTimeline_deinit(SUPER(self));
+	FREE(self->frames);
+	FREE(self);
+}
+
+/* Many timelines have structure identical to struct spBaseTimeline and extend spCurveTimeline. **/
+struct spBaseTimeline* _spBaseTimeline_create (int framesCount, spTimelineType type, int frameSize, /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventsCount, float alpha, spMixPose pose, spMixDirection direction),
+		int (*getPropertyId) (const spTimeline* self)) {
+	struct spBaseTimeline* self = NEW(struct spBaseTimeline);
+	_spCurveTimeline_init(SUPER(self), type, framesCount, _spBaseTimeline_dispose, apply, getPropertyId);
+
+	CONST_CAST(int, self->framesCount) = framesCount * frameSize;
+	CONST_CAST(float*, self->frames) = CALLOC(float, self->framesCount);
+
+	return self;
+}
+
+/**/
+
+void _spRotateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spBone *bone;
+	int frame;
+	float prevRotation, frameTime, percent, r;
+
+	spRotateTimeline* self = SUB_CAST(spRotateTimeline, timeline);
+
+	bone = skeleton->bones[self->boneIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				bone->rotation = bone->data->rotation;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				r = bone->data->rotation - bone->rotation;
+				r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+				bone->rotation += r * alpha;
+		}
+		return;
+	}
+
+	if (time >= self->frames[self->framesCount - ROTATE_ENTRIES]) { /* Time is after last frame. */
+		if (pose == SP_MIX_POSE_SETUP)
+			bone->rotation = bone->data->rotation + self->frames[self->framesCount + ROTATE_PREV_ROTATION] * alpha;
+		else {
+			r = bone->data->rotation + self->frames[self->framesCount + ROTATE_PREV_ROTATION] - bone->rotation;
+			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; /* Wrap within -180 and 180. */
+			bone->rotation += r * alpha;
+		}
+		return;
+	}
+
+	/* Interpolate between the previous frame and the current frame. */
+	frame = binarySearch(self->frames, self->framesCount, time, ROTATE_ENTRIES);
+	prevRotation = self->frames[frame + ROTATE_PREV_ROTATION];
+	frameTime = self->frames[frame];
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), (frame >> 1) - 1, 1 - (time - frameTime) / (self->frames[frame + ROTATE_PREV_TIME] - frameTime));
+
+	r = self->frames[frame + ROTATE_ROTATION] - prevRotation;
+	r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+	r = prevRotation + r * percent;
+	if (pose == SP_MIX_POSE_SETUP) {
+		r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+		bone->rotation = bone->data->rotation + r * alpha;
+	} else {
+		r = bone->data->rotation + r - bone->rotation;
+		r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+		bone->rotation += r * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spRotateTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_ROTATE << 25) + SUB_CAST(spRotateTimeline, timeline)->boneIndex;
+}
+
+spRotateTimeline* spRotateTimeline_create (int framesCount) {
+	return _spBaseTimeline_create(framesCount, SP_TIMELINE_ROTATE, ROTATE_ENTRIES, _spRotateTimeline_apply, _spRotateTimeline_getPropertyId);
+}
+
+void spRotateTimeline_setFrame (spRotateTimeline* self, int frameIndex, float time, float degrees) {
+	frameIndex <<= 1;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + ROTATE_ROTATION] = degrees;
+}
+
+/**/
+
+static const int TRANSLATE_PREV_TIME = -3, TRANSLATE_PREV_X = -2, TRANSLATE_PREV_Y = -1;
+static const int TRANSLATE_X = 1, TRANSLATE_Y = 2;
+
+void _spTranslateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spBone *bone;
+	int frame;
+	float frameTime, percent;
+	float x, y;
+	float *frames;
+	int framesCount;
+
+	spTranslateTimeline* self = SUB_CAST(spTranslateTimeline, timeline);
+
+	bone = skeleton->bones[self->boneIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				bone->x = bone->data->x;
+				bone->y = bone->data->y;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				bone->x += (bone->data->x - bone->x) * alpha;
+				bone->y += (bone->data->y - bone->y) * alpha;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - TRANSLATE_ENTRIES]) { /* Time is after last frame. */
+		x = frames[framesCount + TRANSLATE_PREV_X];
+		y = frames[framesCount + TRANSLATE_PREV_Y];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, TRANSLATE_ENTRIES);
+		x = frames[frame + TRANSLATE_PREV_X];
+		y = frames[frame + TRANSLATE_PREV_Y];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TRANSLATE_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + TRANSLATE_PREV_TIME] - frameTime));
+
+		x += (frames[frame + TRANSLATE_X] - x) * percent;
+		y += (frames[frame + TRANSLATE_Y] - y) * percent;
+	}
+	if (pose == SP_MIX_POSE_SETUP) {
+		bone->x = bone->data->x + x * alpha;
+		bone->y = bone->data->y + y * alpha;
+	} else {
+		bone->x += (bone->data->x + x - bone->x) * alpha;
+		bone->y += (bone->data->y + y - bone->y) * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spTranslateTimeline_getPropertyId (const spTimeline* self) {
+	return (SP_TIMELINE_TRANSLATE << 24) + SUB_CAST(spTranslateTimeline, self)->boneIndex;
+}
+
+spTranslateTimeline* spTranslateTimeline_create (int framesCount) {
+	return _spBaseTimeline_create(framesCount, SP_TIMELINE_TRANSLATE, TRANSLATE_ENTRIES, _spTranslateTimeline_apply, _spTranslateTimeline_getPropertyId);
+}
+
+void spTranslateTimeline_setFrame (spTranslateTimeline* self, int frameIndex, float time, float x, float y) {
+	frameIndex *= TRANSLATE_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + TRANSLATE_X] = x;
+	self->frames[frameIndex + TRANSLATE_Y] = y;
+}
+
+/**/
+
+void _spScaleTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spBone *bone;
+	int frame;
+	float frameTime, percent, x, y;
+	float *frames;
+	int framesCount;
+
+	spScaleTimeline* self = SUB_CAST(spScaleTimeline, timeline);
+
+	bone = skeleton->bones[self->boneIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				bone->scaleX = bone->data->scaleX;
+				bone->scaleY = bone->data->scaleY;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				bone->scaleX += (bone->data->scaleX - bone->scaleX) * alpha;
+				bone->scaleY += (bone->data->scaleY - bone->scaleY) * alpha;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - TRANSLATE_ENTRIES]) { /* Time is after last frame. */
+		x = frames[framesCount + TRANSLATE_PREV_X] * bone->data->scaleX;
+		y = frames[framesCount + TRANSLATE_PREV_Y] * bone->data->scaleY;
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, TRANSLATE_ENTRIES);
+		x = frames[frame + TRANSLATE_PREV_X];
+		y = frames[frame + TRANSLATE_PREV_Y];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TRANSLATE_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + TRANSLATE_PREV_TIME] - frameTime));
+
+		x = (x + (frames[frame + TRANSLATE_X] - x) * percent) * bone->data->scaleX;
+		y = (y + (frames[frame + TRANSLATE_Y] - y) * percent) * bone->data->scaleY;
+	}
+	if (alpha == 1) {
+		bone->scaleX = x;
+		bone->scaleY = y;
+	} else {
+		float bx, by;
+		if (pose == SP_MIX_POSE_SETUP) {
+			bx = bone->data->scaleX;
+			by = bone->data->scaleY;
+		} else {
+			bx = bone->scaleX;
+			by = bone->scaleY;
+		}
+		/* Mixing out uses sign of setup or current pose, else use sign of key. */
+		if (direction == SP_MIX_DIRECTION_OUT) {
+			x = ABS(x) * SIGNUM(bx);
+			y = ABS(y) * SIGNUM(by);
+		} else {
+			bx = ABS(bx) * SIGNUM(x);
+			by = ABS(by) * SIGNUM(y);
+		}
+		bone->scaleX = bx + (x - bx) * alpha;
+		bone->scaleY = by + (y - by) * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spScaleTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_SCALE << 24) + SUB_CAST(spScaleTimeline, timeline)->boneIndex;
+}
+
+spScaleTimeline* spScaleTimeline_create (int framesCount) {
+	return _spBaseTimeline_create(framesCount, SP_TIMELINE_SCALE, TRANSLATE_ENTRIES, _spScaleTimeline_apply, _spScaleTimeline_getPropertyId);
+}
+
+void spScaleTimeline_setFrame (spScaleTimeline* self, int frameIndex, float time, float x, float y) {
+	spTranslateTimeline_setFrame(self, frameIndex, time, x, y);
+}
+
+/**/
+
+void _spShearTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+							 int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spBone *bone;
+	int frame;
+	float frameTime, percent, x, y;
+	float *frames;
+	int framesCount;
+
+	spShearTimeline* self = SUB_CAST(spShearTimeline, timeline);
+
+	bone = skeleton->bones[self->boneIndex];
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				bone->shearX = bone->data->shearX;
+				bone->shearY = bone->data->shearY;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				bone->shearX += (bone->data->shearX - bone->shearX) * alpha;
+				bone->shearY += (bone->data->shearY - bone->shearY) * alpha;
+		}
+		return;
+	}
+
+	if (time >= frames[framesCount - TRANSLATE_ENTRIES]) { /* Time is after last frame. */
+		x = frames[framesCount + TRANSLATE_PREV_X];
+		y = frames[framesCount + TRANSLATE_PREV_Y];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, TRANSLATE_ENTRIES);
+		x = frames[frame + TRANSLATE_PREV_X];
+		y = frames[frame + TRANSLATE_PREV_Y];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TRANSLATE_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + TRANSLATE_PREV_TIME] - frameTime));
+
+		x = x + (frames[frame + TRANSLATE_X] - x) * percent;
+		y = y + (frames[frame + TRANSLATE_Y] - y) * percent;
+	}
+	if (pose == SP_MIX_POSE_SETUP) {
+		bone->shearX = bone->data->shearX + x * alpha;
+		bone->shearY = bone->data->shearY + y * alpha;
+	} else {
+		bone->shearX += (bone->data->shearX + x - bone->shearX) * alpha;
+		bone->shearY += (bone->data->shearY + y - bone->shearY) * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spShearTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_SHEAR << 24) + SUB_CAST(spShearTimeline, timeline)->boneIndex;
+}
+
+spShearTimeline* spShearTimeline_create (int framesCount) {
+	return (spShearTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_SHEAR, 3, _spShearTimeline_apply, _spShearTimeline_getPropertyId);
+}
+
+void spShearTimeline_setFrame (spShearTimeline* self, int frameIndex, float time, float x, float y) {
+	spTranslateTimeline_setFrame(self, frameIndex, time, x, y);
+}
+
+/**/
+
+static const int COLOR_PREV_TIME = -5, COLOR_PREV_R = -4, COLOR_PREV_G = -3, COLOR_PREV_B = -2, COLOR_PREV_A = -1;
+static const int COLOR_R = 1, COLOR_G = 2, COLOR_B = 3, COLOR_A = 4;
+
+void _spColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spSlot *slot;
+	int frame;
+	float percent, frameTime;
+	float r, g, b, a;
+	spColor* color;
+	spColor* setup;
+	spColorTimeline* self = (spColorTimeline*)timeline;
+	slot = skeleton->slots[self->slotIndex];
+
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				spColor_setFromColor(&slot->color, &slot->data->color);
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				color = &slot->color;
+				setup = &slot->data->color;
+				spColor_addFloats(color, (setup->r - color->r) * alpha, (setup->g - color->g) * alpha, (setup->b - color->b) * alpha,
+						  (setup->a - color->a) * alpha);
+		}
+		return;
+	}
+
+	if (time >= self->frames[self->framesCount - 5]) { /* Time is after last frame */
+		int i = self->framesCount;
+		r = self->frames[i + COLOR_PREV_R];
+		g = self->frames[i + COLOR_PREV_G];
+		b = self->frames[i + COLOR_PREV_B];
+		a = self->frames[i + COLOR_PREV_A];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(self->frames, self->framesCount, time, COLOR_ENTRIES);
+
+		r = self->frames[frame + COLOR_PREV_R];
+		g = self->frames[frame + COLOR_PREV_G];
+		b = self->frames[frame + COLOR_PREV_B];
+		a = self->frames[frame + COLOR_PREV_A];
+
+		frameTime = self->frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / COLOR_ENTRIES - 1,
+			1 - (time - frameTime) / (self->frames[frame + COLOR_PREV_TIME] - frameTime));
+
+		r += (self->frames[frame + COLOR_R] - r) * percent;
+		g += (self->frames[frame + COLOR_G] - g) * percent;
+		b += (self->frames[frame + COLOR_B] - b) * percent;
+		a += (self->frames[frame + COLOR_A] - a) * percent;
+	}
+	if (alpha == 1) {
+		spColor_setFromFloats(&slot->color, r, g, b, a);
+	} else {
+		if (pose == SP_MIX_POSE_SETUP) {
+			spColor_setFromColor(&slot->color, &slot->data->color);
+		}
+		spColor_addFloats(&slot->color, (r - slot->color.r) * alpha, (g - slot->color.g) * alpha, (b - slot->color.b) * alpha, (a - slot->color.a) * alpha);
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spColorTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_COLOR << 24) + SUB_CAST(spColorTimeline, timeline)->slotIndex;
+}
+
+spColorTimeline* spColorTimeline_create (int framesCount) {
+	return (spColorTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_COLOR, 5, _spColorTimeline_apply, _spColorTimeline_getPropertyId);
+}
+
+void spColorTimeline_setFrame (spColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a) {
+	frameIndex *= COLOR_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + COLOR_R] = r;
+	self->frames[frameIndex + COLOR_G] = g;
+	self->frames[frameIndex + COLOR_B] = b;
+	self->frames[frameIndex + COLOR_A] = a;
+}
+
+/**/
+
+static const int TWOCOLOR_PREV_TIME = -8, TWOCOLOR_PREV_R = -7, TWOCOLOR_PREV_G = -6, TWOCOLOR_PREV_B = -5, TWOCOLOR_PREV_A = -4;
+static const int TWOCOLOR_PREV_R2 = -3, TWOCOLOR_PREV_G2 = -2, TWOCOLOR_PREV_B2 = -1;
+static const int TWOCOLOR_R = 1, TWOCOLOR_G = 2, TWOCOLOR_B = 3, TWOCOLOR_A = 4, TWOCOLOR_R2 = 5, TWOCOLOR_G2 = 6, TWOCOLOR_B2 = 7;
+
+void _spTwoColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+							 int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spSlot *slot;
+	int frame;
+	float percent, frameTime;
+	float r, g, b, a, r2, g2, b2;
+	spColor* light;
+	spColor* dark;
+	spColor* setupLight;
+	spColor* setupDark;
+	spColorTimeline* self = (spColorTimeline*)timeline;
+	slot = skeleton->slots[self->slotIndex];
+
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				spColor_setFromColor(&slot->color, &slot->data->color);
+				spColor_setFromColor(slot->darkColor, slot->data->darkColor);
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				light = &slot->color;
+				dark = slot->darkColor;
+				setupLight = &slot->data->color;
+				setupDark = slot->data->darkColor;
+				spColor_addFloats(light, (setupLight->r - light->r) * alpha, (setupLight->g - light->g) * alpha, (setupLight->b - light->b) * alpha,
+						  (setupLight->a - light->a) * alpha);
+				spColor_addFloats(dark, (setupDark->r - dark->r) * alpha, (setupDark->g - dark->g) * alpha, (setupDark->b - dark->b) * alpha, 0);
+		}
+		return;
+	}
+
+	if (time >= self->frames[self->framesCount - TWOCOLOR_ENTRIES]) { /* Time is after last frame */
+		int i = self->framesCount;
+		r = self->frames[i + TWOCOLOR_PREV_R];
+		g = self->frames[i + TWOCOLOR_PREV_G];
+		b = self->frames[i + TWOCOLOR_PREV_B];
+		a = self->frames[i + TWOCOLOR_PREV_A];
+		r2 = self->frames[i + TWOCOLOR_PREV_R2];
+		g2 = self->frames[i + TWOCOLOR_PREV_G2];
+		b2 = self->frames[i + TWOCOLOR_PREV_B2];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(self->frames, self->framesCount, time, TWOCOLOR_ENTRIES);
+
+		r = self->frames[frame + TWOCOLOR_PREV_R];
+		g = self->frames[frame + TWOCOLOR_PREV_G];
+		b = self->frames[frame + TWOCOLOR_PREV_B];
+		a = self->frames[frame + TWOCOLOR_PREV_A];
+		r2 = self->frames[frame + TWOCOLOR_PREV_R2];
+		g2 = self->frames[frame + TWOCOLOR_PREV_G2];
+		b2 = self->frames[frame + TWOCOLOR_PREV_B2];
+
+		frameTime = self->frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TWOCOLOR_ENTRIES - 1,
+												  1 - (time - frameTime) / (self->frames[frame + TWOCOLOR_PREV_TIME] - frameTime));
+
+		r += (self->frames[frame + TWOCOLOR_R] - r) * percent;
+		g += (self->frames[frame + TWOCOLOR_G] - g) * percent;
+		b += (self->frames[frame + TWOCOLOR_B] - b) * percent;
+		a += (self->frames[frame + TWOCOLOR_A] - a) * percent;
+		r2 += (self->frames[frame + TWOCOLOR_R2] - r2) * percent;
+		g2 += (self->frames[frame + TWOCOLOR_G2] - g2) * percent;
+		b2 += (self->frames[frame + TWOCOLOR_B2] - b2) * percent;
+	}
+	if (alpha == 1) {
+		spColor_setFromFloats(&slot->color, r, g, b, a);
+		spColor_setFromFloats(slot->darkColor, r2, g2, b2, 1);
+	} else {
+		light = &slot->color;
+		dark = slot->darkColor;
+		if (pose == SP_MIX_POSE_SETUP) {
+			spColor_setFromColor(light, &slot->data->color);
+			spColor_setFromColor(dark, slot->data->darkColor);
+		}
+		spColor_addFloats(light, (r - light->r) * alpha, (g - light->g) * alpha, (b - light->b) * alpha, (a - light->a) * alpha);
+		spColor_addFloats(dark, (r2 - dark->r) * alpha, (g2 - dark->g) * alpha, (b2 - dark->b) * alpha, 0);
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spTwoColorTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_TWOCOLOR << 24) + SUB_CAST(spTwoColorTimeline, timeline)->slotIndex;
+}
+
+spTwoColorTimeline* spTwoColorTimeline_create (int framesCount) {
+	return (spTwoColorTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_TWOCOLOR, TWOCOLOR_ENTRIES, _spTwoColorTimeline_apply, _spTwoColorTimeline_getPropertyId);
+}
+
+void spTwoColorTimeline_setFrame (spTwoColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
+	frameIndex *= TWOCOLOR_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + TWOCOLOR_R] = r;
+	self->frames[frameIndex + TWOCOLOR_G] = g;
+	self->frames[frameIndex + TWOCOLOR_B] = b;
+	self->frames[frameIndex + TWOCOLOR_A] = a;
+	self->frames[frameIndex + TWOCOLOR_R2] = r2;
+	self->frames[frameIndex + TWOCOLOR_G2] = g2;
+	self->frames[frameIndex + TWOCOLOR_B2] = b2;
+}
+
+/**/
+
+void _spAttachmentTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	const char* attachmentName;
+	spAttachmentTimeline* self = (spAttachmentTimeline*)timeline;
+	int frameIndex;
+	spSlot* slot = skeleton->slots[self->slotIndex];
+
+	if (direction == SP_MIX_DIRECTION_OUT && pose == SP_MIX_POSE_SETUP) {
+		const char* attachmentName = slot->data->attachmentName;
+        spSlot_setAttachment(slot, attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);
+		return;
+	}
+
+	if (time < self->frames[0]) {
+		if (pose == SP_MIX_POSE_SETUP) {
+			attachmentName = slot->data->attachmentName;
+			spSlot_setAttachment(skeleton->slots[self->slotIndex],
+								 attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);
+		}
+		return;
+	}
+
+	if (time >= self->frames[self->framesCount - 1])
+		frameIndex = self->framesCount - 1;
+	else
+		frameIndex = binarySearch1(self->frames, self->framesCount, time) - 1;
+
+	attachmentName = self->attachmentNames[frameIndex];
+	spSlot_setAttachment(skeleton->slots[self->slotIndex],
+			attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+	UNUSED(alpha);
+}
+
+int _spAttachmentTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_ATTACHMENT << 24) + SUB_CAST(spAttachmentTimeline, timeline)->slotIndex;
+}
+
+void _spAttachmentTimeline_dispose (spTimeline* timeline) {
+	spAttachmentTimeline* self = SUB_CAST(spAttachmentTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesCount; ++i)
+		FREE(self->attachmentNames[i]);
+	FREE(self->attachmentNames);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spAttachmentTimeline* spAttachmentTimeline_create (int framesCount) {
+	spAttachmentTimeline* self = NEW(spAttachmentTimeline);
+	_spTimeline_init(SUPER(self), SP_TIMELINE_ATTACHMENT, _spAttachmentTimeline_dispose, _spAttachmentTimeline_apply, _spAttachmentTimeline_getPropertyId);
+
+	CONST_CAST(int, self->framesCount) = framesCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, framesCount);
+	CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, framesCount);
+
+	return self;
+}
+
+void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->attachmentNames[frameIndex]);
+	if (attachmentName)
+		MALLOC_STR(self->attachmentNames[frameIndex], attachmentName);
+	else
+		self->attachmentNames[frameIndex] = 0;
+}
+
+/**/
+
+void _spDeformTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+							  int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame, i, vertexCount;
+	float percent, frameTime;
+	const float* prevVertices;
+	const float* nextVertices;
+	float* frames;
+	int framesCount;
+	const float** frameVertices;
+	float* vertices;
+	spDeformTimeline* self = (spDeformTimeline*)timeline;
+
+	spSlot *slot = skeleton->slots[self->slotIndex];
+
+	if (slot->attachment != self->attachment) {
+		if (!slot->attachment) return;
+		switch (slot->attachment->type) {
+			case SP_ATTACHMENT_MESH: {
+				spMeshAttachment* mesh = SUB_CAST(spMeshAttachment, slot->attachment);
+				if (!mesh->inheritDeform || mesh->parentMesh != (void*)self->attachment) return;
+				break;
+			}
+			default:
+				return;
+		}
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	vertexCount = self->frameVerticesCount;
+	if (slot->attachmentVerticesCount < vertexCount) {
+		if (slot->attachmentVerticesCapacity < vertexCount) {
+			FREE(slot->attachmentVertices);
+			slot->attachmentVertices = MALLOC(float, vertexCount);
+			slot->attachmentVerticesCapacity = vertexCount;
+		}
+	}
+	slot->attachmentVerticesCount = vertexCount;
+
+	frameVertices = self->frameVertices;
+	vertices = slot->attachmentVertices;
+
+	if (time < frames[0]) { /* Time is before first frame. */
+		spVertexAttachment* vertexAttachment = SUB_CAST(spVertexAttachment, slot->attachment);
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				if (!vertexAttachment->bones) {
+					memcpy(vertices, vertexAttachment->vertices, vertexCount * sizeof(float));
+				} else {
+					for (i = 0; i < vertexCount; i++) vertices[i] = 0;
+				}
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				if (alpha == 1) break;
+				if (!vertexAttachment->bones) {
+					float* setupVertices = vertexAttachment->vertices;
+					for (i = 0; i < vertexCount; i++) {
+						vertices[i] += (setupVertices[i] - vertices[i]) * alpha;
+					}
+				} else {
+					alpha = 1 - alpha;
+					for (i = 0; i < vertexCount; i++) {
+						vertices[i] *= alpha;
+					}
+				}
+		}
+		return;
+	}
+
+	if (time >= frames[framesCount - 1]) { /* Time is after last frame. */
+		const float* lastVertices = self->frameVertices[framesCount - 1];
+		if (alpha == 1) {
+			/* Vertex positions or deform offsets, no alpha. */
+			memcpy(vertices, lastVertices, vertexCount * sizeof(float));
+		} else if (pose == SP_MIX_POSE_SETUP) {
+			spVertexAttachment* vertexAttachment = SUB_CAST(spVertexAttachment, slot->attachment);
+			if (!vertexAttachment->bones) {
+				/* Unweighted vertex positions, with alpha. */
+				float* setupVertices = vertexAttachment->vertices;
+				for (i = 0; i < vertexCount; i++) {
+					float setup = setupVertices[i];
+					vertices[i] = setup + (lastVertices[i] - setup) * alpha;
+				}
+			} else {
+				/* Weighted deform offsets, with alpha. */
+				for (i = 0; i < vertexCount; i++)
+					vertices[i] = lastVertices[i] * alpha;
+			}
+		} else {
+			/* Vertex positions or deform offsets, with alpha. */
+			for (i = 0; i < vertexCount; i++)
+				vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
+		}
+		return;
+	}
+
+	/* Interpolate between the previous frame and the current frame. */
+	frame = binarySearch(frames, framesCount, time, 1);
+	prevVertices = frameVertices[frame - 1];
+	nextVertices = frameVertices[frame];
+	frameTime = frames[frame];
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
+
+	if (alpha == 1) {
+		/* Vertex positions or deform offsets, no alpha. */
+		for (i = 0; i < vertexCount; i++) {
+			float prev = prevVertices[i];
+			vertices[i] = prev + (nextVertices[i] - prev) * percent;
+		}
+	} else if (pose == SP_MIX_POSE_SETUP) {
+		spVertexAttachment* vertexAttachment = SUB_CAST(spVertexAttachment, slot->attachment);
+		if (!vertexAttachment->bones) {
+			/* Unweighted vertex positions, with alpha. */
+			float* setupVertices = vertexAttachment->vertices;
+			for (i = 0; i < vertexCount; i++) {
+				float prev = prevVertices[i], setup = setupVertices[i];
+				vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
+			}
+		} else {
+			/* Weighted deform offsets, with alpha. */
+			for (i = 0; i < vertexCount; i++) {
+				float prev = prevVertices[i];
+				vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
+			}
+		}
+	} else {
+		/* Vertex positions or deform offsets, with alpha. */
+		for (i = 0; i < vertexCount; i++) {
+			float prev = prevVertices[i];
+			vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
+		}
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spDeformTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_DEFORM << 27) + SUB_CAST(spVertexAttachment, SUB_CAST(spDeformTimeline, timeline)->attachment)->id + SUB_CAST(spDeformTimeline, timeline)->slotIndex;
+}
+
+void _spDeformTimeline_dispose (spTimeline* timeline) {
+	spDeformTimeline* self = SUB_CAST(spDeformTimeline, timeline);
+	int i;
+
+	_spCurveTimeline_deinit(SUPER(self));
+
+	for (i = 0; i < self->framesCount; ++i)
+		FREE(self->frameVertices[i]);
+	FREE(self->frameVertices);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spDeformTimeline* spDeformTimeline_create (int framesCount, int frameVerticesCount) {
+	spDeformTimeline* self = NEW(spDeformTimeline);
+	_spCurveTimeline_init(SUPER(self), SP_TIMELINE_DEFORM, framesCount, _spDeformTimeline_dispose, _spDeformTimeline_apply, _spDeformTimeline_getPropertyId);
+	CONST_CAST(int, self->framesCount) = framesCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, self->framesCount);
+	CONST_CAST(float**, self->frameVertices) = CALLOC(float*, framesCount);
+	CONST_CAST(int, self->frameVerticesCount) = frameVerticesCount;
+	return self;
+}
+
+void spDeformTimeline_setFrame (spDeformTimeline* self, int frameIndex, float time, float* vertices) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->frameVertices[frameIndex]);
+	if (!vertices)
+		self->frameVertices[frameIndex] = 0;
+	else {
+		self->frameVertices[frameIndex] = MALLOC(float, self->frameVerticesCount);
+		memcpy(CONST_CAST(float*, self->frameVertices[frameIndex]), vertices, self->frameVerticesCount * sizeof(float));
+	}
+}
+
+
+/**/
+
+/** Fires events for frames > lastTime and <= time. */
+void _spEventTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	spEventTimeline* self = (spEventTimeline*)timeline;
+	int frame;
+	if (!firedEvents) return;
+
+	if (lastTime > time) { /* Fire events after last time for looped animations. */
+		_spEventTimeline_apply(timeline, skeleton, lastTime, (float)INT_MAX, firedEvents, eventsCount, alpha, pose, direction);
+		lastTime = -1;
+	} else if (lastTime >= self->frames[self->framesCount - 1]) /* Last time is after last frame. */
+	return;
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	if (lastTime < self->frames[0])
+		frame = 0;
+	else {
+		float frameTime;
+		frame = binarySearch1(self->frames, self->framesCount, lastTime);
+		frameTime = self->frames[frame];
+		while (frame > 0) { /* Fire multiple events with the same frame. */
+			if (self->frames[frame - 1] != frameTime) break;
+			frame--;
+		}
+	}
+	for (; frame < self->framesCount && time >= self->frames[frame]; ++frame) {
+		firedEvents[*eventsCount] = self->events[frame];
+		(*eventsCount)++;
+	}
+}
+
+int _spEventTimeline_getPropertyId (const spTimeline* timeline) {
+	return SP_TIMELINE_EVENT << 24;
+}
+
+void _spEventTimeline_dispose (spTimeline* timeline) {
+	spEventTimeline* self = SUB_CAST(spEventTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesCount; ++i)
+		spEvent_dispose(self->events[i]);
+	FREE(self->events);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spEventTimeline* spEventTimeline_create (int framesCount) {
+	spEventTimeline* self = NEW(spEventTimeline);
+	_spTimeline_init(SUPER(self), SP_TIMELINE_EVENT, _spEventTimeline_dispose, _spEventTimeline_apply, _spEventTimeline_getPropertyId);
+
+	CONST_CAST(int, self->framesCount) = framesCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, framesCount);
+	CONST_CAST(spEvent**, self->events) = CALLOC(spEvent*, framesCount);
+
+	return self;
+}
+
+void spEventTimeline_setFrame (spEventTimeline* self, int frameIndex, spEvent* event) {
+	self->frames[frameIndex] = event->time;
+
+	FREE(self->events[frameIndex]);
+	self->events[frameIndex] = event;
+}
+
+/**/
+
+void _spDrawOrderTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int i;
+	int frame;
+	const int* drawOrderToSetupIndex;
+	spDrawOrderTimeline* self = (spDrawOrderTimeline*)timeline;
+
+	if (direction == SP_MIX_DIRECTION_OUT && pose == SP_MIX_POSE_SETUP) {
+		memcpy(skeleton->drawOrder, skeleton->slots, self->slotsCount * sizeof(spSlot*));
+		return;
+	}
+
+	if (time < self->frames[0]) {
+		if (pose == SP_MIX_POSE_SETUP) memcpy(skeleton->drawOrder, skeleton->slots, self->slotsCount * sizeof(spSlot*));
+		return;
+	}
+
+	if (time >= self->frames[self->framesCount - 1]) /* Time is after last frame. */
+		frame = self->framesCount - 1;
+	else
+		frame = binarySearch1(self->frames, self->framesCount, time) - 1;
+
+	drawOrderToSetupIndex = self->drawOrders[frame];
+	if (!drawOrderToSetupIndex)
+		memcpy(skeleton->drawOrder, skeleton->slots, self->slotsCount * sizeof(spSlot*));
+	else {
+		for (i = 0; i < self->slotsCount; ++i)
+			skeleton->drawOrder[i] = skeleton->slots[drawOrderToSetupIndex[i]];
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+	UNUSED(alpha);
+}
+
+int _spDrawOrderTimeline_getPropertyId (const spTimeline* timeline) {
+	return SP_TIMELINE_DRAWORDER << 24;
+}
+
+void _spDrawOrderTimeline_dispose (spTimeline* timeline) {
+	spDrawOrderTimeline* self = SUB_CAST(spDrawOrderTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesCount; ++i)
+		FREE(self->drawOrders[i]);
+	FREE(self->drawOrders);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spDrawOrderTimeline* spDrawOrderTimeline_create (int framesCount, int slotsCount) {
+	spDrawOrderTimeline* self = NEW(spDrawOrderTimeline);
+	_spTimeline_init(SUPER(self), SP_TIMELINE_DRAWORDER, _spDrawOrderTimeline_dispose, _spDrawOrderTimeline_apply, _spDrawOrderTimeline_getPropertyId);
+
+	CONST_CAST(int, self->framesCount) = framesCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, framesCount);
+	CONST_CAST(int**, self->drawOrders) = CALLOC(int*, framesCount);
+	CONST_CAST(int, self->slotsCount) = slotsCount;
+
+	return self;
+}
+
+void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, float time, const int* drawOrder) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->drawOrders[frameIndex]);
+	if (!drawOrder)
+		self->drawOrders[frameIndex] = 0;
+	else {
+		self->drawOrders[frameIndex] = MALLOC(int, self->slotsCount);
+		memcpy(CONST_CAST(int*, self->drawOrders[frameIndex]), drawOrder, self->slotsCount * sizeof(int));
+	}
+}
+
+/**/
+
+static const int IKCONSTRAINT_PREV_TIME = -3, IKCONSTRAINT_PREV_MIX = -2, IKCONSTRAINT_PREV_BEND_DIRECTION = -1;
+static const int IKCONSTRAINT_MIX = 1, IKCONSTRAINT_BEND_DIRECTION = 2;
+
+void _spIkConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame;
+	float frameTime, percent, mix;
+	float *frames;
+	int framesCount;
+	spIkConstraint* constraint;
+	spIkConstraintTimeline* self = (spIkConstraintTimeline*)timeline;
+
+	constraint = skeleton->ikConstraints[self->ikConstraintIndex];
+
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				constraint->mix = constraint->data->mix;
+				constraint->bendDirection = constraint->data->bendDirection;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				constraint->mix += (constraint->data->mix - constraint->mix) * alpha;
+				constraint->bendDirection = constraint->data->bendDirection;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - IKCONSTRAINT_ENTRIES]) { /* Time is after last frame. */
+		if (pose == SP_MIX_POSE_SETUP) {
+			constraint->mix = constraint->data->mix + (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->data->mix) * alpha;
+			constraint->bendDirection = direction == SP_MIX_DIRECTION_OUT ? constraint->data->bendDirection
+												 : (int)frames[framesCount + IKCONSTRAINT_PREV_BEND_DIRECTION];
+		} else {
+			constraint->mix += (frames[framesCount + IKCONSTRAINT_PREV_MIX] - constraint->mix) * alpha;
+			if (direction == SP_MIX_DIRECTION_IN) constraint->bendDirection = (int)frames[framesCount + IKCONSTRAINT_PREV_BEND_DIRECTION];
+		}
+		return;
+	}
+
+	/* Interpolate between the previous frame and the current frame. */
+	frame = binarySearch(self->frames, self->framesCount, time, IKCONSTRAINT_ENTRIES);
+	mix = self->frames[frame + IKCONSTRAINT_PREV_MIX];
+	frameTime = self->frames[frame];
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / IKCONSTRAINT_ENTRIES - 1, 1 - (time - frameTime) / (self->frames[frame + IKCONSTRAINT_PREV_TIME] - frameTime));
+
+	if (pose == SP_MIX_POSE_SETUP) {
+		constraint->mix = constraint->data->mix + (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->data->mix) * alpha;
+		constraint->bendDirection = direction == SP_MIX_DIRECTION_OUT ? constraint->data->bendDirection : (int)frames[frame + IKCONSTRAINT_PREV_BEND_DIRECTION];
+	} else {
+		constraint->mix += (mix + (frames[frame + IKCONSTRAINT_MIX] - mix) * percent - constraint->mix) * alpha;
+		if (direction == SP_MIX_DIRECTION_IN) constraint->bendDirection = (int)frames[frame + IKCONSTRAINT_PREV_BEND_DIRECTION];
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spIkConstraintTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_IKCONSTRAINT << 24) + SUB_CAST(spIkConstraintTimeline, timeline)->ikConstraintIndex;
+}
+
+spIkConstraintTimeline* spIkConstraintTimeline_create (int framesCount) {
+	return (spIkConstraintTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_IKCONSTRAINT, IKCONSTRAINT_ENTRIES, _spIkConstraintTimeline_apply, _spIkConstraintTimeline_getPropertyId);
+}
+
+void spIkConstraintTimeline_setFrame (spIkConstraintTimeline* self, int frameIndex, float time, float mix, int bendDirection) {
+	frameIndex *= IKCONSTRAINT_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + IKCONSTRAINT_MIX] = mix;
+	self->frames[frameIndex + IKCONSTRAINT_BEND_DIRECTION] = (float)bendDirection;
+}
+
+/**/
+static const int TRANSFORMCONSTRAINT_PREV_TIME = -5;
+static const int TRANSFORMCONSTRAINT_PREV_ROTATE = -4;
+static const int TRANSFORMCONSTRAINT_PREV_TRANSLATE = -3;
+static const int TRANSFORMCONSTRAINT_PREV_SCALE = -2;
+static const int TRANSFORMCONSTRAINT_PREV_SHEAR = -1;
+static const int TRANSFORMCONSTRAINT_ROTATE = 1;
+static const int TRANSFORMCONSTRAINT_TRANSLATE = 2;
+static const int TRANSFORMCONSTRAINT_SCALE = 3;
+static const int TRANSFORMCONSTRAINT_SHEAR = 4;
+
+void _spTransformConstraintTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+									spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame;
+	float frameTime, percent, rotate, translate, scale, shear;
+	spTransformConstraint* constraint;
+	spTransformConstraintTimeline* self = (spTransformConstraintTimeline*)timeline;
+	float *frames;
+	int framesCount;
+
+	constraint = skeleton->transformConstraints[self->transformConstraintIndex];
+	if (time < self->frames[0]) {
+		spTransformConstraintData* data = constraint->data;
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				constraint->rotateMix = data->rotateMix;
+				constraint->translateMix = data->translateMix;
+				constraint->scaleMix = data->scaleMix;
+				constraint->shearMix = data->shearMix;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				constraint->rotateMix += (data->rotateMix - constraint->rotateMix) * alpha;
+				constraint->translateMix += (data->translateMix - constraint->translateMix) * alpha;
+				constraint->scaleMix += (data->scaleMix - constraint->scaleMix) * alpha;
+				constraint->shearMix += (data->shearMix - constraint->shearMix) * alpha;
+		}
+		return;
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - TRANSFORMCONSTRAINT_ENTRIES]) { /* Time is after last frame. */
+		int i = framesCount;
+		rotate = frames[i + TRANSFORMCONSTRAINT_PREV_ROTATE];
+		translate = frames[i + TRANSFORMCONSTRAINT_PREV_TRANSLATE];
+		scale = frames[i + TRANSFORMCONSTRAINT_PREV_SCALE];
+		shear = frames[i + TRANSFORMCONSTRAINT_PREV_SHEAR];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, TRANSFORMCONSTRAINT_ENTRIES);
+		rotate = frames[frame + TRANSFORMCONSTRAINT_PREV_ROTATE];
+		translate = frames[frame + TRANSFORMCONSTRAINT_PREV_TRANSLATE];
+		scale = frames[frame + TRANSFORMCONSTRAINT_PREV_SCALE];
+		shear = frames[frame + TRANSFORMCONSTRAINT_PREV_SHEAR];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / TRANSFORMCONSTRAINT_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + TRANSFORMCONSTRAINT_PREV_TIME] - frameTime));
+
+		rotate += (frames[frame + TRANSFORMCONSTRAINT_ROTATE] - rotate) * percent;
+		translate += (frames[frame + TRANSFORMCONSTRAINT_TRANSLATE] - translate) * percent;
+		scale += (frames[frame + TRANSFORMCONSTRAINT_SCALE] - scale) * percent;
+		shear += (frames[frame + TRANSFORMCONSTRAINT_SHEAR] - shear) * percent;
+	}
+	if (pose == SP_MIX_POSE_SETUP) {
+		spTransformConstraintData* data = constraint->data;
+		constraint->rotateMix = data->rotateMix + (rotate - data->rotateMix) * alpha;
+		constraint->translateMix = data->translateMix + (translate - data->translateMix) * alpha;
+		constraint->scaleMix = data->scaleMix + (scale - data->scaleMix) * alpha;
+		constraint->shearMix = data->shearMix + (shear - data->shearMix) * alpha;
+	} else {
+		constraint->rotateMix += (rotate - constraint->rotateMix) * alpha;
+		constraint->translateMix += (translate - constraint->translateMix) * alpha;
+		constraint->scaleMix += (scale - constraint->scaleMix) * alpha;
+		constraint->shearMix += (shear - constraint->shearMix) * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spTransformConstraintTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_TRANSFORMCONSTRAINT << 24) + SUB_CAST(spTransformConstraintTimeline, timeline)->transformConstraintIndex;
+}
+
+spTransformConstraintTimeline* spTransformConstraintTimeline_create (int framesCount) {
+	return (spTransformConstraintTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_TRANSFORMCONSTRAINT, TRANSFORMCONSTRAINT_ENTRIES, _spTransformConstraintTimeline_apply, _spTransformConstraintTimeline_getPropertyId);
+}
+
+void spTransformConstraintTimeline_setFrame (spTransformConstraintTimeline* self, int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
+	frameIndex *= TRANSFORMCONSTRAINT_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + TRANSFORMCONSTRAINT_ROTATE] = rotateMix;
+	self->frames[frameIndex + TRANSFORMCONSTRAINT_TRANSLATE] = translateMix;
+	self->frames[frameIndex + TRANSFORMCONSTRAINT_SCALE] = scaleMix;
+	self->frames[frameIndex + TRANSFORMCONSTRAINT_SHEAR] = shearMix;
+}
+
+/**/
+
+static const int PATHCONSTRAINTPOSITION_PREV_TIME = -2;
+static const int PATHCONSTRAINTPOSITION_PREV_VALUE = -1;
+static const int PATHCONSTRAINTPOSITION_VALUE = 1;
+
+void _spPathConstraintPositionTimeline_apply(const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame;
+	float frameTime, percent, position;
+	spPathConstraint* constraint;
+	spPathConstraintPositionTimeline* self = (spPathConstraintPositionTimeline*)timeline;
+	float* frames;
+	int framesCount;
+
+	constraint = skeleton->pathConstraints[self->pathConstraintIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				constraint->position = constraint->data->position;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				constraint->position += (constraint->data->position - constraint->position) * alpha;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - PATHCONSTRAINTPOSITION_ENTRIES]) /* Time is after last frame. */
+		position = frames[framesCount + PATHCONSTRAINTPOSITION_PREV_VALUE];
+	else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, PATHCONSTRAINTPOSITION_ENTRIES);
+		position = frames[frame + PATHCONSTRAINTPOSITION_PREV_VALUE];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / PATHCONSTRAINTPOSITION_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + PATHCONSTRAINTPOSITION_PREV_TIME] - frameTime));
+
+		position += (frames[frame + PATHCONSTRAINTPOSITION_VALUE] - position) * percent;
+	}
+	if (pose == SP_MIX_POSE_SETUP)
+		constraint->position = constraint->data->position + (position - constraint->data->position) * alpha;
+	else
+		constraint->position += (position - constraint->position) * alpha;
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spPathConstraintPositionTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_PATHCONSTRAINTPOSITION << 24) + SUB_CAST(spPathConstraintPositionTimeline, timeline)->pathConstraintIndex;
+}
+
+spPathConstraintPositionTimeline* spPathConstraintPositionTimeline_create (int framesCount) {
+	return (spPathConstraintPositionTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_PATHCONSTRAINTPOSITION, PATHCONSTRAINTPOSITION_ENTRIES, _spPathConstraintPositionTimeline_apply, _spPathConstraintPositionTimeline_getPropertyId);
+}
+
+void spPathConstraintPositionTimeline_setFrame (spPathConstraintPositionTimeline* self, int frameIndex, float time, float value) {
+	frameIndex *= PATHCONSTRAINTPOSITION_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + PATHCONSTRAINTPOSITION_VALUE] = value;
+}
+
+/**/
+static const int PATHCONSTRAINTSPACING_PREV_TIME = -2;
+static const int PATHCONSTRAINTSPACING_PREV_VALUE = -1;
+static const int PATHCONSTRAINTSPACING_VALUE = 1;
+
+void _spPathConstraintSpacingTimeline_apply(const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame;
+	float frameTime, percent, spacing;
+	spPathConstraint* constraint;
+	spPathConstraintSpacingTimeline* self = (spPathConstraintSpacingTimeline*)timeline;
+	float* frames;
+	int framesCount;
+
+	constraint = skeleton->pathConstraints[self->pathConstraintIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				constraint->spacing = constraint->data->spacing;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				constraint->spacing += (constraint->data->spacing - constraint->spacing) * alpha;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - PATHCONSTRAINTSPACING_ENTRIES]) /* Time is after last frame. */
+		spacing = frames[framesCount + PATHCONSTRAINTSPACING_PREV_VALUE];
+	else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, PATHCONSTRAINTSPACING_ENTRIES);
+		spacing = frames[frame + PATHCONSTRAINTSPACING_PREV_VALUE];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / PATHCONSTRAINTSPACING_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + PATHCONSTRAINTSPACING_PREV_TIME] - frameTime));
+
+		spacing += (frames[frame + PATHCONSTRAINTSPACING_VALUE] - spacing) * percent;
+	}
+
+	if (pose == SP_MIX_POSE_SETUP)
+		constraint->spacing = constraint->data->spacing + (spacing - constraint->data->spacing) * alpha;
+	else
+		constraint->spacing += (spacing - constraint->spacing) * alpha;
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spPathConstraintSpacingTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_PATHCONSTRAINTSPACING << 24) + SUB_CAST(spPathConstraintSpacingTimeline, timeline)->pathConstraintIndex;
+}
+
+spPathConstraintSpacingTimeline* spPathConstraintSpacingTimeline_create (int framesCount) {
+	return (spPathConstraintSpacingTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_PATHCONSTRAINTSPACING, PATHCONSTRAINTSPACING_ENTRIES, _spPathConstraintSpacingTimeline_apply, _spPathConstraintSpacingTimeline_getPropertyId);
+}
+
+void spPathConstraintSpacingTimeline_setFrame (spPathConstraintSpacingTimeline* self, int frameIndex, float time, float value) {
+	frameIndex *= PATHCONSTRAINTSPACING_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + PATHCONSTRAINTSPACING_VALUE] = value;
+}
+
+/**/
+
+static const int PATHCONSTRAINTMIX_PREV_TIME = -3;
+static const int PATHCONSTRAINTMIX_PREV_ROTATE = -2;
+static const int PATHCONSTRAINTMIX_PREV_TRANSLATE = -1;
+static const int PATHCONSTRAINTMIX_ROTATE = 1;
+static const int PATHCONSTRAINTMIX_TRANSLATE = 2;
+
+void _spPathConstraintMixTimeline_apply(const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+											spEvent** firedEvents, int* eventsCount, float alpha, spMixPose pose, spMixDirection direction) {
+	int frame;
+	float frameTime, percent, rotate, translate;
+	spPathConstraint* constraint;
+	spPathConstraintMixTimeline* self = (spPathConstraintMixTimeline*)timeline;
+	float* frames;
+	int framesCount;
+
+	constraint = skeleton->pathConstraints[self->pathConstraintIndex];
+	if (time < self->frames[0]) {
+		switch (pose) {
+			case SP_MIX_POSE_SETUP:
+				constraint->rotateMix = constraint->data->rotateMix;
+				constraint->translateMix = constraint->data->translateMix;
+				return;
+			case SP_MIX_POSE_CURRENT:
+			case SP_MIX_POSE_CURRENT_LAYERED: /* to appease compiler */
+				constraint->rotateMix += (constraint->data->rotateMix - constraint->rotateMix) * alpha;
+				constraint->translateMix += (constraint->data->translateMix - constraint->translateMix) * alpha;
+		}
+		return;
+	}
+
+	frames = self->frames;
+	framesCount = self->framesCount;
+	if (time >= frames[framesCount - PATHCONSTRAINTMIX_ENTRIES]) { /* Time is after last frame. */
+		rotate = frames[framesCount + PATHCONSTRAINTMIX_PREV_ROTATE];
+		translate = frames[framesCount + PATHCONSTRAINTMIX_PREV_TRANSLATE];
+	} else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = binarySearch(frames, framesCount, time, PATHCONSTRAINTMIX_ENTRIES);
+		rotate = frames[frame + PATHCONSTRAINTMIX_PREV_ROTATE];
+		translate = frames[frame + PATHCONSTRAINTMIX_PREV_TRANSLATE];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / PATHCONSTRAINTMIX_ENTRIES - 1,
+										1 - (time - frameTime) / (frames[frame + PATHCONSTRAINTMIX_PREV_TIME] - frameTime));
+
+		rotate += (frames[frame + PATHCONSTRAINTMIX_ROTATE] - rotate) * percent;
+		translate += (frames[frame + PATHCONSTRAINTMIX_TRANSLATE] - translate) * percent;
+	}
+
+	if (pose == SP_MIX_POSE_SETUP) {
+		constraint->rotateMix = constraint->data->rotateMix + (rotate - constraint->data->rotateMix) * alpha;
+		constraint->translateMix = constraint->data->translateMix + (translate - constraint->data->translateMix) * alpha;
+	} else {
+		constraint->rotateMix += (rotate - constraint->rotateMix) * alpha;
+		constraint->translateMix += (translate - constraint->translateMix) * alpha;
+	}
+
+	UNUSED(lastTime);
+	UNUSED(firedEvents);
+	UNUSED(eventsCount);
+}
+
+int _spPathConstraintMixTimeline_getPropertyId (const spTimeline* timeline) {
+	return (SP_TIMELINE_PATHCONSTRAINTMIX << 24) + SUB_CAST(spPathConstraintMixTimeline, timeline)->pathConstraintIndex;
+}
+
+spPathConstraintMixTimeline* spPathConstraintMixTimeline_create (int framesCount) {
+	return (spPathConstraintMixTimeline*)_spBaseTimeline_create(framesCount, SP_TIMELINE_PATHCONSTRAINTMIX, PATHCONSTRAINTMIX_ENTRIES, _spPathConstraintMixTimeline_apply, _spPathConstraintMixTimeline_getPropertyId);
+}
+
+void spPathConstraintMixTimeline_setFrame (spPathConstraintMixTimeline* self, int frameIndex, float time, float rotateMix, float translateMix) {
+	frameIndex *= PATHCONSTRAINTMIX_ENTRIES;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + PATHCONSTRAINTMIX_ROTATE] = rotateMix;
+	self->frames[frameIndex + PATHCONSTRAINTMIX_TRANSLATE] = translateMix;
+}

+ 932 - 0
spine-cpp/spine-cpp/src/spine/AnimationState.c

@@ -0,0 +1,932 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/AnimationState.h>
+#include <spine/extension.h>
+#include <limits.h>
+
+#define SUBSEQUENT 0
+#define FIRST 1
+#define DIP 2
+#define DIP_MIX 3
+
+_SP_ARRAY_IMPLEMENT_TYPE(spTrackEntryArray, spTrackEntry*)
+
+static spAnimation* SP_EMPTY_ANIMATION = 0;
+void spAnimationState_disposeStatics () {
+	if (SP_EMPTY_ANIMATION) spAnimation_dispose(SP_EMPTY_ANIMATION);
+	SP_EMPTY_ANIMATION = 0;
+}
+
+/* Forward declaration of some "private" functions so we can keep
+   the same function order in C as we have method order in Java */
+void _spAnimationState_disposeTrackEntry (spTrackEntry* entry);
+void _spAnimationState_disposeTrackEntries (spAnimationState* state, spTrackEntry* entry);
+int /*boolean*/ _spAnimationState_updateMixingFrom (spAnimationState* self, spTrackEntry* entry, float delta);
+float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* entry, spSkeleton* skeleton, spMixPose currentPose);
+void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline* timeline, spSkeleton* skeleton, float time, float alpha, spMixPose pose, float* timelinesRotation, int i, int /*boolean*/ firstFrame);
+void _spAnimationState_queueEvents (spAnimationState* self, spTrackEntry* entry, float animationTime);
+void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* current, int /*boolean*/ interrupt);
+spTrackEntry* _spAnimationState_expandToIndex (spAnimationState* self, int index);
+spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackIndex, spAnimation* animation, int /*boolean*/ loop, spTrackEntry* last);
+void _spAnimationState_disposeNext (spAnimationState* self, spTrackEntry* entry);
+void _spAnimationState_animationsChanged (spAnimationState* self);
+float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSize);
+int* _spAnimationState_resizeTimelinesFirst(spTrackEntry* entry, int newSize);
+void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity);
+int _spAnimationState_addPropertyID(spAnimationState* self, int id);
+spTrackEntry* _spTrackEntry_setTimelineData(spTrackEntry* self, spTrackEntry* to, spTrackEntryArray* mixingToArray, spAnimationState* state);
+
+
+_spEventQueue* _spEventQueue_create (_spAnimationState* state) {
+	_spEventQueue *self = CALLOC(_spEventQueue, 1);
+	self->state = state;
+	self->objectsCount = 0;
+	self->objectsCapacity = 16;
+	self->objects = CALLOC(_spEventQueueItem, self->objectsCapacity);
+	self->drainDisabled = 0;
+	return self;
+}
+
+void _spEventQueue_free (_spEventQueue* self) {
+	FREE(self->objects);
+    FREE(self);
+}
+
+void _spEventQueue_ensureCapacity (_spEventQueue* self, int newElements) {
+	if (self->objectsCount + newElements > self->objectsCapacity) {
+		_spEventQueueItem* newObjects;
+		self->objectsCapacity <<= 1;
+		newObjects = CALLOC(_spEventQueueItem, self->objectsCapacity);
+		memcpy(newObjects, self->objects, sizeof(_spEventQueueItem) * self->objectsCount);
+		FREE(self->objects);
+		self->objects = newObjects;
+	}
+}
+
+void _spEventQueue_addType (_spEventQueue* self, spEventType type) {
+	_spEventQueue_ensureCapacity(self, 1);
+	self->objects[self->objectsCount++].type = type;
+}
+
+void _spEventQueue_addEntry (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_ensureCapacity(self, 1);
+	self->objects[self->objectsCount++].entry = entry;
+}
+
+void _spEventQueue_addEvent (_spEventQueue* self, spEvent* event) {
+	_spEventQueue_ensureCapacity(self, 1);
+	self->objects[self->objectsCount++].event = event;
+}
+
+void _spEventQueue_start (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_addType(self, SP_ANIMATION_START);
+	_spEventQueue_addEntry(self, entry);
+	self->state->animationsChanged = 1;
+}
+
+void _spEventQueue_interrupt (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_addType(self, SP_ANIMATION_INTERRUPT);
+	_spEventQueue_addEntry(self, entry);
+}
+
+void _spEventQueue_end (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_addType(self, SP_ANIMATION_END);
+	_spEventQueue_addEntry(self, entry);
+	self->state->animationsChanged = 1;
+}
+
+void _spEventQueue_dispose (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_addType(self, SP_ANIMATION_DISPOSE);
+	_spEventQueue_addEntry(self, entry);
+}
+
+void _spEventQueue_complete (_spEventQueue* self, spTrackEntry* entry) {
+	_spEventQueue_addType(self, SP_ANIMATION_COMPLETE);
+	_spEventQueue_addEntry(self, entry);
+}
+
+void _spEventQueue_event (_spEventQueue* self, spTrackEntry* entry, spEvent* event) {
+	_spEventQueue_addType(self, SP_ANIMATION_EVENT);
+	_spEventQueue_addEntry(self, entry);
+	_spEventQueue_addEvent(self, event);
+}
+
+void _spEventQueue_clear (_spEventQueue* self) {
+	self->objectsCount = 0;
+}
+
+void _spEventQueue_drain (_spEventQueue* self) {
+	int i;
+	if (self->drainDisabled) return;
+	self->drainDisabled = 1;
+	for (i = 0; i < self->objectsCount; i += 2) {
+		spEventType type = (spEventType)self->objects[i].type;
+		spTrackEntry* entry = self->objects[i+1].entry;
+		spEvent* event;
+		switch (type) {
+			case SP_ANIMATION_START:
+			case SP_ANIMATION_INTERRUPT:
+			case SP_ANIMATION_COMPLETE:
+				if (entry->listener) entry->listener(SUPER(self->state), type, entry, 0);
+				if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, 0);
+				break;
+			case SP_ANIMATION_END:
+				if (entry->listener) entry->listener(SUPER(self->state), type, entry, 0);
+				if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, 0);
+				/* Fall through. */
+			case SP_ANIMATION_DISPOSE:
+				if (entry->listener) entry->listener(SUPER(self->state), SP_ANIMATION_DISPOSE, entry, 0);
+				if (self->state->super.listener) self->state->super.listener(SUPER(self->state), SP_ANIMATION_DISPOSE, entry, 0);
+				_spAnimationState_disposeTrackEntry(entry);
+				break;
+			case SP_ANIMATION_EVENT:
+				event = self->objects[i+2].event;
+				if (entry->listener) entry->listener(SUPER(self->state), type, entry, event);
+				if (self->state->super.listener) self->state->super.listener(SUPER(self->state), type, entry, event);
+				i++;
+				break;
+		}
+	}
+	_spEventQueue_clear(self);
+
+	self->drainDisabled = 0;
+}
+
+void _spAnimationState_disposeTrackEntry (spTrackEntry* entry) {
+	spIntArray_dispose(entry->timelineData);
+	spTrackEntryArray_dispose(entry->timelineDipMix);
+	FREE(entry->timelinesRotation);
+	FREE(entry);
+}
+
+void _spAnimationState_disposeTrackEntries (spAnimationState* state, spTrackEntry* entry) {
+	while (entry) {
+		spTrackEntry* next = entry->next;
+		spTrackEntry* from = entry->mixingFrom;
+		while (from) {
+			spTrackEntry* nextFrom = from->mixingFrom;
+			if (entry->listener) entry->listener(state, SP_ANIMATION_DISPOSE, from, 0);
+			if (state->listener) state->listener(state, SP_ANIMATION_DISPOSE, from, 0);
+			_spAnimationState_disposeTrackEntry(from);
+			from = nextFrom;
+		}
+		if (entry->listener) entry->listener(state, SP_ANIMATION_DISPOSE, entry, 0);
+		if (state->listener) state->listener(state, SP_ANIMATION_DISPOSE, entry, 0);
+		_spAnimationState_disposeTrackEntry(entry);
+		entry = next;
+	}
+}
+
+spAnimationState* spAnimationState_create (spAnimationStateData* data) {
+	_spAnimationState* internal;
+	spAnimationState* self;
+
+	if (!SP_EMPTY_ANIMATION) {
+		SP_EMPTY_ANIMATION = (spAnimation*)1; /* dirty trick so we can recursively call spAnimation_create */
+		SP_EMPTY_ANIMATION = spAnimation_create("<empty>", 0);
+	}
+
+	internal = NEW(_spAnimationState);
+	self = SUPER(internal);
+
+	CONST_CAST(spAnimationStateData*, self->data) = data;
+	self->timeScale = 1;
+
+	internal->queue = _spEventQueue_create(internal);
+	internal->events = CALLOC(spEvent*, 128);
+
+	internal->propertyIDs = CALLOC(int, 128);
+	internal->propertyIDsCapacity = 128;
+
+	self->mixingTo = spTrackEntryArray_create(16);
+
+	return self;
+}
+
+void spAnimationState_dispose (spAnimationState* self) {
+	int i;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	for (i = 0; i < self->tracksCount; i++)
+		_spAnimationState_disposeTrackEntries(self, self->tracks[i]);
+	FREE(self->tracks);
+	_spEventQueue_free(internal->queue);
+	FREE(internal->events);
+	FREE(internal->propertyIDs);
+	spTrackEntryArray_dispose(self->mixingTo);
+    FREE(internal);
+}
+
+void spAnimationState_update (spAnimationState* self, float delta) {
+	int i, n;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	delta *= self->timeScale;
+	for (i = 0, n = self->tracksCount; i < n; i++) {
+		float currentDelta;
+		spTrackEntry* current = self->tracks[i];
+		spTrackEntry* next;
+		if (!current) continue;
+
+		current->animationLast = current->nextAnimationLast;
+		current->trackLast = current->nextTrackLast;
+
+		currentDelta = delta * current->timeScale;
+
+		if (current->delay > 0) {
+			current->delay -= currentDelta;
+			if (current->delay > 0) continue;
+			currentDelta = -current->delay;
+			current->delay = 0;
+		}
+
+		next = current->next;
+		if (next) {
+			/* When the next entry's delay is passed, change to the next entry, preserving leftover time. */
+			float nextTime = current->trackLast - next->delay;
+			if (nextTime >= 0) {
+				next->delay = 0;
+				next->trackTime = nextTime + delta * next->timeScale;
+				current->trackTime += currentDelta;
+				_spAnimationState_setCurrent(self, i, next, 1);
+				while (next->mixingFrom) {
+					next->mixTime += currentDelta;
+					next = next->mixingFrom;
+				}
+				continue;
+			}
+		} else {
+			/* Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. */
+			if (current->trackLast >= current->trackEnd && current->mixingFrom == 0) {
+				self->tracks[i] = 0;
+				_spEventQueue_end(internal->queue, current);
+				_spAnimationState_disposeNext(self, current);
+				continue;
+			}
+		}
+		if (current->mixingFrom != 0 && _spAnimationState_updateMixingFrom(self, current, delta)) {
+			/* End mixing from entries once all have completed. */
+			spTrackEntry* from = current->mixingFrom;
+			current->mixingFrom = 0;
+			while (from != 0) {
+				_spEventQueue_end(internal->queue, from);
+				from = from->mixingFrom;
+			}
+		}
+
+		current->trackTime += currentDelta;
+	}
+
+	_spEventQueue_drain(internal->queue);
+}
+
+int /*boolean*/ _spAnimationState_updateMixingFrom (spAnimationState* self, spTrackEntry* to, float delta) {
+	spTrackEntry* from = to->mixingFrom;
+	int finished;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	if (!from) return -1;
+
+	finished = _spAnimationState_updateMixingFrom(self, from, delta);
+
+	/* Require mixTime > 0 to ensure the mixing from entry was applied at least once. */
+	if (to->mixTime > 0 && (to->mixTime >= to->mixDuration || to->timeScale == 0)) {
+		/* Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). */
+		if (from->totalAlpha == 0 || to->mixDuration == 0) {
+			to->mixingFrom = from->mixingFrom;
+			to->interruptAlpha = from->interruptAlpha;
+			_spEventQueue_end(internal->queue, from);
+		}
+		return finished;
+	}
+
+	from->animationLast = from->nextAnimationLast;
+	from->trackLast = from->nextTrackLast;
+	from->trackTime += delta * from->timeScale;
+	to->mixTime += delta * to->timeScale;
+	return 0;
+}
+
+int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	spTrackEntry* current;
+	int i, ii, n;
+	float animationLast, animationTime;
+	int timelineCount;
+	spTimeline** timelines;
+	int /*boolean*/ firstFrame;
+	float* timelinesRotation;
+	spTimeline* timeline;
+	int applied = 0;
+	spMixPose currentPose;
+	spMixPose pose;
+
+	if (internal->animationsChanged) _spAnimationState_animationsChanged(self);
+
+	for (i = 0, n = self->tracksCount; i < n; i++) {
+		float mix;
+		current = self->tracks[i];
+		if (!current || current->delay > 0) continue;
+		applied = -1;
+		currentPose = i == 0 ? SP_MIX_POSE_CURRENT : SP_MIX_POSE_CURRENT_LAYERED;
+
+		/* Apply mixing from entries first. */
+		mix = current->alpha;
+		if (current->mixingFrom)
+            mix *= _spAnimationState_applyMixingFrom(self, current, skeleton, currentPose);
+        else if (current->trackTime >= current->trackEnd && current->next == 0)
+            mix = 0;
+
+		/* Apply current entry. */
+		animationLast = current->animationLast; animationTime = spTrackEntry_getAnimationTime(current);
+		timelineCount = current->animation->timelinesCount;
+		timelines = current->animation->timelines;
+		if (mix == 1) {
+			for (ii = 0; ii < timelineCount; ii++)
+				spTimeline_apply(timelines[ii], skeleton, animationLast, animationTime, internal->events, &internal->eventsCount, 1, SP_MIX_POSE_SETUP, SP_MIX_DIRECTION_IN);
+		} else {
+			spIntArray* timelineData = current->timelineData;
+
+			firstFrame = current->timelinesRotationCount == 0;
+			if (firstFrame) _spAnimationState_resizeTimelinesRotation(current, timelineCount << 1);
+			timelinesRotation = current->timelinesRotation;
+
+			for (ii = 0; ii < timelineCount; ii++) {
+				timeline = timelines[ii];
+				pose = timelineData->items[ii] >= FIRST ? SP_MIX_POSE_SETUP : currentPose;
+				if (timeline->type == SP_TIMELINE_ROTATE)
+					_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame);
+				else
+					spTimeline_apply(timeline, skeleton, animationLast, animationTime, internal->events, &internal->eventsCount, mix, pose, SP_MIX_DIRECTION_IN);
+			}
+		}
+		_spAnimationState_queueEvents(self, current, animationTime);
+		internal->eventsCount = 0;
+		current->nextAnimationLast = animationTime;
+		current->nextTrackLast = current->trackTime;
+	}
+
+	_spEventQueue_drain(internal->queue);
+	return applied;
+}
+
+float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* to, spSkeleton* skeleton, spMixPose currentPose) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	float mix;
+	spEvent** events;
+	int /*boolean*/ attachments;
+	int /*boolean*/ drawOrder;
+	float animationLast;
+	float animationTime;
+	int timelineCount;
+	spTimeline** timelines;
+	spIntArray* timelineData;
+	spTrackEntryArray* timelineDipMix;
+	float alphaDip;
+	float alphaMix;
+	float alpha;
+	int /*boolean*/ firstFrame;
+	float* timelinesRotation;
+	spMixPose pose;
+	int i;
+	spTrackEntry* dipMix;
+
+	spTrackEntry* from = to->mixingFrom;
+	if (from->mixingFrom) _spAnimationState_applyMixingFrom(self, from, skeleton, currentPose);
+
+	if (to->mixDuration == 0) /* Single frame mix to undo mixingFrom changes. */
+		mix = 1;
+	else {
+		mix = to->mixTime / to->mixDuration;
+		if (mix > 1) mix = 1;
+	}
+
+	events = mix < from->eventThreshold ? internal->events : 0;
+	attachments = mix < from->attachmentThreshold;
+	drawOrder = mix < from->drawOrderThreshold;
+	animationLast = from->animationLast;
+	animationTime = spTrackEntry_getAnimationTime(from);
+	timelineCount = from->animation->timelinesCount;
+	timelines = from->animation->timelines;
+	timelineData = from->timelineData;
+	timelineDipMix = from->timelineDipMix;
+
+	firstFrame = from->timelinesRotationCount == 0;
+	if (firstFrame) _spAnimationState_resizeTimelinesRotation(from, timelineCount << 1);
+	timelinesRotation = from->timelinesRotation;
+
+	alphaDip = from->alpha * to->interruptAlpha; alphaMix = alphaDip * (1 - mix);
+	from->totalAlpha = 0;
+	for (i = 0; i < timelineCount; i++) {
+		spTimeline* timeline = timelines[i];
+		switch (timelineData->items[i]) {
+			case SUBSEQUENT:
+				if (!attachments && timeline->type == SP_TIMELINE_ATTACHMENT) continue;
+				if (!drawOrder && timeline->type == SP_TIMELINE_DRAWORDER) continue;
+				pose = currentPose;
+				alpha = alphaMix;
+				break;
+			case FIRST:
+				pose = SP_MIX_POSE_SETUP;
+				alpha = alphaMix;
+				break;
+			case DIP:
+				pose = SP_MIX_POSE_SETUP;
+				alpha = alphaDip;
+				break;
+			default:
+				pose = SP_MIX_POSE_SETUP;
+				alpha = alphaDip;
+				dipMix = timelineDipMix->items[i];
+				alpha *= MAX(0, 1 - dipMix->mixTime / dipMix->mixDuration);
+				break;
+		}
+		from->totalAlpha += alpha;
+		if (timeline->type == SP_TIMELINE_ROTATE)
+			_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame);
+		else {
+			spTimeline_apply(timeline, skeleton, animationLast, animationTime, events, &internal->eventsCount, alpha, pose, SP_MIX_DIRECTION_OUT);
+		}
+	}
+
+
+	if (to->mixDuration > 0) _spAnimationState_queueEvents(self, from, animationTime);
+	internal->eventsCount = 0;
+	from->nextAnimationLast = animationTime;
+	from->nextTrackLast = from->trackTime;
+
+	return mix;
+}
+
+void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline* timeline, spSkeleton* skeleton, float time, float alpha, spMixPose pose, float* timelinesRotation, int i, int /*boolean*/ firstFrame) {
+	spRotateTimeline *rotateTimeline;
+	float *frames;
+	spBone* bone;
+	float r1, r2;
+	int frame;
+	float prevRotation;
+	float frameTime;
+	float percent;
+	float total, diff;
+	int /*boolean*/ current, dir;
+
+	if (firstFrame) timelinesRotation[i] = 0;
+
+	if (alpha == 1) {
+		spTimeline_apply(timeline, skeleton, 0, time, 0, 0, 1, pose, SP_MIX_DIRECTION_IN);
+		return;
+	}
+
+	rotateTimeline = SUB_CAST(spRotateTimeline, timeline);
+	frames = rotateTimeline->frames;
+	bone = skeleton->bones[rotateTimeline->boneIndex];
+	if (time < frames[0]) {
+		if (pose == SP_MIX_POSE_SETUP) {
+			bone->rotation = bone->data->rotation;
+		}
+		return; /* Time is before first frame. */
+	}
+
+	if (time >= frames[rotateTimeline->framesCount - ROTATE_ENTRIES]) /* Time is after last frame. */
+		r2 = bone->data->rotation + frames[rotateTimeline->framesCount + ROTATE_PREV_ROTATION];
+	else {
+		/* Interpolate between the previous frame and the current frame. */
+		frame = _spCurveTimeline_binarySearch(frames, rotateTimeline->framesCount, time, ROTATE_ENTRIES);
+		prevRotation = frames[frame + ROTATE_PREV_ROTATION];
+		frameTime = frames[frame];
+		percent = spCurveTimeline_getCurvePercent(SUPER(rotateTimeline), (frame >> 1) - 1,
+													   1 - (time - frameTime) / (frames[frame + ROTATE_PREV_TIME] - frameTime));
+
+		r2 = frames[frame + ROTATE_ROTATION] - prevRotation;
+		r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
+		r2 = prevRotation + r2 * percent + bone->data->rotation;
+		r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
+	}
+
+	/* Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. */
+	r1 = pose == SP_MIX_POSE_SETUP ? bone->data->rotation : bone->rotation;
+	diff = r2 - r1;
+	if (diff == 0) {
+		total = timelinesRotation[i];
+	} else {
+		float lastTotal, lastDiff;
+		diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
+		if (firstFrame) {
+			lastTotal = 0;
+			lastDiff = diff;
+		} else {
+			lastTotal = timelinesRotation[i]; /* Angle and direction of mix, including loops. */
+			lastDiff = timelinesRotation[i + 1]; /* Difference between bones. */
+		}
+		current = diff > 0;
+		dir = lastTotal >= 0;
+		/* Detect cross at 0 (not 180). */
+		if (SIGNUM(lastDiff) != SIGNUM(diff) && ABS(lastDiff) <= 90) {
+			/* A cross after a 360 rotation is a loop. */
+			if (ABS(lastTotal) > 180) lastTotal += 360 * SIGNUM(lastTotal);
+			dir = current;
+		}
+		total = diff + lastTotal - FMOD(lastTotal, 360); /* Store loops as part of lastTotal. */
+		if (dir != current) total += 360 * SIGNUM(lastTotal);
+		timelinesRotation[i] = total;
+	}
+	timelinesRotation[i + 1] = diff;
+	r1 += total * alpha;
+	bone->rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360;
+}
+
+void _spAnimationState_queueEvents (spAnimationState* self, spTrackEntry* entry, float animationTime) {
+	spEvent** events;
+	spEvent* event;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	int i, n;
+	float animationStart = entry->animationStart, animationEnd = entry->animationEnd;
+	float duration = animationEnd - animationStart;
+	float trackLastWrapped = FMOD(entry->trackLast, duration);
+
+	/* Queue events before complete. */
+	events = internal->events;
+	for (i = 0, n = internal->eventsCount; i < n; i++) {
+		event = events[i];
+		if (event->time < trackLastWrapped) break;
+		if (event->time > animationEnd) continue; /* Discard events outside animation start/end. */
+		_spEventQueue_event(internal->queue, entry, event);
+	}
+
+	/* Queue complete if completed a loop iteration or the animation. */
+	if (entry->loop ? (trackLastWrapped > FMOD(entry->trackTime, duration))
+				   : (animationTime >= animationEnd && entry->animationLast < animationEnd)) {
+		_spEventQueue_complete(internal->queue, entry);
+	}
+
+	/* Queue events after complete. */
+	for (; i < n; i++) {
+		event = events[i];
+		if (event->time < animationStart) continue; /* Discard events outside animation start/end. */
+		_spEventQueue_event(internal->queue, entry, event);
+	}
+}
+
+void spAnimationState_clearTracks (spAnimationState* self) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	int i, n, oldDrainDisabled;
+	oldDrainDisabled = internal->queue->drainDisabled;
+	internal->queue->drainDisabled = 1;
+	for (i = 0, n = self->tracksCount; i < n; i++)
+		spAnimationState_clearTrack(self, i);
+	self->tracksCount = 0;
+	internal->queue->drainDisabled = oldDrainDisabled;
+	_spEventQueue_drain(internal->queue);
+}
+
+void spAnimationState_clearTrack (spAnimationState* self, int trackIndex) {
+	spTrackEntry* current;
+	spTrackEntry* entry;
+	spTrackEntry* from;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+
+	if (trackIndex >= self->tracksCount) return;
+	current = self->tracks[trackIndex];
+	if (!current) return;
+
+	_spEventQueue_end(internal->queue, current);
+
+	_spAnimationState_disposeNext(self, current);
+
+	entry = current;
+	while (1) {
+		from = entry->mixingFrom;
+		if (!from) break;
+		_spEventQueue_end(internal->queue, from);
+		entry->mixingFrom = 0;
+		entry = from;
+	}
+
+	self->tracks[current->trackIndex] = 0;
+	_spEventQueue_drain(internal->queue);
+}
+
+void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* current, int /*boolean*/ interrupt) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	spTrackEntry* from = _spAnimationState_expandToIndex(self, index);
+	self->tracks[index] = current;
+
+	if (from) {
+		if (interrupt) _spEventQueue_interrupt(internal->queue, from);
+		current->mixingFrom = from;
+		current->mixTime = 0;
+
+		/* Store the interrupted mix percentage. */
+		if (from->mixingFrom != 0 && from->mixDuration > 0)
+			current->interruptAlpha *= MIN(1, from->mixTime / from->mixDuration);
+
+		from->timelinesRotationCount = 0;
+	}
+
+	_spEventQueue_start(internal->queue, current);
+}
+
+/** Set the current animation. Any queued animations are cleared. */
+spTrackEntry* spAnimationState_setAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+												   int/*bool*/loop) {
+	spAnimation* animation = spSkeletonData_findAnimation(self->data->skeletonData, animationName);
+	return spAnimationState_setAnimation(self, trackIndex, animation, loop);
+}
+
+spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop) {
+	spTrackEntry* entry;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	int interrupt = 1;
+	spTrackEntry* current = _spAnimationState_expandToIndex(self, trackIndex);
+	if (current) {
+		if (current->nextTrackLast == -1) {
+			/* Don't mix from an entry that was never applied. */
+			self->tracks[trackIndex] = current->mixingFrom;
+			_spEventQueue_interrupt(internal->queue, current);
+			_spEventQueue_end(internal->queue, current);
+			_spAnimationState_disposeNext(self, current);
+			current = current->mixingFrom;
+			interrupt = 0;
+		} else
+			_spAnimationState_disposeNext(self, current);
+	}
+	entry = _spAnimationState_trackEntry(self, trackIndex, animation, loop, current);
+	_spAnimationState_setCurrent(self, trackIndex, entry, interrupt);
+	_spEventQueue_drain(internal->queue);
+	return entry;
+}
+
+/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix
+ * duration. */
+spTrackEntry* spAnimationState_addAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+												   int/*bool*/loop, float delay) {
+	spAnimation* animation = spSkeletonData_findAnimation(self->data->skeletonData, animationName);
+	return spAnimationState_addAnimation(self, trackIndex, animation, loop, delay);
+}
+
+spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop,
+											 float delay) {
+	spTrackEntry* entry;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	spTrackEntry* last = _spAnimationState_expandToIndex(self, trackIndex);
+	if (last) {
+		while (last->next)
+			last = last->next;
+	}
+
+	entry = _spAnimationState_trackEntry(self, trackIndex, animation, loop, last);
+
+	if (!last) {
+		_spAnimationState_setCurrent(self, trackIndex, entry, 1);
+		_spEventQueue_drain(internal->queue);
+	} else {
+		last->next = entry;
+		if (delay <= 0) {
+			float duration = last->animationEnd - last->animationStart;
+			if (duration != 0)
+				delay += duration * (1 + (int)(last->trackTime / duration)) - spAnimationStateData_getMix(self->data, last->animation, animation);
+			else
+				delay = 0;
+		}
+	}
+
+	entry->delay = delay;
+	return entry;
+}
+
+spTrackEntry* spAnimationState_setEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration) {
+	spTrackEntry* entry = spAnimationState_setAnimation(self, trackIndex, SP_EMPTY_ANIMATION, 0);
+	entry->mixDuration = mixDuration;
+	entry->trackEnd = mixDuration;
+	return entry;
+}
+
+spTrackEntry* spAnimationState_addEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration, float delay) {
+	spTrackEntry* entry;
+	if (delay <= 0) delay -= mixDuration;
+	entry = spAnimationState_addAnimation(self, trackIndex, SP_EMPTY_ANIMATION, 0, delay);
+	entry->mixDuration = mixDuration;
+	entry->trackEnd = mixDuration;
+	return entry;
+}
+
+void spAnimationState_setEmptyAnimations(spAnimationState* self, float mixDuration) {
+	int i, n, oldDrainDisabled;
+	spTrackEntry* current;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	oldDrainDisabled = internal->queue->drainDisabled;
+	internal->queue->drainDisabled = 1;
+	for (i = 0, n = self->tracksCount; i < n; i++) {
+		current = self->tracks[i];
+		if (current) spAnimationState_setEmptyAnimation(self, current->trackIndex, mixDuration);
+	}
+	internal->queue->drainDisabled = oldDrainDisabled;
+	_spEventQueue_drain(internal->queue);
+}
+
+spTrackEntry* _spAnimationState_expandToIndex (spAnimationState* self, int index) {
+	spTrackEntry** newTracks;
+	if (index < self->tracksCount) return self->tracks[index];
+	newTracks = CALLOC(spTrackEntry*, index + 1);
+	memcpy(newTracks, self->tracks, self->tracksCount * sizeof(spTrackEntry*));
+	FREE(self->tracks);
+	self->tracks = newTracks;
+	self->tracksCount = index + 1;
+	return 0;
+}
+
+spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackIndex, spAnimation* animation, int /*boolean*/ loop, spTrackEntry* last) {
+	spTrackEntry* entry = NEW(spTrackEntry);
+	entry->trackIndex = trackIndex;
+	entry->animation = animation;
+	entry->loop = loop;
+
+	entry->eventThreshold = 0;
+	entry->attachmentThreshold = 0;
+	entry->drawOrderThreshold = 0;
+
+	entry->animationStart = 0;
+	entry->animationEnd = animation->duration;
+	entry->animationLast = -1;
+	entry->nextAnimationLast = -1;
+
+	entry->delay = 0;
+	entry->trackTime = 0;
+	entry->trackLast = -1;
+	entry->nextTrackLast = -1;
+	entry->trackEnd = (float)INT_MAX;
+	entry->timeScale = 1;
+
+	entry->alpha = 1;
+	entry->interruptAlpha = 1;
+	entry->mixTime = 0;
+	entry->mixDuration = !last ? 0 : spAnimationStateData_getMix(self->data, last->animation, animation);
+
+	entry->timelineData = spIntArray_create(16);
+	entry->timelineDipMix = spTrackEntryArray_create(16);
+	return entry;
+}
+
+void _spAnimationState_disposeNext (spAnimationState* self, spTrackEntry* entry) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	spTrackEntry* next = entry->next;
+	while (next) {
+		_spEventQueue_dispose(internal->queue, next);
+		next = next->next;
+	}
+	entry->next = 0;
+}
+
+void _spAnimationState_animationsChanged (spAnimationState* self) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	int i, n;
+	spTrackEntry* entry;
+	spTrackEntryArray* mixingTo;
+	internal->animationsChanged = 0;
+
+	internal->propertyIDsCount = 0;
+	i = 0; n = self->tracksCount;
+
+	mixingTo = self->mixingTo;
+
+	for (;i < n; i++) {
+		entry = self->tracks[i];
+		if (entry != 0) _spTrackEntry_setTimelineData(entry, 0, mixingTo, self);
+	}
+}
+
+float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSize) {
+	if (entry->timelinesRotationCount != newSize) {
+		float* newTimelinesRotation = CALLOC(float, newSize);
+		FREE(entry->timelinesRotation);
+		entry->timelinesRotation = newTimelinesRotation;
+		entry->timelinesRotationCount = newSize;
+	}
+	return entry->timelinesRotation;
+}
+
+void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	if (internal->propertyIDsCapacity < capacity) {
+		int *newPropertyIDs = CALLOC(int, capacity << 1);
+		memcpy(newPropertyIDs, internal->propertyIDs, sizeof(int) * internal->propertyIDsCount);
+		FREE(internal->propertyIDs);
+		internal->propertyIDs = newPropertyIDs;
+		internal->propertyIDsCapacity = capacity << 1;
+	}
+}
+
+int _spAnimationState_addPropertyID(spAnimationState* self, int id) {
+	int i, n;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+
+	for (i = 0, n = internal->propertyIDsCount; i < n; i++) {
+		if (internal->propertyIDs[i] == id) return 0;
+	}
+
+	_spAnimationState_ensureCapacityPropertyIDs(self, internal->propertyIDsCount + 1);
+	internal->propertyIDs[internal->propertyIDsCount] = id;
+	internal->propertyIDsCount++;
+	return 1;
+}
+
+spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex) {
+	if (trackIndex >= self->tracksCount) return 0;
+	return self->tracks[trackIndex];
+}
+
+void spAnimationState_clearListenerNotifications(spAnimationState* self) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	_spEventQueue_clear(internal->queue);
+}
+
+float spTrackEntry_getAnimationTime (spTrackEntry* entry) {
+	if (entry->loop) {
+		float duration = entry->animationEnd - entry->animationStart;
+		if (duration == 0) return entry->animationStart;
+		return FMOD(entry->trackTime, duration) + entry->animationStart;
+	}
+	return MIN(entry->trackTime + entry->animationStart, entry->animationEnd);
+}
+
+int /*boolean*/ _spTrackEntry_hasTimeline(spTrackEntry* self, int id) {
+	spTimeline** timelines = self->animation->timelines;
+	int i, n;
+	for (i = 0, n = self->animation->timelinesCount; i < n; i++)
+		if (spTimeline_getPropertyId(timelines[i]) == id) return 1;
+	return 0;
+}
+
+spTrackEntry* _spTrackEntry_setTimelineData(spTrackEntry* self, spTrackEntry* to, spTrackEntryArray* mixingToArray, spAnimationState* state) {
+	spTrackEntry* lastEntry;
+	spTrackEntry** mixingTo;
+	int mixingToLast;
+	spTimeline** timelines;
+	int timelinesCount;
+	int* timelineData;
+	spTrackEntry** timelineDipMix;
+	int i, ii;
+
+	if (to != 0) spTrackEntryArray_add(mixingToArray, to);
+	lastEntry = self->mixingFrom != 0 ? _spTrackEntry_setTimelineData(self->mixingFrom, self, mixingToArray, state) : self;
+	if (to != 0) spTrackEntryArray_pop(mixingToArray);
+
+	mixingTo = mixingToArray->items;
+	mixingToLast = mixingToArray->size - 1;
+	timelines = self->animation->timelines;
+	timelinesCount = self->animation->timelinesCount;
+	timelineData = spIntArray_setSize(self->timelineData, timelinesCount)->items;
+	spTrackEntryArray_clear(self->timelineDipMix);
+	timelineDipMix = spTrackEntryArray_setSize(self->timelineDipMix, timelinesCount)->items;
+
+	i = 0;
+	continue_outer:
+	for (; i < timelinesCount; i++) {
+		int id = spTimeline_getPropertyId(timelines[i]);
+		if (!_spAnimationState_addPropertyID(state, id))
+			timelineData[i] = SUBSEQUENT;
+		else if (to == 0 || !_spTrackEntry_hasTimeline(to, id))
+			timelineData[i] = FIRST;
+		else {
+			for (ii = mixingToLast; ii >= 0; ii--) {
+				spTrackEntry* entry = mixingTo[ii];
+				if (!_spTrackEntry_hasTimeline(entry, id)) {
+					if (entry->mixDuration > 0) {
+						timelineData[i] = DIP_MIX;
+						timelineDipMix[i] = entry;
+						i++;
+						goto continue_outer;
+					}
+				}
+				break;
+			}
+			timelineData[i] = DIP;
+		}
+	}
+	return lastEntry;
+}

+ 151 - 0
spine-cpp/spine-cpp/src/spine/AnimationStateData.c

@@ -0,0 +1,151 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/AnimationStateData.h>
+#include <spine/extension.h>
+
+typedef struct _ToEntry _ToEntry;
+struct _ToEntry {
+	spAnimation* animation;
+	float duration;
+	_ToEntry* next;
+};
+
+_ToEntry* _ToEntry_create (spAnimation* to, float duration) {
+	_ToEntry* self = NEW(_ToEntry);
+	self->animation = to;
+	self->duration = duration;
+	return self;
+}
+
+void _ToEntry_dispose (_ToEntry* self) {
+	FREE(self);
+}
+
+/**/
+
+typedef struct _FromEntry _FromEntry;
+struct _FromEntry {
+	spAnimation* animation;
+	_ToEntry* toEntries;
+	_FromEntry* next;
+};
+
+_FromEntry* _FromEntry_create (spAnimation* from) {
+	_FromEntry* self = NEW(_FromEntry);
+	self->animation = from;
+	return self;
+}
+
+void _FromEntry_dispose (_FromEntry* self) {
+	FREE(self);
+}
+
+/**/
+
+spAnimationStateData* spAnimationStateData_create (spSkeletonData* skeletonData) {
+	spAnimationStateData* self = NEW(spAnimationStateData);
+	CONST_CAST(spSkeletonData*, self->skeletonData) = skeletonData;
+	return self;
+}
+
+void spAnimationStateData_dispose (spAnimationStateData* self) {
+	_ToEntry* toEntry;
+	_ToEntry* nextToEntry;
+	_FromEntry* nextFromEntry;
+
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		toEntry = fromEntry->toEntries;
+		while (toEntry) {
+			nextToEntry = toEntry->next;
+			_ToEntry_dispose(toEntry);
+			toEntry = nextToEntry;
+		}
+		nextFromEntry = fromEntry->next;
+		_FromEntry_dispose(fromEntry);
+		fromEntry = nextFromEntry;
+	}
+
+	FREE(self);
+}
+
+void spAnimationStateData_setMixByName (spAnimationStateData* self, const char* fromName, const char* toName, float duration) {
+	spAnimation* to;
+	spAnimation* from = spSkeletonData_findAnimation(self->skeletonData, fromName);
+	if (!from) return;
+	to = spSkeletonData_findAnimation(self->skeletonData, toName);
+	if (!to) return;
+	spAnimationStateData_setMix(self, from, to, duration);
+}
+
+void spAnimationStateData_setMix (spAnimationStateData* self, spAnimation* from, spAnimation* to, float duration) {
+	/* Find existing FromEntry. */
+	_ToEntry* toEntry;
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		if (fromEntry->animation == from) {
+			/* Find existing ToEntry. */
+			toEntry = fromEntry->toEntries;
+			while (toEntry) {
+				if (toEntry->animation == to) {
+					toEntry->duration = duration;
+					return;
+				}
+				toEntry = toEntry->next;
+			}
+			break; /* Add new ToEntry to the existing FromEntry. */
+		}
+		fromEntry = fromEntry->next;
+	}
+	if (!fromEntry) {
+		fromEntry = _FromEntry_create(from);
+		fromEntry->next = (_FromEntry*)self->entries;
+		CONST_CAST(_FromEntry*, self->entries) = fromEntry;
+	}
+	toEntry = _ToEntry_create(to, duration);
+	toEntry->next = fromEntry->toEntries;
+	fromEntry->toEntries = toEntry;
+}
+
+float spAnimationStateData_getMix (spAnimationStateData* self, spAnimation* from, spAnimation* to) {
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		if (fromEntry->animation == from) {
+			_ToEntry* toEntry = fromEntry->toEntries;
+			while (toEntry) {
+				if (toEntry->animation == to) return toEntry->duration;
+				toEntry = toEntry->next;
+			}
+		}
+		fromEntry = fromEntry->next;
+	}
+	return self->defaultMix;
+}

+ 39 - 0
spine-cpp/spine-cpp/src/spine/Array.c

@@ -0,0 +1,39 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Array.h>
+#include <spine/extension.h>
+
+_SP_ARRAY_IMPLEMENT_TYPE(spFloatArray, float)
+_SP_ARRAY_IMPLEMENT_TYPE(spIntArray, int)
+_SP_ARRAY_IMPLEMENT_TYPE(spShortArray, short)
+_SP_ARRAY_IMPLEMENT_TYPE(spUnsignedShortArray, unsigned short)
+_SP_ARRAY_IMPLEMENT_TYPE(spArrayFloatArray, spFloatArray*)
+_SP_ARRAY_IMPLEMENT_TYPE(spArrayShortArray, spShortArray*)

+ 359 - 0
spine-cpp/spine-cpp/src/spine/Atlas.c

@@ -0,0 +1,359 @@
+/******************************************************************************
+* Spine Runtimes Software License v2.5
+*
+* Copyright (c) 2013-2016, Esoteric Software
+* All rights reserved.
+*
+* You are granted a perpetual, non-exclusive, non-sublicensable, and
+* non-transferable license to use, install, execute, and perform the Spine
+* Runtimes software and derivative works solely for personal or internal
+* use. Without the written permission of Esoteric Software (see Section 2 of
+* the Spine Software License Agreement), you may not (a) modify, translate,
+* adapt, or develop new applications using the Spine Runtimes or otherwise
+* create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+* USE, DATA, OR PROFITS) 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.
+*****************************************************************************/
+
+#include <spine/Atlas.h>
+#include <ctype.h>
+#include <spine/extension.h>
+
+spAtlasPage* spAtlasPage_create(spAtlas* atlas, const char* name) {
+	spAtlasPage* self = NEW(spAtlasPage);
+	CONST_CAST(spAtlas*, self->atlas) = atlas;
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spAtlasPage_dispose(spAtlasPage* self) {
+	_spAtlasPage_disposeTexture(self);
+	FREE(self->name);
+	FREE(self);
+}
+
+/**/
+
+spAtlasRegion* spAtlasRegion_create() {
+	return NEW(spAtlasRegion);
+}
+
+void spAtlasRegion_dispose(spAtlasRegion* self) {
+	FREE(self->name);
+	FREE(self->splits);
+	FREE(self->pads);
+	FREE(self);
+}
+
+/**/
+
+typedef struct {
+	const char* begin;
+	const char* end;
+} Str;
+
+static void trim(Str* str) {
+	while (isspace((unsigned char)*str->begin) && str->begin < str->end)
+		(str->begin)++;
+	if (str->begin == str->end) return;
+	str->end--;
+	while (isspace((unsigned char)*str->end) && str->end >= str->begin)
+		str->end--;
+	str->end++;
+}
+
+/* Tokenize string without modification. Returns 0 on failure. */
+static int readLine(const char** begin, const char* end, Str* str) {
+	if (*begin == end) return 0;
+	str->begin = *begin;
+
+	/* Find next delimiter. */
+	while (*begin != end && **begin != '\n')
+		(*begin)++;
+
+	str->end = *begin;
+	trim(str);
+
+	if (*begin != end) (*begin)++;
+	return 1;
+}
+
+/* Moves str->begin past the first occurence of c. Returns 0 on failure. */
+static int beginPast(Str* str, char c) {
+	const char* begin = str->begin;
+	while (1) {
+		char lastSkippedChar = *begin;
+		if (begin == str->end) return 0;
+		begin++;
+		if (lastSkippedChar == c) break;
+	}
+	str->begin = begin;
+	return 1;
+}
+
+/* Returns 0 on failure. */
+static int readValue(const char** begin, const char* end, Str* str) {
+	readLine(begin, end, str);
+	if (!beginPast(str, ':')) return 0;
+	trim(str);
+	return 1;
+}
+
+/* Returns the number of tuple values read (1, 2, 4, or 0 for failure). */
+static int readTuple(const char** begin, const char* end, Str tuple[]) {
+	int i;
+	Str str = { NULL, NULL };
+	readLine(begin, end, &str);
+	if (!beginPast(&str, ':')) return 0;
+
+	for (i = 0; i < 3; ++i) {
+		tuple[i].begin = str.begin;
+		if (!beginPast(&str, ',')) break;
+		tuple[i].end = str.begin - 2;
+		trim(&tuple[i]);
+	}
+	tuple[i].begin = str.begin;
+	tuple[i].end = str.end;
+	trim(&tuple[i]);
+	return i + 1;
+}
+
+static char* mallocString(Str* str) {
+	int length = (int)(str->end - str->begin);
+	char* string = MALLOC(char, length + 1);
+	memcpy(string, str->begin, length);
+	string[length] = '\0';
+	return string;
+}
+
+static int indexOf(const char** array, int count, Str* str) {
+	int length = (int)(str->end - str->begin);
+	int i;
+	for (i = count - 1; i >= 0; i--)
+		if (strncmp(array[i], str->begin, length) == 0) return i;
+	return 0;
+}
+
+static int equals(Str* str, const char* other) {
+	return strncmp(other, str->begin, str->end - str->begin) == 0;
+}
+
+static int toInt(Str* str) {
+	return (int)strtol(str->begin, (char**)&str->end, 10);
+}
+
+static spAtlas* abortAtlas(spAtlas* self) {
+	spAtlas_dispose(self);
+	return 0;
+}
+
+static const char* formatNames[] = { "", "Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888" };
+static const char* textureFilterNames[] = { "", "Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest",
+"MipMapNearestLinear", "MipMapLinearLinear" };
+
+spAtlas* spAtlas_create(const char* begin, int length, const char* dir, void* rendererObject) {
+	spAtlas* self;
+
+	int count;
+	const char* end = begin + length;
+	int dirLength = (int)strlen(dir);
+	int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
+
+	spAtlasPage *page = 0;
+	spAtlasPage *lastPage = 0;
+	spAtlasRegion *lastRegion = 0;
+	Str str;
+	Str tuple[4];
+
+	self = NEW(spAtlas);
+	self->rendererObject = rendererObject;
+
+	while (readLine(&begin, end, &str)) {
+		if (str.end - str.begin == 0) {
+			page = 0;
+		}
+		else if (!page) {
+			char* name = mallocString(&str);
+			char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1);
+			memcpy(path, dir, dirLength);
+			if (needsSlash) path[dirLength] = '/';
+			strcpy(path + dirLength + needsSlash, name);
+
+			page = spAtlasPage_create(self, name);
+			FREE(name);
+			if (lastPage)
+				lastPage->next = page;
+			else
+				self->pages = page;
+			lastPage = page;
+
+			switch (readTuple(&begin, end, tuple)) {
+			case 0:
+				return abortAtlas(self);
+			case 2: /* size is only optional for an atlas packed with an old TexturePacker. */
+				page->width = toInt(tuple);
+				page->height = toInt(tuple + 1);
+				if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
+			}
+			page->format = (spAtlasFormat)indexOf(formatNames, 8, tuple);
+
+			if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
+			page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple);
+			page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 8, tuple + 1);
+
+			if (!readValue(&begin, end, &str)) return abortAtlas(self);
+
+			page->uWrap = SP_ATLAS_CLAMPTOEDGE;
+			page->vWrap = SP_ATLAS_CLAMPTOEDGE;
+			if (!equals(&str, "none")) {
+				if (str.end - str.begin == 1) {
+					if (*str.begin == 'x')
+						page->uWrap = SP_ATLAS_REPEAT;
+					else if (*str.begin == 'y')
+						page->vWrap = SP_ATLAS_REPEAT;
+				}
+				else if (equals(&str, "xy")) {
+					page->uWrap = SP_ATLAS_REPEAT;
+					page->vWrap = SP_ATLAS_REPEAT;
+				}
+			}
+
+			_spAtlasPage_createTexture(page, path);
+			FREE(path);
+		}
+		else {
+			spAtlasRegion *region = spAtlasRegion_create();
+			if (lastRegion)
+				lastRegion->next = region;
+			else
+				self->regions = region;
+			lastRegion = region;
+
+			region->page = page;
+			region->name = mallocString(&str);
+
+			if (!readValue(&begin, end, &str)) return abortAtlas(self);
+			region->rotate = equals(&str, "true");
+
+			if (readTuple(&begin, end, tuple) != 2) return abortAtlas(self);
+			region->x = toInt(tuple);
+			region->y = toInt(tuple + 1);
+
+			if (readTuple(&begin, end, tuple) != 2) return abortAtlas(self);
+			region->width = toInt(tuple);
+			region->height = toInt(tuple + 1);
+
+			region->u = region->x / (float)page->width;
+			region->v = region->y / (float)page->height;
+			if (region->rotate) {
+				region->u2 = (region->x + region->height) / (float)page->width;
+				region->v2 = (region->y + region->width) / (float)page->height;
+			}
+			else {
+				region->u2 = (region->x + region->width) / (float)page->width;
+				region->v2 = (region->y + region->height) / (float)page->height;
+			}
+
+			count = readTuple(&begin, end, tuple);
+			if (!count) return abortAtlas(self);
+			if (count == 4) { /* split is optional */
+				region->splits = MALLOC(int, 4);
+				region->splits[0] = toInt(tuple);
+				region->splits[1] = toInt(tuple + 1);
+				region->splits[2] = toInt(tuple + 2);
+				region->splits[3] = toInt(tuple + 3);
+
+				count = readTuple(&begin, end, tuple);
+				if (!count) return abortAtlas(self);
+				if (count == 4) { /* pad is optional, but only present with splits */
+					region->pads = MALLOC(int, 4);
+					region->pads[0] = toInt(tuple);
+					region->pads[1] = toInt(tuple + 1);
+					region->pads[2] = toInt(tuple + 2);
+					region->pads[3] = toInt(tuple + 3);
+
+					if (!readTuple(&begin, end, tuple)) return abortAtlas(self);
+				}
+			}
+
+			region->originalWidth = toInt(tuple);
+			region->originalHeight = toInt(tuple + 1);
+
+			readTuple(&begin, end, tuple);
+			region->offsetX = toInt(tuple);
+			region->offsetY = toInt(tuple + 1);
+
+			if (!readValue(&begin, end, &str)) return abortAtlas(self);
+			region->index = toInt(&str);
+		}
+	}
+
+	return self;
+}
+
+spAtlas* spAtlas_createFromFile(const char* path, void* rendererObject) {
+	int dirLength;
+	char *dir;
+	int length;
+	const char* data;
+
+	spAtlas* atlas = 0;
+
+	/* Get directory from atlas path. */
+	const char* lastForwardSlash = strrchr(path, '/');
+	const char* lastBackwardSlash = strrchr(path, '\\');
+	const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash;
+	if (lastSlash == path) lastSlash++; /* Never drop starting slash. */
+	dirLength = (int)(lastSlash ? lastSlash - path : 0);
+	dir = MALLOC(char, dirLength + 1);
+	memcpy(dir, path, dirLength);
+	dir[dirLength] = '\0';
+
+	data = _spUtil_readFile(path, &length);
+	if (data) atlas = spAtlas_create(data, length, dir, rendererObject);
+
+	FREE(data);
+	FREE(dir);
+	return atlas;
+}
+
+void spAtlas_dispose(spAtlas* self) {
+	spAtlasRegion* region, *nextRegion;
+	spAtlasPage* page = self->pages;
+	while (page) {
+		spAtlasPage* nextPage = page->next;
+		spAtlasPage_dispose(page);
+		page = nextPage;
+	}
+
+	region = self->regions;
+	while (region) {
+		nextRegion = region->next;
+		spAtlasRegion_dispose(region);
+		region = nextRegion;
+	}
+
+	FREE(self);
+}
+
+spAtlasRegion* spAtlas_findRegion(const spAtlas* self, const char* name) {
+	spAtlasRegion* region = self->regions;
+	while (region) {
+		if (strcmp(region->name, name) == 0) return region;
+		region = region->next;
+	}
+	return 0;
+}

+ 100 - 0
spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.c

@@ -0,0 +1,100 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/extension.h>
+
+spAttachment* _spAtlasAttachmentLoader_createAttachment (spAttachmentLoader* loader, spSkin* skin, spAttachmentType type,
+		const char* name, const char* path) {
+	spAtlasAttachmentLoader* self = SUB_CAST(spAtlasAttachmentLoader, loader);
+	switch (type) {
+	case SP_ATTACHMENT_REGION: {
+		spRegionAttachment* attachment;
+		spAtlasRegion* region = spAtlas_findRegion(self->atlas, path);
+		if (!region) {
+			_spAttachmentLoader_setError(loader, "Region not found: ", path);
+			return 0;
+		}
+		attachment = spRegionAttachment_create(name);
+		attachment->rendererObject = region;
+		spRegionAttachment_setUVs(attachment, region->u, region->v, region->u2, region->v2, region->rotate);
+		attachment->regionOffsetX = region->offsetX;
+		attachment->regionOffsetY = region->offsetY;
+		attachment->regionWidth = region->width;
+		attachment->regionHeight = region->height;
+		attachment->regionOriginalWidth = region->originalWidth;
+		attachment->regionOriginalHeight = region->originalHeight;
+		return SUPER(attachment);
+	}
+	case SP_ATTACHMENT_MESH:
+	case SP_ATTACHMENT_LINKED_MESH: {
+		spMeshAttachment* attachment;
+		spAtlasRegion* region = spAtlas_findRegion(self->atlas, path);
+		if (!region) {
+			_spAttachmentLoader_setError(loader, "Region not found: ", path);
+			return 0;
+		}
+		attachment = spMeshAttachment_create(name);
+		attachment->rendererObject = region;
+		attachment->regionU = region->u;
+		attachment->regionV = region->v;
+		attachment->regionU2 = region->u2;
+		attachment->regionV2 = region->v2;
+		attachment->regionRotate = region->rotate;
+		attachment->regionOffsetX = region->offsetX;
+		attachment->regionOffsetY = region->offsetY;
+		attachment->regionWidth = region->width;
+		attachment->regionHeight = region->height;
+		attachment->regionOriginalWidth = region->originalWidth;
+		attachment->regionOriginalHeight = region->originalHeight;
+		return SUPER(SUPER(attachment));
+	}
+	case SP_ATTACHMENT_BOUNDING_BOX:
+		return SUPER(SUPER(spBoundingBoxAttachment_create(name)));
+	case SP_ATTACHMENT_PATH:
+		return SUPER(SUPER(spPathAttachment_create(name)));
+	case SP_ATTACHMENT_POINT:
+		return SUPER(SUPER(spPointAttachment_create(name)));
+	case SP_ATTACHMENT_CLIPPING:
+		return SUPER(SUPER(spClippingAttachment_create(name)));
+	default:
+		_spAttachmentLoader_setUnknownTypeError(loader, type);
+		return 0;
+	}
+
+	UNUSED(skin);
+}
+
+spAtlasAttachmentLoader* spAtlasAttachmentLoader_create (spAtlas* atlas) {
+	spAtlasAttachmentLoader* self = NEW(spAtlasAttachmentLoader);
+	_spAttachmentLoader_init(SUPER(self), _spAttachmentLoader_deinit, _spAtlasAttachmentLoader_createAttachment, 0, 0);
+	self->atlas = atlas;
+	return self;
+}

+ 57 - 0
spine-cpp/spine-cpp/src/spine/Attachment.c

@@ -0,0 +1,57 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Attachment.h>
+#include <spine/extension.h>
+#include <spine/Slot.h>
+
+typedef struct _spAttachmentVtable {
+	void (*dispose) (spAttachment* self);
+} _spAttachmentVtable;
+
+void _spAttachment_init (spAttachment* self, const char* name, spAttachmentType type, /**/
+		void (*dispose) (spAttachment* self)) {
+
+	CONST_CAST(_spAttachmentVtable*, self->vtable) = NEW(_spAttachmentVtable);
+	VTABLE(spAttachment, self) ->dispose = dispose;
+
+	MALLOC_STR(self->name, name);
+	CONST_CAST(spAttachmentType, self->type) = type;
+}
+
+void _spAttachment_deinit (spAttachment* self) {
+	if (self->attachmentLoader) spAttachmentLoader_disposeAttachment(self->attachmentLoader, self);
+	FREE(self->vtable);
+	FREE(self->name);
+}
+
+void spAttachment_dispose (spAttachment* self) {
+	VTABLE(spAttachment, self) ->dispose(self);
+}

+ 98 - 0
spine-cpp/spine-cpp/src/spine/AttachmentLoader.c

@@ -0,0 +1,98 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/AttachmentLoader.h>
+#include <stdio.h>
+#include <spine/extension.h>
+
+typedef struct _spAttachmentLoaderVtable {
+	spAttachment* (*createAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name,
+			const char* path);
+	void (*configureAttachment) (spAttachmentLoader* self, spAttachment*);
+	void (*disposeAttachment) (spAttachmentLoader* self, spAttachment*);
+	void (*dispose) (spAttachmentLoader* self);
+} _spAttachmentLoaderVtable;
+
+void _spAttachmentLoader_init (spAttachmentLoader* self,
+	void (*dispose) (spAttachmentLoader* self),
+	spAttachment* (*createAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name,
+		const char* path),
+	void (*configureAttachment) (spAttachmentLoader* self, spAttachment*),
+	void (*disposeAttachment) (spAttachmentLoader* self, spAttachment*)
+) {
+	CONST_CAST(_spAttachmentLoaderVtable*, self->vtable) = NEW(_spAttachmentLoaderVtable);
+	VTABLE(spAttachmentLoader, self)->dispose = dispose;
+	VTABLE(spAttachmentLoader, self)->createAttachment = createAttachment;
+	VTABLE(spAttachmentLoader, self)->configureAttachment = configureAttachment;
+	VTABLE(spAttachmentLoader, self)->disposeAttachment = disposeAttachment;
+}
+
+void _spAttachmentLoader_deinit (spAttachmentLoader* self) {
+	FREE(self->vtable);
+	FREE(self->error1);
+	FREE(self->error2);
+}
+
+void spAttachmentLoader_dispose (spAttachmentLoader* self) {
+	VTABLE(spAttachmentLoader, self)->dispose(self);
+	FREE(self);
+}
+
+spAttachment* spAttachmentLoader_createAttachment (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name,
+		const char* path) {
+	FREE(self->error1);
+	FREE(self->error2);
+	self->error1 = 0;
+	self->error2 = 0;
+	return VTABLE(spAttachmentLoader, self)->createAttachment(self, skin, type, name, path);
+}
+
+void spAttachmentLoader_configureAttachment (spAttachmentLoader* self, spAttachment* attachment) {
+	if (!VTABLE(spAttachmentLoader, self)->configureAttachment) return;
+	VTABLE(spAttachmentLoader, self)->configureAttachment(self, attachment);
+}
+
+void spAttachmentLoader_disposeAttachment (spAttachmentLoader* self, spAttachment* attachment) {
+	if (!VTABLE(spAttachmentLoader, self)->disposeAttachment) return;
+	VTABLE(spAttachmentLoader, self)->disposeAttachment(self, attachment);
+}
+
+void _spAttachmentLoader_setError (spAttachmentLoader* self, const char* error1, const char* error2) {
+	FREE(self->error1);
+	FREE(self->error2);
+	MALLOC_STR(self->error1, error1);
+	MALLOC_STR(self->error2, error2);
+}
+
+void _spAttachmentLoader_setUnknownTypeError (spAttachmentLoader* self, spAttachmentType type) {
+	char buffer[16];
+	sprintf(buffer, "%d", type);
+	_spAttachmentLoader_setError(self, "Unknown attachment type: ", buffer);
+}

+ 304 - 0
spine-cpp/spine-cpp/src/spine/Bone.c

@@ -0,0 +1,304 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Bone.h>
+#include <spine/extension.h>
+#include <stdio.h>
+static int yDown;
+
+void spBone_setYDown (int value) {
+	yDown = value;
+}
+
+int spBone_isYDown () {
+	return yDown;
+}
+
+spBone* spBone_create (spBoneData* data, spSkeleton* skeleton, spBone* parent) {
+	spBone* self = NEW(spBone);
+	CONST_CAST(spBoneData*, self->data) = data;
+	CONST_CAST(spSkeleton*, self->skeleton) = skeleton;
+	CONST_CAST(spBone*, self->parent) = parent;
+	CONST_CAST(float, self->a) = 1.0f;
+	CONST_CAST(float, self->d) = 1.0f;
+	spBone_setToSetupPose(self);
+	return self;
+}
+
+void spBone_dispose (spBone* self) {
+	FREE(self->children);
+	FREE(self);
+}
+
+void spBone_updateWorldTransform (spBone* self) {
+	spBone_updateWorldTransformWith(self, self->x, self->y, self->rotation, self->scaleX, self->scaleY, self->shearX, self->shearY);
+}
+
+void spBone_updateWorldTransformWith (spBone* self, float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
+	float cosine, sine;
+	float pa, pb, pc, pd;
+	spBone* parent = self->parent;
+
+	self->ax = x;
+	self->ay = y;
+	self->arotation = rotation;
+	self->ascaleX = scaleX;
+	self->ascaleY = scaleY;
+	self->ashearX = shearX;
+	self->ashearY = shearY;
+	self->appliedValid = 1;
+
+	if (!parent) { /* Root bone. */
+		float rotationY = rotation + 90 + shearY;
+		float la = COS_DEG(rotation + shearX) * scaleX;
+		float lb = COS_DEG(rotationY) * scaleY;
+		float lc = SIN_DEG(rotation + shearX) * scaleX;
+		float ld = SIN_DEG(rotationY) * scaleY;
+		if (self->skeleton->flipX) {
+			x = -x;
+			la = -la;
+			lb = -lb;
+		}
+		if (self->skeleton->flipY != yDown) {
+			y = -y;
+			lc = -lc;
+			ld = -ld;
+		}
+		CONST_CAST(float, self->a) = la;
+		CONST_CAST(float, self->b) = lb;
+		CONST_CAST(float, self->c) = lc;
+		CONST_CAST(float, self->d) = ld;
+		CONST_CAST(float, self->worldX) = x + self->skeleton->x;
+		CONST_CAST(float, self->worldY) = y + self->skeleton->y;
+		return;
+	}
+
+	pa = parent->a;
+	pb = parent->b;
+	pc = parent->c;
+	pd = parent->d;
+
+	CONST_CAST(float, self->worldX) = pa * x + pb * y + parent->worldX;
+	CONST_CAST(float, self->worldY) = pc * x + pd * y + parent->worldY;
+
+	switch (self->data->transformMode) {
+		case SP_TRANSFORMMODE_NORMAL: {
+			float rotationY = rotation + 90 + shearY;
+			float la = COS_DEG(rotation + shearX) * scaleX;
+			float lb = COS_DEG(rotationY) * scaleY;
+			float lc = SIN_DEG(rotation + shearX) * scaleX;
+			float ld = SIN_DEG(rotationY) * scaleY;
+			CONST_CAST(float, self->a) = pa * la + pb * lc;
+			CONST_CAST(float, self->b) = pa * lb + pb * ld;
+			CONST_CAST(float, self->c) = pc * la + pd * lc;
+			CONST_CAST(float, self->d) = pc * lb + pd * ld;
+			return;
+		}
+		case SP_TRANSFORMMODE_ONLYTRANSLATION: {
+			float rotationY = rotation + 90 + shearY;
+			CONST_CAST(float, self->a) = COS_DEG(rotation + shearX) * scaleX;
+			CONST_CAST(float, self->b) = COS_DEG(rotationY) * scaleY;
+			CONST_CAST(float, self->c) = SIN_DEG(rotation + shearX) * scaleX;
+			CONST_CAST(float, self->d) = SIN_DEG(rotationY) * scaleY;
+			break;
+		}
+		case SP_TRANSFORMMODE_NOROTATIONORREFLECTION: {
+			float s = pa * pa + pc * pc;
+			float prx, rx, ry, la, lb, lc, ld;
+			if (s > 0.0001f) {
+				s = ABS(pa * pd - pb * pc) / s;
+				pb = pc * s;
+				pd = pa * s;
+				prx = ATAN2(pc, pa) * RAD_DEG;
+			} else {
+				pa = 0;
+				pc = 0;
+				prx = 90 - ATAN2(pd, pb) * RAD_DEG;
+			}
+			rx = rotation + shearX - prx;
+			ry = rotation + shearY - prx + 90;
+			la = COS_DEG(rx) * scaleX;
+			lb = COS_DEG(ry) * scaleY;
+			lc = SIN_DEG(rx) * scaleX;
+			ld = SIN_DEG(ry) * scaleY;
+			CONST_CAST(float, self->a) = pa * la - pb * lc;
+			CONST_CAST(float, self->b) = pa * lb - pb * ld;
+			CONST_CAST(float, self->c) = pc * la + pd * lc;
+			CONST_CAST(float, self->d) = pc * lb + pd * ld;
+			break;
+		}
+		case SP_TRANSFORMMODE_NOSCALE:
+		case SP_TRANSFORMMODE_NOSCALEORREFLECTION: {
+			float za, zc, s;
+			float r, zb, zd, la, lb, lc, ld;
+			cosine = COS_DEG(rotation); sine = SIN_DEG(rotation);
+			za = pa * cosine + pb * sine;
+			zc = pc * cosine + pd * sine;
+			s = SQRT(za * za + zc * zc);
+			if (s > 0.00001f) s = 1 / s;
+			za *= s;
+			zc *= s;
+			s = SQRT(za * za + zc * zc);
+			r = PI / 2 + atan2(zc, za);
+			zb = COS(r) * s;
+			zd = SIN(r) * s;
+			la = COS_DEG(shearX) * scaleX;
+			lb = COS_DEG(90 + shearY) * scaleY;
+			lc = SIN_DEG(shearX) * scaleX;
+			ld = SIN_DEG(90 + shearY) * scaleY;
+			if (self->data->transformMode != SP_TRANSFORMMODE_NOSCALEORREFLECTION ? pa * pd - pb * pc < 0 : self->skeleton->flipX != self->skeleton->flipY) {
+				zb = -zb;
+				zd = -zd;
+			}
+			CONST_CAST(float, self->a) = za * la + zb * lc;
+			CONST_CAST(float, self->b) = za * lb + zb * ld;
+			CONST_CAST(float, self->c) = zc * la + zd * lc;
+			CONST_CAST(float, self->d) = zc * lb + zd * ld;
+			return;
+		}
+	}
+	if (self->skeleton->flipX) {
+		CONST_CAST(float, self->a) = -self->a;
+		CONST_CAST(float, self->b) = -self->b;
+	}
+	if (self->skeleton->flipY != yDown) {
+		CONST_CAST(float, self->c) = -self->c;
+		CONST_CAST(float, self->d) = -self->d;
+	}
+}
+
+void spBone_setToSetupPose (spBone* self) {
+	self->x = self->data->x;
+	self->y = self->data->y;
+	self->rotation = self->data->rotation;
+	self->scaleX = self->data->scaleX;
+	self->scaleY = self->data->scaleY;
+	self->shearX = self->data->shearX;
+	self->shearY = self->data->shearY;
+}
+
+float spBone_getWorldRotationX (spBone* self) {
+	return ATAN2(self->c, self->a) * RAD_DEG;
+}
+
+float spBone_getWorldRotationY (spBone* self) {
+	return ATAN2(self->d, self->b) * RAD_DEG;
+}
+
+float spBone_getWorldScaleX (spBone* self) {
+	return SQRT(self->a * self->a + self->c * self->c);
+}
+
+float spBone_getWorldScaleY (spBone* self) {
+	return SQRT(self->b * self->b + self->d * self->d);
+}
+
+/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using
+ * the applied transform after the world transform has been modified directly (eg, by a constraint).
+ * <p>
+ * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */
+void spBone_updateAppliedTransform (spBone* self) {
+	spBone* parent = self->parent;
+	self->appliedValid = 1;
+	if (!parent) {
+		self->ax = self->worldX;
+		self->ay = self->worldY;
+		self->arotation = ATAN2(self->c, self->a) * RAD_DEG;
+		self->ascaleX = SQRT(self->a * self->a + self->c * self->c);
+		self->ascaleY = SQRT(self->b * self->b + self->d * self->d);
+		self->ashearX = 0;
+		self->ashearY = ATAN2(self->a * self->b + self->c * self->d, self->a * self->d - self->b * self->c) * RAD_DEG;
+	} else {
+		float pa = parent->a, pb = parent->b, pc = parent->c, pd = parent->d;
+		float pid = 1 / (pa * pd - pb * pc);
+		float dx = self->worldX - parent->worldX, dy = self->worldY - parent->worldY;
+		float ia = pid * pd;
+		float id = pid * pa;
+		float ib = pid * pb;
+		float ic = pid * pc;
+		float ra = ia * self->a - ib * self->c;
+		float rb = ia * self->b - ib * self->d;
+		float rc = id * self->c - ic * self->a;
+		float rd = id * self->d - ic * self->b;
+		self->ax = (dx * pd * pid - dy * pb * pid);
+		self->ay = (dy * pa * pid - dx * pc * pid);
+		self->ashearX = 0;
+		self->ascaleX = SQRT(ra * ra + rc * rc);
+		if (self->ascaleX > 0.0001f) {
+			float det = ra * rd - rb * rc;
+			self->ascaleY = det / self->ascaleX;
+			self->ashearY = ATAN2(ra * rb + rc * rd, det) * RAD_DEG;
+			self->arotation = ATAN2(rc, ra) * RAD_DEG;
+		} else {
+			self->ascaleX = 0;
+			self->ascaleY = SQRT(rb * rb + rd * rd);
+			self->ashearY = 0;
+			self->arotation = 90 - ATAN2(rd, rb) * RAD_DEG;
+		}
+	}
+}
+
+void spBone_worldToLocal (spBone* self, float worldX, float worldY, float* localX, float* localY) {
+	float a = self->a, b = self->b, c = self->c, d = self->d;
+	float invDet = 1 / (a * d - b * c);
+	float x = worldX - self->worldX, y = worldY - self->worldY;
+	*localX = (x * d * invDet - y * b * invDet);
+	*localY = (y * a * invDet - x * c * invDet);
+}
+
+void spBone_localToWorld (spBone* self, float localX, float localY, float* worldX, float* worldY) {
+	float x = localX, y = localY;
+	*worldX = x * self->a + y * self->b + self->worldX;
+	*worldY = x * self->c + y * self->d + self->worldY;
+}
+
+float spBone_worldToLocalRotation (spBone* self, float worldRotation) {
+	float sine, cosine;
+	sine = SIN_DEG(worldRotation);
+	cosine = COS_DEG(worldRotation);
+	return ATAN2(self->a * sine - self->c * cosine, self->d * cosine - self->b * sine) * RAD_DEG;
+}
+
+float spBone_localToWorldRotation (spBone* self, float localRotation) {
+	float sine, cosine;
+	sine = SIN_DEG(localRotation);
+	cosine = COS_DEG(localRotation);
+	return ATAN2(cosine * self->c + sine * self->d, cosine * self->a + sine * self->b) * RAD_DEG;
+}
+
+void spBone_rotateWorld (spBone* self, float degrees) {
+	float a = self->a, b = self->b, c = self->c, d = self->d;
+	float cosine = COS_DEG(degrees), sine = SIN_DEG(degrees);
+	CONST_CAST(float, self->a) = cosine * a - sine * c;
+	CONST_CAST(float, self->b) = cosine * b - sine * d;
+	CONST_CAST(float, self->c) = sine * a + cosine * c;
+	CONST_CAST(float, self->d) = sine * b + cosine * d;
+	CONST_CAST(int, self->appliedValid) = 0;
+}

+ 48 - 0
spine-cpp/spine-cpp/src/spine/BoneData.c

@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/BoneData.h>
+#include <spine/extension.h>
+
+spBoneData* spBoneData_create (int index, const char* name, spBoneData* parent) {
+	spBoneData* self = NEW(spBoneData);
+	CONST_CAST(int, self->index) = index;
+	MALLOC_STR(self->name, name);
+	CONST_CAST(spBoneData*, self->parent) = parent;
+	self->scaleX = 1;
+	self->scaleY = 1;
+	self->transformMode = SP_TRANSFORMMODE_NORMAL;
+	return self;
+}
+
+void spBoneData_dispose (spBoneData* self) {
+	FREE(self->name);
+	FREE(self);
+}

+ 47 - 0
spine-cpp/spine-cpp/src/spine/BoundingBoxAttachment.c

@@ -0,0 +1,47 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/extension.h>
+
+void _spBoundingBoxAttachment_dispose (spAttachment* attachment) {
+	spBoundingBoxAttachment* self = SUB_CAST(spBoundingBoxAttachment, attachment);
+
+	_spVertexAttachment_deinit(SUPER(self));
+
+	FREE(self);
+}
+
+spBoundingBoxAttachment* spBoundingBoxAttachment_create (const char* name) {
+	spBoundingBoxAttachment* self = NEW(spBoundingBoxAttachment);
+	_spVertexAttachment_init(SUPER(self));
+	_spAttachment_init(SUPER(SUPER(self)), name, SP_ATTACHMENT_BOUNDING_BOX, _spBoundingBoxAttachment_dispose);
+	return self;
+}

+ 48 - 0
spine-cpp/spine-cpp/src/spine/ClippingAttachment.c

@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/ClippingAttachment.h>
+#include <spine/extension.h>
+
+void _spClippingAttachment_dispose (spAttachment* attachment) {
+	spClippingAttachment* self = SUB_CAST(spClippingAttachment, attachment);
+
+	_spVertexAttachment_deinit(SUPER(self));
+
+	FREE(self);
+}
+
+spClippingAttachment* spClippingAttachment_create (const char* name) {
+	spClippingAttachment* self = NEW(spClippingAttachment);
+	_spVertexAttachment_init(SUPER(self));
+	_spAttachment_init(SUPER(SUPER(self)), name, SP_ATTACHMENT_CLIPPING, _spClippingAttachment_dispose);
+	self->endSlot = 0;
+	return self;
+}

+ 84 - 0
spine-cpp/spine-cpp/src/spine/Color.c

@@ -0,0 +1,84 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Color.h>
+#include <spine/extension.h>
+
+spColor* spColor_create() {
+	return MALLOC(spColor, 1);
+}
+
+void spColor_dispose(spColor* self) {
+	if (self) FREE(self);
+}
+
+void spColor_setFromFloats(spColor* self, float r, float g, float b, float a) {
+	self->r = r;
+	self->g = g;
+	self->b = b;
+	self->a = a;
+}
+
+void spColor_setFromColor(spColor* self, spColor* otherColor) {
+	self->r = otherColor->r;
+	self->g = otherColor->g;
+	self->b = otherColor->b;
+	self->a = otherColor->a;
+}
+
+void spColor_addColor(spColor* self, spColor* otherColor) {
+	self->r += otherColor->r;
+	self->g += otherColor->g;
+	self->b += otherColor->b;
+	self->a += otherColor->a;
+	spColor_clamp(self);
+}
+
+void spColor_addFloats(spColor* self, float r, float g, float b, float a) {
+	self->r += r;
+	self->g += g;
+	self->b += b;
+	self->a += a;
+	spColor_clamp(self);
+}
+
+void spColor_clamp(spColor* self) {
+	if (self->r < 0) self->r = 0;
+	else if (self->r > 1) self->r = 1;
+
+	if (self->g < 0) self->g = 0;
+	else if (self->g > 1) self->g = 1;
+
+	if (self->b < 0) self->b = 0;
+	else if (self->b > 1) self->b = 1;
+
+	if (self->a < 0) self->a = 0;
+	else if (self->a > 1) self->a = 1;
+}

+ 44 - 0
spine-cpp/spine-cpp/src/spine/Event.c

@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Event.h>
+#include <spine/extension.h>
+
+spEvent* spEvent_create (float time, spEventData* data) {
+	spEvent* self = NEW(spEvent);
+	CONST_CAST(spEventData*, self->data) = data;
+	CONST_CAST(float, self->time) = time;
+	return self;
+}
+
+void spEvent_dispose (spEvent* self) {
+	FREE(self->stringValue);
+	FREE(self);
+}

+ 44 - 0
spine-cpp/spine-cpp/src/spine/EventData.c

@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/EventData.h>
+#include <spine/extension.h>
+
+spEventData* spEventData_create (const char* name) {
+	spEventData* self = NEW(spEventData);
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spEventData_dispose (spEventData* self) {
+	FREE(self->stringValue);
+	FREE(self->name);
+	FREE(self);
+}

+ 208 - 0
spine-cpp/spine-cpp/src/spine/IkConstraint.c

@@ -0,0 +1,208 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/IkConstraint.h>
+#include <spine/Skeleton.h>
+#include <spine/extension.h>
+#include <float.h>
+
+spIkConstraint *spIkConstraint_create(spIkConstraintData *data, const spSkeleton *skeleton) {
+	int i;
+
+	spIkConstraint *self = NEW(spIkConstraint);
+	CONST_CAST(spIkConstraintData*, self->data) = data;
+	self->bendDirection = data->bendDirection;
+	self->mix = data->mix;
+
+	self->bonesCount = self->data->bonesCount;
+	self->bones = MALLOC(spBone*, self->bonesCount);
+	for (i = 0; i < self->bonesCount; ++i)
+		self->bones[i] = spSkeleton_findBone(skeleton, self->data->bones[i]->name);
+	self->target = spSkeleton_findBone(skeleton, self->data->target->name);
+
+	return self;
+}
+
+void spIkConstraint_dispose(spIkConstraint *self) {
+	FREE(self->bones);
+	FREE(self);
+}
+
+void spIkConstraint_apply(spIkConstraint *self) {
+	switch (self->bonesCount) {
+		case 1:
+			spIkConstraint_apply1(self->bones[0], self->target->worldX, self->target->worldY, self->mix);
+			break;
+		case 2:
+			spIkConstraint_apply2(self->bones[0], self->bones[1], self->target->worldX, self->target->worldY, self->bendDirection, self->mix);
+			break;
+	}
+}
+
+void spIkConstraint_apply1 (spBone* bone, float targetX, float targetY, float alpha) {
+	spBone* p = bone->parent;
+	float id, x, y, tx, ty, rotationIK;
+	if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
+	id = 1 / (p->a * p->d - p->b * p->c);
+	x = targetX - p->worldX, y = targetY - p->worldY;
+	tx = (x * p->d - y * p->b) * id - bone->ax; ty = (y * p->a - x * p->c) * id - bone->ay;
+	rotationIK = ATAN2(ty, tx) * RAD_DEG - bone->ashearX - bone->arotation;
+	if (bone->ascaleX < 0) rotationIK += 180;
+	if (rotationIK > 180) rotationIK -= 360;
+	else if (rotationIK < -180) rotationIK += 360;
+	spBone_updateWorldTransformWith(bone, bone->ax, bone->ay, bone->arotation + rotationIK * alpha, bone->ascaleX,
+		bone->ascaleY, bone->ashearX, bone->ashearY);
+}
+
+void spIkConstraint_apply2 (spBone* parent, spBone* child, float targetX, float targetY, int bendDir, float alpha) {
+	float px, py, psx, psy;
+	float cx, cy, csx, cwx, cwy;
+	int o1, o2, s2, u;
+	spBone* pp = parent->parent;
+	float tx, ty, dx, dy, l1, l2, a1, a2, r;
+	float id, x, y;
+	if (alpha == 0) {
+		spBone_updateWorldTransform(child);
+		return;
+	}
+	if (!parent->appliedValid) spBone_updateAppliedTransform(parent);
+	if (!child->appliedValid) spBone_updateAppliedTransform(child);
+	px = parent->ax; py = parent->ay; psx = parent->ascaleX; psy = parent->ascaleY; csx = child->ascaleX;
+	if (psx < 0) {
+		psx = -psx;
+		o1 = 180;
+		s2 = -1;
+	} else {
+		o1 = 0;
+		s2 = 1;
+	}
+	if (psy < 0) {
+		psy = -psy;
+		s2 = -s2;
+	}
+	if (csx < 0) {
+		csx = -csx;
+		o2 = 180;
+	} else
+		o2 = 0;
+	r = psx - psy;
+	cx = child->ax;
+	u = (r < 0 ? -r : r) <= 0.0001f;
+	if (!u) {
+		cy = 0;
+		cwx = parent->a * cx + parent->worldX;
+		cwy = parent->c * cx + parent->worldY;
+	} else {
+		cy = child->ay;
+		cwx = parent->a * cx + parent->b * cy + parent->worldX;
+		cwy = parent->c * cx + parent->d * cy + parent->worldY;
+	}
+	id = 1 / (pp->a * pp->d - pp->b * pp->c);
+	x = targetX - pp->worldX;
+	y = targetY - pp->worldY;
+	tx = (x * pp->d - y * pp->b) * id - px;
+	ty = (y * pp->a - x * pp->c) * id - py;
+	x = cwx - pp->worldX;
+	y = cwy - pp->worldY;
+	dx = (x * pp->d - y * pp->b) * id - px;
+	dy = (y * pp->a - x * pp->c) * id - py;
+	l1 = SQRT(dx * dx + dy * dy);
+	l2 = child->data->length * csx;
+	if (u) {
+		float cosine, a, b;
+		l2 *= psx;
+		cosine = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
+		if (cosine < -1) cosine = -1;
+		else if (cosine > 1) cosine = 1;
+		a2 = ACOS(cosine) * bendDir;
+		a = l1 + l2 * cosine;
+		b = l2 * SIN(a2);
+		a1 = ATAN2(ty * a - tx * b, tx * a + ty * b);
+	} else {
+		float a = psx * l2, b = psy * l2;
+		float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty, ta = ATAN2(ty, tx);
+		float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
+		float d = c1 * c1 - 4 * c2 * c0;
+		if (d >= 0) {
+			float q = SQRT(d), r0, r1;
+			if (c1 < 0) q = -q;
+			q = -(c1 + q) / 2;
+			r0 = q / c2; r1 = c0 / q;
+			r = ABS(r0) < ABS(r1) ? r0 : r1;
+			if (r * r <= dd) {
+				y = SQRT(dd - r * r) * bendDir;
+				a1 = ta - ATAN2(y, r);
+				a2 = ATAN2(y / psy, (r - l1) / psx);
+				goto break_outer;
+			}
+		}
+		{
+			float minAngle = PI, minX = l1 - a, minDist = minX * minX, minY = 0;
+			float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0;
+			c0 = -a * l1 / (aa - bb);
+			if (c0 >= -1 && c0 <= 1) {
+				c0 = ACOS(c0);
+				x = a * COS(c0) + l1;
+				y = b * SIN(c0);
+				d = x * x + y * y;
+				if (d < minDist) {
+					minAngle = c0;
+					minDist = d;
+					minX = x;
+					minY = y;
+				}
+				if (d > maxDist) {
+					maxAngle = c0;
+					maxDist = d;
+					maxX = x;
+					maxY = y;
+				}
+			}
+			if (dd <= (minDist + maxDist) / 2) {
+				a1 = ta - ATAN2(minY * bendDir, minX);
+				a2 = minAngle * bendDir;
+			} else {
+				a1 = ta - ATAN2(maxY * bendDir, maxX);
+				a2 = maxAngle * bendDir;
+			}
+		}
+	}
+	break_outer: {
+		float os = ATAN2(cy, cx) * s2;
+		a1 = (a1 - os) * RAD_DEG + o1 - parent->arotation;
+		if (a1 > 180) a1 -= 360;
+		else if (a1 < -180) a1 += 360;
+		spBone_updateWorldTransformWith(parent, px, py, parent->rotation + a1 * alpha, parent->ascaleX, parent->ascaleY, 0, 0);
+		a2 = ((a2 + os) * RAD_DEG - child->ashearX) * s2 + o2 - child->arotation;
+		if (a2 > 180) a2 -= 360;
+		else if (a2 < -180) a2 += 360;
+		spBone_updateWorldTransformWith(child, cx, cy, child->arotation + a2 * alpha, child->ascaleX, child->ascaleY, child->ashearX, child->ashearY);
+	}
+}

+ 46 - 0
spine-cpp/spine-cpp/src/spine/IkConstraintData.c

@@ -0,0 +1,46 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/IkConstraintData.h>
+#include <spine/extension.h>
+
+spIkConstraintData* spIkConstraintData_create (const char* name) {
+	spIkConstraintData* self = NEW(spIkConstraintData);
+	MALLOC_STR(self->name, name);
+	self->bendDirection = 1;
+	self->mix = 1;
+	return self;
+}
+
+void spIkConstraintData_dispose (spIkConstraintData* self) {
+	FREE(self->name);
+	FREE(self->bones);
+	FREE(self);
+}

+ 426 - 0
spine-cpp/spine-cpp/src/spine/Json.c

@@ -0,0 +1,426 @@
+/*
+ Copyright (c) 2009, Dave Gamble
+ Copyright (c) 2013, Esoteric Software
+
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Json */
+/* JSON parser in C. */
+
+#ifndef _DEFAULT_SOURCE
+/* Bring strings.h definitions into string.h, where appropriate */
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _BSD_SOURCE
+/* Bring strings.h definitions into string.h, where appropriate */
+#define _BSD_SOURCE
+#endif
+
+#include "Json.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h> /* strtod (C89), strtof (C99) */
+#include <string.h> /* strcasecmp (4.4BSD - compatibility), _stricmp (_WIN32) */
+#include <spine/extension.h>
+
+#ifndef SPINE_JSON_DEBUG
+/* Define this to do extra NULL and expected-character checking */
+#define SPINE_JSON_DEBUG 0
+#endif
+
+static const char* ep;
+
+const char* Json_getError (void) {
+	return ep;
+}
+
+static int Json_strcasecmp (const char* s1, const char* s2) {
+	/* TODO we may be able to elide these NULL checks if we can prove
+	 * the graph and input (only callsite is Json_getItem) should not have NULLs
+	 */
+	if (s1 && s2) {
+#if defined(_WIN32)
+		return _stricmp(s1, s2);
+#else
+		return strcasecmp( s1, s2 );
+#endif
+	} else {
+		if (s1 < s2)
+			return -1; /* s1 is null, s2 is not */
+		else if (s1 == s2)
+			return 0; /* both are null */
+		else
+			return 1; /* s2 is nul	s1 is not */
+	}
+}
+
+/* Internal constructor. */
+static Json *Json_new (void) {
+	return (Json*)CALLOC(Json, 1);
+}
+
+/* Delete a Json structure. */
+void Json_dispose (Json *c) {
+	Json *next;
+	while (c) {
+		next = c->next;
+		if (c->child) Json_dispose(c->child);
+		if (c->valueString) FREE(c->valueString);
+		if (c->name) FREE(c->name);
+		FREE(c);
+		c = next;
+	}
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char* parse_number (Json *item, const char* num) {
+	char * endptr;
+	float n;
+
+	/* Using strtod and strtof is slightly more permissive than RFC4627,
+	 * accepting for example hex-encoded floating point, but either
+	 * is often leagues faster than any manual implementation.
+	 *
+	 * We also already know that this starts with [-0-9] from parse_value.
+	 */
+#if __STDC_VERSION__ >= 199901L
+	n = strtof(num, &endptr);
+#else
+	n = (float)strtod( num, &endptr );
+#endif
+	/* ignore errno's ERANGE, which returns +/-HUGE_VAL */
+	/* n is 0 on any other error */
+
+	if (endptr != num) {
+		/* Parse success, number found. */
+		item->valueFloat = n;
+		item->valueInt = (int)n;
+		item->type = Json_Number;
+		return endptr;
+	} else {
+		/* Parse failure, ep is set. */
+		ep = num;
+		return 0;
+	}
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+static const char* parse_string (Json *item, const char* str) {
+	const char* ptr = str + 1;
+	char* ptr2;
+	char* out;
+	int len = 0;
+	unsigned uc, uc2;
+	if (*str != '\"') { /* TODO: don't need this check when called from parse_value, but do need from parse_object */
+		ep = str;
+		return 0;
+	} /* not a string! */
+
+	while (*ptr != '\"' && *ptr && ++len)
+		if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
+
+	out = MALLOC(char, len + 1); /* The length needed for the string, roughly. */
+	if (!out) return 0;
+
+	ptr = str + 1;
+	ptr2 = out;
+	while (*ptr != '\"' && *ptr) {
+		if (*ptr != '\\')
+			*ptr2++ = *ptr++;
+		else {
+			ptr++;
+			switch (*ptr) {
+			case 'b':
+				*ptr2++ = '\b';
+				break;
+			case 'f':
+				*ptr2++ = '\f';
+				break;
+			case 'n':
+				*ptr2++ = '\n';
+				break;
+			case 'r':
+				*ptr2++ = '\r';
+				break;
+			case 't':
+				*ptr2++ = '\t';
+				break;
+			case 'u': /* transcode utf16 to utf8. */
+				sscanf(ptr + 1, "%4x", &uc);
+				ptr += 4; /* get the unicode char. */
+
+				if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid.	*/
+
+				/* TODO provide an option to ignore surrogates, use unicode replacement character? */
+				if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs.	*/
+				{
+					if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate.	*/
+					sscanf(ptr + 3, "%4x", &uc2);
+					ptr += 6;
+					if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate.	*/
+					uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
+				}
+
+				len = 4;
+				if (uc < 0x80)
+					len = 1;
+				else if (uc < 0x800)
+					len = 2;
+				else if (uc < 0x10000) len = 3;
+				ptr2 += len;
+
+				switch (len) {
+				case 4:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+					/* fallthrough */
+				case 3:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+					/* fallthrough */
+				case 2:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+					/* fallthrough */
+				case 1:
+					*--ptr2 = (uc | firstByteMark[len]);
+				}
+				ptr2 += len;
+				break;
+			default:
+				*ptr2++ = *ptr;
+				break;
+			}
+			ptr++;
+		}
+	}
+	*ptr2 = 0;
+	if (*ptr == '\"') ptr++; /* TODO error handling if not \" or \0 ? */
+	item->valueString = out;
+	item->type = Json_String;
+	return ptr;
+}
+
+/* Predeclare these prototypes. */
+static const char* parse_value (Json *item, const char* value);
+static const char* parse_array (Json *item, const char* value);
+static const char* parse_object (Json *item, const char* value);
+
+/* Utility to jump whitespace and cr/lf */
+static const char* skip (const char* in) {
+	if (!in) return 0; /* must propagate NULL since it's often called in skip(f(...)) form */
+	while (*in && (unsigned char)*in <= 32)
+		in++;
+	return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+Json *Json_create (const char* value) {
+	Json *c;
+	ep = 0;
+	if (!value) return 0; /* only place we check for NULL other than skip() */
+	c = Json_new();
+	if (!c) return 0; /* memory fail */
+
+	value = parse_value(c, skip(value));
+	if (!value) {
+		Json_dispose(c);
+		return 0;
+	} /* parse failure. ep is set. */
+
+	return c;
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char* parse_value (Json *item, const char* value) {
+	/* Referenced by Json_create(), parse_array(), and parse_object(). */
+	/* Always called with the result of skip(). */
+#if SPINE_JSON_DEBUG /* Checked at entry to graph, Json_create, and after every parse_ call. */
+	if (!value) return 0; /* Fail on null. */
+#endif
+
+	switch (*value) {
+	case 'n': {
+		if (!strncmp(value + 1, "ull", 3)) {
+			item->type = Json_NULL;
+			return value + 4;
+		}
+		break;
+	}
+	case 'f': {
+		if (!strncmp(value + 1, "alse", 4)) {
+			item->type = Json_False;
+			/* calloc prevents us needing item->type = Json_False or valueInt = 0 here */
+			return value + 5;
+		}
+		break;
+	}
+	case 't': {
+		if (!strncmp(value + 1, "rue", 3)) {
+			item->type = Json_True;
+			item->valueInt = 1;
+			return value + 4;
+		}
+		break;
+	}
+	case '\"':
+		return parse_string(item, value);
+	case '[':
+		return parse_array(item, value);
+	case '{':
+		return parse_object(item, value);
+	case '-': /* fallthrough */
+	case '0': /* fallthrough */
+	case '1': /* fallthrough */
+	case '2': /* fallthrough */
+	case '3': /* fallthrough */
+	case '4': /* fallthrough */
+	case '5': /* fallthrough */
+	case '6': /* fallthrough */
+	case '7': /* fallthrough */
+	case '8': /* fallthrough */
+	case '9':
+		return parse_number(item, value);
+	default:
+		break;
+	}
+
+	ep = value;
+	return 0; /* failure. */
+}
+
+/* Build an array from input text. */
+static const char* parse_array (Json *item, const char* value) {
+	Json *child;
+
+#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */
+	if (*value != '[') {
+		ep = value;
+		return 0;
+	} /* not an array! */
+#endif
+
+	item->type = Json_Array;
+	value = skip(value + 1);
+	if (*value == ']') return value + 1; /* empty array. */
+
+	item->child = child = Json_new();
+	if (!item->child) return 0; /* memory fail */
+	value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */
+	if (!value) return 0;
+	item->size = 1;
+
+	while (*value == ',') {
+		Json *new_item = Json_new();
+		if (!new_item) return 0; /* memory fail */
+		child->next = new_item;
+#if SPINE_JSON_HAVE_PREV
+		new_item->prev = child;
+#endif
+		child = new_item;
+		value = skip(parse_value(child, skip(value + 1)));
+		if (!value) return 0; /* parse fail */
+		item->size++;
+	}
+
+	if (*value == ']') return value + 1; /* end of array */
+	ep = value;
+	return 0; /* malformed. */
+}
+
+/* Build an object from the text. */
+static const char* parse_object (Json *item, const char* value) {
+	Json *child;
+
+#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */
+	if (*value != '{') {
+		ep = value;
+		return 0;
+	} /* not an object! */
+#endif
+
+	item->type = Json_Object;
+	value = skip(value + 1);
+	if (*value == '}') return value + 1; /* empty array. */
+
+	item->child = child = Json_new();
+	if (!item->child) return 0;
+	value = skip(parse_string(child, skip(value)));
+	if (!value) return 0;
+	child->name = child->valueString;
+	child->valueString = 0;
+	if (*value != ':') {
+		ep = value;
+		return 0;
+	} /* fail! */
+	value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+	if (!value) return 0;
+	item->size = 1;
+
+	while (*value == ',') {
+		Json *new_item = Json_new();
+		if (!new_item) return 0; /* memory fail */
+		child->next = new_item;
+#if SPINE_JSON_HAVE_PREV
+		new_item->prev = child;
+#endif
+		child = new_item;
+		value = skip(parse_string(child, skip(value + 1)));
+		if (!value) return 0;
+		child->name = child->valueString;
+		child->valueString = 0;
+		if (*value != ':') {
+			ep = value;
+			return 0;
+		} /* fail! */
+		value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+		if (!value) return 0;
+		item->size++;
+	}
+
+	if (*value == '}') return value + 1; /* end of array */
+	ep = value;
+	return 0; /* malformed. */
+}
+
+Json *Json_getItem (Json *object, const char* string) {
+	Json *c = object->child;
+	while (c && Json_strcasecmp(c->name, string))
+		c = c->next;
+	return c;
+}
+
+const char* Json_getString (Json* object, const char* name, const char* defaultValue) {
+	object = Json_getItem(object, name);
+	if (object) return object->valueString;
+	return defaultValue;
+}
+
+float Json_getFloat (Json* value, const char* name, float defaultValue) {
+	value = Json_getItem(value, name);
+	return value ? value->valueFloat : defaultValue;
+}
+
+int Json_getInt (Json* value, const char* name, int defaultValue) {
+	value = Json_getItem(value, name);
+	return value ? value->valueInt : defaultValue;
+}

+ 83 - 0
spine-cpp/spine-cpp/src/spine/Json.h

@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+ 
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Esoteric Software: Removed everything except parsing, shorter method names, more get methods, double to float, formatted. */
+
+#ifndef SPINE_JSON_H_
+#define SPINE_JSON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Json Types: */
+#define Json_False 0
+#define Json_True 1
+#define Json_NULL 2
+#define Json_Number 3
+#define Json_String 4
+#define Json_Array 5
+#define Json_Object 6
+
+#ifndef SPINE_JSON_HAVE_PREV
+/* Spine doesn't use the "prev" link in the Json sibling lists. */
+#define SPINE_JSON_HAVE_PREV 0
+#endif
+
+/* The Json structure: */
+typedef struct Json {
+	struct Json* next;
+#if SPINE_JSON_HAVE_PREV
+	struct Json* prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */
+#endif
+	struct Json* child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+	int type; /* The type of the item, as above. */
+	int size; /* The number of children. */
+
+	const char* valueString; /* The item's string, if type==Json_String */
+	int valueInt; /* The item's number, if type==Json_Number */
+	float valueFloat; /* The item's number, if type==Json_Number */
+
+	const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} Json;
+
+/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */
+Json* Json_create (const char* value);
+
+/* Delete a Json entity and all subentities. */
+void Json_dispose (Json* json);
+
+/* Get item "string" from object. Case insensitive. */
+Json* Json_getItem (Json* json, const char* string);
+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);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
+const char* Json_getError (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_JSON_H_ */

+ 100 - 0
spine-cpp/spine-cpp/src/spine/MeshAttachment.c

@@ -0,0 +1,100 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/MeshAttachment.h>
+#include <spine/extension.h>
+
+void _spMeshAttachment_dispose (spAttachment* attachment) {
+	spMeshAttachment* self = SUB_CAST(spMeshAttachment, attachment);
+	FREE(self->path);
+	FREE(self->uvs);
+	if (!self->parentMesh) {
+		_spVertexAttachment_deinit(SUPER(self));
+		FREE(self->regionUVs);
+		FREE(self->triangles);
+		FREE(self->edges);
+	} else
+		_spAttachment_deinit(attachment);
+	FREE(self);
+}
+
+spMeshAttachment* spMeshAttachment_create (const char* name) {
+	spMeshAttachment* self = NEW(spMeshAttachment);
+	_spVertexAttachment_init(SUPER(self));
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+	_spAttachment_init(SUPER(SUPER(self)), name, SP_ATTACHMENT_MESH, _spMeshAttachment_dispose);
+	return self;
+}
+
+void spMeshAttachment_updateUVs (spMeshAttachment* self) {
+	int i;
+	float width = self->regionU2 - self->regionU, height = self->regionV2 - self->regionV;
+	int verticesLength = SUPER(self)->worldVerticesLength;
+	FREE(self->uvs);
+	self->uvs = MALLOC(float, verticesLength);
+	if (self->regionRotate) {
+		for (i = 0; i < verticesLength; i += 2) {
+			self->uvs[i] = self->regionU + self->regionUVs[i + 1] * width;
+			self->uvs[i + 1] = self->regionV + height - self->regionUVs[i] * height;
+		}
+	} else {
+		for (i = 0; i < verticesLength; i += 2) {
+			self->uvs[i] = self->regionU + self->regionUVs[i] * width;
+			self->uvs[i + 1] = self->regionV + self->regionUVs[i + 1] * height;
+		}
+	}
+}
+
+void spMeshAttachment_setParentMesh (spMeshAttachment* self, spMeshAttachment* parentMesh) {
+	CONST_CAST(spMeshAttachment*, self->parentMesh) = parentMesh;
+	if (parentMesh) {
+
+		self->super.bones = parentMesh->super.bones;
+		self->super.bonesCount = parentMesh->super.bonesCount;
+
+		self->super.vertices = parentMesh->super.vertices;
+		self->super.verticesCount = parentMesh->super.verticesCount;
+
+		self->regionUVs = parentMesh->regionUVs;
+
+		self->triangles = parentMesh->triangles;
+		self->trianglesCount = parentMesh->trianglesCount;
+
+		self->hullLength = parentMesh->hullLength;
+		
+		self->super.worldVerticesLength = parentMesh->super.worldVerticesLength;
+
+		self->edges = parentMesh->edges;
+		self->edgesCount = parentMesh->edgesCount;
+
+		self->width = parentMesh->width;
+		self->height = parentMesh->height;
+	}
+}

+ 48 - 0
spine-cpp/spine-cpp/src/spine/PathAttachment.c

@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/PathAttachment.h>
+#include <spine/extension.h>
+
+void _spPathAttachment_dispose (spAttachment* attachment) {
+	spPathAttachment* self = SUB_CAST(spPathAttachment, attachment);
+
+	_spVertexAttachment_deinit(SUPER(self));
+
+	FREE(self->lengths);
+	FREE(self);
+}
+
+spPathAttachment* spPathAttachment_create (const char* name) {
+	spPathAttachment* self = NEW(spPathAttachment);
+	_spVertexAttachment_init(SUPER(self));
+	_spAttachment_init(SUPER(SUPER(self)), name, SP_ATTACHMENT_PATH, _spPathAttachment_dispose);
+	return self;
+}

+ 467 - 0
spine-cpp/spine-cpp/src/spine/PathConstraint.c

@@ -0,0 +1,467 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/PathConstraint.h>
+#include <spine/Skeleton.h>
+#include <spine/extension.h>
+
+#define PATHCONSTRAINT_NONE -1
+#define PATHCONSTRAINT_BEFORE -2
+#define PATHCONSTRAINT_AFTER -3
+
+spPathConstraint* spPathConstraint_create (spPathConstraintData* data, const spSkeleton* skeleton) {
+	int i;
+	spPathConstraint *self = NEW(spPathConstraint);
+	CONST_CAST(spPathConstraintData*, self->data) = data;
+	self->bonesCount = data->bonesCount;
+	CONST_CAST(spBone**, self->bones) = MALLOC(spBone*, self->bonesCount);
+	for (i = 0; i < self->bonesCount; ++i)
+		self->bones[i] = spSkeleton_findBone(skeleton, self->data->bones[i]->name);
+	self->target = spSkeleton_findSlot(skeleton, self->data->target->name);
+	self->position = data->position;
+	self->spacing = data->spacing;
+	self->rotateMix = data->rotateMix;
+	self->translateMix = data->translateMix;
+	self->spacesCount = 0;
+	self->spaces = 0;
+	self->positionsCount = 0;
+	self->positions = 0;
+	self->worldCount = 0;
+	self->world = 0;
+	self->curvesCount = 0;
+	self->curves = 0;
+	self->lengthsCount = 0;
+	self->lengths = 0;
+	return self;
+}
+
+void spPathConstraint_dispose (spPathConstraint* self) {
+	FREE(self->bones);
+	FREE(self->spaces);
+	if (self->positions) FREE(self->positions);
+	if (self->world) FREE(self->world);
+	if (self->curves) FREE(self->curves);
+	if (self->lengths) FREE(self->lengths);
+	FREE(self);
+}
+
+void spPathConstraint_apply (spPathConstraint* self) {
+	int i, p, n;
+	float length, setupLength, x, y, dx, dy, s;
+	float* spaces, *lengths, *positions;
+	float spacing;
+	float boneX, boneY, offsetRotation;
+	int/*bool*/tip;
+	float rotateMix = self->rotateMix, translateMix = self->translateMix;
+	int/*bool*/ translate = translateMix > 0, rotate = rotateMix > 0;
+	spPathAttachment* attachment = (spPathAttachment*)self->target->attachment;
+	spPathConstraintData* data = self->data;
+	spSpacingMode spacingMode = data->spacingMode;
+	int lengthSpacing = spacingMode == SP_SPACING_MODE_LENGTH;
+	spRotateMode rotateMode = data->rotateMode;
+	int tangents = rotateMode == SP_ROTATE_MODE_TANGENT, scale = rotateMode == SP_ROTATE_MODE_CHAIN_SCALE;
+	int boneCount = self->bonesCount, spacesCount = tangents ? boneCount : boneCount + 1;
+	spBone** bones = self->bones;
+	spBone* pa;
+
+	if (!translate && !rotate) return;
+	if ((attachment == 0) || (attachment->super.super.type != SP_ATTACHMENT_PATH)) return;
+
+	if (self->spacesCount != spacesCount) {
+		if (self->spaces) FREE(self->spaces);
+		self->spaces = MALLOC(float, spacesCount);
+		self->spacesCount = spacesCount;
+	}
+	spaces = self->spaces;
+	spaces[0] = 0;
+	lengths = 0;
+	spacing = self->spacing;
+	if (scale || lengthSpacing) {
+		if (scale) {
+			if (self->lengthsCount != boneCount) {
+				if (self->lengths) FREE(self->lengths);
+				self->lengths = MALLOC(float, boneCount);
+				self->lengthsCount = boneCount;
+			}
+			lengths = self->lengths;
+		}
+		for (i = 0, n = spacesCount - 1; i < n;) {
+			spBone* bone = bones[i];
+			setupLength = bone->data->length;
+			if (setupLength == 0) setupLength = 0.000000001f;
+			x = setupLength * bone->a, y = setupLength * bone->c;
+			length = SQRT(x * x + y * y);
+			if (scale) lengths[i] = length;
+			spaces[++i] =  (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
+		}
+	} else {
+		for (i = 1; i < spacesCount; i++) {
+			spaces[i] = spacing;
+		}
+	}
+
+	positions = spPathConstraint_computeWorldPositions(self, attachment, spacesCount, tangents,
+		data->positionMode == SP_POSITION_MODE_PERCENT, spacingMode == SP_SPACING_MODE_PERCENT);
+	boneX = positions[0], boneY = positions[1], offsetRotation = self->data->offsetRotation;
+	tip = 0;
+	if (offsetRotation == 0)
+		tip = rotateMode == SP_ROTATE_MODE_CHAIN;
+	else {
+		tip = 0;
+		pa = self->target->bone;
+		offsetRotation *= pa->a * pa->d - pa->b * pa->c > 0 ? DEG_RAD : -DEG_RAD;
+	}
+	for (i = 0, p = 3; i < boneCount; i++, p += 3) {
+		spBone* bone = bones[i];
+		CONST_CAST(float, bone->worldX) += (boneX - bone->worldX) * translateMix;
+		CONST_CAST(float, bone->worldY) += (boneY - bone->worldY) * translateMix;
+		x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
+		if (scale) {
+			length = lengths[i];
+			if (length != 0) {
+				s = (SQRT(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
+				CONST_CAST(float, bone->a) *= s;
+				CONST_CAST(float, bone->c) *= s;
+			}
+		}
+		boneX = x;
+		boneY = y;
+		if (rotate) {
+			float a = bone->a, b = bone->b, c = bone->c, d = bone->d, r, cosine, sine;
+			if (tangents)
+				r = positions[p - 1];
+			else if (spaces[i + 1] == 0)
+				r = positions[p + 2];
+			else
+				r = ATAN2(dy, dx);
+			r -= ATAN2(c, a) - offsetRotation * DEG_RAD;
+			if (tip) {
+				cosine = COS(r);
+				sine = SIN(r);
+				length = bone->data->length;
+				boneX += (length * (cosine * a - sine * c) - dx) * rotateMix;
+				boneY += (length * (sine * a + cosine * c) - dy) * rotateMix;
+			} else
+				r += offsetRotation;
+			if (r > PI)
+				r -= PI2;
+			else if (r < -PI)
+				r += PI2;
+			r *= rotateMix;
+			cosine = COS(r);
+			sine = SIN(r);
+			CONST_CAST(float, bone->a) = cosine * a - sine * c;
+			CONST_CAST(float, bone->b) = cosine * b - sine * d;
+			CONST_CAST(float, bone->c) = sine * a + cosine * c;
+			CONST_CAST(float, bone->d) = sine * b + cosine * d;
+		}
+		CONST_CAST(int, bone->appliedValid) = -1;
+	}
+}
+
+static void _addBeforePosition(float p, float* temp, int i, float* out, int o) {
+	float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = ATAN2(dy, dx);
+	out[o] = x1 + p * COS(r);
+	out[o + 1] = y1 + p * SIN(r);
+	out[o + 2] = r;
+}
+
+static void _addAfterPosition (float p, float* temp, int i, float* out, int o) {
+	float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = ATAN2(dy, dx);
+	out[o] = x1 + p * COS(r);
+	out[o + 1] = y1 + p * SIN(r);
+	out[o + 2] = r;
+}
+
+/* Need to pass 0 as an argument, so VC++ doesn't error with C2124 */
+static int _isNan(float value, float zero) {
+	float _nan =  (float)0.0 / zero;
+	return 0 == memcmp((void*)&value, (void*)&_nan, sizeof(value));
+}
+
+static void _addCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
+		float* out, int o, int/*bool*/tangents) {
+	float tt, ttt, u, uu, uuu;
+	float ut, ut3, uut3, utt3;
+	float x, y;
+	if (p == 0 || _isNan(p, 0)) p = 0.0001f;
+	tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
+	ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
+	x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
+	out[o] = x;
+	out[o + 1] = y;
+	if (tangents) out[o + 2] = ATAN2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
+}
+
+float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAttachment* path, int spacesCount, int/*bool*/ tangents, int/*bool*/percentPosition, int/**/percentSpacing) {
+	int i, o, w, curve, segment, /*bool*/closed, verticesLength, curveCount, prevCurve;
+	float* out, *curves, *segments;
+	float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy, pathLength, curveLength, p;
+	float x1, y1, cx1, cy1, cx2, cy2, x2, y2;
+	spSlot* target = self->target;
+	float position = self->position;
+	float* spaces = self->spaces, *world = 0;
+	if (self->positionsCount != spacesCount * 3 + 2) {
+		if (self->positions) FREE(self->positions);
+		self->positions = MALLOC(float, spacesCount * 3 + 2);
+		self->positionsCount = spacesCount * 3 + 2;
+	}
+	out = self->positions;
+	closed = path->closed;
+	verticesLength = path->super.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PATHCONSTRAINT_NONE;
+
+	if (!path->constantSpeed) {
+		float* lengths = path->lengths;
+		curveCount -= closed ? 1 : 2;
+		pathLength = lengths[curveCount];
+		if (percentPosition) position *= pathLength;
+		if (percentSpacing) {
+			for (i = 0; i < spacesCount; i++)
+				spaces[i] *= pathLength;
+		}
+		if (self->worldCount != 8) {
+			if (self->world) FREE(self->world);
+			self->world = MALLOC(float, 8);
+			self->worldCount = 8;
+		}
+		world = self->world;
+		for (i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
+			float space = spaces[i];
+			position += space;
+			p = position;
+
+			if (closed) {
+				p = FMOD(p, pathLength);
+				if (p < 0) p += pathLength;
+				curve = 0;
+			} else if (p < 0) {
+				if (prevCurve != PATHCONSTRAINT_BEFORE) {
+					prevCurve = PATHCONSTRAINT_BEFORE;
+					spVertexAttachment_computeWorldVertices(SUPER(path), target, 2, 4, world, 0, 2);
+				}
+				_addBeforePosition(p, world, 0, out, o);
+				continue;
+			} else if (p > pathLength) {
+				if (prevCurve != PATHCONSTRAINT_AFTER) {
+					prevCurve = PATHCONSTRAINT_AFTER;
+					spVertexAttachment_computeWorldVertices(SUPER(path), target, verticesLength - 6, 4, world, 0, 2);
+				}
+				_addAfterPosition(p - pathLength, world, 0, out, o);
+				continue;
+			}
+
+			/* Determine curve containing position. */
+			for (;; curve++) {
+				float length = lengths[curve];
+				if (p > length) continue;
+				if (curve == 0)
+					p /= length;
+				else {
+					float prev = lengths[curve - 1];
+					p = (p - prev) / (length - prev);
+				}
+				break;
+			}
+			if (curve != prevCurve) {
+				prevCurve = curve;
+				if (closed && curve == curveCount) {
+					spVertexAttachment_computeWorldVertices(SUPER(path), target, verticesLength - 4, 4, world, 0, 2);
+					spVertexAttachment_computeWorldVertices(SUPER(path), target, 0, 4, world, 4, 2);
+				} else
+					spVertexAttachment_computeWorldVertices(SUPER(path), target, curve * 6 + 2, 8, world, 0, 2);
+			}
+			_addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
+				tangents || (i > 0 && space == 0));
+		}
+		return out;
+	}
+
+	/* World vertices. */
+	if (closed) {
+		verticesLength += 2;
+		if (self->worldCount != verticesLength) {
+			if (self->world) FREE(self->world);
+			self->world = MALLOC(float, verticesLength);
+			self->worldCount = verticesLength;
+		}
+		world = self->world;
+		spVertexAttachment_computeWorldVertices(SUPER(path), target, 2, verticesLength - 4, world, 0, 2);
+		spVertexAttachment_computeWorldVertices(SUPER(path), target, 0, 2, world, verticesLength - 4, 2);
+		world[verticesLength - 2] = world[0];
+		world[verticesLength - 1] = world[1];
+	} else {
+		curveCount--;
+		verticesLength -= 4;
+		if (self->worldCount != verticesLength) {
+			if (self->world) FREE(self->world);
+			self->world = MALLOC(float, verticesLength);
+			self->worldCount = verticesLength;
+		}
+		world = self->world;
+		spVertexAttachment_computeWorldVertices(SUPER(path), target, 2, verticesLength, world, 0, 2);
+	}
+
+	/* Curve lengths. */
+	if (self->curvesCount != curveCount) {
+		if (self->curves) FREE(self->curves);
+		self->curves = MALLOC(float, curveCount);
+		self->curvesCount = curveCount;
+	}
+	curves = self->curves;
+	pathLength = 0;
+	x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
+	for (i = 0, w = 2; i < curveCount; i++, w += 6) {
+		cx1 = world[w];
+		cy1 = world[w + 1];
+		cx2 = world[w + 2];
+		cy2 = world[w + 3];
+		x2 = world[w + 4];
+		y2 = world[w + 5];
+		tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
+		tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
+		dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
+		dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
+		ddfx = tmpx * 2 + dddfx;
+		ddfy = tmpy * 2 + dddfy;
+		dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
+		dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
+		pathLength += SQRT(dfx * dfx + dfy * dfy);
+		dfx += ddfx;
+		dfy += ddfy;
+		ddfx += dddfx;
+		ddfy += dddfy;
+		pathLength += SQRT(dfx * dfx + dfy * dfy);
+		dfx += ddfx;
+		dfy += ddfy;
+		pathLength += SQRT(dfx * dfx + dfy * dfy);
+		dfx += ddfx + dddfx;
+		dfy += ddfy + dddfy;
+		pathLength += SQRT(dfx * dfx + dfy * dfy);
+		curves[i] = pathLength;
+		x1 = x2;
+		y1 = y2;
+	}
+	if (percentPosition) position *= pathLength;
+	if (percentSpacing) {
+		for (i = 0; i < spacesCount; i++)
+			spaces[i] *= pathLength;
+	}
+
+	segments = self->segments;
+	curveLength = 0;
+	for (i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
+		float space = spaces[i];
+		position += space;
+		p = position;
+
+		if (closed) {
+			p = FMOD(p, pathLength);
+			if (p < 0) p += pathLength;
+			curve = 0;
+		} else if (p < 0) {
+			_addBeforePosition(p, world, 0, out, o);
+			continue;
+		} else if (p > pathLength) {
+			_addAfterPosition(p - pathLength, world, verticesLength - 4, out, o);
+			continue;
+		}
+
+		/* Determine curve containing position. */
+		for (;; curve++) {
+			float length = curves[curve];
+			if (p > length) continue;
+			if (curve == 0)
+				p /= length;
+			else {
+				float prev = curves[curve - 1];
+				p = (p - prev) / (length - prev);
+			}
+			break;
+		}
+
+		/* Curve segment lengths. */
+		if (curve != prevCurve) {
+			int ii;
+			prevCurve = curve;
+			ii = curve * 6;
+			x1 = world[ii];
+			y1 = world[ii + 1];
+			cx1 = world[ii + 2];
+			cy1 = world[ii + 3];
+			cx2 = world[ii + 4];
+			cy2 = world[ii + 5];
+			x2 = world[ii + 6];
+			y2 = world[ii + 7];
+			tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
+			tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
+			dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
+			dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
+			ddfx = tmpx * 2 + dddfx;
+			ddfy = tmpy * 2 + dddfy;
+			dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
+			dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
+			curveLength = SQRT(dfx * dfx + dfy * dfy);
+			segments[0] = curveLength;
+			for (ii = 1; ii < 8; ii++) {
+				dfx += ddfx;
+				dfy += ddfy;
+				ddfx += dddfx;
+				ddfy += dddfy;
+				curveLength += SQRT(dfx * dfx + dfy * dfy);
+				segments[ii] = curveLength;
+			}
+			dfx += ddfx;
+			dfy += ddfy;
+			curveLength += SQRT(dfx * dfx + dfy * dfy);
+			segments[8] = curveLength;
+			dfx += ddfx + dddfx;
+			dfy += ddfy + dddfy;
+			curveLength += SQRT(dfx * dfx + dfy * dfy);
+			segments[9] = curveLength;
+			segment = 0;
+		}
+
+		/* Weight by segment length. */
+		p *= curveLength;
+		for (;; segment++) {
+			float length = segments[segment];
+			if (p > length) continue;
+			if (segment == 0)
+				p /= length;
+			else {
+				float prev = segments[segment - 1];
+				p = segment + (p - prev) / (length - prev);
+			}
+			break;
+		}
+		_addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0));
+	}
+	return out;
+}

+ 44 - 0
spine-cpp/spine-cpp/src/spine/PathConstraintData.c

@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/PathConstraintData.h>
+#include <spine/extension.h>
+
+spPathConstraintData* spPathConstraintData_create (const char* name) {
+	spPathConstraintData* self = NEW(spPathConstraintData);
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spPathConstraintData_dispose (spPathConstraintData* self) {
+	FREE(self->name);
+	FREE(self->bones);
+	FREE(self);
+}

+ 61 - 0
spine-cpp/spine-cpp/src/spine/PointAttachment.c

@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/PointAttachment.h>
+#include <spine/extension.h>
+
+void _spPointAttachment_dispose (spAttachment* attachment) {
+	spPathAttachment* self = SUB_CAST(spPathAttachment, attachment);
+
+	_spVertexAttachment_deinit(SUPER(self));
+
+	FREE(self);
+}
+
+spPointAttachment* spPointAttachment_create (const char* name) {
+	spPointAttachment* self = NEW(spPointAttachment);
+	_spVertexAttachment_init(SUPER(self));
+	_spAttachment_init(SUPER(SUPER(self)), name, SP_ATTACHMENT_POINT, _spPointAttachment_dispose);
+	return self;
+}
+
+void spPointAttachment_computeWorldPosition (spPointAttachment* self, spBone* bone, float* x, float* y) {
+	*x = self->x * bone->a + self->y * bone->b + bone->worldX;
+	*y = self->x * bone->c + self->y * bone->d + bone->worldY;
+}
+
+float spPointAttachment_computeWorldRotation (spPointAttachment* self, spBone* bone) {
+	float cosine, sine, x, y;
+	cosine = COS_DEG(self->rotation);
+	sine = SIN_DEG(self->rotation);
+	x = cosine * bone->a + sine * bone->b;
+	y = cosine * bone->c + sine * bone->d;
+	return ATAN2(y, x) * RAD_DEG;
+}

+ 130 - 0
spine-cpp/spine-cpp/src/spine/RegionAttachment.c

@@ -0,0 +1,130 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/RegionAttachment.h>
+#include <spine/extension.h>
+
+typedef enum {
+	BLX = 0, BLY, ULX, ULY, URX, URY, BRX, BRY
+} spVertexIndex;
+
+void _spRegionAttachment_dispose (spAttachment* attachment) {
+	spRegionAttachment* self = SUB_CAST(spRegionAttachment, attachment);
+	_spAttachment_deinit(attachment);
+	FREE(self->path);
+	FREE(self);
+}
+
+spRegionAttachment* spRegionAttachment_create (const char* name) {
+	spRegionAttachment* self = NEW(spRegionAttachment);
+	self->scaleX = 1;
+	self->scaleY = 1;
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+	_spAttachment_init(SUPER(self), name, SP_ATTACHMENT_REGION, _spRegionAttachment_dispose);
+	return self;
+}
+
+void spRegionAttachment_setUVs (spRegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate) {
+	if (rotate) {
+		self->uvs[URX] = u;
+		self->uvs[URY] = v2;
+		self->uvs[BRX] = u;
+		self->uvs[BRY] = v;
+		self->uvs[BLX] = u2;
+		self->uvs[BLY] = v;
+		self->uvs[ULX] = u2;
+		self->uvs[ULY] = v2;
+	} else {
+		self->uvs[ULX] = u;
+		self->uvs[ULY] = v2;
+		self->uvs[URX] = u;
+		self->uvs[URY] = v;
+		self->uvs[BRX] = u2;
+		self->uvs[BRY] = v;
+		self->uvs[BLX] = u2;
+		self->uvs[BLY] = v2;
+	}
+}
+
+void spRegionAttachment_updateOffset (spRegionAttachment* self) {
+	float regionScaleX = self->width / self->regionOriginalWidth * self->scaleX;
+	float regionScaleY = self->height / self->regionOriginalHeight * self->scaleY;
+	float localX = -self->width / 2 * self->scaleX + self->regionOffsetX * regionScaleX;
+	float localY = -self->height / 2 * self->scaleY + self->regionOffsetY * regionScaleY;
+	float localX2 = localX + self->regionWidth * regionScaleX;
+	float localY2 = localY + self->regionHeight * regionScaleY;
+	float radians = self->rotation * DEG_RAD;
+	float cosine = COS(radians), sine = SIN(radians);
+	float localXCos = localX * cosine + self->x;
+	float localXSin = localX * sine;
+	float localYCos = localY * cosine + self->y;
+	float localYSin = localY * sine;
+	float localX2Cos = localX2 * cosine + self->x;
+	float localX2Sin = localX2 * sine;
+	float localY2Cos = localY2 * cosine + self->y;
+	float localY2Sin = localY2 * sine;
+	self->offset[BLX] = localXCos - localYSin;
+	self->offset[BLY] = localYCos + localXSin;
+	self->offset[ULX] = localXCos - localY2Sin;
+	self->offset[ULY] = localY2Cos + localXSin;
+	self->offset[URX] = localX2Cos - localY2Sin;
+	self->offset[URY] = localY2Cos + localX2Sin;
+	self->offset[BRX] = localX2Cos - localYSin;
+	self->offset[BRY] = localYCos + localX2Sin;
+}
+
+void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, spBone* bone, float* vertices, int offset, int stride) {
+	const float* offsets = self->offset;
+	float x = bone->worldX, y = bone->worldY;
+	float offsetX, offsetY;
+
+	offsetX = offsets[BRX];
+	offsetY = offsets[BRY];
+	vertices[offset] = offsetX * bone->a + offsetY * bone->b + x; /* br */
+	vertices[offset + 1] = offsetX * bone->c + offsetY * bone->d + y;
+	offset += stride;
+
+	offsetX = offsets[BLX];
+	offsetY = offsets[BLY];
+	vertices[offset] = offsetX * bone->a + offsetY * bone->b + x; /* bl */
+	vertices[offset + 1] = offsetX * bone->c + offsetY * bone->d + y;
+	offset += stride;
+
+	offsetX = offsets[ULX];
+	offsetY = offsets[ULY];
+	vertices[offset] = offsetX * bone->a + offsetY * bone->b + x; /* ul */
+	vertices[offset + 1] = offsetX * bone->c + offsetY * bone->d + y;
+	offset += stride;
+
+	offsetX = offsets[URX];
+	offsetY = offsets[URY];
+	vertices[offset] = offsetX * bone->a + offsetY * bone->b + x; /* ur */
+	vertices[offset + 1] = offsetX * bone->c + offsetY * bone->d + y;
+}

+ 583 - 0
spine-cpp/spine-cpp/src/spine/Skeleton.c

@@ -0,0 +1,583 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Skeleton.h>
+#include <stdlib.h>
+#include <string.h>
+#include <spine/extension.h>
+
+typedef enum {
+	SP_UPDATE_BONE, SP_UPDATE_IK_CONSTRAINT, SP_UPDATE_PATH_CONSTRAINT, SP_UPDATE_TRANSFORM_CONSTRAINT
+} _spUpdateType;
+
+typedef struct {
+	_spUpdateType type;
+	void* object;
+} _spUpdate;
+
+typedef struct {
+	spSkeleton super;
+
+	int updateCacheCount;
+	int updateCacheCapacity;
+	_spUpdate* updateCache;
+
+	int updateCacheResetCount;
+	int updateCacheResetCapacity;
+	spBone** updateCacheReset;
+} _spSkeleton;
+
+spSkeleton* spSkeleton_create (spSkeletonData* data) {
+	int i;
+	int* childrenCounts;
+
+	_spSkeleton* internal = NEW(_spSkeleton);
+	spSkeleton* self = SUPER(internal);
+	CONST_CAST(spSkeletonData*, self->data) = data;
+
+	self->bonesCount = self->data->bonesCount;
+	self->bones = MALLOC(spBone*, self->bonesCount);
+	childrenCounts = CALLOC(int, self->bonesCount);
+
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBoneData* boneData = self->data->bones[i];
+		spBone* newBone;
+		if (!boneData->parent)
+			newBone = spBone_create(boneData, self, 0);
+		else {
+			spBone* parent = self->bones[boneData->parent->index];
+			newBone = spBone_create(boneData, self, parent);
+			++childrenCounts[boneData->parent->index];
+		}
+		self->bones[i] = newBone;
+	}
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBoneData* boneData = self->data->bones[i];
+		spBone* bone = self->bones[i];
+		CONST_CAST(spBone**, bone->children) = MALLOC(spBone*, childrenCounts[boneData->index]);
+	}
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBone* bone = self->bones[i];
+		spBone* parent = bone->parent;
+		if (parent)
+			parent->children[parent->childrenCount++] = bone;
+	}
+	CONST_CAST(spBone*, self->root) = (self->bonesCount > 0 ? self->bones[0] : NULL);
+
+	self->slotsCount = data->slotsCount;
+	self->slots = MALLOC(spSlot*, self->slotsCount);
+	for (i = 0; i < self->slotsCount; ++i) {
+		spSlotData *slotData = data->slots[i];
+		spBone* bone = self->bones[slotData->boneData->index];
+		self->slots[i] = spSlot_create(slotData, bone);
+	}
+
+	self->drawOrder = MALLOC(spSlot*, self->slotsCount);
+	memcpy(self->drawOrder, self->slots, sizeof(spSlot*) * self->slotsCount);
+
+	self->ikConstraintsCount = data->ikConstraintsCount;
+	self->ikConstraints = MALLOC(spIkConstraint*, self->ikConstraintsCount);
+	for (i = 0; i < self->data->ikConstraintsCount; ++i)
+		self->ikConstraints[i] = spIkConstraint_create(self->data->ikConstraints[i], self);
+
+	self->transformConstraintsCount = data->transformConstraintsCount;
+	self->transformConstraints = MALLOC(spTransformConstraint*, self->transformConstraintsCount);
+	for (i = 0; i < self->data->transformConstraintsCount; ++i)
+		self->transformConstraints[i] = spTransformConstraint_create(self->data->transformConstraints[i], self);
+
+	self->pathConstraintsCount = data->pathConstraintsCount;
+	self->pathConstraints = MALLOC(spPathConstraint*, self->pathConstraintsCount);
+	for (i = 0; i < self->data->pathConstraintsCount; i++)
+		self->pathConstraints[i] = spPathConstraint_create(self->data->pathConstraints[i], self);
+
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+
+	spSkeleton_updateCache(self);
+
+	FREE(childrenCounts);
+
+	return self;
+}
+
+void spSkeleton_dispose (spSkeleton* self) {
+	int i;
+	_spSkeleton* internal = SUB_CAST(_spSkeleton, self);
+
+	FREE(internal->updateCache);
+	FREE(internal->updateCacheReset);
+
+	for (i = 0; i < self->bonesCount; ++i)
+		spBone_dispose(self->bones[i]);
+	FREE(self->bones);
+
+	for (i = 0; i < self->slotsCount; ++i)
+		spSlot_dispose(self->slots[i]);
+	FREE(self->slots);
+
+	for (i = 0; i < self->ikConstraintsCount; ++i)
+		spIkConstraint_dispose(self->ikConstraints[i]);
+	FREE(self->ikConstraints);
+
+	for (i = 0; i < self->transformConstraintsCount; ++i)
+		spTransformConstraint_dispose(self->transformConstraints[i]);
+	FREE(self->transformConstraints);
+
+	for (i = 0; i < self->pathConstraintsCount; i++)
+		spPathConstraint_dispose(self->pathConstraints[i]);
+	FREE(self->pathConstraints);
+
+	FREE(self->drawOrder);
+	FREE(self);
+}
+
+static void _addToUpdateCache(_spSkeleton* const internal, _spUpdateType type, void *object) {
+	_spUpdate* update;
+	if (internal->updateCacheCount == internal->updateCacheCapacity) {
+		internal->updateCacheCapacity *= 2;
+		internal->updateCache = (_spUpdate*)realloc(internal->updateCache, sizeof(_spUpdate) * internal->updateCacheCapacity);
+	}
+	update = internal->updateCache + internal->updateCacheCount;
+	update->type = type;
+	update->object = object;
+	++internal->updateCacheCount;
+}
+
+static void _addToUpdateCacheReset(_spSkeleton* const internal, spBone* bone) {
+	if (internal->updateCacheResetCount == internal->updateCacheResetCapacity) {
+		internal->updateCacheResetCapacity *= 2;
+		internal->updateCacheReset = (spBone**)realloc(internal->updateCacheReset, sizeof(spBone*) * internal->updateCacheResetCapacity);
+	}
+	internal->updateCacheReset[internal->updateCacheResetCount] = bone;
+	++internal->updateCacheResetCount;
+}
+
+static void _sortBone(_spSkeleton* const internal, spBone* bone) {
+	if (bone->sorted) return;
+	if (bone->parent) _sortBone(internal, bone->parent);
+	bone->sorted = 1;
+	_addToUpdateCache(internal, SP_UPDATE_BONE, bone);
+}
+
+static void _sortPathConstraintAttachmentBones(_spSkeleton* const internal, spAttachment* attachment, spBone* slotBone) {
+	spPathAttachment* pathAttachment = (spPathAttachment*)attachment;
+	int* pathBones;
+	int pathBonesCount;
+	if (pathAttachment->super.super.type != SP_ATTACHMENT_PATH) return;
+	pathBones = pathAttachment->super.bones;
+	pathBonesCount = pathAttachment->super.bonesCount;
+	if (pathBones == 0)
+		_sortBone(internal, slotBone);
+	else {
+		spBone** bones = internal->super.bones;
+		int i = 0, n;
+		while (i < pathBonesCount) {
+			int boneCount = pathBones[i++];
+			for (n = i + boneCount; i < n; i++)
+				_sortBone(internal, bones[pathBones[i]]);
+		}
+	}
+}
+
+static void _sortPathConstraintAttachment(_spSkeleton* const internal, spSkin* skin, int slotIndex, spBone* slotBone) {
+	_Entry* entry = SUB_CAST(_spSkin, skin)->entries;
+	while (entry) {
+		if (entry->slotIndex == slotIndex) _sortPathConstraintAttachmentBones(internal, entry->attachment, slotBone);
+		entry = entry->next;
+	}
+}
+
+static void _sortReset(spBone** bones, int bonesCount) {
+	int i;
+	for (i = 0; i < bonesCount; ++i) {
+		spBone* bone = bones[i];
+		if (bone->sorted) _sortReset(bone->children, bone->childrenCount);
+		bone->sorted = 0;
+	}
+}
+
+static void _sortIkConstraint (_spSkeleton* const internal, spIkConstraint* constraint) {
+	int /*bool*/ contains = 0;
+	int i;
+	spBone* target = constraint->target;
+	spBone** constrained;
+	spBone* parent;
+	_sortBone(internal, target);
+
+	constrained = constraint->bones;
+	parent = constrained[0];
+	_sortBone(internal, parent);
+
+	if (constraint->bonesCount > 1) {
+		spBone* child = constrained[constraint->bonesCount - 1];
+		contains = 0;
+		for (i = 0; i < internal->updateCacheCount; i++) {
+			_spUpdate update = internal->updateCache[i];
+			if (update.object == child) {
+				contains = -1;
+				break;
+			}
+		}
+		if (!contains)
+			_addToUpdateCacheReset(internal, child);
+	}
+
+	_addToUpdateCache(internal, SP_UPDATE_IK_CONSTRAINT, constraint);
+
+	_sortReset(parent->children, parent->childrenCount);
+	constrained[constraint->bonesCount-1]->sorted = 1;
+}
+
+static void _sortPathConstraint(_spSkeleton* const internal, spPathConstraint* constraint) {
+	spSlot* slot = constraint->target;
+	int slotIndex = slot->data->index;
+	spBone* slotBone = slot->bone;
+	int i, n, boneCount;
+	spAttachment* attachment;
+	spBone** constrained;
+	spSkeleton* skeleton = SUPER_CAST(spSkeleton, internal);
+	if (skeleton->skin) _sortPathConstraintAttachment(internal, skeleton->skin, slotIndex, slotBone);
+	if (skeleton->data->defaultSkin && skeleton->data->defaultSkin != skeleton->skin)
+		_sortPathConstraintAttachment(internal, skeleton->data->defaultSkin, slotIndex, slotBone);
+	for (i = 0, n = skeleton->data->skinsCount; i < n; i++)
+		_sortPathConstraintAttachment(internal, skeleton->data->skins[i], slotIndex, slotBone);
+
+	attachment = slot->attachment;
+	if (attachment && attachment->type == SP_ATTACHMENT_PATH) _sortPathConstraintAttachmentBones(internal, attachment, slotBone);
+
+	constrained = constraint->bones;
+	boneCount = constraint->bonesCount;
+	for (i = 0; i < boneCount; i++)
+		_sortBone(internal, constrained[i]);
+
+	_addToUpdateCache(internal, SP_UPDATE_PATH_CONSTRAINT, constraint);
+
+	for (i = 0; i < boneCount; i++)
+		_sortReset(constrained[i]->children, constrained[i]->childrenCount);
+	for (i = 0; i < boneCount; i++)
+		constrained[i]->sorted = 1;
+}
+
+static void _sortTransformConstraint(_spSkeleton* const internal, spTransformConstraint* constraint) {
+	int i, boneCount;
+	spBone** constrained;
+	spBone* child;
+	int /*boolean*/ contains = 0;
+	_sortBone(internal, constraint->target);
+
+	constrained = constraint->bones;
+	boneCount = constraint->bonesCount;
+	if (constraint->data->local) {
+		for (i = 0; i < boneCount; i++) {
+			child = constrained[i];
+			_sortBone(internal, child);
+			contains = 0;
+			for (i = 0; i < internal->updateCacheCount; i++) {
+				_spUpdate update = internal->updateCache[i];
+				if (update.object == child) {
+					contains = -1;
+					break;
+				}
+			}
+			if (!contains) _addToUpdateCacheReset(internal, child);
+		}
+	} else {
+		for (i = 0; i < boneCount; i++)
+			_sortBone(internal, constrained[i]);
+	}
+
+	_addToUpdateCache(internal, SP_UPDATE_TRANSFORM_CONSTRAINT, constraint);
+
+	for (i = 0; i < boneCount; i++)
+		_sortReset(constrained[i]->children, constrained[i]->childrenCount);
+	for (i = 0; i < boneCount; i++)
+		constrained[i]->sorted = 1;
+}
+
+void spSkeleton_updateCache (spSkeleton* self) {
+	int i, ii;
+	spBone** bones;
+	spIkConstraint** ikConstraints;
+	spPathConstraint** pathConstraints;
+	spTransformConstraint** transformConstraints;
+	int ikCount, transformCount, pathCount, constraintCount;
+	_spSkeleton* internal = SUB_CAST(_spSkeleton, self);
+
+	internal->updateCacheCapacity = self->bonesCount + self->ikConstraintsCount + self->transformConstraintsCount + self->pathConstraintsCount;
+	FREE(internal->updateCache);
+	internal->updateCache = MALLOC(_spUpdate, internal->updateCacheCapacity);
+	internal->updateCacheCount = 0;
+
+	internal->updateCacheResetCapacity = self->bonesCount;
+	FREE(internal->updateCacheReset);
+	internal->updateCacheReset = MALLOC(spBone*, internal->updateCacheResetCapacity);
+	internal->updateCacheResetCount = 0;
+
+	bones = self->bones;
+	for (i = 0; i < self->bonesCount; ++i)
+		bones[i]->sorted = 0;
+
+	/* IK first, lowest hierarchy depth first. */
+	ikConstraints = self->ikConstraints;
+	transformConstraints = self->transformConstraints;
+	pathConstraints = self->pathConstraints;
+	ikCount = self->ikConstraintsCount; transformCount = self->transformConstraintsCount; pathCount = self->pathConstraintsCount;
+	constraintCount = ikCount + transformCount + pathCount;
+
+	i = 0;
+	continue_outer:
+	for (; i < constraintCount; i++) {
+		for (ii = 0; ii < ikCount; ii++) {
+			spIkConstraint* ikConstraint = ikConstraints[ii];
+			if (ikConstraint->data->order == i) {
+				_sortIkConstraint(internal, ikConstraint);
+				i++;
+				goto continue_outer;
+			}
+		}
+
+		for (ii = 0; ii < transformCount; ii++) {
+			spTransformConstraint* transformConstraint = transformConstraints[ii];
+			if (transformConstraint->data->order == i) {
+				_sortTransformConstraint(internal, transformConstraint);
+				i++;
+				goto continue_outer;
+			}
+		}
+
+		for (ii = 0; ii < pathCount; ii++) {
+			spPathConstraint* pathConstraint = pathConstraints[ii];
+			if (pathConstraint->data->order == i) {
+				_sortPathConstraint(internal, pathConstraint);
+				i++;
+				goto continue_outer;
+			}
+		}
+	}
+
+	for (i = 0; i < self->bonesCount; ++i)
+		_sortBone(internal, self->bones[i]);
+}
+
+void spSkeleton_updateWorldTransform (const spSkeleton* self) {
+	int i;
+	_spSkeleton* internal = SUB_CAST(_spSkeleton, self);
+	spBone** updateCacheReset = internal->updateCacheReset;
+	for (i = 0; i < internal->updateCacheResetCount; i++) {
+		spBone* bone = updateCacheReset[i];
+		CONST_CAST(float, bone->ax) = bone->x;
+		CONST_CAST(float, bone->ay) = bone->y;
+		CONST_CAST(float, bone->arotation) = bone->rotation;
+		CONST_CAST(float, bone->ascaleX) = bone->scaleX;
+		CONST_CAST(float, bone->ascaleY) = bone->scaleY;
+		CONST_CAST(float, bone->ashearX) = bone->shearX;
+		CONST_CAST(float, bone->ashearY) = bone->shearY;
+		CONST_CAST(int, bone->appliedValid) = 1;
+	}
+
+	for (i = 0; i < internal->updateCacheCount; ++i) {
+		_spUpdate* update = internal->updateCache + i;
+		switch (update->type) {
+		case SP_UPDATE_BONE:
+			spBone_updateWorldTransform((spBone*)update->object);
+			break;
+		case SP_UPDATE_IK_CONSTRAINT:
+			spIkConstraint_apply((spIkConstraint*)update->object);
+			break;
+		case SP_UPDATE_TRANSFORM_CONSTRAINT:
+			spTransformConstraint_apply((spTransformConstraint*)update->object);
+			break;
+		case SP_UPDATE_PATH_CONSTRAINT:
+			spPathConstraint_apply((spPathConstraint*)update->object);
+			break;
+		}
+	}
+}
+
+void spSkeleton_setToSetupPose (const spSkeleton* self) {
+	spSkeleton_setBonesToSetupPose(self);
+	spSkeleton_setSlotsToSetupPose(self);
+}
+
+void spSkeleton_setBonesToSetupPose (const spSkeleton* self) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		spBone_setToSetupPose(self->bones[i]);
+
+	for (i = 0; i < self->ikConstraintsCount; ++i) {
+		spIkConstraint* ikConstraint = self->ikConstraints[i];
+		ikConstraint->bendDirection = ikConstraint->data->bendDirection;
+		ikConstraint->mix = ikConstraint->data->mix;
+	}
+
+	for (i = 0; i < self->transformConstraintsCount; ++i) {
+		spTransformConstraint* constraint = self->transformConstraints[i];
+		spTransformConstraintData* data = constraint->data;
+		constraint->rotateMix = data->rotateMix;
+		constraint->translateMix = data->translateMix;
+		constraint->scaleMix = data->scaleMix;
+		constraint->shearMix = data->shearMix;
+	}
+
+	for (i = 0; i < self->pathConstraintsCount; ++i) {
+		spPathConstraint* constraint = self->pathConstraints[i];
+		spPathConstraintData* data = constraint->data;
+		constraint->position = data->position;
+		constraint->spacing = data->spacing;
+		constraint->rotateMix = data->rotateMix;
+		constraint->translateMix = data->translateMix;
+	}
+}
+
+void spSkeleton_setSlotsToSetupPose (const spSkeleton* self) {
+	int i;
+	memcpy(self->drawOrder, self->slots, self->slotsCount * sizeof(spSlot*));
+	for (i = 0; i < self->slotsCount; ++i)
+		spSlot_setToSetupPose(self->slots[i]);
+}
+
+spBone* spSkeleton_findBone (const spSkeleton* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		if (strcmp(self->data->bones[i]->name, boneName) == 0) return self->bones[i];
+	return 0;
+}
+
+int spSkeleton_findBoneIndex (const spSkeleton* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		if (strcmp(self->data->bones[i]->name, boneName) == 0) return i;
+	return -1;
+}
+
+spSlot* spSkeleton_findSlot (const spSkeleton* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotsCount; ++i)
+		if (strcmp(self->data->slots[i]->name, slotName) == 0) return self->slots[i];
+	return 0;
+}
+
+int spSkeleton_findSlotIndex (const spSkeleton* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotsCount; ++i)
+		if (strcmp(self->data->slots[i]->name, slotName) == 0) return i;
+	return -1;
+}
+
+int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName) {
+	spSkin *skin;
+	if (!skinName) {
+		spSkeleton_setSkin(self, 0);
+		return 1;
+	}
+	skin = spSkeletonData_findSkin(self->data, skinName);
+	if (!skin) return 0;
+	spSkeleton_setSkin(self, skin);
+	return 1;
+}
+
+void spSkeleton_setSkin (spSkeleton* self, spSkin* newSkin) {
+	if (newSkin) {
+		if (self->skin)
+			spSkin_attachAll(newSkin, self, self->skin);
+		else {
+			/* No previous skin, attach setup pose attachments. */
+			int i;
+			for (i = 0; i < self->slotsCount; ++i) {
+				spSlot* slot = self->slots[i];
+				if (slot->data->attachmentName) {
+					spAttachment* attachment = spSkin_getAttachment(newSkin, i, slot->data->attachmentName);
+					if (attachment) spSlot_setAttachment(slot, attachment);
+				}
+			}
+		}
+	}
+	CONST_CAST(spSkin*, self->skin) = newSkin;
+}
+
+spAttachment* spSkeleton_getAttachmentForSlotName (const spSkeleton* self, const char* slotName, const char* attachmentName) {
+	int slotIndex = spSkeletonData_findSlotIndex(self->data, slotName);
+	return spSkeleton_getAttachmentForSlotIndex(self, slotIndex, attachmentName);
+}
+
+spAttachment* spSkeleton_getAttachmentForSlotIndex (const spSkeleton* self, int slotIndex, const char* attachmentName) {
+	if (slotIndex == -1) return 0;
+	if (self->skin) {
+		spAttachment *attachment = spSkin_getAttachment(self->skin, slotIndex, attachmentName);
+		if (attachment) return attachment;
+	}
+	if (self->data->defaultSkin) {
+		spAttachment *attachment = spSkin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName);
+		if (attachment) return attachment;
+	}
+	return 0;
+}
+
+int spSkeleton_setAttachment (spSkeleton* self, const char* slotName, const char* attachmentName) {
+	int i;
+	for (i = 0; i < self->slotsCount; ++i) {
+		spSlot *slot = self->slots[i];
+		if (strcmp(slot->data->name, slotName) == 0) {
+			if (!attachmentName)
+				spSlot_setAttachment(slot, 0);
+			else {
+				spAttachment* attachment = spSkeleton_getAttachmentForSlotIndex(self, i, attachmentName);
+				if (!attachment) return 0;
+				spSlot_setAttachment(slot, attachment);
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+spIkConstraint* spSkeleton_findIkConstraint (const spSkeleton* self, const char* constraintName) {
+	int i;
+	for (i = 0; i < self->ikConstraintsCount; ++i)
+		if (strcmp(self->ikConstraints[i]->data->name, constraintName) == 0) return self->ikConstraints[i];
+	return 0;
+}
+
+spTransformConstraint* spSkeleton_findTransformConstraint (const spSkeleton* self, const char* constraintName) {
+	int i;
+	for (i = 0; i < self->transformConstraintsCount; ++i)
+		if (strcmp(self->transformConstraints[i]->data->name, constraintName) == 0) return self->transformConstraints[i];
+	return 0;
+}
+
+spPathConstraint* spSkeleton_findPathConstraint (const spSkeleton* self, const char* constraintName) {
+	int i;
+	for (i = 0; i < self->pathConstraintsCount; ++i)
+		if (strcmp(self->pathConstraints[i]->data->name, constraintName) == 0) return self->pathConstraints[i];
+	return 0;
+}
+
+void spSkeleton_update (spSkeleton* self, float deltaTime) {
+	self->time += deltaTime;
+}

+ 1095 - 0
spine-cpp/spine-cpp/src/spine/SkeletonBinary.c

@@ -0,0 +1,1095 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SkeletonBinary.h>
+#include <stdio.h>
+#include <spine/extension.h>
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/Animation.h>
+#include "kvec.h"
+
+typedef struct {
+	const unsigned char* cursor; 
+	const unsigned char* end;
+} _dataInput;
+
+typedef struct {
+	const char* parent;
+	const char* skin;
+	int slotIndex;
+	spMeshAttachment* mesh;
+} _spLinkedMesh;
+
+typedef struct {
+	spSkeletonBinary super;
+	int ownsLoader;
+
+	int linkedMeshCount;
+	int linkedMeshCapacity;
+	_spLinkedMesh* linkedMeshes;
+} _spSkeletonBinary;
+
+spSkeletonBinary* spSkeletonBinary_createWithLoader (spAttachmentLoader* attachmentLoader) {
+	spSkeletonBinary* self = SUPER(NEW(_spSkeletonBinary));
+	self->scale = 1;
+	self->attachmentLoader = attachmentLoader;
+	return self;
+}
+
+spSkeletonBinary* spSkeletonBinary_create (spAtlas* atlas) {
+	spAtlasAttachmentLoader* attachmentLoader = spAtlasAttachmentLoader_create(atlas);
+	spSkeletonBinary* self = spSkeletonBinary_createWithLoader(SUPER(attachmentLoader));
+	SUB_CAST(_spSkeletonBinary, self)->ownsLoader = 1;
+	return self;
+}
+
+void spSkeletonBinary_dispose (spSkeletonBinary* self) {
+	int i;
+	_spSkeletonBinary* internal = SUB_CAST(_spSkeletonBinary, self);
+	if (internal->ownsLoader) spAttachmentLoader_dispose(self->attachmentLoader);
+	for (i = 0; i < internal->linkedMeshCount; ++i) {
+		FREE(internal->linkedMeshes[i].parent);
+		FREE(internal->linkedMeshes[i].skin);
+	}
+	FREE(internal->linkedMeshes);
+	FREE(self->error);
+	FREE(self);
+}
+
+void _spSkeletonBinary_setError (spSkeletonBinary* self, const char* value1, const char* value2) {
+	char message[256];
+	int length;
+	FREE(self->error);
+	strcpy(message, value1);
+	length = (int)strlen(value1);
+	if (value2) strncat(message + length, value2, 255 - length);
+	MALLOC_STR(self->error, message);
+}
+
+static unsigned char readByte (_dataInput* input) {
+	return *input->cursor++;
+}
+
+static signed char readSByte (_dataInput* input) {
+	return (signed char)readByte(input);
+}
+
+static int readBoolean (_dataInput* input) {
+	return readByte(input) != 0;
+}
+
+static int readInt (_dataInput* input) {
+	int result = readByte(input);
+	result <<= 8;
+	result |= readByte(input);
+	result <<= 8;
+	result |= readByte(input);
+	result <<= 8;
+	result |= readByte(input);
+	return result;
+}
+
+static int readVarint (_dataInput* input, int/*bool*/optimizePositive) {
+	unsigned char b = readByte(input);
+	int value = b & 0x7F;
+	if (b & 0x80) {
+		b = readByte(input);
+		value |= (b & 0x7F) << 7;
+		if (b & 0x80) {
+				b = readByte(input);
+				value |= (b & 0x7F) << 14;
+				if (b & 0x80) {
+					b = readByte(input);
+					value |= (b & 0x7F) << 21;
+					if (b & 0x80) value |= (readByte(input) & 0x7F) << 28;
+				}
+		}
+	}
+	if (!optimizePositive) value = (((unsigned int)value >> 1) ^ -(value & 1));
+	return value;
+}
+
+float readFloat (_dataInput* input) {
+	union {
+		int intValue;
+		float floatValue;
+	} intToFloat;
+	intToFloat.intValue = readInt(input);
+	return intToFloat.floatValue;
+}
+
+char* readString (_dataInput* input) {
+	int length = readVarint(input, 1);
+	char* string;
+	if (length == 0) {
+		return 0;
+	}
+	string = MALLOC(char, length);
+	memcpy(string, input->cursor, length - 1);
+	input->cursor += length - 1;
+	string[length - 1] = '\0';
+	return string;
+}
+
+static void readColor (_dataInput* input, float *r, float *g, float *b, float *a) {
+	*r = readByte(input) / 255.0f;
+	*g = readByte(input) / 255.0f;
+	*b = readByte(input) / 255.0f;
+	*a = readByte(input) / 255.0f;
+}
+
+#define ATTACHMENT_REGION 0
+#define ATTACHMENT_BOUNDING_BOX 1
+#define ATTACHMENT_MESH 2
+#define ATTACHMENT_LINKED_MESH 3
+#define ATTACHMENT_PATH 4
+
+#define BLEND_MODE_NORMAL 0
+#define BLEND_MODE_ADDITIVE 1
+#define BLEND_MODE_MULTIPLY 2
+#define BLEND_MODE_SCREEN 3
+
+#define CURVE_LINEAR 0
+#define CURVE_STEPPED 1
+#define CURVE_BEZIER 2
+
+#define BONE_ROTATE 0
+#define BONE_TRANSLATE 1
+#define BONE_SCALE 2
+#define BONE_SHEAR 3
+
+#define SLOT_ATTACHMENT 0
+#define SLOT_COLOR 1
+#define SLOT_TWO_COLOR 2
+
+#define PATH_POSITION 0
+#define PATH_SPACING 1
+#define PATH_MIX 2
+
+#define PATH_POSITION_FIXED 0
+#define PATH_POSITION_PERCENT 1
+
+#define PATH_SPACING_LENGTH 0
+#define PATH_SPACING_FIXED 1
+#define PATH_SPACING_PERCENT 2
+
+#define PATH_ROTATE_TANGENT 0
+#define PATH_ROTATE_CHAIN 1
+#define PATH_ROTATE_CHAIN_SCALE 2
+
+static void readCurve (_dataInput* input, spCurveTimeline* timeline, int frameIndex) {
+	switch (readByte(input)) {
+		case CURVE_STEPPED: {
+			spCurveTimeline_setStepped(timeline, frameIndex);
+			break;
+		}
+		case CURVE_BEZIER: {
+			float cx1 = readFloat(input);
+			float cy1 = readFloat(input);
+			float cx2 = readFloat(input);
+			float cy2 = readFloat(input);
+			spCurveTimeline_setCurve(timeline, frameIndex, cx1, cy1, cx2, cy2);
+			break;
+		}
+	}
+}
+
+static void _spSkeletonBinary_addLinkedMesh (spSkeletonBinary* self, spMeshAttachment* mesh,
+		const char* skin, int slotIndex, const char* parent) {
+	_spLinkedMesh* linkedMesh;
+	_spSkeletonBinary* internal = SUB_CAST(_spSkeletonBinary, self);
+
+	if (internal->linkedMeshCount == internal->linkedMeshCapacity) {
+		_spLinkedMesh* linkedMeshes;
+		internal->linkedMeshCapacity *= 2;
+		if (internal->linkedMeshCapacity < 8) internal->linkedMeshCapacity = 8;
+		/* TODO Why not realloc? */
+		linkedMeshes = MALLOC(_spLinkedMesh, internal->linkedMeshCapacity);
+		memcpy(linkedMeshes, internal->linkedMeshes, sizeof(_spLinkedMesh) * internal->linkedMeshCount);
+		FREE(internal->linkedMeshes);
+		internal->linkedMeshes = linkedMeshes;
+	}
+
+	linkedMesh = internal->linkedMeshes + internal->linkedMeshCount++;
+	linkedMesh->mesh = mesh;
+	linkedMesh->skin = skin;
+	linkedMesh->slotIndex = slotIndex;
+	linkedMesh->parent = parent;
+}
+
+static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, const char* name,
+		_dataInput* input, spSkeletonData *skeletonData) {
+	kvec_t(spTimeline*) timelines;
+	float duration = 0;
+	int i, n, ii, nn, iii, nnn;
+	int frameIndex;
+	int drawOrderCount, eventCount;
+	spAnimation* animation;
+
+	kv_init(timelines);
+
+	/* Slot timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		int slotIndex = readVarint(input, 1);
+		for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) {
+			unsigned char timelineType = readByte(input);
+			int frameCount = readVarint(input, 1);
+			switch (timelineType) {
+				case SLOT_ATTACHMENT: {
+					spAttachmentTimeline* timeline = spAttachmentTimeline_create(frameCount);
+					timeline->slotIndex = slotIndex;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						const char* attachmentName = readString(input);
+						/* TODO Avoid copying of attachmentName inside */
+						spAttachmentTimeline_setFrame(timeline, frameIndex, time, attachmentName);
+						FREE(attachmentName);
+					}
+					kv_push(spTimeline*, timelines, SUPER(timeline));
+					duration = MAX(duration, timeline->frames[frameCount - 1]);
+					break;
+				}
+				case SLOT_COLOR: {
+					spColorTimeline* timeline = spColorTimeline_create(frameCount);
+					timeline->slotIndex = slotIndex;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float r, g, b, a;
+						readColor(input, &r, &g, &b, &a);
+						spColorTimeline_setFrame(timeline, frameIndex, time, r, g, b, a);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * COLOR_ENTRIES]);
+					break;
+				}
+				case SLOT_TWO_COLOR: {
+					spTwoColorTimeline* timeline = spTwoColorTimeline_create(frameCount);
+					timeline->slotIndex = slotIndex;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float r, g, b, a;
+						float r2, g2, b2, a2;
+						readColor(input, &r, &g, &b, &a);
+						readColor(input, &a2, &r2, &g2, &b2);
+						spTwoColorTimeline_setFrame(timeline, frameIndex, time, r, g, b, a, r2, g2, b2);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * TWOCOLOR_ENTRIES]);
+					break;
+				}
+				default: {
+					int i;
+					for (i = 0; i < kv_size(timelines); ++i)
+						spTimeline_dispose(kv_A(timelines, i));
+					kv_destroy(timelines);
+					_spSkeletonBinary_setError(self, "Invalid timeline type for a slot: ", skeletonData->slots[slotIndex]->name);
+					return 0;
+				}
+			}
+		}
+	}
+
+	/* Bone timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		int boneIndex = readVarint(input, 1);
+		for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) {
+			unsigned char timelineType = readByte(input);
+			int frameCount = readVarint(input, 1);
+			switch (timelineType) {
+				case BONE_ROTATE: {
+					spRotateTimeline *timeline = spRotateTimeline_create(frameCount);
+					timeline->boneIndex = boneIndex;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float degrees = readFloat(input);
+						spRotateTimeline_setFrame(timeline, frameIndex, time, degrees);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * ROTATE_ENTRIES]);
+					break;
+				}
+				case BONE_TRANSLATE:
+				case BONE_SCALE:
+				case BONE_SHEAR: {
+					float timelineScale = 1;
+					spTranslateTimeline *timeline = 0;
+					switch (timelineType) {
+						case BONE_SCALE:
+							timeline = spScaleTimeline_create(frameCount);
+							break;
+						case BONE_SHEAR:
+							timeline = spShearTimeline_create(frameCount);
+							break;
+						case BONE_TRANSLATE:
+							timeline = spTranslateTimeline_create(frameCount);
+							timelineScale = self->scale;
+							break;
+						default:
+							break;
+					}
+					timeline->boneIndex = boneIndex;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float x = readFloat(input) * timelineScale;
+						float y = readFloat(input) * timelineScale;
+						spTranslateTimeline_setFrame(timeline, frameIndex, time, x, y);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER_CAST(spTimeline, timeline));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * TRANSLATE_ENTRIES]);
+					break;
+				}
+				default: {
+					int i;
+					for (i = 0; i < kv_size(timelines); ++i)
+						spTimeline_dispose(kv_A(timelines, i));
+					kv_destroy(timelines);
+					_spSkeletonBinary_setError(self, "Invalid timeline type for a bone: ", skeletonData->bones[boneIndex]->name);
+					return 0;
+				}
+			}
+		}
+	}
+
+	/* IK constraint timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		int index = readVarint(input, 1);
+		int frameCount = readVarint(input, 1);
+		spIkConstraintTimeline* timeline = spIkConstraintTimeline_create(frameCount);
+		timeline->ikConstraintIndex = index;
+		for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+			float time = readFloat(input);
+			float mix = readFloat(input);
+			signed char bendDirection = readSByte(input);
+			spIkConstraintTimeline_setFrame(timeline, frameIndex, time, mix, bendDirection);
+			if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+		}
+		kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+		duration = MAX(duration, timeline->frames[(frameCount - 1) * IKCONSTRAINT_ENTRIES]);
+	}
+
+	/* Transform constraint timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		int index = readVarint(input, 1);
+		int frameCount = readVarint(input, 1);
+		spTransformConstraintTimeline* timeline = spTransformConstraintTimeline_create(frameCount);
+		timeline->transformConstraintIndex = index;
+		for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+			float time = readFloat(input);
+			float rotateMix = readFloat(input);
+			float translateMix = readFloat(input);
+			float scaleMix = readFloat(input);
+			float shearMix = readFloat(input);
+			spTransformConstraintTimeline_setFrame(timeline, frameIndex, time, rotateMix, translateMix,
+					scaleMix, shearMix);
+			if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+		}
+		kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+		duration = MAX(duration, timeline->frames[(frameCount - 1) * TRANSFORMCONSTRAINT_ENTRIES]);
+	}
+
+	/* Path constraint timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		int index = readVarint(input, 1);
+		spPathConstraintData* data = skeletonData->pathConstraints[index];
+		for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) {
+			unsigned char timelineType = readByte(input);
+			int frameCount = readVarint(input, 1);
+			switch (timelineType) {
+				case PATH_POSITION:
+				case PATH_SPACING: {
+					spPathConstraintPositionTimeline* timeline = 0;
+					float timelineScale = 1;
+					if (timelineType == PATH_SPACING) {
+						timeline = (spPathConstraintPositionTimeline*)spPathConstraintSpacingTimeline_create(frameCount);
+						if (data->spacingMode == SP_SPACING_MODE_LENGTH || data->spacingMode == SP_SPACING_MODE_FIXED)
+							timelineScale = self->scale;
+					} else {
+						timeline = spPathConstraintPositionTimeline_create(frameCount);
+						if (data->positionMode == SP_POSITION_MODE_FIXED)
+							timelineScale = self->scale;
+					}
+					timeline->pathConstraintIndex = index;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float value = readFloat(input) * timelineScale;
+						spPathConstraintPositionTimeline_setFrame(timeline, frameIndex, time, value);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * PATHCONSTRAINTPOSITION_ENTRIES]);
+					break;
+				}
+				case PATH_MIX: {
+					spPathConstraintMixTimeline* timeline = spPathConstraintMixTimeline_create(frameCount);
+					timeline->pathConstraintIndex = index;
+					for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+						float time = readFloat(input);
+						float rotateMix = readFloat(input);
+						float translateMix = readFloat(input);
+						spPathConstraintMixTimeline_setFrame(timeline, frameIndex, time, rotateMix, translateMix);
+						if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+					}
+					kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+					duration = MAX(duration, timeline->frames[(frameCount - 1) * PATHCONSTRAINTMIX_ENTRIES]);
+				}
+			}
+		}
+	}
+
+	/* Deform timelines. */
+	for (i = 0, n = readVarint(input, 1); i < n; ++i) {
+		spSkin* skin = skeletonData->skins[readVarint(input, 1)];
+		for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) {
+			int slotIndex = readVarint(input, 1);
+			for (iii = 0, nnn = readVarint(input, 1); iii < nnn; ++iii) {
+				float* tempDeform;
+				spDeformTimeline *timeline;
+				int weighted, deformLength;
+				const char* attachmentName = readString(input);
+				int frameCount;
+
+				spVertexAttachment* attachment = SUB_CAST(spVertexAttachment,
+						spSkin_getAttachment(skin, slotIndex, attachmentName));
+				if (!attachment) {
+					int i;
+					for (i = 0; i < kv_size(timelines); ++i)
+						spTimeline_dispose(kv_A(timelines, i));
+					kv_destroy(timelines);
+					_spSkeletonBinary_setError(self, "Attachment not found: ", attachmentName);
+					FREE(attachmentName);
+					return 0;
+				}
+				FREE(attachmentName);
+
+				weighted = attachment->bones != 0;
+				deformLength = weighted ? attachment->verticesCount / 3 * 2 : attachment->verticesCount;
+				tempDeform = MALLOC(float, deformLength);
+
+				frameCount = readVarint(input, 1);
+				timeline = spDeformTimeline_create(frameCount, deformLength);
+				timeline->slotIndex = slotIndex;
+				timeline->attachment = SUPER(attachment);
+
+				for (frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
+					float time = readFloat(input);
+					float* deform;
+					int end = readVarint(input, 1);
+					if (!end) {
+						if (weighted) {
+							deform = tempDeform;
+							memset(deform, 0, sizeof(float) * deformLength);
+						} else
+							deform = attachment->vertices;
+					} else {
+						int v, start = readVarint(input, 1);
+						deform = tempDeform;
+						memset(deform, 0, sizeof(float) * start);
+						end += start;
+						if (self->scale == 1) {
+							for (v = start; v < end; ++v)
+								deform[v] = readFloat(input);
+						} else {
+							for (v = start; v < end; ++v)
+								deform[v] = readFloat(input) * self->scale;
+						}
+						memset(deform + v, 0, sizeof(float) * (deformLength - v));
+						if (!weighted) {
+							float* vertices = attachment->vertices;
+							for (v = 0; v < deformLength; ++v)
+								deform[v] += vertices[v];
+						}
+					}
+					spDeformTimeline_setFrame(timeline, frameIndex, time, deform);
+					if (frameIndex < frameCount - 1) readCurve(input, SUPER(timeline), frameIndex);
+				}
+				FREE(tempDeform);
+
+				kv_push(spTimeline*, timelines, SUPER(SUPER(timeline)));
+				duration = MAX(duration, timeline->frames[frameCount - 1]);
+			}
+		}
+	}
+
+	/* Draw order timeline. */
+	drawOrderCount = readVarint(input, 1);
+	if (drawOrderCount) {
+		spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrderCount, skeletonData->slotsCount);
+		for (i = 0; i < drawOrderCount; ++i) {
+			float time = readFloat(input);
+			int offsetCount = readVarint(input, 1);
+			int* drawOrder = MALLOC(int, skeletonData->slotsCount);
+			int* unchanged = MALLOC(int, skeletonData->slotsCount - offsetCount);
+			int originalIndex = 0, unchangedIndex = 0;
+			memset(drawOrder, -1, sizeof(int) * skeletonData->slotsCount);
+			for (ii = 0; ii < offsetCount; ++ii) {
+				int slotIndex = readVarint(input, 1);
+				/* Collect unchanged items. */
+				while (originalIndex != slotIndex)
+					unchanged[unchangedIndex++] = originalIndex++;
+				/* Set changed items. */
+				drawOrder[originalIndex + readVarint(input, 1)] = originalIndex;
+				++originalIndex;
+			}
+			/* Collect remaining unchanged items. */
+			while (originalIndex < skeletonData->slotsCount)
+				unchanged[unchangedIndex++] = originalIndex++;
+			/* Fill in unchanged items. */
+			for (ii = skeletonData->slotsCount - 1; ii >= 0; ii--)
+				if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+			FREE(unchanged);
+			/* TODO Avoid copying of drawOrder inside */
+			spDrawOrderTimeline_setFrame(timeline, i, time, drawOrder);
+			FREE(drawOrder);
+		}
+		kv_push(spTimeline*, timelines, SUPER(timeline));
+		duration = MAX(duration, timeline->frames[drawOrderCount - 1]);
+	}
+
+	/* Event timeline. */
+	eventCount = readVarint(input, 1);
+	if (eventCount) {
+		spEventTimeline* timeline = spEventTimeline_create(eventCount);
+		for (i = 0; i < eventCount; ++i) {
+			float time = readFloat(input);
+			spEventData* eventData = skeletonData->events[readVarint(input, 1)];
+			spEvent* event = spEvent_create(time, eventData);
+			event->intValue = readVarint(input, 0);
+			event->floatValue = readFloat(input);
+			if (readBoolean(input))
+				event->stringValue = readString(input);
+			else
+				MALLOC_STR(event->stringValue, eventData->stringValue);
+			spEventTimeline_setFrame(timeline, i, event);
+		}
+		kv_push(spTimeline*, timelines, SUPER(timeline));
+		duration = MAX(duration, timeline->frames[eventCount - 1]);
+	}
+
+	kv_trim(spTimeline*, timelines);
+
+	animation = spAnimation_create(name, 0);
+	FREE(animation->timelines);
+	animation->duration = duration;
+	animation->timelinesCount = kv_size(timelines);
+	animation->timelines = kv_array(timelines);
+	return animation;
+}
+
+static float* _readFloatArray(_dataInput *input, int n, float scale) {
+	float* array = MALLOC(float, n);
+	int i;
+	if (scale == 1)
+		for (i = 0; i < n; ++i)
+			array[i] = readFloat(input);
+	else
+		for (i = 0; i < n; ++i)
+			array[i] = readFloat(input) * scale;
+	return array;
+}
+
+static short* _readShortArray(_dataInput *input, int *length) {
+	int n = readVarint(input, 1);
+	short* array = MALLOC(short, n);
+	int i;
+	*length = n;
+	for (i = 0; i < n; ++i) {
+		array[i] = readByte(input) << 8;
+		array[i] |= readByte(input);
+	}
+	return array;
+}
+
+static void _readVertices(spSkeletonBinary* self, _dataInput* input, spVertexAttachment* attachment,
+		int vertexCount) {
+	int i, ii;
+	int verticesLength = vertexCount << 1;
+	kvec_t(float) weights;
+	kvec_t(int) bones;
+
+	attachment->worldVerticesLength = verticesLength;
+
+	if (!readBoolean(input)) {
+		attachment->verticesCount = verticesLength;
+		attachment->vertices = _readFloatArray(input, verticesLength, self->scale);
+		attachment->bonesCount = 0;
+		attachment->bones = 0;
+		return;
+	}
+
+	kv_init(weights);
+	kv_resize(float, weights, verticesLength * 3 * 3);
+
+	kv_init(bones);
+	kv_resize(int, bones, verticesLength * 3);
+
+	for (i = 0; i < vertexCount; ++i) {
+		int boneCount = readVarint(input, 1);
+		kv_push(int, bones, boneCount);
+		for (ii = 0; ii < boneCount; ++ii) {
+			kv_push(int, bones, readVarint(input, 1));
+			kv_push(float, weights, readFloat(input) * self->scale);
+			kv_push(float, weights, readFloat(input) * self->scale);
+			kv_push(float, weights, readFloat(input));
+		}
+	}
+
+	kv_trim(float, weights);
+	attachment->verticesCount = kv_size(weights);
+	attachment->vertices = kv_array(weights);
+
+	kv_trim(int, bones);
+	attachment->bonesCount = kv_size(bones);
+	attachment->bones = kv_array(bones);
+}
+
+spAttachment* spSkeletonBinary_readAttachment(spSkeletonBinary* self, _dataInput* input,
+		spSkin* skin, int slotIndex, const char* attachmentName, spSkeletonData* skeletonData, int/*bool*/ nonessential) {
+	int i;
+	spAttachmentType type;
+	const char* name = readString(input);
+	int freeName = name != 0;
+	if (!name) {
+		freeName = 0;
+		name = attachmentName;
+	}
+
+	type = (spAttachmentType)readByte(input);
+
+	switch (type) {
+		case SP_ATTACHMENT_REGION: {
+			const char* path = readString(input);
+			spAttachment* attachment;
+			spRegionAttachment* region;
+			if (!path) MALLOC_STR(path, name);
+			attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, path);
+			region = SUB_CAST(spRegionAttachment, attachment);
+			region->path = path;
+			region->rotation = readFloat(input);
+			region->x = readFloat(input) * self->scale;
+			region->y = readFloat(input) * self->scale;
+			region->scaleX = readFloat(input);
+			region->scaleY = readFloat(input);
+			region->width = readFloat(input) * self->scale;
+			region->height = readFloat(input) * self->scale;
+			readColor(input, &region->color.r, &region->color.g, &region->color.b, &region->color.a);
+			spRegionAttachment_updateOffset(region);
+			spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+			if (freeName) FREE(name);
+			return attachment;
+		}
+		case SP_ATTACHMENT_BOUNDING_BOX: {
+			int vertexCount = readVarint(input, 1);
+			spAttachment* attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, 0);
+			_readVertices(self, input, SUB_CAST(spVertexAttachment, attachment), vertexCount);
+			if (nonessential) readInt(input); /* Skip color. */
+			spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+			if (freeName) FREE(name);
+			return attachment;
+		}
+		case SP_ATTACHMENT_MESH: {
+			int vertexCount;
+			spAttachment* attachment;
+			spMeshAttachment* mesh;
+			const char* path = readString(input);
+			if (!path) MALLOC_STR(path, name);
+			attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, path);
+			mesh = SUB_CAST(spMeshAttachment, attachment);
+			mesh->path = path;
+			readColor(input, &mesh->color.r, &mesh->color.g, &mesh->color.b, &mesh->color.a);
+			vertexCount = readVarint(input, 1);
+			mesh->regionUVs = _readFloatArray(input, vertexCount << 1, 1);
+			mesh->triangles = (unsigned short*)_readShortArray(input, &mesh->trianglesCount);
+			_readVertices(self, input, SUPER(mesh), vertexCount);
+			spMeshAttachment_updateUVs(mesh);
+			mesh->hullLength = readVarint(input, 1) << 1;
+			if (nonessential) {
+				mesh->edges = (int*)_readShortArray(input, &mesh->edgesCount);
+				mesh->width = readFloat(input) * self->scale;
+				mesh->height = readFloat(input) * self->scale;
+			} else {
+				mesh->edges = 0;
+				mesh->width = 0;
+				mesh->height = 0;
+			}
+			spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+			if (freeName) FREE(name);
+			return attachment;
+		}
+		case SP_ATTACHMENT_LINKED_MESH: {
+			const char* skinName;
+			const char* parent;
+			spAttachment* attachment;
+			spMeshAttachment* mesh;
+			const char* path = readString(input);
+			if (!path) MALLOC_STR(path, name);
+			attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, path);
+			mesh = SUB_CAST(spMeshAttachment, attachment);
+			mesh->path = path;
+			readColor(input, &mesh->color.r, &mesh->color.g, &mesh->color.b, &mesh->color.a);
+			skinName = readString(input);
+			parent = readString(input);
+			mesh->inheritDeform = readBoolean(input);
+			if (nonessential) {
+				mesh->width = readFloat(input) * self->scale;
+				mesh->height = readFloat(input) * self->scale;
+			}
+			_spSkeletonBinary_addLinkedMesh(self, mesh, skinName, slotIndex, parent);
+			if (freeName) FREE(name);
+			return attachment;
+		}
+		case SP_ATTACHMENT_PATH: {
+			spAttachment* attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, 0);
+			spPathAttachment* path = SUB_CAST(spPathAttachment, attachment);
+			int vertexCount = 0;
+			path->closed = readBoolean(input);
+			path->constantSpeed = readBoolean(input);
+			vertexCount = readVarint(input, 1);
+			_readVertices(self, input, SUPER(path), vertexCount);
+			path->lengthsLength = vertexCount / 3;
+			path->lengths = MALLOC(float, path->lengthsLength);
+			for (i = 0; i < path->lengthsLength; ++i) {
+				path->lengths[i] = readFloat(input) * self->scale;
+			}
+			if (nonessential) readInt(input); /* Skip color. */
+			if (freeName) FREE(name);
+			return attachment;
+		}
+		case SP_ATTACHMENT_POINT: {
+			spAttachment* attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, 0);
+			spPointAttachment* point = SUB_CAST(spPointAttachment, attachment);
+			point->rotation = readFloat(input);
+			point->x = readFloat(input) * self->scale;
+			point->y = readFloat(input) * self->scale;
+
+			if (nonessential) {
+				readColor(input, &point->color.r, &point->color.g, &point->color.b, &point->color.a);
+			}
+			return attachment;
+		}
+		case SP_ATTACHMENT_CLIPPING: {
+			int endSlotIndex = readVarint(input, 1);
+			int vertexCount = readVarint(input, 1);
+			spAttachment* attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, name, 0);
+			spClippingAttachment* clip = SUB_CAST(spClippingAttachment, attachment);
+			_readVertices(self, input, SUB_CAST(spVertexAttachment, attachment), vertexCount);
+			if (nonessential) readInt(input); /* Skip color. */
+			clip->endSlot = skeletonData->slots[endSlotIndex];
+			spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+			if (freeName) FREE(name);
+			return attachment;
+		}
+	}
+
+	if (freeName) FREE(name);
+	return 0;
+}
+
+spSkin* spSkeletonBinary_readSkin(spSkeletonBinary* self, _dataInput* input,
+		const char* skinName, spSkeletonData* skeletonData, int/*bool*/ nonessential) {
+	spSkin* skin;
+	int slotCount = readVarint(input, 1);
+	int i, ii, nn;
+	if (slotCount == 0)
+		return 0;
+	skin = spSkin_create(skinName);
+	for (i = 0; i < slotCount; ++i) {
+		int slotIndex = readVarint(input, 1);
+		for (ii = 0, nn = readVarint(input, 1); ii < nn; ++ii) {
+			const char* name = readString(input);
+			spAttachment* attachment = spSkeletonBinary_readAttachment(self, input, skin, slotIndex, name, skeletonData, nonessential);
+			if (attachment) spSkin_addAttachment(skin, slotIndex, name, attachment);
+			FREE(name);
+		}
+	}
+	return skin;
+}
+
+spSkeletonData* spSkeletonBinary_readSkeletonDataFile (spSkeletonBinary* self, const char* path) {
+	int length;
+	spSkeletonData* skeletonData;
+	const char* binary = _spUtil_readFile(path, &length);
+	if (length == 0 || !binary) {
+		_spSkeletonBinary_setError(self, "Unable to read skeleton file: ", path);
+		return 0;
+	}
+	skeletonData = spSkeletonBinary_readSkeletonData(self, (unsigned char*)binary, length);
+	FREE(binary);
+	return skeletonData;
+}
+
+spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const unsigned char* binary,
+		const int length) {
+	int i, ii, nonessential;
+	spSkeletonData* skeletonData;
+	_spSkeletonBinary* internal = SUB_CAST(_spSkeletonBinary, self);
+
+	_dataInput* input = NEW(_dataInput);
+	input->cursor = binary;
+	input->end = binary + length;
+
+	FREE(self->error);
+	CONST_CAST(char*, self->error) = 0;
+	internal->linkedMeshCount = 0;
+
+	skeletonData = spSkeletonData_create();
+
+	skeletonData->hash = readString(input);
+	if (!strlen(skeletonData->hash)) {
+		FREE(skeletonData->hash);
+		skeletonData->hash = 0;
+	}
+
+	skeletonData->version = readString(input);
+	if (!strlen(skeletonData->version)) {
+		FREE(skeletonData->version);
+		skeletonData->version = 0;
+	}
+
+	skeletonData->width = readFloat(input);
+	skeletonData->height = readFloat(input);
+
+	nonessential = readBoolean(input);
+
+	if (nonessential) {
+		/* Skip images path & fps */
+		readFloat(input);
+		FREE(readString(input));
+	}
+
+	/* Bones. */
+	skeletonData->bonesCount = readVarint(input, 1);
+	skeletonData->bones = MALLOC(spBoneData*, skeletonData->bonesCount);
+	for (i = 0; i < skeletonData->bonesCount; ++i) {
+		spBoneData* data;
+		int mode;
+		const char* name = readString(input);
+		spBoneData* parent = i == 0 ? 0 : skeletonData->bones[readVarint(input, 1)];
+		/* TODO Avoid copying of name */
+		data = spBoneData_create(i, name, parent);
+		FREE(name);
+		data->rotation = readFloat(input);
+		data->x = readFloat(input) * self->scale;
+		data->y = readFloat(input) * self->scale;
+		data->scaleX = readFloat(input);
+		data->scaleY = readFloat(input);
+		data->shearX = readFloat(input);
+		data->shearY = readFloat(input);
+		data->length = readFloat(input) * self->scale;
+		mode = readVarint(input, 1);
+		switch (mode) {
+			case 0: data->transformMode = SP_TRANSFORMMODE_NORMAL; break;
+			case 1: data->transformMode = SP_TRANSFORMMODE_ONLYTRANSLATION; break;
+			case 2: data->transformMode = SP_TRANSFORMMODE_NOROTATIONORREFLECTION; break;
+			case 3: data->transformMode = SP_TRANSFORMMODE_NOSCALE; break;
+			case 4: data->transformMode = SP_TRANSFORMMODE_NOSCALEORREFLECTION; break;
+		}
+		if (nonessential) readInt(input); /* Skip bone color. */
+		skeletonData->bones[i] = data;
+	}
+
+	/* Slots. */
+	skeletonData->slotsCount = readVarint(input, 1);
+	skeletonData->slots = MALLOC(spSlotData*, skeletonData->slotsCount);
+	for (i = 0; i < skeletonData->slotsCount; ++i) {
+		int r, g, b, a;
+		const char* slotName = readString(input);
+		spBoneData* boneData = skeletonData->bones[readVarint(input, 1)];
+		/* TODO Avoid copying of slotName */
+		spSlotData* slotData = spSlotData_create(i, slotName, boneData);
+		FREE(slotName);
+		readColor(input, &slotData->color.r, &slotData->color.g, &slotData->color.b, &slotData->color.a);
+		r = readByte(input);
+		g = readByte(input);
+		b = readByte(input);
+		a = readByte(input);
+		if (!(r == 0xff && g == 0xff && b == 0xff && a == 0xff)) {
+			slotData->darkColor = spColor_create();
+			spColor_setFromFloats(slotData->darkColor, r / 255.0f, g / 255.0f, b / 255.0f, 1);
+		}
+		slotData->attachmentName = readString(input);
+		slotData->blendMode = (spBlendMode)readVarint(input, 1);
+		skeletonData->slots[i] = slotData;
+	}
+
+	/* IK constraints. */
+	skeletonData->ikConstraintsCount = readVarint(input, 1);
+	skeletonData->ikConstraints = MALLOC(spIkConstraintData*, skeletonData->ikConstraintsCount);
+	for (i = 0; i < skeletonData->ikConstraintsCount; ++i) {
+		const char* name = readString(input);
+		/* TODO Avoid copying of name */
+		spIkConstraintData* data = spIkConstraintData_create(name);
+		data->order = readVarint(input, 1);
+		FREE(name);
+		data->bonesCount = readVarint(input, 1);
+		data->bones = MALLOC(spBoneData*, data->bonesCount);
+		for (ii = 0; ii < data->bonesCount; ++ii)
+			data->bones[ii] = skeletonData->bones[readVarint(input, 1)];
+		data->target = skeletonData->bones[readVarint(input, 1)];
+		data->mix = readFloat(input);
+		data->bendDirection = readSByte(input);
+		skeletonData->ikConstraints[i] = data;
+	}
+
+	/* Transform constraints. */
+	skeletonData->transformConstraintsCount = readVarint(input, 1);
+	skeletonData->transformConstraints = MALLOC(
+			spTransformConstraintData*, skeletonData->transformConstraintsCount);
+	for (i = 0; i < skeletonData->transformConstraintsCount; ++i) {
+		const char* name = readString(input);
+		/* TODO Avoid copying of name */
+		spTransformConstraintData* data = spTransformConstraintData_create(name);
+		data->order = readVarint(input, 1);
+		FREE(name);
+		data->bonesCount = readVarint(input, 1);
+		CONST_CAST(spBoneData**, data->bones) = MALLOC(spBoneData*, data->bonesCount);
+		for (ii = 0; ii < data->bonesCount; ++ii)
+			data->bones[ii] = skeletonData->bones[readVarint(input, 1)];
+		data->target = skeletonData->bones[readVarint(input, 1)];
+		data->local = readBoolean(input);
+		data->relative = readBoolean(input);
+		data->offsetRotation = readFloat(input);
+		data->offsetX = readFloat(input) * self->scale;
+		data->offsetY = readFloat(input) * self->scale;
+		data->offsetScaleX = readFloat(input);
+		data->offsetScaleY = readFloat(input);
+		data->offsetShearY = readFloat(input);
+		data->rotateMix = readFloat(input);
+		data->translateMix = readFloat(input);
+		data->scaleMix = readFloat(input);
+		data->shearMix = readFloat(input);
+		skeletonData->transformConstraints[i] = data;
+	}
+
+	/* Path constraints */
+	skeletonData->pathConstraintsCount = readVarint(input, 1);
+	skeletonData->pathConstraints = MALLOC(spPathConstraintData*, skeletonData->pathConstraintsCount);
+	for (i = 0; i < skeletonData->pathConstraintsCount; ++i) {
+		const char* name = readString(input);
+		/* TODO Avoid copying of name */
+		spPathConstraintData* data = spPathConstraintData_create(name);
+		data->order = readVarint(input, 1);
+		FREE(name);
+		data->bonesCount = readVarint(input, 1);
+		CONST_CAST(spBoneData**, data->bones) = MALLOC(spBoneData*, data->bonesCount);
+		for (ii = 0; ii < data->bonesCount; ++ii)
+			data->bones[ii] = skeletonData->bones[readVarint(input, 1)];
+		data->target = skeletonData->slots[readVarint(input, 1)];
+		data->positionMode = (spPositionMode)readVarint(input, 1);
+		data->spacingMode = (spSpacingMode)readVarint(input, 1);
+		data->rotateMode = (spRotateMode)readVarint(input, 1);
+		data->offsetRotation = readFloat(input);
+		data->position = readFloat(input);
+		if (data->positionMode == SP_POSITION_MODE_FIXED) data->position *= self->scale;
+		data->spacing = readFloat(input);
+		if (data->spacingMode == SP_SPACING_MODE_LENGTH || data->spacingMode == SP_SPACING_MODE_FIXED) data->spacing *= self->scale;
+		data->rotateMix = readFloat(input);
+		data->translateMix = readFloat(input);
+		skeletonData->pathConstraints[i] = data;
+	}
+
+	/* Default skin. */
+	skeletonData->defaultSkin = spSkeletonBinary_readSkin(self, input, "default", skeletonData, nonessential);
+	skeletonData->skinsCount = readVarint(input, 1);
+
+	if (skeletonData->defaultSkin)
+		++skeletonData->skinsCount;
+
+	skeletonData->skins = MALLOC(spSkin*, skeletonData->skinsCount);
+
+	if (skeletonData->defaultSkin)
+		skeletonData->skins[0] = skeletonData->defaultSkin;
+
+	/* Skins. */
+	for (i = skeletonData->defaultSkin ? 1 : 0; i < skeletonData->skinsCount; ++i) {
+		const char* skinName = readString(input);
+		/* TODO Avoid copying of skinName */
+		skeletonData->skins[i] = spSkeletonBinary_readSkin(self, input, skinName, skeletonData, nonessential);
+		FREE(skinName);
+	}
+
+	/* Linked meshes. */
+	for (i = 0; i < internal->linkedMeshCount; ++i) {
+		_spLinkedMesh* linkedMesh = internal->linkedMeshes + i;
+		spSkin* skin = !linkedMesh->skin ? skeletonData->defaultSkin : spSkeletonData_findSkin(skeletonData, linkedMesh->skin);
+		spAttachment* parent;
+		if (!skin) {
+			FREE(input);
+			spSkeletonData_dispose(skeletonData);
+			_spSkeletonBinary_setError(self, "Skin not found: ", linkedMesh->skin);
+			return 0;
+		}
+		parent = spSkin_getAttachment(skin, linkedMesh->slotIndex, linkedMesh->parent);
+		if (!parent) {
+			FREE(input);
+			spSkeletonData_dispose(skeletonData);
+			_spSkeletonBinary_setError(self, "Parent mesh not found: ", linkedMesh->parent);
+			return 0;
+		}
+		spMeshAttachment_setParentMesh(linkedMesh->mesh, SUB_CAST(spMeshAttachment, parent));
+		spMeshAttachment_updateUVs(linkedMesh->mesh);
+		spAttachmentLoader_configureAttachment(self->attachmentLoader, SUPER(SUPER(linkedMesh->mesh)));
+	}
+
+	/* Events. */
+	skeletonData->eventsCount = readVarint(input, 1);
+	skeletonData->events = MALLOC(spEventData*, skeletonData->eventsCount);
+	for (i = 0; i < skeletonData->eventsCount; ++i) {
+		const char* name = readString(input);
+		/* TODO Avoid copying of skinName */
+		spEventData* eventData = spEventData_create(name);
+		FREE(name);
+		eventData->intValue = readVarint(input, 0);
+		eventData->floatValue = readFloat(input);
+		eventData->stringValue = readString(input);
+		skeletonData->events[i] = eventData;
+	}
+
+	/* Animations. */
+	skeletonData->animationsCount = readVarint(input, 1);
+	skeletonData->animations = MALLOC(spAnimation*, skeletonData->animationsCount);
+	for (i = 0; i < skeletonData->animationsCount; ++i) {
+		const char* name = readString(input);
+		spAnimation* animation = _spSkeletonBinary_readAnimation(self, name, input, skeletonData);
+		FREE(name);
+		if (!animation) {
+			FREE(input);
+			spSkeletonData_dispose(skeletonData);
+			return 0;
+		}
+		skeletonData->animations[i] = animation;
+	}
+
+	FREE(input);
+	return skeletonData;
+}

+ 205 - 0
spine-cpp/spine-cpp/src/spine/SkeletonBounds.c

@@ -0,0 +1,205 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SkeletonBounds.h>
+#include <limits.h>
+#include <spine/extension.h>
+
+spPolygon* spPolygon_create (int capacity) {
+	spPolygon* self = NEW(spPolygon);
+	self->capacity = capacity;
+	CONST_CAST(float*, self->vertices) = MALLOC(float, capacity);
+	return self;
+}
+
+void spPolygon_dispose (spPolygon* self) {
+	FREE(self->vertices);
+	FREE(self);
+}
+
+int/*bool*/spPolygon_containsPoint (spPolygon* self, float x, float y) {
+	int prevIndex = self->count - 2;
+	int inside = 0;
+	int i;
+	for (i = 0; i < self->count; i += 2) {
+		float vertexY = self->vertices[i + 1];
+		float prevY = self->vertices[prevIndex + 1];
+		if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
+			float vertexX = self->vertices[i];
+			if (vertexX + (y - vertexY) / (prevY - vertexY) * (self->vertices[prevIndex] - vertexX) < x) inside = !inside;
+		}
+		prevIndex = i;
+	}
+	return inside;
+}
+
+int/*bool*/spPolygon_intersectsSegment (spPolygon* self, float x1, float y1, float x2, float y2) {
+	float width12 = x1 - x2, height12 = y1 - y2;
+	float det1 = x1 * y2 - y1 * x2;
+	float x3 = self->vertices[self->count - 2], y3 = self->vertices[self->count - 1];
+	int i;
+	for (i = 0; i < self->count; i += 2) {
+		float x4 = self->vertices[i], y4 = self->vertices[i + 1];
+		float det2 = x3 * y4 - y3 * x4;
+		float width34 = x3 - x4, height34 = y3 - y4;
+		float det3 = width12 * height34 - height12 * width34;
+		float x = (det1 * width34 - width12 * det2) / det3;
+		if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
+			float y = (det1 * height34 - height12 * det2) / det3;
+			if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return 1;
+		}
+		x3 = x4;
+		y3 = y4;
+	}
+	return 0;
+}
+
+/**/
+
+typedef struct {
+	spSkeletonBounds super;
+	int capacity;
+} _spSkeletonBounds;
+
+spSkeletonBounds* spSkeletonBounds_create () {
+	return SUPER(NEW(_spSkeletonBounds));
+}
+
+void spSkeletonBounds_dispose (spSkeletonBounds* self) {
+	int i;
+	for (i = 0; i < SUB_CAST(_spSkeletonBounds, self)->capacity; ++i)
+		if (self->polygons[i]) spPolygon_dispose(self->polygons[i]);
+	FREE(self->polygons);
+	FREE(self->boundingBoxes);
+	FREE(self);
+}
+
+void spSkeletonBounds_update (spSkeletonBounds* self, spSkeleton* skeleton, int/*bool*/updateAabb) {
+	int i;
+
+	_spSkeletonBounds* internal = SUB_CAST(_spSkeletonBounds, self);
+	if (internal->capacity < skeleton->slotsCount) {
+		spPolygon** newPolygons;
+
+		FREE(self->boundingBoxes);
+		self->boundingBoxes = MALLOC(spBoundingBoxAttachment*, skeleton->slotsCount);
+
+		newPolygons = CALLOC(spPolygon*, skeleton->slotsCount);
+		memcpy(newPolygons, self->polygons, sizeof(spPolygon*) * internal->capacity);
+		FREE(self->polygons);
+		self->polygons = newPolygons;
+
+		internal->capacity = skeleton->slotsCount;
+	}
+
+	self->minX = (float)INT_MAX;
+	self->minY = (float)INT_MAX;
+	self->maxX = (float)INT_MIN;
+	self->maxY = (float)INT_MIN;
+
+	self->count = 0;
+	for (i = 0; i < skeleton->slotsCount; ++i) {
+		spPolygon* polygon;
+		spBoundingBoxAttachment* boundingBox;
+
+		spSlot* slot = skeleton->slots[i];
+		spAttachment* attachment = slot->attachment;
+		if (!attachment || attachment->type != SP_ATTACHMENT_BOUNDING_BOX) continue;
+		boundingBox = (spBoundingBoxAttachment*)attachment;
+		self->boundingBoxes[self->count] = boundingBox;
+
+		polygon = self->polygons[self->count];
+		if (!polygon || polygon->capacity < boundingBox->super.worldVerticesLength) {
+			if (polygon) spPolygon_dispose(polygon);
+			self->polygons[self->count] = polygon = spPolygon_create(boundingBox->super.worldVerticesLength);
+		}
+		polygon->count = boundingBox->super.worldVerticesLength;
+		spVertexAttachment_computeWorldVertices(SUPER(boundingBox), slot, 0, polygon->count, polygon->vertices, 0, 2);
+
+		if (updateAabb) {
+			int ii = 0;
+			for (; ii < polygon->count; ii += 2) {
+				float x = polygon->vertices[ii];
+				float y = polygon->vertices[ii + 1];
+				if (x < self->minX) self->minX = x;
+				if (y < self->minY) self->minY = y;
+				if (x > self->maxX) self->maxX = x;
+				if (y > self->maxY) self->maxY = y;
+			}
+		}
+
+		self->count++;
+	}
+}
+
+int/*bool*/spSkeletonBounds_aabbContainsPoint (spSkeletonBounds* self, float x, float y) {
+	return x >= self->minX && x <= self->maxX && y >= self->minY && y <= self->maxY;
+}
+
+int/*bool*/spSkeletonBounds_aabbIntersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2) {
+	float m, x, y;
+	if ((x1 <= self->minX && x2 <= self->minX) || (y1 <= self->minY && y2 <= self->minY) || (x1 >= self->maxX && x2 >= self->maxX)
+			|| (y1 >= self->maxY && y2 >= self->maxY)) return 0;
+	m = (y2 - y1) / (x2 - x1);
+	y = m * (self->minX - x1) + y1;
+	if (y > self->minY && y < self->maxY) return 1;
+	y = m * (self->maxX - x1) + y1;
+	if (y > self->minY && y < self->maxY) return 1;
+	x = (self->minY - y1) / m + x1;
+	if (x > self->minX && x < self->maxX) return 1;
+	x = (self->maxY - y1) / m + x1;
+	if (x > self->minX && x < self->maxX) return 1;
+	return 0;
+}
+
+int/*bool*/spSkeletonBounds_aabbIntersectsSkeleton (spSkeletonBounds* self, spSkeletonBounds* bounds) {
+	return self->minX < bounds->maxX && self->maxX > bounds->minX && self->minY < bounds->maxY && self->maxY > bounds->minY;
+}
+
+spBoundingBoxAttachment* spSkeletonBounds_containsPoint (spSkeletonBounds* self, float x, float y) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (spPolygon_containsPoint(self->polygons[i], x, y)) return self->boundingBoxes[i];
+	return 0;
+}
+
+spBoundingBoxAttachment* spSkeletonBounds_intersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (spPolygon_intersectsSegment(self->polygons[i], x1, y1, x2, y2)) return self->boundingBoxes[i];
+	return 0;
+}
+
+spPolygon* spSkeletonBounds_getPolygon (spSkeletonBounds* self, spBoundingBoxAttachment* boundingBox) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (self->boundingBoxes[i] == boundingBox) return self->polygons[i];
+	return 0;
+}

+ 313 - 0
spine-cpp/spine-cpp/src/spine/SkeletonClipping.c

@@ -0,0 +1,313 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SkeletonClipping.h>
+#include <spine/extension.h>
+
+spSkeletonClipping* spSkeletonClipping_create() {
+	spSkeletonClipping* clipping = CALLOC(spSkeletonClipping, 1);
+
+	clipping->triangulator = spTriangulator_create();
+	clipping->clippingPolygon = spFloatArray_create(128);
+	clipping->clipOutput = spFloatArray_create(128);
+	clipping->clippedVertices = spFloatArray_create(128);
+	clipping->clippedUVs = spFloatArray_create(128);
+	clipping->clippedTriangles = spUnsignedShortArray_create(128);
+	clipping->scratch = spFloatArray_create(128);
+
+	return clipping;
+}
+
+void spSkeletonClipping_dispose(spSkeletonClipping* self) {
+	spTriangulator_dispose(self->triangulator);
+	spFloatArray_dispose(self->clippingPolygon);
+	spFloatArray_dispose(self->clipOutput);
+	spFloatArray_dispose(self->clippedVertices);
+	spFloatArray_dispose(self->clippedUVs);
+	spUnsignedShortArray_dispose(self->clippedTriangles);
+	spFloatArray_dispose(self->scratch);
+	FREE(self);
+}
+
+static void _makeClockwise (spFloatArray* polygon) {
+	int i, n, lastX;
+	float* vertices = polygon->items;
+	int verticeslength = polygon->size;
+
+	float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
+	for (i = 0, n = verticeslength - 3; i < n; i += 2) {
+		p1x = vertices[i];
+		p1y = vertices[i + 1];
+		p2x = vertices[i + 2];
+		p2y = vertices[i + 3];
+		area += p1x * p2y - p2x * p1y;
+	}
+	if (area < 0) return;
+
+	for (i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
+		float x = vertices[i], y = vertices[i + 1];
+		int other = lastX - i;
+		vertices[i] = vertices[other];
+		vertices[i + 1] = vertices[other + 1];
+		vertices[other] = x;
+		vertices[other + 1] = y;
+	}
+}
+
+int spSkeletonClipping_clipStart(spSkeletonClipping* self, spSlot* slot, spClippingAttachment* clip) {
+	int i, n;
+	float* vertices;
+	if (self->clipAttachment) return 0;
+	self->clipAttachment = clip;
+
+	n = clip->super.worldVerticesLength;
+	vertices = spFloatArray_setSize(self->clippingPolygon, n)->items;
+	spVertexAttachment_computeWorldVertices(SUPER(clip), slot, 0, n, vertices, 0, 2);
+	_makeClockwise(self->clippingPolygon);
+	self->clippingPolygons = spTriangulator_decompose(self->triangulator, self->clippingPolygon, spTriangulator_triangulate(self->triangulator, self->clippingPolygon));
+	for (i = 0, n = self->clippingPolygons->size; i < n; i++) {
+		spFloatArray* polygon = self->clippingPolygons->items[i];
+		_makeClockwise(polygon);
+		spFloatArray_add(polygon, polygon->items[0]);
+		spFloatArray_add(polygon, polygon->items[1]);
+	}
+	return self->clippingPolygons->size;
+}
+
+void spSkeletonClipping_clipEnd(spSkeletonClipping* self, spSlot* slot) {
+	if (self->clipAttachment != 0 && self->clipAttachment->endSlot == slot->data) spSkeletonClipping_clipEnd2(self);
+}
+
+void spSkeletonClipping_clipEnd2(spSkeletonClipping* self) {
+	if (!self->clipAttachment) return;
+	self->clipAttachment = 0;
+	self->clippingPolygons = 0;
+	spFloatArray_clear(self->clippedVertices);
+	spFloatArray_clear(self->clippedUVs);
+	spUnsignedShortArray_clear(self->clippedTriangles);
+	spFloatArray_clear(self->clippingPolygon);
+}
+
+int /*boolean*/ spSkeletonClipping_isClipping(spSkeletonClipping* self) {
+	return self->clipAttachment != 0;
+}
+
+int /*boolean*/ _clip(spSkeletonClipping* self, float x1, float y1, float x2, float y2, float x3, float y3, spFloatArray* clippingArea, spFloatArray* output) {
+	int i;
+	spFloatArray* originalOutput = output;
+	int clipped = 0;
+	float* clippingVertices;
+	int clippingVerticesLast;
+
+	spFloatArray* input = 0;
+	if (clippingArea->size % 4 >= 2) {
+		input = output;
+		output = self->scratch;
+	} else
+		input = self->scratch;
+
+	spFloatArray_clear(input);
+	spFloatArray_add(input, x1);
+	spFloatArray_add(input, y1);
+	spFloatArray_add(input, x2);
+	spFloatArray_add(input, y2);
+	spFloatArray_add(input, x3);
+	spFloatArray_add(input, y3);
+	spFloatArray_add(input, x1);
+	spFloatArray_add(input, y1);
+	spFloatArray_clear(output);
+
+	clippingVertices = clippingArea->items;
+	clippingVerticesLast = clippingArea->size - 4;
+	for (i = 0;; i += 2) {
+		int ii;
+		spFloatArray* temp;
+		float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
+		float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
+		float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
+
+		float* inputVertices = input->items;
+		int inputVerticesLength = input->size - 2, outputStart = output->size;
+		for (ii = 0; ii < inputVerticesLength; ii += 2) {
+			float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
+			float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
+			int side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
+			if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
+				float c0, c2;
+				float ua;
+				if (side2) {
+					spFloatArray_add(output, inputX2);
+					spFloatArray_add(output, inputY2);
+					continue;
+				}
+				c0 = inputY2 - inputY, c2 = inputX2 - inputX;
+				ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
+				spFloatArray_add(output, edgeX + (edgeX2 - edgeX) * ua);
+				spFloatArray_add(output, edgeY + (edgeY2 - edgeY) * ua);
+			} else if (side2) {
+				float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
+				float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY));
+				spFloatArray_add(output, edgeX + (edgeX2 - edgeX) * ua);
+				spFloatArray_add(output, edgeY + (edgeY2 - edgeY) * ua);
+				spFloatArray_add(output, inputX2);
+				spFloatArray_add(output, inputY2);
+			}
+			clipped = 1;
+		}
+
+		if (outputStart == output->size) {
+			spFloatArray_clear(originalOutput);
+			return 1;
+		}
+
+		spFloatArray_add(output, output->items[0]);
+		spFloatArray_add(output, output->items[1]);
+
+		if (i == clippingVerticesLast) break;
+		temp = output;
+		output = input;
+		spFloatArray_clear(output);
+		input = temp;
+	}
+
+	if (originalOutput != output) {
+		spFloatArray_clear(originalOutput);
+		spFloatArray_addAllValues(originalOutput, output->items, 0, output->size - 2);
+	} else
+		spFloatArray_setSize(originalOutput, originalOutput->size - 2);
+
+	return clipped;
+}
+
+void spSkeletonClipping_clipTriangles(spSkeletonClipping* self, float* vertices, int verticesLength, unsigned short* triangles, int trianglesLength, float* uvs, int stride) {
+	int i;
+	spFloatArray* clipOutput = self->clipOutput;
+	spFloatArray* clippedVertices = self->clippedVertices;
+	spFloatArray* clippedUVs = self->clippedUVs;
+	spUnsignedShortArray* clippedTriangles = self->clippedTriangles;
+	spFloatArray** polygons = self->clippingPolygons->items;
+	int polygonsCount = self->clippingPolygons->size;
+
+	short index = 0;
+	spFloatArray_clear(clippedVertices);
+	spFloatArray_clear(clippedUVs);
+	spUnsignedShortArray_clear(clippedTriangles);
+	i = 0;
+	continue_outer:
+	for (; i < trianglesLength; i += 3) {
+		int p;
+		int vertexOffset = triangles[i] * stride;
+		float x2, y2, u2, v2, x3, y3, u3, v3;
+		float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
+		float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
+
+		vertexOffset = triangles[i + 1] * stride;
+		x2 = vertices[vertexOffset]; y2 = vertices[vertexOffset + 1];
+		u2 = uvs[vertexOffset]; v2 = uvs[vertexOffset + 1];
+
+		vertexOffset = triangles[i + 2] * stride;
+		x3 = vertices[vertexOffset]; y3 = vertices[vertexOffset + 1];
+		u3 = uvs[vertexOffset]; v3 = uvs[vertexOffset + 1];
+
+		for (p = 0; p < polygonsCount; p++) {
+			int s = clippedVertices->size;
+			if (_clip(self, x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
+				int ii;
+				float d0, d1, d2, d4, d;
+				unsigned short* clippedTrianglesItems;
+				int clipOutputCount;
+				float* clipOutputItems;
+				float* clippedVerticesItems;
+				float* clippedUVsItems;
+
+				int clipOutputLength = clipOutput->size;
+				if (clipOutputLength == 0) continue;
+				d0 = y2 - y3; d1 = x3 - x2; d2 = x1 - x3; d4 = y3 - y1;
+				d = 1 / (d0 * d2 + d1 * (y1 - y3));
+
+				clipOutputCount = clipOutputLength >> 1;
+				clipOutputItems = clipOutput->items;
+				clippedVerticesItems = spFloatArray_setSize(clippedVertices, s + (clipOutputCount << 1))->items;
+				clippedUVsItems = spFloatArray_setSize(clippedUVs, s + (clipOutputCount << 1))->items;
+				for (ii = 0; ii < clipOutputLength; ii += 2) {
+					float c0, c1, a, b, c;
+					float x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
+					clippedVerticesItems[s] = x;
+					clippedVerticesItems[s + 1] = y;
+					c0 = x - x3; c1 = y - y3;
+					a = (d0 * c0 + d1 * c1) * d;
+					b = (d4 * c0 + d2 * c1) * d;
+					c = 1 - a - b;
+					clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
+					clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
+					s += 2;
+				}
+
+				s = clippedTriangles->size;
+				clippedTrianglesItems = spUnsignedShortArray_setSize(clippedTriangles, s + 3 * (clipOutputCount - 2))->items;
+				clipOutputCount--;
+				for (ii = 1; ii < clipOutputCount; ii++) {
+					clippedTrianglesItems[s] = index;
+					clippedTrianglesItems[s + 1] = (unsigned short)(index + ii);
+					clippedTrianglesItems[s + 2] = (unsigned short)(index + ii + 1);
+					s += 3;
+				}
+				index += clipOutputCount + 1;
+
+			} else {
+				unsigned short* clippedTrianglesItems;
+				float* clippedVerticesItems = spFloatArray_setSize(clippedVertices, s + (3 << 1))->items;
+				float* clippedUVsItems = spFloatArray_setSize(clippedUVs, s + (3 << 1))->items;
+				clippedVerticesItems[s] = x1;
+				clippedVerticesItems[s + 1] = y1;
+				clippedVerticesItems[s + 2] = x2;
+				clippedVerticesItems[s + 3] = y2;
+				clippedVerticesItems[s + 4] = x3;
+				clippedVerticesItems[s + 5] = y3;
+
+				clippedUVsItems[s] = u1;
+				clippedUVsItems[s + 1] = v1;
+				clippedUVsItems[s + 2] = u2;
+				clippedUVsItems[s + 3] = v2;
+				clippedUVsItems[s + 4] = u3;
+				clippedUVsItems[s + 5] = v3;
+
+				s = clippedTriangles->size;
+				clippedTrianglesItems = spUnsignedShortArray_setSize(clippedTriangles, s + 3)->items;
+				clippedTrianglesItems[s] = index;
+				clippedTrianglesItems[s + 1] = (unsigned short)(index + 1);
+				clippedTrianglesItems[s + 2] = (unsigned short)(index + 2);
+				index += 3;
+				i += 3;
+				goto continue_outer;
+			}
+		}
+	}
+}

+ 147 - 0
spine-cpp/spine-cpp/src/spine/SkeletonData.c

@@ -0,0 +1,147 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SkeletonData.h>
+#include <string.h>
+#include <spine/extension.h>
+
+spSkeletonData* spSkeletonData_create () {
+	return NEW(spSkeletonData);
+}
+
+void spSkeletonData_dispose (spSkeletonData* self) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		spBoneData_dispose(self->bones[i]);
+	FREE(self->bones);
+
+	for (i = 0; i < self->slotsCount; ++i)
+		spSlotData_dispose(self->slots[i]);
+	FREE(self->slots);
+
+	for (i = 0; i < self->skinsCount; ++i)
+		spSkin_dispose(self->skins[i]);
+	FREE(self->skins);
+
+	for (i = 0; i < self->eventsCount; ++i)
+		spEventData_dispose(self->events[i]);
+	FREE(self->events);
+
+	for (i = 0; i < self->animationsCount; ++i)
+		spAnimation_dispose(self->animations[i]);
+	FREE(self->animations);
+
+	for (i = 0; i < self->ikConstraintsCount; ++i)
+		spIkConstraintData_dispose(self->ikConstraints[i]);
+	FREE(self->ikConstraints);
+
+	for (i = 0; i < self->transformConstraintsCount; ++i)
+		spTransformConstraintData_dispose(self->transformConstraints[i]);
+	FREE(self->transformConstraints);
+
+	for (i = 0; i < self->pathConstraintsCount; i++)
+		spPathConstraintData_dispose(self->pathConstraints[i]);
+	FREE(self->pathConstraints);
+
+	FREE(self->hash);
+	FREE(self->version);
+
+	FREE(self);
+}
+
+spBoneData* spSkeletonData_findBone (const spSkeletonData* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		if (strcmp(self->bones[i]->name, boneName) == 0) return self->bones[i];
+	return 0;
+}
+
+int spSkeletonData_findBoneIndex (const spSkeletonData* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->bonesCount; ++i)
+		if (strcmp(self->bones[i]->name, boneName) == 0) return i;
+	return -1;
+}
+
+spSlotData* spSkeletonData_findSlot (const spSkeletonData* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotsCount; ++i)
+		if (strcmp(self->slots[i]->name, slotName) == 0) return self->slots[i];
+	return 0;
+}
+
+int spSkeletonData_findSlotIndex (const spSkeletonData* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotsCount; ++i)
+		if (strcmp(self->slots[i]->name, slotName) == 0) return i;
+	return -1;
+}
+
+spSkin* spSkeletonData_findSkin (const spSkeletonData* self, const char* skinName) {
+	int i;
+	for (i = 0; i < self->skinsCount; ++i)
+		if (strcmp(self->skins[i]->name, skinName) == 0) return self->skins[i];
+	return 0;
+}
+
+spEventData* spSkeletonData_findEvent (const spSkeletonData* self, const char* eventName) {
+	int i;
+	for (i = 0; i < self->eventsCount; ++i)
+		if (strcmp(self->events[i]->name, eventName) == 0) return self->events[i];
+	return 0;
+}
+
+spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, const char* animationName) {
+	int i;
+	for (i = 0; i < self->animationsCount; ++i)
+		if (strcmp(self->animations[i]->name, animationName) == 0) return self->animations[i];
+	return 0;
+}
+
+spIkConstraintData* spSkeletonData_findIkConstraint (const spSkeletonData* self, const char* constraintName) {
+	int i;
+	for (i = 0; i < self->ikConstraintsCount; ++i)
+		if (strcmp(self->ikConstraints[i]->name, constraintName) == 0) return self->ikConstraints[i];
+	return 0;
+}
+
+spTransformConstraintData* spSkeletonData_findTransformConstraint (const spSkeletonData* self, const char* constraintName) {
+	int i;
+	for (i = 0; i < self->transformConstraintsCount; ++i)
+		if (strcmp(self->transformConstraints[i]->name, constraintName) == 0) return self->transformConstraints[i];
+	return 0;
+}
+
+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;
+}

+ 1119 - 0
spine-cpp/spine-cpp/src/spine/SkeletonJson.c

@@ -0,0 +1,1119 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SkeletonJson.h>
+#include <stdio.h>
+#include <locale.h>
+#include "Json.h"
+#include <spine/extension.h>
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/Array.h>
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
+#define strdup _strdup
+#endif
+
+typedef struct {
+	const char* parent;
+	const char* skin;
+	int slotIndex;
+	spMeshAttachment* mesh;
+} _spLinkedMesh;
+
+typedef struct {
+	spSkeletonJson super;
+	int ownsLoader;
+
+	int linkedMeshCount;
+	int linkedMeshCapacity;
+	_spLinkedMesh* linkedMeshes;
+} _spSkeletonJson;
+
+spSkeletonJson* spSkeletonJson_createWithLoader (spAttachmentLoader* attachmentLoader) {
+	spSkeletonJson* self = SUPER(NEW(_spSkeletonJson));
+	self->scale = 1;
+	self->attachmentLoader = attachmentLoader;
+	return self;
+}
+
+spSkeletonJson* spSkeletonJson_create (spAtlas* atlas) {
+	spAtlasAttachmentLoader* attachmentLoader = spAtlasAttachmentLoader_create(atlas);
+	spSkeletonJson* self = spSkeletonJson_createWithLoader(SUPER(attachmentLoader));
+	SUB_CAST(_spSkeletonJson, self)->ownsLoader = 1;
+	return self;
+}
+
+void spSkeletonJson_dispose (spSkeletonJson* self) {
+	_spSkeletonJson* internal = SUB_CAST(_spSkeletonJson, self);
+	if (internal->ownsLoader) spAttachmentLoader_dispose(self->attachmentLoader);
+	FREE(internal->linkedMeshes);
+	FREE(self->error);
+	FREE(self);
+}
+
+void _spSkeletonJson_setError (spSkeletonJson* self, Json* root, const char* value1, const char* value2) {
+	char message[256];
+	int length;
+	FREE(self->error);
+	strcpy(message, value1);
+	length = (int)strlen(value1);
+	if (value2) strncat(message + length, value2, 255 - length);
+	MALLOC_STR(self->error, message);
+	if (root) Json_dispose(root);
+}
+
+static float toColor (const char* value, int index) {
+	char digits[3];
+	char *error;
+	int color;
+
+	if (strlen(value) / 2 < index) return -1;
+	value += index * 2;
+
+	digits[0] = *value;
+	digits[1] = *(value + 1);
+	digits[2] = '\0';
+	color = (int)strtoul(digits, &error, 16);
+	if (*error != 0) return -1;
+	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 if (curve->type == Json_Array) {
+		Json* child0 = curve->child;
+		Json* child1 = child0->next;
+		Json* child2 = child1->next;
+		Json* child3 = child2->next;
+		spCurveTimeline_setCurve(timeline, frameIndex, child0->valueFloat, child1->valueFloat, child2->valueFloat,
+				child3->valueFloat);
+	}
+}
+
+static void _spSkeletonJson_addLinkedMesh (spSkeletonJson* self, spMeshAttachment* mesh, const char* skin, int slotIndex,
+		const char* parent) {
+	_spLinkedMesh* linkedMesh;
+	_spSkeletonJson* internal = SUB_CAST(_spSkeletonJson, self);
+
+	if (internal->linkedMeshCount == internal->linkedMeshCapacity) {
+		_spLinkedMesh* linkedMeshes;
+		internal->linkedMeshCapacity *= 2;
+		if (internal->linkedMeshCapacity < 8) internal->linkedMeshCapacity = 8;
+		linkedMeshes = MALLOC(_spLinkedMesh, internal->linkedMeshCapacity);
+		memcpy(linkedMeshes, internal->linkedMeshes, sizeof(_spLinkedMesh) * internal->linkedMeshCount);
+		FREE(internal->linkedMeshes);
+		internal->linkedMeshes = linkedMeshes;
+	}
+
+	linkedMesh = internal->linkedMeshes + internal->linkedMeshCount++;
+	linkedMesh->mesh = mesh;
+	linkedMesh->skin = skin;
+	linkedMesh->slotIndex = slotIndex;
+	linkedMesh->parent = parent;
+}
+
+static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) {
+	int frameIndex;
+	spAnimation* animation;
+	Json* valueMap;
+	int timelinesCount = 0;
+
+	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* paths = Json_getItem(root, "paths");
+	Json* deform = Json_getItem(root, "deform");
+	Json* drawOrder = Json_getItem(root, "drawOrder");
+	Json* events = Json_getItem(root, "events");
+	Json *boneMap, *slotMap, *constraintMap;
+	if (!drawOrder) drawOrder = 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 = paths ? paths->child : 0; constraintMap; constraintMap = constraintMap->next)
+		timelinesCount += constraintMap->size;
+	for (constraintMap = deform ? deform->child : 0; constraintMap; constraintMap = constraintMap->next)
+		for (slotMap = constraintMap->child; slotMap; slotMap = slotMap->next)
+			timelinesCount += slotMap->size;
+	if (drawOrder) ++timelinesCount;
+	if (events) ++timelinesCount;
+
+	animation = spAnimation_create(root->name, timelinesCount);
+	animation->timelinesCount = 0;
+
+	/* 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]);
+
+			} else {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineMap->name);
+				return 0;
+			}
+		}
+	}
+
+	/* Bone timelines. */
+	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) {
+		Json *timelineMap;
+
+		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 timelineScale = isTranslate ? self->scale: 1;
+					spTranslateTimeline *timeline = 0;
+					if (isScale) timeline = spScaleTimeline_create(timelineMap->size);
+					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", 0) * timelineScale,
+								Json_getFloat(valueMap, "y", 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) * TRANSLATE_ENTRIES]);
+
+				} else {
+					spAnimation_dispose(animation);
+					_spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineMap->name);
+					return 0;
+				}
+			}
+		}
+	}
+
+	/* 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_getInt(valueMap, "bendPositive", 1) ? 1 : -1);
+			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]);
+	}
+
+	/* 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]);
+	}
+
+	/** Path constraint timelines. */
+	for(constraintMap = paths ? paths->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;
+			}
+		}
+
+		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]);
+			}
+		}
+	}
+
+	/* Deform timelines. */
+	for (constraintMap = deform ? deform->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));
+				if (!attachment) {
+					spAnimation_dispose(animation);
+					_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);
+				timeline->slotIndex = slotIndex;
+				timeline->attachment = SUPER(attachment);
+
+				for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
+					Json* vertices = Json_getItem(valueMap, "vertices");
+					float* deform;
+					if (!vertices) {
+						if (weighted) {
+							deform = tempDeform;
+							memset(deform, 0, sizeof(float) * deformLength);
+						} else
+							deform = attachment->vertices;
+					} else {
+						int v, start = Json_getInt(valueMap, "offset", 0);
+						Json* vertex;
+						deform = tempDeform;
+						memset(deform, 0, sizeof(float) * start);
+						if (self->scale == 1) {
+							for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+								deform[v] = vertex->valueFloat;
+						} else {
+							for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+								deform[v] = vertex->valueFloat * self->scale;
+						}
+						memset(deform + v, 0, sizeof(float) * (deformLength - v));
+						if (!weighted) {
+							float* vertices = attachment->vertices;
+							for (v = 0; v < deformLength; ++v)
+								deform[v] += vertices[v];
+						}
+					}
+					spDeformTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), deform);
+					readCurve(valueMap, SUPER(timeline), frameIndex);
+				}
+				FREE(tempDeform);
+
+				animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
+				animation->duration = MAX(animation->duration, timeline->frames[timelineMap->size - 1]);
+			}
+		}
+	}
+
+	/* Draw order timeline. */
+	if (drawOrder) {
+		spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotsCount);
+		for (valueMap = drawOrder->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
+			int ii;
+			int* drawOrder = 0;
+			Json* offsets = Json_getItem(valueMap, "offsets");
+			if (offsets) {
+				Json* offsetMap;
+				int* unchanged = MALLOC(int, skeletonData->slotsCount - offsets->size);
+				int originalIndex = 0, unchangedIndex = 0;
+
+				drawOrder = MALLOC(int, skeletonData->slotsCount);
+				for (ii = skeletonData->slotsCount - 1; ii >= 0; --ii)
+					drawOrder[ii] = -1;
+
+				for (offsetMap = offsets->child; offsetMap; offsetMap = offsetMap->next) {
+					int slotIndex = spSkeletonData_findSlotIndex(skeletonData, Json_getString(offsetMap, "slot", 0));
+					if (slotIndex == -1) {
+						spAnimation_dispose(animation);
+						_spSkeletonJson_setError(self, 0, "Slot not found: ", Json_getString(offsetMap, "slot", 0));
+						return 0;
+					}
+					/* Collect unchanged items. */
+					while (originalIndex != slotIndex)
+						unchanged[unchangedIndex++] = originalIndex++;
+					/* Set changed items. */
+					drawOrder[originalIndex + Json_getInt(offsetMap, "offset", 0)] = originalIndex;
+					originalIndex++;
+				}
+				/* Collect remaining unchanged items. */
+				while (originalIndex < skeletonData->slotsCount)
+					unchanged[unchangedIndex++] = originalIndex++;
+				/* Fill in unchanged items. */
+				for (ii = skeletonData->slotsCount - 1; ii >= 0; ii--)
+					if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+				FREE(unchanged);
+			}
+			spDrawOrderTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), drawOrder);
+			FREE(drawOrder);
+		}
+		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
+		animation->duration = MAX(animation->duration, timeline->frames[drawOrder->size - 1]);
+	}
+
+	/* Event timeline. */
+	if (events) {
+		spEventTimeline* timeline = spEventTimeline_create(events->size);
+		for (valueMap = events->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) {
+			spEvent* event;
+			const char* stringValue;
+			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(valueMap, "name", 0));
+			if (!eventData) {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(valueMap, "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);
+			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
+			spEventTimeline_setFrame(timeline, frameIndex, event);
+		}
+		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
+		animation->duration = MAX(animation->duration, timeline->frames[events->size - 1]);
+	}
+
+	return animation;
+}
+
+static void _readVertices (spSkeletonJson* self, Json* attachmentMap, spVertexAttachment* attachment, int verticesLength) {
+	Json* entry;
+	float* vertices;
+	int i, n, nn, entrySize;
+	spFloatArray* weights;
+	spIntArray* bones;
+
+	attachment->worldVerticesLength = verticesLength;
+
+	entry = Json_getItem(attachmentMap, "vertices");
+	entrySize = entry->size;
+	vertices = MALLOC(float, entrySize);
+	for (entry = entry->child, i = 0; entry; entry = entry->next, ++i)
+		vertices[i] = entry->valueFloat;
+
+	if (verticesLength == entrySize) {
+		if (self->scale != 1)
+			for (i = 0; i < entrySize; ++i)
+				vertices[i] *= self->scale;
+		attachment->verticesCount = verticesLength;
+		attachment->vertices = vertices;
+
+		attachment->bonesCount = 0;
+		attachment->bones = 0;
+		return;
+	}
+
+	weights = spFloatArray_create(verticesLength * 3 * 3);
+	bones = spIntArray_create(verticesLength * 3);
+
+	for (i = 0, n = entrySize; i < n;) {
+		int boneCount = (int)vertices[i++];
+		spIntArray_add(bones, boneCount);
+		for (nn = i + boneCount * 4; i < nn; i += 4) {
+			spIntArray_add(bones, (int)vertices[i]);
+			spFloatArray_add(weights, vertices[i + 1] * self->scale);
+			spFloatArray_add(weights, vertices[i + 2] * self->scale);
+			spFloatArray_add(weights, vertices[i + 3]);
+		}
+	}
+
+	attachment->verticesCount = weights->size;
+	attachment->vertices = weights->items;
+	FREE(weights);
+	attachment->bonesCount = bones->size;
+	attachment->bones = bones->items;
+	FREE(bones);
+
+	FREE(vertices);
+}
+
+spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path) {
+	int length;
+	spSkeletonData* skeletonData;
+	const char* json = _spUtil_readFile(path, &length);
+	if (length == 0 || !json) {
+		_spSkeletonJson_setError(self, 0, "Unable to read skeleton file: ", path);
+		return 0;
+	}
+	skeletonData = spSkeletonJson_readSkeletonData(self, json);
+	FREE(json);
+	return skeletonData;
+}
+
+spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const char* json) {
+	int i, ii;
+	spSkeletonData* skeletonData;
+	Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events;
+	char* oldLocale;
+	_spSkeletonJson* internal = SUB_CAST(_spSkeletonJson, self);
+
+	FREE(self->error);
+	CONST_CAST(char*, self->error) = 0;
+	internal->linkedMeshCount = 0;
+
+#ifndef __ANDROID__
+	oldLocale = strdup(setlocale(LC_NUMERIC, NULL));
+	setlocale(LC_NUMERIC, "C");
+#endif
+
+	root = Json_create(json);
+
+#ifndef __ANDROID__
+	setlocale(LC_NUMERIC, oldLocale);
+	free(oldLocale);
+#endif
+
+	if (!root) {
+		_spSkeletonJson_setError(self, 0, "Invalid skeleton JSON: ", Json_getError());
+		return 0;
+	}
+
+	skeletonData = spSkeletonData_create();
+
+	skeleton = Json_getItem(root, "skeleton");
+	if (skeleton) {
+		MALLOC_STR(skeletonData->hash, Json_getString(skeleton, "hash", 0));
+		MALLOC_STR(skeletonData->version, Json_getString(skeleton, "spine", 0));
+		skeletonData->width = Json_getFloat(skeleton, "width", 0);
+		skeletonData->height = Json_getFloat(skeleton, "height", 0);
+	}
+
+	/* Bones. */
+	bones = Json_getItem(root, "bones");
+	skeletonData->bones = MALLOC(spBoneData*, bones->size);
+	for (boneMap = bones->child, i = 0; boneMap; boneMap = boneMap->next, ++i) {
+		spBoneData* data;
+		const char* transformMode;
+
+		spBoneData* parent = 0;
+		const char* parentName = Json_getString(boneMap, "parent", 0);
+		if (parentName) {
+			parent = spSkeletonData_findBone(skeletonData, parentName);
+			if (!parent) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Parent bone not found: ", parentName);
+				return 0;
+			}
+		}
+
+		data = spBoneData_create(skeletonData->bonesCount, Json_getString(boneMap, "name", 0), parent);
+		data->length = Json_getFloat(boneMap, "length", 0) * self->scale;
+		data->x = Json_getFloat(boneMap, "x", 0) * self->scale;
+		data->y = Json_getFloat(boneMap, "y", 0) * self->scale;
+		data->rotation = Json_getFloat(boneMap, "rotation", 0);
+		data->scaleX = Json_getFloat(boneMap, "scaleX", 1);
+		data->scaleY = Json_getFloat(boneMap, "scaleY", 1);
+		data->shearX = Json_getFloat(boneMap, "shearX", 0);
+		data->shearY = Json_getFloat(boneMap, "shearY", 0);
+		transformMode = Json_getString(boneMap, "transform", "normal");
+		data->transformMode = SP_TRANSFORMMODE_NORMAL;
+		if (strcmp(transformMode, "normal") == 0)
+			data->transformMode = SP_TRANSFORMMODE_NORMAL;
+		if (strcmp(transformMode, "onlyTranslation") == 0)
+			data->transformMode = SP_TRANSFORMMODE_ONLYTRANSLATION;
+		if (strcmp(transformMode, "noRotationOrReflection") == 0)
+			data->transformMode = SP_TRANSFORMMODE_NOROTATIONORREFLECTION;
+		if (strcmp(transformMode, "noScale") == 0)
+			data->transformMode = SP_TRANSFORMMODE_NOSCALE;
+		if (strcmp(transformMode, "noScaleOrReflection") == 0)
+			data->transformMode = SP_TRANSFORMMODE_NOSCALEORREFLECTION;
+
+		skeletonData->bones[i] = data;
+		skeletonData->bonesCount++;
+	}
+
+	/* Slots. */
+	slots = Json_getItem(root, "slots");
+	if (slots) {
+		Json *slotMap;
+		skeletonData->slotsCount = slots->size;
+		skeletonData->slots = MALLOC(spSlotData*, slots->size);
+		for (slotMap = slots->child, i = 0; slotMap; slotMap = slotMap->next, ++i) {
+			spSlotData* data;
+			const char* color;
+			const char* dark;
+			Json *item;
+
+			const char* boneName = Json_getString(slotMap, "bone", 0);
+			spBoneData* boneData = spSkeletonData_findBone(skeletonData, boneName);
+			if (!boneData) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Slot bone not found: ", boneName);
+				return 0;
+			}
+
+			data = spSlotData_create(i, Json_getString(slotMap, "name", 0), boneData);
+
+			color = Json_getString(slotMap, "color", 0);
+			if (color) {
+				spColor_setFromFloats(&data->color,
+									  toColor(color, 0),
+									  toColor(color, 1),
+									  toColor(color, 2),
+									  toColor(color, 3));
+			}
+
+			dark = Json_getString(slotMap, "dark", 0);
+			if (dark) {
+				data->darkColor = spColor_create();
+				spColor_setFromFloats(data->darkColor,
+									  toColor(dark, 0),
+									  toColor(dark, 1),
+									  toColor(dark, 2),
+									  toColor(dark, 3));
+			}
+
+			item = Json_getItem(slotMap, "attachment");
+			if (item) spSlotData_setAttachmentName(data, item->valueString);
+
+			item = Json_getItem(slotMap, "blend");
+			if (item) {
+				if (strcmp(item->valueString, "additive") == 0)
+					data->blendMode = SP_BLEND_MODE_ADDITIVE;
+				else if (strcmp(item->valueString, "multiply") == 0)
+					data->blendMode = SP_BLEND_MODE_MULTIPLY;
+				else if (strcmp(item->valueString, "screen") == 0)
+					data->blendMode = SP_BLEND_MODE_SCREEN;
+			}
+
+			skeletonData->slots[i] = data;
+		}
+	}
+
+	/* IK constraints. */
+	ik = Json_getItem(root, "ik");
+	if (ik) {
+		Json *constraintMap;
+		skeletonData->ikConstraintsCount = ik->size;
+		skeletonData->ikConstraints = MALLOC(spIkConstraintData*, ik->size);
+		for (constraintMap = ik->child, i = 0; constraintMap; constraintMap = constraintMap->next, ++i) {
+			const char* targetName;
+
+			spIkConstraintData* data = spIkConstraintData_create(Json_getString(constraintMap, "name", 0));
+			data->order = Json_getInt(constraintMap, "order", 0);
+
+			boneMap = Json_getItem(constraintMap, "bones");
+			data->bonesCount = boneMap->size;
+			data->bones = MALLOC(spBoneData*, boneMap->size);
+			for (boneMap = boneMap->child, ii = 0; boneMap; boneMap = boneMap->next, ++ii) {
+				data->bones[ii] = spSkeletonData_findBone(skeletonData, boneMap->valueString);
+				if (!data->bones[ii]) {
+					spSkeletonData_dispose(skeletonData);
+					_spSkeletonJson_setError(self, root, "IK bone not found: ", boneMap->valueString);
+					return 0;
+				}
+			}
+
+			targetName = Json_getString(constraintMap, "target", 0);
+			data->target = spSkeletonData_findBone(skeletonData, targetName);
+			if (!data->target) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Target bone not found: ", boneMap->name);
+				return 0;
+			}
+
+			data->bendDirection = Json_getInt(constraintMap, "bendPositive", 1) ? 1 : -1;
+			data->mix = Json_getFloat(constraintMap, "mix", 1);
+
+			skeletonData->ikConstraints[i] = data;
+		}
+	}
+
+	/* Transform constraints. */
+	transform = Json_getItem(root, "transform");
+	if (transform) {
+		Json *constraintMap;
+		skeletonData->transformConstraintsCount = transform->size;
+		skeletonData->transformConstraints = MALLOC(spTransformConstraintData*, transform->size);
+		for (constraintMap = transform->child, i = 0; constraintMap; constraintMap = constraintMap->next, ++i) {
+			const char* name;
+
+			spTransformConstraintData* data = spTransformConstraintData_create(Json_getString(constraintMap, "name", 0));
+			data->order = Json_getInt(constraintMap, "order", 0);
+
+			boneMap = Json_getItem(constraintMap, "bones");
+			data->bonesCount = boneMap->size;
+			CONST_CAST(spBoneData**, data->bones) = MALLOC(spBoneData*, boneMap->size);
+			for (boneMap = boneMap->child, ii = 0; boneMap; boneMap = boneMap->next, ++ii) {
+				data->bones[ii] = spSkeletonData_findBone(skeletonData, boneMap->valueString);
+				if (!data->bones[ii]) {
+					spSkeletonData_dispose(skeletonData);
+					_spSkeletonJson_setError(self, root, "Transform bone not found: ", boneMap->valueString);
+					return 0;
+				}
+			}
+
+			name = Json_getString(constraintMap, "target", 0);
+			data->target = spSkeletonData_findBone(skeletonData, name);
+			if (!data->target) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Target bone not found: ", boneMap->name);
+				return 0;
+			}
+
+			data->local = Json_getInt(constraintMap, "local", 0);
+			data->relative = Json_getInt(constraintMap, "relative", 0);
+			data->offsetRotation = Json_getFloat(constraintMap, "rotation", 0);
+			data->offsetX = Json_getFloat(constraintMap, "x", 0) * self->scale;
+			data->offsetY = Json_getFloat(constraintMap, "y", 0) * self->scale;
+			data->offsetScaleX = Json_getFloat(constraintMap, "scaleX", 0);
+			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);
+
+			skeletonData->transformConstraints[i] = data;
+		}
+	}
+
+	/* Path constraints */
+	path = Json_getItem(root, "path");
+	if (path) {
+		Json *constraintMap;
+		skeletonData->pathConstraintsCount = path->size;
+		skeletonData->pathConstraints = MALLOC(spPathConstraintData*, path->size);
+		for (constraintMap = path->child, i = 0; constraintMap; constraintMap = constraintMap->next, ++i) {
+			const char* name;
+			const char* item;
+
+			spPathConstraintData* data = spPathConstraintData_create(Json_getString(constraintMap, "name", 0));
+			data->order = Json_getInt(constraintMap, "order", 0);
+
+			boneMap = Json_getItem(constraintMap, "bones");
+			data->bonesCount = boneMap->size;
+			CONST_CAST(spBoneData**, data->bones) = MALLOC(spBoneData*, boneMap->size);
+			for (boneMap = boneMap->child, ii = 0; boneMap; boneMap = boneMap->next, ++ii) {
+				data->bones[ii] = spSkeletonData_findBone(skeletonData, boneMap->valueString);
+				if (!data->bones[ii]) {
+					spSkeletonData_dispose(skeletonData);
+					_spSkeletonJson_setError(self, root, "Path bone not found: ", boneMap->valueString);
+					return 0;
+				}
+			}
+
+			name = Json_getString(constraintMap, "target", 0);
+			data->target = spSkeletonData_findSlot(skeletonData, name);
+			if (!data->target) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Target slot not found: ", boneMap->name);
+				return 0;
+			}
+
+			item = Json_getString(constraintMap, "positionMode", "percent");
+			if (strcmp(item, "fixed") == 0) data->positionMode = SP_POSITION_MODE_FIXED;
+			else if (strcmp(item, "percent") == 0) data->positionMode = SP_POSITION_MODE_PERCENT;
+
+			item = Json_getString(constraintMap, "spacingMode", "length");
+			if (strcmp(item, "length") == 0) data->spacingMode = SP_SPACING_MODE_LENGTH;
+			else if (strcmp(item, "fixed") == 0) data->spacingMode = SP_SPACING_MODE_FIXED;
+			else if (strcmp(item, "percent") == 0) data->spacingMode = SP_SPACING_MODE_PERCENT;
+
+			item = Json_getString(constraintMap, "rotateMode", "tangent");
+			if (strcmp(item, "tangent") == 0) data->rotateMode = SP_ROTATE_MODE_TANGENT;
+			else if (strcmp(item, "chain") == 0) data->rotateMode = SP_ROTATE_MODE_CHAIN;
+			else if (strcmp(item, "chainScale") == 0) data->rotateMode = SP_ROTATE_MODE_CHAIN_SCALE;
+
+			data->offsetRotation = Json_getFloat(constraintMap, "rotation", 0);
+			data->position = Json_getFloat(constraintMap, "position", 0);
+			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);
+
+			skeletonData->pathConstraints[i] = data;
+		}
+	}
+
+	/* Skins. */
+	skins = Json_getItem(root, "skins");
+	if (skins) {
+		Json *skinMap;
+		skeletonData->skins = MALLOC(spSkin*, skins->size);
+		for (skinMap = skins->child, i = 0; skinMap; skinMap = skinMap->next, ++i) {
+			Json *attachmentsMap;
+			Json *curves;
+			spSkin *skin = spSkin_create(skinMap->name);
+
+			skeletonData->skins[skeletonData->skinsCount++] = skin;
+			if (strcmp(skinMap->name, "default") == 0) skeletonData->defaultSkin = skin;
+
+			for (attachmentsMap = skinMap->child; attachmentsMap; attachmentsMap = attachmentsMap->next) {
+				int slotIndex = spSkeletonData_findSlotIndex(skeletonData, attachmentsMap->name);
+				Json *attachmentMap;
+
+				for (attachmentMap = attachmentsMap->child; attachmentMap; attachmentMap = attachmentMap->next) {
+					spAttachment* attachment;
+					const char* skinAttachmentName = attachmentMap->name;
+					const char* attachmentName = Json_getString(attachmentMap, "name", skinAttachmentName);
+					const char* path = Json_getString(attachmentMap, "path", attachmentName);
+					const char* color;
+					Json* entry;
+
+					const char* typeString = Json_getString(attachmentMap, "type", "region");
+					spAttachmentType type;
+					if (strcmp(typeString, "region") == 0)
+						type = SP_ATTACHMENT_REGION;
+					else if (strcmp(typeString, "mesh") == 0)
+						type = SP_ATTACHMENT_MESH;
+					else if (strcmp(typeString, "linkedmesh") == 0)
+						type = SP_ATTACHMENT_LINKED_MESH;
+					else if (strcmp(typeString, "boundingbox") == 0)
+						type = SP_ATTACHMENT_BOUNDING_BOX;
+					else if (strcmp(typeString, "path") == 0)
+						type = SP_ATTACHMENT_PATH;
+					else if	(strcmp(typeString, "clipping") == 0)
+						type = SP_ATTACHMENT_CLIPPING;
+					else {
+						spSkeletonData_dispose(skeletonData);
+						_spSkeletonJson_setError(self, root, "Unknown attachment type: ", typeString);
+						return 0;
+					}
+
+					attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, type, attachmentName, path);
+					if (!attachment) {
+						if (self->attachmentLoader->error1) {
+							spSkeletonData_dispose(skeletonData);
+							_spSkeletonJson_setError(self, root, self->attachmentLoader->error1, self->attachmentLoader->error2);
+							return 0;
+						}
+						continue;
+					}
+
+					switch (attachment->type) {
+					case SP_ATTACHMENT_REGION: {
+						spRegionAttachment* region = SUB_CAST(spRegionAttachment, attachment);
+						if (path) MALLOC_STR(region->path, path);
+						region->x = Json_getFloat(attachmentMap, "x", 0) * self->scale;
+						region->y = Json_getFloat(attachmentMap, "y", 0) * self->scale;
+						region->scaleX = Json_getFloat(attachmentMap, "scaleX", 1);
+						region->scaleY = Json_getFloat(attachmentMap, "scaleY", 1);
+						region->rotation = Json_getFloat(attachmentMap, "rotation", 0);
+						region->width = Json_getFloat(attachmentMap, "width", 32) * self->scale;
+						region->height = Json_getFloat(attachmentMap, "height", 32) * self->scale;
+
+						color = Json_getString(attachmentMap, "color", 0);
+						if (color) {
+							spColor_setFromFloats(&region->color,
+												  toColor(color, 0),
+												  toColor(color, 1),
+												  toColor(color, 2),
+												  toColor(color, 3));
+						}
+
+						spRegionAttachment_updateOffset(region);
+
+						spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+						break;
+					}
+					case SP_ATTACHMENT_MESH:
+					case SP_ATTACHMENT_LINKED_MESH: {
+						spMeshAttachment* mesh = SUB_CAST(spMeshAttachment, attachment);
+
+						MALLOC_STR(mesh->path, path);
+
+						color = Json_getString(attachmentMap, "color", 0);
+						if (color) {
+							spColor_setFromFloats(&mesh->color,
+												  toColor(color, 0),
+												  toColor(color, 1),
+												  toColor(color, 2),
+												  toColor(color, 3));
+						}
+
+						mesh->width = Json_getFloat(attachmentMap, "width", 32) * self->scale;
+						mesh->height = Json_getFloat(attachmentMap, "height", 32) * self->scale;
+
+						entry = Json_getItem(attachmentMap, "parent");
+						if (!entry) {
+							int verticesLength;
+							entry = Json_getItem(attachmentMap, "triangles");
+							mesh->trianglesCount = entry->size;
+							mesh->triangles = MALLOC(unsigned short, entry->size);
+							for (entry = entry->child, ii = 0; entry; entry = entry->next, ++ii)
+								mesh->triangles[ii] = (unsigned short)entry->valueInt;
+
+							entry = Json_getItem(attachmentMap, "uvs");
+							verticesLength = entry->size;
+							mesh->regionUVs = MALLOC(float, verticesLength);
+							for (entry = entry->child, ii = 0; entry; entry = entry->next, ++ii)
+								mesh->regionUVs[ii] = entry->valueFloat;
+
+							_readVertices(self, attachmentMap, SUPER(mesh), verticesLength);
+
+							spMeshAttachment_updateUVs(mesh);
+
+							mesh->hullLength = Json_getInt(attachmentMap, "hull", 0);
+
+							entry = Json_getItem(attachmentMap, "edges");
+							if (entry) {
+								mesh->edgesCount = entry->size;
+								mesh->edges = MALLOC(int, entry->size);
+								for (entry = entry->child, ii = 0; entry; entry = entry->next, ++ii)
+									mesh->edges[ii] = entry->valueInt;
+							}
+
+							spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+						} else {
+							mesh->inheritDeform = Json_getInt(attachmentMap, "deform", 1);
+							_spSkeletonJson_addLinkedMesh(self, SUB_CAST(spMeshAttachment, attachment), Json_getString(attachmentMap, "skin", 0), slotIndex,
+									entry->valueString);
+						}
+						break;
+					}
+					case SP_ATTACHMENT_BOUNDING_BOX: {
+						spBoundingBoxAttachment* box = SUB_CAST(spBoundingBoxAttachment, attachment);
+						int vertexCount = Json_getInt(attachmentMap, "vertexCount", 0) << 1;
+						_readVertices(self, attachmentMap, SUPER(box), vertexCount);
+						box->super.verticesCount = vertexCount;
+						spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+						break;
+					}
+					case SP_ATTACHMENT_PATH: {
+						spPathAttachment* path = SUB_CAST(spPathAttachment, attachment);
+						int vertexCount = 0;
+						path->closed = Json_getInt(attachmentMap, "closed", 0);
+						path->constantSpeed = Json_getInt(attachmentMap, "constantSpeed", 1);
+						vertexCount = Json_getInt(attachmentMap, "vertexCount", 0);
+						_readVertices(self, attachmentMap, SUPER(path), vertexCount << 1);
+
+						path->lengthsLength = vertexCount / 3;
+						path->lengths = MALLOC(float, path->lengthsLength);
+
+						curves = Json_getItem(attachmentMap, "lengths");
+						for (curves = curves->child, ii = 0; curves; curves = curves->next, ++ii) {
+							path->lengths[ii] = curves->valueFloat * self->scale;
+						}
+						break;
+					}
+					case SP_ATTACHMENT_POINT: {
+						spPointAttachment* point = SUB_CAST(spPointAttachment, attachment);
+						point->x = Json_getFloat(attachmentMap, "x", 0) * self->scale;
+						point->y = Json_getFloat(attachmentMap, "y", 0) * self->scale;
+						point->rotation = Json_getFloat(attachmentMap, "rotation", 0);
+
+						color = Json_getString(attachmentMap, "color", 0);
+						if (color) {
+							spColor_setFromFloats(&point->color,
+												  toColor(color, 0),
+												  toColor(color, 1),
+												  toColor(color, 2),
+												  toColor(color, 3));
+						}
+						break;
+					}
+					case SP_ATTACHMENT_CLIPPING: {
+						spClippingAttachment* clip = SUB_CAST(spClippingAttachment, attachment);
+						int vertexCount = 0;
+						const char* end = Json_getString(attachmentMap, "end", 0);
+						if (end) {
+							spSlotData* slot = spSkeletonData_findSlot(skeletonData, end);
+							clip->endSlot = slot;
+						}
+						vertexCount = Json_getInt(attachmentMap, "vertexCount", 0) << 1;
+						_readVertices(self, attachmentMap, SUPER(clip), vertexCount);
+						spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment);
+						break;
+					}
+					}
+
+					spSkin_addAttachment(skin, slotIndex, skinAttachmentName, attachment);
+				}
+			}
+		}
+	}
+
+	/* Linked meshes. */
+	for (i = 0; i < internal->linkedMeshCount; i++) {
+		spAttachment* parent;
+		_spLinkedMesh* linkedMesh = internal->linkedMeshes + i;
+		spSkin* skin = !linkedMesh->skin ? skeletonData->defaultSkin : spSkeletonData_findSkin(skeletonData, linkedMesh->skin);
+		if (!skin) {
+			spSkeletonData_dispose(skeletonData);
+			_spSkeletonJson_setError(self, 0, "Skin not found: ", linkedMesh->skin);
+			return 0;
+		}
+		parent = spSkin_getAttachment(skin, linkedMesh->slotIndex, linkedMesh->parent);
+		if (!parent) {
+			spSkeletonData_dispose(skeletonData);
+			_spSkeletonJson_setError(self, 0, "Parent mesh not found: ", linkedMesh->parent);
+			return 0;
+		}
+		spMeshAttachment_setParentMesh(linkedMesh->mesh, SUB_CAST(spMeshAttachment, parent));
+		spMeshAttachment_updateUVs(linkedMesh->mesh);
+		spAttachmentLoader_configureAttachment(self->attachmentLoader, SUPER(SUPER(linkedMesh->mesh)));
+	}
+
+	/* Events. */
+	events = Json_getItem(root, "events");
+	if (events) {
+		Json *eventMap;
+		const char* stringValue;
+		skeletonData->eventsCount = events->size;
+		skeletonData->events = MALLOC(spEventData*, events->size);
+		for (eventMap = events->child, i = 0; eventMap; eventMap = eventMap->next, ++i) {
+			spEventData* eventData = spEventData_create(eventMap->name);
+			eventData->intValue = Json_getInt(eventMap, "int", 0);
+			eventData->floatValue = Json_getFloat(eventMap, "float", 0);
+			stringValue = Json_getString(eventMap, "string", 0);
+			if (stringValue) MALLOC_STR(eventData->stringValue, stringValue);
+			skeletonData->events[i] = eventData;
+		}
+	}
+
+	/* Animations. */
+	animations = Json_getItem(root, "animations");
+	if (animations) {
+		Json *animationMap;
+		skeletonData->animations = MALLOC(spAnimation*, animations->size);
+		for (animationMap = animations->child; animationMap; animationMap = animationMap->next) {
+			spAnimation* animation = _spSkeletonJson_readAnimation(self, animationMap, skeletonData);
+			if (!animation) {
+				spSkeletonData_dispose(skeletonData);
+				return 0;
+			}
+			skeletonData->animations[skeletonData->animationsCount++] = animation;
+		}
+	}
+
+	Json_dispose(root);
+	return skeletonData;
+}

+ 106 - 0
spine-cpp/spine-cpp/src/spine/Skin.c

@@ -0,0 +1,106 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Skin.h>
+#include <spine/extension.h>
+
+_Entry* _Entry_create (int slotIndex, const char* name, spAttachment* attachment) {
+	_Entry* self = NEW(_Entry);
+	self->slotIndex = slotIndex;
+	MALLOC_STR(self->name, name);
+	self->attachment = attachment;
+	return self;
+}
+
+void _Entry_dispose (_Entry* self) {
+	spAttachment_dispose(self->attachment);
+	FREE(self->name);
+	FREE(self);
+}
+
+/**/
+
+spSkin* spSkin_create (const char* name) {
+	spSkin* self = SUPER(NEW(_spSkin));
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spSkin_dispose (spSkin* self) {
+	_Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	while (entry) {
+		_Entry* nextEntry = entry->next;
+		_Entry_dispose(entry);
+		entry = nextEntry;
+	}
+
+	FREE(self->name);
+	FREE(self);
+}
+
+void spSkin_addAttachment (spSkin* self, int slotIndex, const char* name, spAttachment* attachment) {
+	_Entry* newEntry = _Entry_create(slotIndex, name, attachment);
+	newEntry->next = SUB_CAST(_spSkin, self)->entries;
+	SUB_CAST(_spSkin, self)->entries = newEntry;
+}
+
+spAttachment* spSkin_getAttachment (const spSkin* self, int slotIndex, const char* name) {
+	const _Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	while (entry) {
+		if (entry->slotIndex == slotIndex && strcmp(entry->name, name) == 0) return entry->attachment;
+		entry = entry->next;
+	}
+	return 0;
+}
+
+const char* spSkin_getAttachmentName (const spSkin* self, int slotIndex, int attachmentIndex) {
+	const _Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	int i = 0;
+	while (entry) {
+		if (entry->slotIndex == slotIndex) {
+			if (i == attachmentIndex) return entry->name;
+			i++;
+		}
+		entry = entry->next;
+	}
+	return 0;
+}
+
+void spSkin_attachAll (const spSkin* self, spSkeleton* skeleton, const spSkin* oldSkin) {
+	const _Entry *entry = SUB_CAST(_spSkin, oldSkin)->entries;
+	while (entry) {
+		spSlot *slot = skeleton->slots[entry->slotIndex];
+		if (slot->attachment == entry->attachment) {
+			spAttachment *attachment = spSkin_getAttachment(self, entry->slotIndex, entry->name);
+			if (attachment) spSlot_setAttachment(slot, attachment);
+		}
+		entry = entry->next;
+	}
+}

+ 82 - 0
spine-cpp/spine-cpp/src/spine/Slot.c

@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Slot.h>
+#include <spine/extension.h>
+
+typedef struct {
+	spSlot super;
+	float attachmentTime;
+} _spSlot;
+
+spSlot* spSlot_create (spSlotData* data, spBone* bone) {
+	spSlot* self = SUPER(NEW(_spSlot));
+	CONST_CAST(spSlotData*, self->data) = data;
+	CONST_CAST(spBone*, self->bone) = bone;
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+	self->darkColor = data->darkColor == 0 ? 0 : spColor_create();
+	spSlot_setToSetupPose(self);
+	return self;
+}
+
+void spSlot_dispose (spSlot* self) {
+	FREE(self->attachmentVertices);
+	FREE(self->darkColor);
+	FREE(self);
+}
+
+void spSlot_setAttachment (spSlot* self, spAttachment* attachment) {
+	if (attachment == self->attachment) return;
+	CONST_CAST(spAttachment*, self->attachment) = attachment;
+	SUB_CAST(_spSlot, self)->attachmentTime = self->bone->skeleton->time;
+	self->attachmentVerticesCount = 0;
+}
+
+void spSlot_setAttachmentTime (spSlot* self, float time) {
+	SUB_CAST(_spSlot, self)->attachmentTime = self->bone->skeleton->time - time;
+}
+
+float spSlot_getAttachmentTime (const spSlot* self) {
+	return self->bone->skeleton->time - SUB_CAST(_spSlot, self) ->attachmentTime;
+}
+
+void spSlot_setToSetupPose (spSlot* self) {
+	spColor_setFromColor(&self->color, &self->data->color);
+	if (self->darkColor) spColor_setFromColor(self->darkColor, self->data->darkColor);
+
+	if (!self->data->attachmentName)
+		spSlot_setAttachment(self, 0);
+	else {
+		spAttachment* attachment = spSkeleton_getAttachmentForSlotIndex(
+				self->bone->skeleton, self->data->index, self->data->attachmentName);
+		CONST_CAST(spAttachment*, self->attachment) = 0;
+		spSlot_setAttachment(self, attachment);
+	}
+}

+ 56 - 0
spine-cpp/spine-cpp/src/spine/SlotData.c

@@ -0,0 +1,56 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/SlotData.h>
+#include <spine/extension.h>
+
+spSlotData* spSlotData_create (const int index, const char* name, spBoneData* boneData) {
+	spSlotData* self = NEW(spSlotData);
+	CONST_CAST(int, self->index) = index;
+	MALLOC_STR(self->name, name);
+	CONST_CAST(spBoneData*, self->boneData) = boneData;
+	spColor_setFromFloats(&self->color, 1, 1, 1, 1);
+	return self;
+}
+
+void spSlotData_dispose (spSlotData* self) {
+	FREE(self->name);
+	FREE(self->attachmentName);
+	FREE(self->darkColor);
+	FREE(self);
+}
+
+void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName) {
+	FREE(self->attachmentName);
+	if (attachmentName)
+		MALLOC_STR(self->attachmentName, attachmentName);
+	else
+		CONST_CAST(char*, self->attachmentName) = 0;
+}

+ 273 - 0
spine-cpp/spine-cpp/src/spine/TransformConstraint.c

@@ -0,0 +1,273 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/TransformConstraint.h>
+#include <spine/Skeleton.h>
+#include <spine/extension.h>
+
+spTransformConstraint* spTransformConstraint_create (spTransformConstraintData* data, const spSkeleton* skeleton) {
+	int i;
+	spTransformConstraint* self = NEW(spTransformConstraint);
+	CONST_CAST(spTransformConstraintData*, self->data) = data;
+	self->rotateMix = data->rotateMix;
+	self->translateMix = data->translateMix;
+	self->scaleMix = data->scaleMix;
+	self->shearMix = data->shearMix;
+	self->bonesCount = data->bonesCount;
+	CONST_CAST(spBone**, self->bones) = MALLOC(spBone*, self->bonesCount);
+	for (i = 0; i < self->bonesCount; ++i)
+		self->bones[i] = spSkeleton_findBone(skeleton, self->data->bones[i]->name);
+	self->target = spSkeleton_findBone(skeleton, self->data->target->name);
+	return self;
+}
+
+void spTransformConstraint_dispose (spTransformConstraint* self) {
+	FREE(self->bones);
+	FREE(self);
+}
+
+void _spTransformConstraint_applyAbsoluteWorld (spTransformConstraint* self) {
+	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+	spBone* target = self->target;
+	float ta = target->a, tb = target->b, tc = target->c, td = target->d;
+	float degRadReflect = ta * td - tb * tc > 0 ? DEG_RAD : -DEG_RAD;
+	float offsetRotation = self->data->offsetRotation * degRadReflect, offsetShearY = self->data->offsetShearY * degRadReflect;
+	int /*bool*/ modified;
+	int i;
+	float a, b, c, d, r, cosine, sine, x, y, s, ts, by;
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBone* bone = self->bones[i];
+		modified = 0;
+
+		if (rotateMix != 0) {
+			a = bone->a, b = bone->b, c = bone->c, d = bone->d;
+			r = ATAN2(tc, ta) - ATAN2(c, a) + offsetRotation;
+			if (r > PI) r -= PI2;
+			else if (r < -PI) r += PI2;
+			r *= rotateMix;
+			cosine = COS(r);
+			sine = SIN(r);
+			CONST_CAST(float, bone->a) = cosine * a - sine * c;
+			CONST_CAST(float, bone->b) = cosine * b - sine * d;
+			CONST_CAST(float, bone->c) = sine * a + cosine * c;
+			CONST_CAST(float, bone->d) = sine * b + cosine * d;
+			modified = 1;
+		}
+
+		if (translateMix != 0) {
+			spBone_localToWorld(target, self->data->offsetX, self->data->offsetY, &x, &y);
+			CONST_CAST(float, bone->worldX) += (x - bone->worldX) * translateMix;
+			CONST_CAST(float, bone->worldY) += (y - bone->worldY) * translateMix;
+			modified = 1;
+		}
+
+		if (scaleMix > 0) {
+			s = SQRT(bone->a * bone->a + bone->c * bone->c);
+			ts = SQRT(ta * ta + tc * tc);
+			if (s > 0.00001f) s = (s + (ts - s + self->data->offsetScaleX) * scaleMix) / s;
+			CONST_CAST(float, bone->a) *= s;
+			CONST_CAST(float, bone->c) *= s;
+			s = SQRT(bone->b * bone->b + bone->d * bone->d);
+			ts = SQRT(tb * tb + td * td);
+			if (s > 0.00001f) s = (s + (ts - s + self->data->offsetScaleY) * scaleMix) / s;
+			CONST_CAST(float, bone->b) *= s;
+			CONST_CAST(float, bone->d) *= s;
+			modified = 1;
+		}
+
+		if (shearMix > 0) {
+			b = bone->b, d = bone->d;
+			by = ATAN2(d, b);
+			r = ATAN2(td, tb) - ATAN2(tc, ta) - (by - ATAN2(bone->c, bone->a));
+			s = SQRT(b * b + d * d);
+			if (r > PI) r -= PI2;
+			else if (r < -PI) r += PI2;
+			r = by + (r + offsetShearY) * shearMix;
+			CONST_CAST(float, bone->b) = COS(r) * s;
+			CONST_CAST(float, bone->d) = SIN(r) * s;
+			modified = 1;
+		}
+
+		if (modified) CONST_CAST(int, bone->appliedValid) = 0;
+	}
+}
+
+void _spTransformConstraint_applyRelativeWorld (spTransformConstraint* self) {
+	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+	spBone* target = self->target;
+	float ta = target->a, tb = target->b, tc = target->c, td = target->d;
+	float degRadReflect = ta * td - tb * tc > 0 ? DEG_RAD : -DEG_RAD;
+	float offsetRotation = self->data->offsetRotation * degRadReflect, offsetShearY = self->data->offsetShearY * degRadReflect;
+	int /*bool*/ modified;
+	int i;
+	float a, b, c, d, r, cosine, sine, x, y, s;
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBone* bone = self->bones[i];
+		modified = 0;
+
+		if (rotateMix != 0) {
+			a = bone->a, b = bone->b, c = bone->c, d = bone->d;
+			r = ATAN2(tc, ta) + offsetRotation;
+			if (r > PI) r -= PI2;
+			else if (r < -PI) r += PI2;
+			r *= rotateMix;
+			cosine = COS(r);
+			sine = SIN(r);
+			CONST_CAST(float, bone->a) = cosine * a - sine * c;
+			CONST_CAST(float, bone->b) = cosine * b - sine * d;
+			CONST_CAST(float, bone->c) = sine * a + cosine * c;
+			CONST_CAST(float, bone->d) = sine * b + cosine * d;
+			modified = 1;
+		}
+
+		if (translateMix != 0) {
+			spBone_localToWorld(target, self->data->offsetX, self->data->offsetY, &x, &y);
+			CONST_CAST(float, bone->worldX) += (x * translateMix);
+			CONST_CAST(float, bone->worldY) += (y * translateMix);
+			modified = 1;
+		}
+
+		if (scaleMix > 0) {
+			s = (SQRT(ta * ta + tc * tc) - 1 + self->data->offsetScaleX) * scaleMix + 1;
+			CONST_CAST(float, bone->a) *= s;
+			CONST_CAST(float, bone->c) *= s;
+			s = (SQRT(tb * tb + td * td) - 1 + self->data->offsetScaleY) * scaleMix + 1;
+			CONST_CAST(float, bone->b) *= s;
+			CONST_CAST(float, bone->d) *= s;
+			modified = 1;
+		}
+
+		if (shearMix > 0) {
+			r = ATAN2(td, tb) - ATAN2(tc, ta);
+			if (r > PI) r -= PI2;
+			else if (r < -PI) r += PI2;
+			b = bone->b, d = bone->d;
+			r = ATAN2(d, b) + (r - PI / 2 + offsetShearY) * shearMix;
+			s = SQRT(b * b + d * d);
+			CONST_CAST(float, bone->b) = COS(r) * s;
+			CONST_CAST(float, bone->d) = SIN(r) * s;
+			modified = 1;
+		}
+
+		if (modified) CONST_CAST(int, bone->appliedValid) = 0;
+	}
+}
+
+void _spTransformConstraint_applyAbsoluteLocal (spTransformConstraint* self) {
+	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+	spBone* target = self->target;
+	int i;
+	float rotation, r, x, y, scaleX, scaleY, shearY;
+
+	if (!target->appliedValid) spBone_updateAppliedTransform(target);
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBone* bone = self->bones[i];
+		if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
+
+		rotation = bone->arotation;
+		if (rotateMix != 0) {
+			r = target->arotation - rotation + self->data->offsetRotation;
+			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+			rotation += r * rotateMix;
+		}
+
+		x = bone->ax, y = bone->ay;
+		if (translateMix != 0) {
+			x += (target->ax - x + self->data->offsetX) * translateMix;
+			y += (target->ay - y + self->data->offsetY) * translateMix;
+		}
+
+		scaleX = bone->ascaleX, scaleY = bone->ascaleY;
+		if (scaleMix > 0) {
+			if (scaleX > 0.00001) scaleX = (scaleX + (target->ascaleX - scaleX + self->data->offsetScaleX) * scaleMix) / scaleX;
+			if (scaleY > 0.00001) scaleY = (scaleY + (target->ascaleY - scaleY + self->data->offsetScaleY) * scaleMix) / scaleY;
+		}
+
+		shearY = bone->ashearY;
+		if (shearMix > 0) {
+			r = target->ashearY - shearY + self->data->offsetShearY;
+			r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
+			bone->shearY += r * shearMix;
+		}
+
+		spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
+	}
+}
+
+void _spTransformConstraint_applyRelativeLocal (spTransformConstraint* self) {
+	float rotateMix = self->rotateMix, translateMix = self->translateMix, scaleMix = self->scaleMix, shearMix = self->shearMix;
+	spBone* target = self->target;
+	int i;
+	float rotation, x, y, scaleX, scaleY, shearY;
+
+	if (!target->appliedValid) spBone_updateAppliedTransform(target);
+
+	for (i = 0; i < self->bonesCount; ++i) {
+		spBone* bone = self->bones[i];
+		if (!bone->appliedValid) spBone_updateAppliedTransform(bone);
+
+		rotation = bone->arotation;
+		if (rotateMix != 0) rotation += (target->arotation + self->data->offsetRotation) * rotateMix;
+
+		x = bone->ax;
+		y = bone->ay;
+		if (translateMix != 0) {
+			x += (target->ax + self->data->offsetX) * translateMix;
+			y += (target->ay + self->data->offsetY) * translateMix;
+		}
+
+		scaleX = bone->ascaleX;
+		scaleY = bone->ascaleY;
+		if (scaleMix > 0) {
+			if (scaleX > 0.00001f) scaleX *= ((target->ascaleX - 1 + self->data->offsetScaleX) * scaleMix) + 1;
+			if (scaleY > 0.00001f) scaleY *= ((target->ascaleY - 1 + self->data->offsetScaleY) * scaleMix) + 1;
+		}
+
+		shearY = bone->ashearY;
+		if (shearMix > 0) shearY += (target->ashearY + self->data->offsetShearY) * shearMix;
+
+		spBone_updateWorldTransformWith(bone, x, y, rotation, scaleX, scaleY, bone->ashearX, shearY);
+	}
+}
+
+void spTransformConstraint_apply (spTransformConstraint* self) {
+	if (self->data->local) {
+		if (self->data->relative)
+			_spTransformConstraint_applyRelativeLocal(self);
+		else
+			_spTransformConstraint_applyAbsoluteLocal(self);
+
+	} else {
+		if (self->data->relative)
+			_spTransformConstraint_applyRelativeWorld(self);
+		else
+			_spTransformConstraint_applyAbsoluteWorld(self);
+	}
+}

+ 44 - 0
spine-cpp/spine-cpp/src/spine/TransformConstraintData.c

@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/TransformConstraintData.h>
+#include <spine/extension.h>
+
+spTransformConstraintData* spTransformConstraintData_create (const char* name) {
+	spTransformConstraintData* self = NEW(spTransformConstraintData);
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spTransformConstraintData_dispose (spTransformConstraintData* self) {
+	FREE(self->name);
+	FREE(self->bones);
+	FREE(self);
+}

+ 361 - 0
spine-cpp/spine-cpp/src/spine/Triangulator.c

@@ -0,0 +1,361 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/Triangulator.h>
+#include <spine/extension.h>
+#include <stdio.h>
+
+spTriangulator* spTriangulator_create() {
+	spTriangulator* triangulator = CALLOC(spTriangulator, 1);
+
+	triangulator->convexPolygons = spArrayFloatArray_create(16);
+	triangulator->convexPolygonsIndices = spArrayShortArray_create(16);
+	triangulator->indicesArray = spShortArray_create(128);
+	triangulator->isConcaveArray = spIntArray_create(128);
+	triangulator->triangles = spShortArray_create(128);
+	triangulator->polygonPool = spArrayFloatArray_create(16);
+	triangulator->polygonIndicesPool = spArrayShortArray_create(128);
+
+	return triangulator;
+}
+
+void spTriangulator_dispose(spTriangulator* self) {
+	int i;
+
+	for (i = 0; i < self->convexPolygons->size; i++) {
+		spFloatArray_dispose(self->convexPolygons->items[i]);
+	}
+	spArrayFloatArray_dispose(self->convexPolygons);
+
+	for (i = 0; i < self->convexPolygonsIndices->size; i++) {
+		spShortArray_dispose(self->convexPolygonsIndices->items[i]);
+	}
+	spArrayShortArray_dispose(self->convexPolygonsIndices);
+
+	spShortArray_dispose(self->indicesArray);
+	spIntArray_dispose(self->isConcaveArray);
+	spShortArray_dispose(self->triangles);
+
+	for (i = 0; i < self->polygonPool->size; i++) {
+		spFloatArray_dispose(self->polygonPool->items[i]);
+	}
+	spArrayFloatArray_dispose(self->polygonPool);
+
+	for (i = 0; i < self->polygonIndicesPool->size; i++) {
+		spShortArray_dispose(self->polygonIndicesPool->items[i]);
+	}
+	spArrayShortArray_dispose(self->polygonIndicesPool);
+
+	FREE(self);
+}
+
+static spFloatArray* _obtainPolygon(spTriangulator* self) {
+	if (self->polygonPool->size == 0) return spFloatArray_create(16);
+	else return spArrayFloatArray_pop(self->polygonPool);
+}
+
+static void _freePolygon(spTriangulator* self, spFloatArray* polygon) {
+	spArrayFloatArray_add(self->polygonPool, polygon);
+}
+
+static void _freeAllPolygons(spTriangulator* self, spArrayFloatArray* polygons) {
+	int i;
+	for (i = 0; i < polygons->size; i++) {
+		_freePolygon(self, polygons->items[i]);
+	}
+}
+
+static spShortArray* _obtainPolygonIndices(spTriangulator* self) {
+	if (self->polygonIndicesPool->size == 0) return spShortArray_create(16);
+	else return spArrayShortArray_pop(self->polygonIndicesPool);
+}
+
+static void _freePolygonIndices(spTriangulator* self, spShortArray* indices) {
+	spArrayShortArray_add(self->polygonIndicesPool, indices);
+}
+
+static void _freeAllPolygonIndices(spTriangulator* self, spArrayShortArray* polygonIndices) {
+	int i;
+	for (i = 0; i < polygonIndices->size; i++) {
+		_freePolygonIndices(self, polygonIndices->items[i]);
+	}
+}
+
+static int _positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
+	return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
+}
+
+static int _isConcave(int index, int vertexCount, float* vertices, short* indices) {
+	int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
+	int current = indices[index] << 1;
+	int next = indices[(index + 1) % vertexCount] << 1;
+	return !_positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1],
+						 vertices[next],
+						 vertices[next + 1]);
+}
+
+static int _winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
+	float px = p2x - p1x, py = p2y - p1y;
+	return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
+}
+
+spShortArray* spTriangulator_triangulate(spTriangulator* self, spFloatArray* verticesArray) {
+	float* vertices = verticesArray->items;
+	int vertexCount = verticesArray->size >> 1;
+	int i, n, ii;
+
+	spShortArray* indicesArray = self->indicesArray;
+	short* indices;
+	spIntArray* isConcaveArray;
+	int* isConcave;
+	spShortArray* triangles;
+
+	spShortArray_clear(indicesArray);
+	indices = spShortArray_setSize(indicesArray, vertexCount)->items;
+	for (i = 0; i < vertexCount; i++)
+		indices[i] = (short)i;
+
+	isConcaveArray = self->isConcaveArray;
+	isConcave = spIntArray_setSize(isConcaveArray, vertexCount)->items;
+	for (i = 0, n = vertexCount; i < n; ++i)
+		isConcave[i] = _isConcave(i, vertexCount, vertices, indices);
+
+	triangles = self->triangles;
+	spShortArray_clear(triangles);
+	spShortArray_ensureCapacity(triangles, MAX(0, vertexCount - 2) << 2);
+
+	while (vertexCount > 3) {
+		int previous = vertexCount - 1, i = 0, next = 1;
+		int previousIndex, nextIndex;
+		while (1) {
+			if (!isConcave[i]) {
+				int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
+				float p1x = vertices[p1], p1y = vertices[p1 + 1];
+				float p2x = vertices[p2], p2y = vertices[p2 + 1];
+				float p3x = vertices[p3], p3y = vertices[p3 + 1];
+				for (ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
+					int v;
+					float vx, vy;
+					if (!isConcave[ii]) continue;
+					v = indices[ii] << 1;
+					vx = vertices[v]; vy = vertices[v + 1];
+					if (_positiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
+						if (_positiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
+							if (_positiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto break_outer;
+						}
+					}
+				}
+				break;
+			}
+			break_outer:
+
+			if (next == 0) {
+				do {
+					if (!isConcave[i]) break;
+					i--;
+				} while (i > 0);
+				break;
+			}
+
+			previous = i;
+			i = next;
+			next = (next + 1) % vertexCount;
+		}
+
+		spShortArray_add(triangles, indices[(vertexCount + i - 1) % vertexCount]);
+		spShortArray_add(triangles, indices[i]);
+		spShortArray_add(triangles, indices[(i + 1) % vertexCount]);
+		spShortArray_removeAt(indicesArray, i);
+		spIntArray_removeAt(isConcaveArray, i);
+		vertexCount--;
+
+		previousIndex = (vertexCount + i - 1) % vertexCount;
+		nextIndex = i == vertexCount ? 0 : i;
+		isConcave[previousIndex] = _isConcave(previousIndex, vertexCount, vertices, indices);
+		isConcave[nextIndex] = _isConcave(nextIndex, vertexCount, vertices, indices);
+	}
+
+	if (vertexCount == 3) {
+		spShortArray_add(triangles, indices[2]);
+		spShortArray_add(triangles, indices[0]);
+		spShortArray_add(triangles, indices[1]);
+	}
+
+	return triangles;
+}
+
+spArrayFloatArray* spTriangulator_decompose(spTriangulator* self, spFloatArray* verticesArray, spShortArray* triangles) {
+	float* vertices = verticesArray->items;
+
+	spArrayFloatArray* convexPolygons = self->convexPolygons;
+	spArrayShortArray* convexPolygonsIndices;
+	spShortArray* polygonIndices;
+	spFloatArray* polygon;
+
+	int fanBaseIndex, lastWinding;
+	short* trianglesItems;
+	int i, n;
+
+	_freeAllPolygons(self, convexPolygons);
+	spArrayFloatArray_clear(convexPolygons);
+
+	convexPolygonsIndices = self->convexPolygonsIndices;
+	_freeAllPolygonIndices(self, convexPolygonsIndices);
+	spArrayShortArray_clear(convexPolygonsIndices);
+
+	polygonIndices = _obtainPolygonIndices(self);
+	spShortArray_clear(polygonIndices);
+
+	polygon = _obtainPolygon(self);
+	spFloatArray_clear(polygon);
+
+	fanBaseIndex = -1; lastWinding = 0;
+	trianglesItems = triangles->items;
+	for (i = 0, n = triangles->size; i < n; i += 3) {
+		int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
+		float x1 = vertices[t1], y1 = vertices[t1 + 1];
+		float x2 = vertices[t2], y2 = vertices[t2 + 1];
+		float x3 = vertices[t3], y3 = vertices[t3 + 1];
+
+		int merged = 0;
+		if (fanBaseIndex == t1) {
+			int o = polygon->size - 4;
+			float* p = polygon->items;
+			int winding1 = _winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
+			int winding2 = _winding(x3, y3, p[0], p[1], p[2], p[3]);
+			if (winding1 == lastWinding && winding2 == lastWinding) {
+				spFloatArray_add(polygon, x3);
+				spFloatArray_add(polygon, y3);
+				spShortArray_add(polygonIndices, t3);
+				merged = 1;
+			}
+		}
+
+		if (!merged) {
+			if (polygon->size > 0) {
+				spArrayFloatArray_add(convexPolygons, polygon);
+				spArrayShortArray_add(convexPolygonsIndices, polygonIndices);
+			} else {
+				_freePolygon(self, polygon);
+				_freePolygonIndices(self, polygonIndices);
+			}
+			polygon = _obtainPolygon(self);
+			spFloatArray_clear(polygon);
+			spFloatArray_add(polygon, x1);
+			spFloatArray_add(polygon, y1);
+			spFloatArray_add(polygon, x2);
+			spFloatArray_add(polygon, y2);
+			spFloatArray_add(polygon, x3);
+			spFloatArray_add(polygon, y3);
+			polygonIndices = _obtainPolygonIndices(self);
+			spShortArray_clear(polygonIndices);
+			spShortArray_add(polygonIndices, t1);
+			spShortArray_add(polygonIndices, t2);
+			spShortArray_add(polygonIndices, t3);
+			lastWinding = _winding(x1, y1, x2, y2, x3, y3);
+			fanBaseIndex = t1;
+		}
+	}
+
+	if (polygon->size > 0) {
+		spArrayFloatArray_add(convexPolygons, polygon);
+		spArrayShortArray_add(convexPolygonsIndices, polygonIndices);
+	}
+
+	for (i = 0, n = convexPolygons->size; i < n; i++) {
+		int firstIndex, lastIndex;
+		int o;
+		float* p;
+		float prevPrevX, prevPrevY, prevX, prevY, firstX, firstY, secondX, secondY;
+		int winding;
+		int ii;
+
+		polygonIndices = convexPolygonsIndices->items[i];
+		if (polygonIndices->size == 0) continue;
+		firstIndex = polygonIndices->items[0];
+		lastIndex = polygonIndices->items[polygonIndices->size - 1];
+
+		polygon = convexPolygons->items[i];
+		o = polygon->size - 4;
+		p = polygon->items;
+		prevPrevX = p[o]; prevPrevY = p[o + 1];
+		prevX = p[o + 2]; prevY = p[o + 3];
+		firstX = p[0]; firstY = p[1];
+		secondX = p[2]; secondY = p[3];
+		winding = _winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
+
+		for (ii = 0; ii < n; ii++) {
+			spShortArray* otherIndices;
+			int otherFirstIndex, otherSecondIndex, otherLastIndex;
+			spFloatArray* otherPoly;
+			float x3, y3;
+			int winding1, winding2;
+
+			if (ii == i) continue;
+			otherIndices = convexPolygonsIndices->items[ii];
+			if (otherIndices->size != 3) continue;
+			otherFirstIndex = otherIndices->items[0];
+			otherSecondIndex = otherIndices->items[1];
+			otherLastIndex = otherIndices->items[2];
+
+			otherPoly = convexPolygons->items[ii];
+			x3 = otherPoly->items[otherPoly->size - 2]; y3 = otherPoly->items[otherPoly->size - 1];
+
+			if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
+			winding1 = _winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
+			winding2 = _winding(x3, y3, firstX, firstY, secondX, secondY);
+			if (winding1 == winding && winding2 == winding) {
+				spFloatArray_clear(otherPoly);
+				spShortArray_clear(otherIndices);
+				spFloatArray_add(polygon, x3);
+				spFloatArray_add(polygon, y3);
+				spShortArray_add(polygonIndices, otherLastIndex);
+				prevPrevX = prevX;
+				prevPrevY = prevY;
+				prevX = x3;
+				prevY = y3;
+				ii = 0;
+			}
+		}
+	}
+
+	for (i = convexPolygons->size - 1; i >= 0; i--) {
+		polygon = convexPolygons->items[i];
+		if (polygon->size == 0) {
+			spArrayFloatArray_removeAt(convexPolygons, i);
+			_freePolygon(self, polygon);
+			polygonIndices = convexPolygonsIndices->items[i];
+			spArrayShortArray_removeAt(convexPolygonsIndices, i);
+			_freePolygonIndices(self, polygonIndices);
+		}
+	}
+
+	return convexPolygons;
+}

+ 114 - 0
spine-cpp/spine-cpp/src/spine/VertexAttachment.c

@@ -0,0 +1,114 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/VertexAttachment.h>
+#include <spine/extension.h>
+
+/* FIXME this is not thread-safe */
+static int nextID = 0;
+
+void _spVertexAttachment_init (spVertexAttachment* attachment) {
+	attachment->id = (nextID++ & 65535) << 11;
+}
+
+void _spVertexAttachment_deinit (spVertexAttachment* attachment) {
+	_spAttachment_deinit(SUPER(attachment));
+	FREE(attachment->bones);
+	FREE(attachment->vertices);
+}
+
+void spVertexAttachment_computeWorldVertices (spVertexAttachment* self, spSlot* slot, int start, int count, float* worldVertices, int offset, int stride) {
+	spSkeleton* skeleton;
+	int deformLength;
+	float* deform;
+	float* vertices;
+	int* bones;
+
+	count += offset;
+	skeleton = slot->bone->skeleton;
+	deformLength = slot->attachmentVerticesCount;
+	deform = slot->attachmentVertices;
+	vertices = self->vertices;
+	bones = self->bones;
+	if (!bones) {
+		spBone* bone;
+		int v, w;
+		float x, y;
+		if (deformLength > 0) vertices = deform;
+		bone = slot->bone;
+		x = bone->worldX;
+		y = bone->worldY;
+		for (v = start, w = offset; w < count; v += 2, w += stride) {
+			float vx = vertices[v], vy = vertices[v + 1];
+			worldVertices[w] = vx * bone->a + vy * bone->b + x;
+			worldVertices[w + 1] = vx * bone->c + vy * bone->d + y;
+		}
+	} else {
+		int v = 0, skip = 0, i;
+		spBone** skeletonBones;
+		for (i = 0; i < start; i += 2) {
+			int n = bones[v];
+			v += n + 1;
+			skip += n;
+		}
+		skeletonBones = skeleton->bones;
+		if (deformLength == 0) {
+			int w, b;
+			for (w = offset, b = skip * 3; w < count; w += stride) {
+				float wx = 0, wy = 0;
+				int n = bones[v++];
+				n += v;
+				for (; v < n; v++, b += 3) {
+					spBone* bone = skeletonBones[bones[v]];
+					float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
+					wx += (vx * bone->a + vy * bone->b + bone->worldX) * weight;
+					wy += (vx * bone->c + vy * bone->d + bone->worldY) * weight;
+				}
+				worldVertices[w] = wx;
+				worldVertices[w + 1] = wy;
+			}
+		} else {
+			int w, b, f;
+			for (w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
+				float wx = 0, wy = 0;
+				int n = bones[v++];
+				n += v;
+				for (; v < n; v++, b += 3, f += 2) {
+					spBone* bone = skeletonBones[bones[v]];
+					float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
+					wx += (vx * bone->a + vy * bone->b + bone->worldX) * weight;
+					wy += (vx * bone->c + vy * bone->d + bone->worldY) * weight;
+				}
+				worldVertices[w] = wx;
+				worldVertices[w + 1] = wy;
+			}
+		}
+	}
+}

+ 98 - 0
spine-cpp/spine-cpp/src/spine/VertexEffect.c

@@ -0,0 +1,98 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/VertexEffect.h>
+#include <spine/extension.h>
+
+void _spJitterVertexEffect_begin(spVertexEffect* self, spSkeleton* skeleton) {
+}
+
+void _spJitterVertexEffect_transform(spVertexEffect* self, float* x, float* y, float* u, float* v, spColor* light, spColor* dark) {
+	spJitterVertexEffect* internal = (spJitterVertexEffect*)self;
+	float jitterX = internal->jitterX;
+	float jitterY = internal->jitterY;
+	(*x) += _spMath_randomTriangular(-jitterX, jitterY);
+	(*y) += _spMath_randomTriangular(-jitterX, jitterY);
+}
+
+void _spJitterVertexEffect_end(spVertexEffect* self) {
+}
+
+spJitterVertexEffect* spJitterVertexEffect_create(float jitterX, float jitterY) {
+	spJitterVertexEffect* effect = CALLOC(spJitterVertexEffect, 1);
+	effect->super.begin = _spJitterVertexEffect_begin;
+	effect->super.transform = _spJitterVertexEffect_transform;
+	effect->super.end = _spJitterVertexEffect_end;
+	effect->jitterX = jitterX;
+	effect->jitterY = jitterY;
+	return effect;
+}
+
+void spJitterVertexEffect_dispose(spJitterVertexEffect* effect) {
+	FREE(effect);
+}
+
+void _spSwirlVertexEffect_begin(spVertexEffect* self, spSkeleton* skeleton) {
+	spSwirlVertexEffect* internal = (spSwirlVertexEffect*)self;
+	internal->worldX = skeleton->x + internal->centerX;
+	internal->worldY = skeleton->y + internal->centerY;
+}
+
+void _spSwirlVertexEffect_transform(spVertexEffect* self, float* positionX, float* positionY, float* u, float* v, spColor* light, spColor* dark) {
+	spSwirlVertexEffect* internal = (spSwirlVertexEffect*)self;
+	float radAngle = internal->angle * DEG_RAD;
+	float x = *positionX - internal->worldX;
+	float y = *positionY - internal->worldY;
+	float dist = SQRT(x * x + y * y);
+	if (dist < internal->radius) {
+		float theta = _spMath_interpolate(_spMath_pow2_apply, 0, radAngle, (internal->radius - dist) / internal->radius);
+		float cosine = COS(theta);
+		float sine = SIN(theta);
+		(*positionX) = cosine * x - sine * y + internal->worldX;
+		(*positionY) = sine * x + cosine * y + internal->worldY;
+	}
+}
+
+void _spSwirlVertexEffect_end(spVertexEffect* self) {
+}
+
+spSwirlVertexEffect* spSwirlVertexEffect_create(float radius) {
+	spSwirlVertexEffect* effect = CALLOC(spSwirlVertexEffect, 1);
+	effect->super.begin = _spSwirlVertexEffect_begin;
+	effect->super.transform = _spSwirlVertexEffect_transform;
+	effect->super.end = _spSwirlVertexEffect_end;
+	effect->radius = radius;
+	return effect;
+}
+
+void spSwirlVertexEffect_dispose(spSwirlVertexEffect* effect) {
+	FREE(effect);
+}
+

+ 128 - 0
spine-cpp/spine-cpp/src/spine/extension.c

@@ -0,0 +1,128 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, Esoteric Software
+ * All rights reserved.
+ *
+ * You are granted a perpetual, non-exclusive, non-sublicensable, and
+ * non-transferable license to use, install, execute, and perform the Spine
+ * Runtimes software and derivative works solely for personal or internal
+ * use. Without the written permission of Esoteric Software (see Section 2 of
+ * the Spine Software License Agreement), you may not (a) modify, translate,
+ * adapt, or develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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 SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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.
+ *****************************************************************************/
+
+#include <spine/extension.h>
+#include <stdio.h>
+
+float _spInternalRandom () {
+	return rand() / (float)RAND_MAX;
+}
+
+static void* (*mallocFunc) (size_t size) = malloc;
+static void* (*reallocFunc) (void* ptr, size_t size) = realloc;
+static void* (*debugMallocFunc) (size_t size, const char* file, int line) = NULL;
+static void (*freeFunc) (void* ptr) = free;
+static float (*randomFunc) () = _spInternalRandom;
+
+void* _spMalloc (size_t size, const char* file, int line) {
+	if(debugMallocFunc)
+		return debugMallocFunc(size, file, line);
+
+	return mallocFunc(size);
+}
+void* _spCalloc (size_t num, size_t size, const char* file, int line) {
+	void* ptr = _spMalloc(num * size, file, line);
+	if (ptr) memset(ptr, 0, num * size);
+	return ptr;
+}
+void* _spRealloc(void* ptr, size_t size) {
+	return reallocFunc(ptr, size);
+}
+void _spFree (void* ptr) {
+	freeFunc(ptr);
+}
+
+float _spRandom () {
+	return randomFunc();
+}
+
+void _spSetDebugMalloc(void* (*malloc) (size_t size, const char* file, int line)) {
+	debugMallocFunc = malloc;
+}
+
+void _spSetMalloc (void* (*malloc) (size_t size)) {
+	mallocFunc = malloc;
+}
+
+void _spSetRealloc (void* (*realloc) (void* ptr, size_t size)) {
+	reallocFunc = realloc;
+}
+
+void _spSetFree (void (*free) (void* ptr)) {
+	freeFunc = free;
+}
+
+void _spSetRandom (float (*random) ()) {
+	randomFunc = random;
+}
+
+char* _spReadFile (const char* path, int* length) {
+	char *data;
+	FILE *file = fopen(path, "rb");
+	if (!file) return 0;
+
+	fseek(file, 0, SEEK_END);
+	*length = (int)ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	data = MALLOC(char, *length);
+	fread(data, 1, *length, file);
+	fclose(file);
+
+	return data;
+}
+
+float _spMath_random(float min, float max) {
+	return min + (max - min) * _spRandom();
+}
+
+float _spMath_randomTriangular(float min, float max) {
+	return _spMath_randomTriangularWith(min, max, (min + max) * 0.5f);
+}
+
+float _spMath_randomTriangularWith(float min, float max, float mode) {
+	float u = _spRandom();
+	float d = max - min;
+	if (u <= (mode - min) / d) return min + SQRT(u * d * (mode - min));
+	return max - SQRT((1 - u) * d * (max - mode));
+}
+
+float _spMath_interpolate(float (*apply) (float a), float start, float end, float a) {
+	return start + (end - start) * apply(a);
+}
+
+float _spMath_pow2_apply(float a) {
+	if (a <= 0.5) return POW(a * 2, 2) / 2;
+	return POW((a - 1) * 2, 2) / -2 + 1;
+}
+
+float _spMath_pow2out_apply(float a) {
+	return POW(a - 1, 2) * -1 + 1;
+}

+ 105 - 0
spine-cpp/spine-cpp/src/spine/kvec.h

@@ -0,0 +1,105 @@
+/* The MIT License
+
+   Copyright (c) 2008, by Attractive Chaos <[email protected]>
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+/*
+  An example:
+
+#include "kvec.h"
+int main() {
+	kvec_t(int) array;
+	kv_init(array);
+	kv_push(int, array, 10); // append
+	kv_a(int, array, 20) = 5; // dynamic
+	kv_A(array, 20) = 4; // static
+	kv_destroy(array);
+	return 0;
+}
+*/
+
+/*
+  2008-09-22 (0.1.0):
+
+	* The initial version.
+
+  2017-19-18 (0.1.1):
+
+	Spine Special Edition
+	* Made helper macros for alloc, free and memcpy, which can be overridden.
+	* Made these helpers point to the Spine C Runtime alloc and free functions by default
+	* Reimplemented kv_resize to use alloc and free instead of realloc
+	* Changed kv_push to use kv_resize instead of realloc
+	* Removed kv_pushp and kv_a macros because the weren't used
+	* Removed stdlib include
+*/
+
+#ifndef AC_KVEC_H
+#define AC_KVEC_H
+
+#ifndef _kv_free
+#define _kv_free(type, p) (FREE(p))
+#endif
+
+#ifndef _kv_alloc
+#define _kv_alloc(type, s) ((type*)(MALLOC(type, (s))))
+#endif
+
+#ifndef _kv_copy
+#define _kv_copy(type, d, s, n) memcpy((d), (s), sizeof(type) * (n))
+#endif
+
+#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+
+#define kvec_t(type) struct { size_t n, m; type *a; }
+#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
+#define kv_destroy(v) _kv_free(type, (v).a)
+#define kv_A(v, i) ((v).a[(i)])
+#define kv_array(v) ((v).a)
+#define kv_pop(v) ((v).a[--(v).n])
+#define kv_size(v) ((v).n)
+#define kv_max(v) ((v).m)
+
+#define kv_resize(type, v, s) do {									\
+		type* b = _kv_alloc(type, (s));								\
+		if (((s) > 0) && ((v).m > 0))								\
+			_kv_copy(type, b, (v).a, ((s) < (v).m)? (s) : (v).m);	\
+		_kv_free(type, (v).a);										\
+		(v).a = b; (v).m = (s);										\
+	} while (0)
+	
+#define kv_trim(type, v) kv_resize(type, (v), kv_size(v))
+
+#define kv_copy(type, v1, v0) do {								\
+		if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n);		\
+		(v1).n = (v0).n;										\
+		_kv_copy(type, (v1).a, (v0).a, (v0).n);					\
+	} while (0)													\
+
+#define kv_push(type, v, x) do {								\
+		if ((v).n == (v).m) 									\
+			kv_resize(type, (v), ((v).m? (v).m<<1 : 2));		\
+		(v).a[(v).n++] = (x);									\
+	} while (0)
+
+#endif