Stephen Gowen 7 éve
szülő
commit
be2dfc7ff4

+ 72 - 1
spine-cpp/spine-cpp/include/spine/Json.h

@@ -31,9 +31,80 @@
 #ifndef Spine_Json_h
 #define Spine_Json_h
 
+#ifndef SPINE_JSON_HAVE_PREV
+/* Spine doesn't use the "prev" link in the Json sibling lists. */
+#define SPINE_JSON_HAVE_PREV 0
+#endif
+
 namespace Spine
 {
-    // TODO
+    class Json
+    {
+    public:
+        /* Json Types: */
+        static const int JSON_FALSE;
+        static const int JSON_TRUE;
+        static const int JSON_NULL;
+        static const int JSON_NUMBER;
+        static const int JSON_STRING;
+        static const int JSON_ARRAY;
+        static const int JSON_OBJECT;
+        
+        /* Get item "string" from object. Case insensitive. */
+        static Json* getItem(Json *object, const char* string);
+        
+        static const char* getString(Json *object, const char* name, const char* defaultValue);
+        
+        static float getFloat(Json *object, const char* name, float defaultValue);
+        
+        static int getInt(Json *object, 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. */
+        static const char* getError();
+        
+        /* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */
+        Json(const char* value);
+        
+        ~Json();
+        
+    private:
+        static const char* JSON_ERROR;
+        
+        Json* _next;
+#if SPINE_JSON_HAVE_PREV
+        Json* _prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */
+#endif
+        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. */
+        
+        /* Utility to jump whitespace and cr/lf */
+        static const char* skip(const char* inValue);
+        
+        /* Parser core - when encountering text, process appropriately. */
+        static const char* parseValue(Json *item, const char* value);
+        
+        /* Parse the input text into an unescaped cstring, and populate item. */
+        static const char* parseString(Json *item, const char* str);
+        
+        /* Parse the input text to generate a number, and populate the result into item. */
+        static const char* parseNumber(Json *item, const char* num);
+        
+        /* Build an array from input text. */
+        static const char* parseArray(Json *item, const char* value);
+        
+        /* Build an object from the text. */
+        static const char* parseObject(Json *item, const char* value);
+        
+        static int strcasecmp(const char* s1, const char* s2);
+    };
 }
 
 #endif /* Spine_Json_h */

+ 22 - 2
spine-cpp/spine-cpp/include/spine/SkeletonJson.h

@@ -33,8 +33,15 @@
 
 #include <spine/Vector.h>
 
+#include <string>
+
 namespace Spine
 {
+    class MeshAttachment;
+    class CurveTimeline;
+    class VertexAttachment;
+    class Animation;
+    class Json;
     class SkeletonData;
     class Atlas;
     class AttachmentLoader;
@@ -49,15 +56,28 @@ namespace Spine
         
         ~SkeletonJson();
         
-        SkeletonData* readSkeletonData(const char* json);
-        
         SkeletonData* readSkeletonDataFile(const char* path);
         
+        SkeletonData* readSkeletonData(const char* json);
+        
     private:
         AttachmentLoader* _attachmentLoader;
         Vector<LinkedMesh*> _linkedMeshes;
         float _scale;
         const bool _ownsLoader;
+        std::string _error;
+        
+        static float toColor(const char* value, int index);
+        
+        static void readCurve(Json* frame, CurveTimeline* timeline, int frameIndex);
+        
+        void addLinkedMesh(MeshAttachment* mesh, const char* skin, int slotIndex, const char* parent);
+        
+        Animation* readAnimation(Json* root, SkeletonData *skeletonData);
+        
+        void readVertices(Json* attachmentMap, VertexAttachment* attachment, int verticesLength);
+        
+        void setError(Json* root, const char* value1, const char* value2);
     };
 }
 

+ 615 - 1
spine-cpp/spine-cpp/src/spine/Json.cpp

@@ -28,7 +28,621 @@
 * POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/
 
+/* Json */
+/* JSON parser in CPP, shamelessly ripped from json.c in the spine-c runtime */
+
+#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 <spine/Json.h>
+
+#include <assert.h>
+#include <math.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>
+#include <new>
+
 namespace Spine
 {
-    // TODO
+    const int Json::JSON_FALSE = 0;
+    const int Json::JSON_TRUE = 1;
+    const int Json::JSON_NULL = 2;
+    const int Json::JSON_NUMBER = 3;
+    const int Json::JSON_STRING = 4;
+    const int Json::JSON_ARRAY = 5;
+    const int Json::JSON_OBJECT = 6;
+    
+    const char* Json::JSON_ERROR = NULL;
+    
+    Json* Json::getItem(Json *object, const char* string)
+    {
+        Json *c = object->_child;
+        while (c && strcasecmp(c->_name, string))
+        {
+            c = c->_next;
+        }
+        return c;
+    }
+    
+    const char* Json::getString(Json *object, const char* name, const char* defaultValue)
+    {
+        object = getItem(object, name);
+        if (object)
+        {
+            return object->_valueString;
+        }
+        
+        return defaultValue;
+    }
+    
+    float Json::getFloat(Json *value, const char* name, float defaultValue)
+    {
+        value = getItem(value, name);
+        return value ? value->_valueFloat : defaultValue;
+    }
+    
+    int Json::getInt(Json *value, const char* name, int defaultValue)
+    {
+        value = getItem(value, name);
+        return value ? value->_valueInt : defaultValue;
+    }
+    
+    const char* Json::getError()
+    {
+        return JSON_ERROR;
+    }
+    
+    Json::Json(const char* value) :
+    _next(NULL),
+#if SPINE_JSON_HAVE_PREV
+    _prev(NULL),
+#endif
+    _child(NULL),
+    _type(0),
+    _size(0),
+    _valueString(NULL),
+    _valueInt(0),
+    _valueFloat(0),
+    _name(NULL)
+    {
+        if (value)
+        {
+            value = parseValue(this, skip(value));
+            
+            assert(value);
+        }
+    }
+    
+    Json::~Json()
+    {
+        if (_child)
+        {
+            DESTROY(Json, _child);
+        }
+        
+        if (_valueString)
+        {
+            FREE(_valueString);
+        }
+        
+        if (_name)
+        {
+            FREE(_name);
+        }
+        
+        if (_next)
+        {
+            DESTROY(Json, _next);
+        }
+    }
+    
+    const char* Json::skip(const char* inValue)
+    {
+        if (!inValue)
+        {
+            /* must propagate NULL since it's often called in skip(f(...)) form */
+            return NULL;
+        }
+        
+        while (*inValue && (unsigned char)*inValue <= 32)
+        {
+            inValue++;
+        }
+        
+        return inValue;
+    }
+    
+    const char* Json::parseValue(Json *item, const char* value)
+    {
+        /* Referenced by constructor, parseArray(), and parseObject(). */
+        /* Always called with the result of skip(). */
+#if SPINE_JSON_DEBUG /* Checked at entry to graph, constructor, and after every parse call. */
+        if (!value)
+        {
+            /* Fail on null. */
+            return 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 parseString(item, value);
+            case '[':
+                return parseArray(item, value);
+            case '{':
+                return parseObject(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 parseNumber(item, value);
+            default:
+                break;
+        }
+        
+        JSON_ERROR = value;
+        return NULL; /* failure. */
+    }
+    
+    static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+    const char* Json::parseString(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 parseValue, but do need from parseObject */
+            JSON_ERROR = 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 = NULL;
+        
+        if (*ptr == '\"')
+        {
+            ptr++; /* TODO error handling if not \" or \0 ? */
+        }
+        
+        item->_valueString = out;
+        item->_type = JSON_STRING;
+        
+        return ptr;
+    }
+    
+    const char* Json::parseNumber(Json *item, const char* num)
+    {
+        double result = 0.0;
+        int negative = 0;
+        char* ptr = (char*)num;
+        
+        if (*ptr == '-')
+        {
+            negative = -1;
+            ++ptr;
+        }
+        
+        while (*ptr >= '0' && *ptr <= '9')
+        {
+            result = result * 10.0 + (*ptr - '0');
+            ++ptr;
+        }
+        
+        if (*ptr == '.')
+        {
+            double fraction = 0.0;
+            int n = 0;
+            ++ptr;
+            
+            while (*ptr >= '0' && *ptr <= '9')
+            {
+                fraction = (fraction * 10.0) + (*ptr - '0');
+                ++ptr;
+                ++n;
+            }
+            result += fraction / pow(10.0, n);
+        }
+        
+        if (negative)
+        {
+            result = -result;
+        }
+        
+        if (*ptr == 'e' || *ptr == 'E')
+        {
+            double exponent = 0;
+            int expNegative = 0;
+            int n = 0;
+            ++ptr;
+            
+            if (*ptr == '-')
+            {
+                expNegative = -1;
+                ++ptr;
+            }
+            else if (*ptr == '+')
+            {
+                ++ptr;
+            }
+            
+            while (*ptr >= '0' && *ptr <= '9')
+            {
+                exponent = (exponent * 10.0) + (*ptr - '0');
+                ++ptr;
+                ++n;
+            }
+            
+            if (expNegative)
+            {
+                result = result / pow(10, exponent);
+            }
+            else
+            {
+                result = result * pow(10, exponent);
+            }
+        }
+        
+        if (ptr != num)
+        {
+            /* Parse success, number found. */
+            item->_valueFloat = result;
+            item->_valueInt = static_cast<int>(result);
+            item->_type = JSON_NUMBER;
+            return ptr;
+        }
+        else
+        {
+            /* Parse failure, JSON_ERROR is set. */
+            JSON_ERROR = num;
+            return NULL;
+        }
+    }
+    
+    const char* Json::parseArray(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 = NEW(Json);
+        new (item->_child) Json(NULL);
+        if (!item->_child)
+        {
+            return NULL; /* memory fail */
+        }
+        
+        value = skip(parseValue(child, skip(value))); /* skip any spacing, get the value. */
+        
+        if (!value)
+        {
+            return NULL;
+        }
+        
+        item->_size = 1;
+        
+        while (*value == ',')
+        {
+            Json *new_item = NEW(Json);
+            new (new_item) Json(NULL);
+            if (!new_item)
+            {
+                return NULL; /* memory fail */
+            }
+            child->_next = new_item;
+#if SPINE_JSON_HAVE_PREV
+            new_item->prev = child;
+#endif
+            child = new_item;
+            value = skip(parseValue(child, skip(value + 1)));
+            if (!value)
+            {
+                return NULL; /* parse fail */
+            }
+            item->_size++;
+        }
+        
+        if (*value == ']')
+        {
+            return value + 1; /* end of array */
+        }
+        
+        JSON_ERROR = value;
+        
+        return NULL; /* malformed. */
+    }
+    
+    /* Build an object from the text. */
+    const char* Json::parseObject(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 = NEW(Json);
+        new (item->_child) Json(NULL);
+        if (!item->_child)
+        {
+            return NULL;
+        }
+        value = skip(parseString(child, skip(value)));
+        if (!value)
+        {
+            return NULL;
+        }
+        child->_name = child->_valueString;
+        child->_valueString = 0;
+        if (*value != ':')
+        {
+            JSON_ERROR = value;
+            return NULL;
+        } /* fail! */
+        
+        value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */
+        if (!value)
+        {
+            return NULL;
+        }
+        
+        item->_size = 1;
+        
+        while (*value == ',')
+        {
+            Json *new_item = NEW(Json);
+            new (new_item) Json(NULL);
+            if (!new_item)
+            {
+                return NULL; /* memory fail */
+            }
+            child->_next = new_item;
+#if SPINE_JSON_HAVE_PREV
+            new_item->prev = child;
+#endif
+            child = new_item;
+            value = skip(parseString(child, skip(value + 1)));
+            if (!value)
+            {
+                return NULL;
+            }
+            child->_name = child->_valueString;
+            child->_valueString = 0;
+            if (*value != ':')
+            {
+                JSON_ERROR = value;
+                return NULL;
+            } /* fail! */
+            
+            value = skip(parseValue(child, skip(value + 1))); /* skip any spacing, get the value. */
+            if (!value)
+            {
+                return NULL;
+            }
+            item->_size++;
+        }
+        
+        if (*value == '}')
+        {
+            return value + 1; /* end of array */
+        }
+        
+        JSON_ERROR = value;
+        
+        return NULL; /* malformed. */
+    }
+    
+    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 */
+            }
+        }
+    }
 }

+ 1184 - 1
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -30,6 +30,10 @@
 
 #include <spine/SkeletonJson.h>
 
+#include <stdio.h>
+
+#include <spine/Json.h>
+#include <spine/SkeletonData.h>
 #include <spine/Atlas.h>
 #include <spine/AtlasAttachmentLoader.h>
 #include <spine/LinkedMesh.h>
@@ -37,6 +41,10 @@
 #include <spine/Extension.h>
 #include <spine/ContainerUtil.h>
 
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
+#define strdup _strdup
+#endif
+
 namespace Spine
 {
     SkeletonJson::SkeletonJson(Vector<Atlas*>& atlasArray) : _attachmentLoader(NEW(AtlasAttachmentLoader)), _scale(1), _ownsLoader(true)
@@ -59,13 +67,1188 @@ namespace Spine
         }
     }
     
+    SkeletonData* SkeletonJson::readSkeletonDataFile(const char* path)
+    {
+        int length;
+        SkeletonData* skeletonData;
+        const char* json = SPINE_EXTENSION->spineReadFile(path, &length);
+        if (length == 0 || !json)
+        {
+            setError(NULL, "Unable to read skeleton file: ", path);
+            return NULL;
+        }
+        
+        skeletonData = readSkeletonData(json);
+        
+        FREE(json);
+        
+        return skeletonData;
+    }
+    
     SkeletonData* SkeletonJson::readSkeletonData(const char* json)
     {
+//        int i, ii;
+//        SkeletonData* skeletonData;
+//        Json *root, *skeleton, *bones, *boneMap, *ik, *transform, *path, *slots, *skins, *animations, *events;
+//        _spSkeletonJson* internal = SUB_CAST(_spSkeletonJson, self);
+//
+//        FREE(self->error);
+//        CONST_CAST(char*, self->error) = 0;
+//        internal->linkedMeshCount = 0;
+//
+//        root = Json_create(json);
+//
+//        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* attachmentPath = 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, attachmentPath);
+//                        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, attachmentPath);
+//                                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, attachmentPath);
+//
+//                                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* pathAttatchment = SUB_CAST(spPathAttachment, attachment);
+//                                int vertexCount = 0;
+//                                pathAttatchment->closed = Json_getInt(attachmentMap, "closed", 0);
+//                                pathAttatchment->constantSpeed = Json_getInt(attachmentMap, "constantSpeed", 1);
+//                                vertexCount = Json_getInt(attachmentMap, "vertexCount", 0);
+//                                _readVertices(self, attachmentMap, SUPER(pathAttatchment), vertexCount << 1);
+//
+//                                pathAttatchment->lengthsLength = vertexCount / 3;
+//                                pathAttatchment->lengths = MALLOC(float, pathAttatchment->lengthsLength);
+//
+//                                curves = Json_getItem(attachmentMap, "lengths");
+//                                for (curves = curves->child, ii = 0; curves; curves = curves->next, ++ii) {
+//                                    pathAttatchment->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;
         return NULL;
     }
     
-    SkeletonData* SkeletonJson::readSkeletonDataFile(const char* path)
+    float SkeletonJson::toColor(const char* value, int index)
     {
+//        char digits[3];
+//        char *error;
+//        int color;
+//
+//        if (index >= strlen(value) / 2)
+//        {
+//            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;
+        return 0;
+    }
+    
+    void SkeletonJson::readCurve(Json* frame, CurveTimeline* 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);
+//        }
+    }
+    
+    void SkeletonJson::addLinkedMesh(MeshAttachment* 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;
+    }
+    
+    Animation* SkeletonJson::readAnimation(Json* root, SkeletonData *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* deform2;
+//                        if (!vertices)
+//                        {
+//                            if (weighted)
+//                            {
+//                                deform2 = tempDeform;
+//                                memset(deform, 0, sizeof(float) * deformLength);
+//                            }
+//                            else
+//                            {
+//                                deform2 = attachment->vertices;
+//                            }
+//                        }
+//                        else
+//                        {
+//                            int v, start = Json_getInt(valueMap, "offset", 0);
+//                            Json* vertex;
+//                            deform2 = tempDeform;
+//                            memset(deform, 0, sizeof(float) * start);
+//                            if (self->scale == 1)
+//                            {
+//                                for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+//                                {
+//                                    deform2[v] = vertex->valueFloat;
+//                                }
+//                            }
+//                            else
+//                            {
+//                                for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v)
+//                                {
+//                                    deform2[v] = vertex->valueFloat * self->scale;
+//                                }
+//                            }
+//                            memset(deform + v, 0, sizeof(float) * (deformLength - v));
+//                            if (!weighted)
+//                            {
+//                                float* verticesAttachment = attachment->vertices;
+//                                for (v = 0; v < deformLength; ++v)
+//                                {
+//                                    deform2[v] += verticesAttachment[v];
+//                                }
+//                            }
+//                        }
+//                        spDeformTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), deform2);
+//                        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* drawOrder2 = 0;
+//                Json* offsets = Json_getItem(valueMap, "offsets");
+//                if (offsets)
+//                {
+//                    Json* offsetMap;
+//                    int* unchanged = MALLOC(int, skeletonData->slotsCount - offsets->size);
+//                    int originalIndex = 0, unchangedIndex = 0;
+//
+//                    drawOrder2 = MALLOC(int, skeletonData->slotsCount);
+//                    for (ii = skeletonData->slotsCount - 1; ii >= 0; --ii)
+//                    {
+//                        drawOrder2[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. */
+//                        drawOrder2[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 (drawOrder2[ii] == -1)
+//                        {
+//                            drawOrder2[ii] = unchanged[--unchangedIndex];
+//                        }
+//                    }
+//                    FREE(unchanged);
+//                }
+//                spDrawOrderTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), drawOrder2);
+//                FREE(drawOrder2);
+//            }
+//            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;
         return NULL;
     }
+    
+    void SkeletonJson::readVertices(Json* attachmentMap, VertexAttachment* 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);
+    }
+    
+    void SkeletonJson::setError(Json* root, const char* value1, const char* value2)
+    {
+//        char message[256];
+//        int length;
+//        strcpy(message, value1);
+//        length = (int)strlen(value1);
+//        if (value2)
+//        {
+//            strncat(message + length, value2, 255 - length);
+//        }
+//
+//        _error = std::string(message);
+//        if (root)
+//        {
+//            DESTROY(Json, root);
+//        }
+    }
 }