diff --git a/spine-c/src/spine/SkeletonData.c b/spine-c/src/spine/SkeletonData.c index d586415b7..eeea05c5c 100644 --- a/spine-c/src/spine/SkeletonData.c +++ b/spine-c/src/spine/SkeletonData.c @@ -1,122 +1,126 @@ -/****************************************************************************** - * Spine Runtimes Software License - * Version 2.1 - * - * Copyright (c) 2013, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable and - * non-transferable license to install, execute and perform the Spine Runtimes - * Software (the "Software") solely for internal use. Without the written - * permission of Esoteric Software (typically granted by licensing Spine), you - * may not (a) modify, translate, adapt or otherwise create derivative works, - * improvements of the Software or develop new applications using the Software - * or (b) remove, delete, alter or obscure any trademarks or any copyright, - * trademark, patent or other intellectual property or proprietary rights - * notices on or in the Software, including any copy thereof. Redistributions - * in binary or source form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include -#include -#include - -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); - - 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* ikConstraintName) { - int i; - for (i = 0; i < self->ikConstraintsCount; ++i) - if (strcmp(self->ikConstraints[i]->name, ikConstraintName) == 0) return self->ikConstraints[i]; - return 0; -} +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include +#include + +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); + + 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* ikConstraintName) { + int i; + for (i = 0; i < self->ikConstraintsCount; ++i) + if (strcmp(self->ikConstraints[i]->name, ikConstraintName) == 0) return self->ikConstraints[i]; + return 0; +} diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index 8db167d16..bc4418368 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/src/spine/SkeletonJson.c @@ -1,786 +1,786 @@ -/****************************************************************************** - * Spine Runtimes Software License - * Version 2.1 - * - * Copyright (c) 2013, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable and - * non-transferable license to install, execute and perform the Spine Runtimes - * Software (the "Software") solely for internal use. Without the written - * permission of Esoteric Software (typically granted by licensing Spine), you - * may not (a) modify, translate, adapt or otherwise create derivative works, - * improvements of the Software or develop new applications using the Software - * or (b) remove, delete, alter or obscure any trademarks or any copyright, - * trademark, patent or other intellectual property or proprietary rights - * notices on or in the Software, including any copy thereof. Redistributions - * in binary or source form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include -#include -#include "Json.h" -#include -#include - -typedef struct { - spSkeletonJson super; - int ownsLoader; -} _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) { - if (SUB_CAST(_spSkeletonJson, self)->ownsLoader) spAttachmentLoader_dispose(self->attachmentLoader); - 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, 256 - 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) != 8) 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 (spCurveTimeline* timeline, int frameIndex, Json* frame) { - 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 spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) { - int i; - spAnimation* animation; - Json* frame; - float duration; - int timelinesCount = 0; - - Json* bones = Json_getItem(root, "bones"); - Json* slots = Json_getItem(root, "slots"); - Json* ik = Json_getItem(root, "ik"); - Json* ffd = Json_getItem(root, "ffd"); - Json* drawOrder = Json_getItem(root, "drawOrder"); - Json* events = Json_getItem(root, "events"); - Json* flipX = Json_getItem(root, "flipx"); - Json* flipY = Json_getItem(root, "flipy"); - Json *boneMap, *slotMap, *ikMap, *ffdMap; - 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; - for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next) - for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next) - timelinesCount += slotMap->size; - if (drawOrder) ++timelinesCount; - if (events) ++timelinesCount; - if (flipX) ++timelinesCount; - if (flipY) ++timelinesCount; - - animation = spAnimation_create(root->name, timelinesCount); - animation->timelinesCount = 0; - skeletonData->animations[skeletonData->animationsCount++] = animation; - - /* Slot timelines. */ - for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) { - Json *timelineArray; - - 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 (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) { - if (strcmp(timelineArray->name, "color") == 0) { - spColorTimeline *timeline = spColorTimeline_create(timelineArray->size); - timeline->slotIndex = slotIndex; - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { - const char* s = Json_getString(frame, "color", 0); - spColorTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), - toColor(s, 3)); - readCurve(SUPER(timeline), i, frame); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size * 5 - 5]; - if (duration > animation->duration) animation->duration = duration; - - } else if (strcmp(timelineArray->name, "attachment") == 0) { - spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size); - timeline->slotIndex = slotIndex; - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { - Json* name = Json_getItem(frame, "name"); - spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), - name->type == Json_NULL ? 0 : name->valueString); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size - 1]; - if (duration > animation->duration) animation->duration = duration; - - } else { - spAnimation_dispose(animation); - _spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineArray->name); - return 0; - } - } - } - - /* Bone timelines. */ - for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) { - Json *timelineArray; - - 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 (timelineArray = boneMap->child; timelineArray; timelineArray = timelineArray->next) { - if (strcmp(timelineArray->name, "rotate") == 0) { - spRotateTimeline *timeline = spRotateTimeline_create(timelineArray->size); - timeline->boneIndex = boneIndex; - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { - spRotateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "angle", 0)); - readCurve(SUPER(timeline), i, frame); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size * 2 - 2]; - if (duration > animation->duration) animation->duration = duration; - - } else { - int isScale = strcmp(timelineArray->name, "scale") == 0; - if (isScale || strcmp(timelineArray->name, "translate") == 0) { - float scale = isScale ? 1 : self->scale; - spTranslateTimeline *timeline = - isScale ? spScaleTimeline_create(timelineArray->size) : spTranslateTimeline_create(timelineArray->size); - timeline->boneIndex = boneIndex; - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { - spTranslateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "x", 0) * scale, - Json_getFloat(frame, "y", 0) * scale); - readCurve(SUPER(timeline), i, frame); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size * 3 - 3]; - if (duration > animation->duration) animation->duration = duration; - } else if (strcmp(timelineArray->name, "flipX") == 0 || strcmp(timelineArray->name, "flipY") == 0) { - int x = strcmp(timelineArray->name, "flipX") == 0; - char* field = x ? "x" : "y"; - spFlipTimeline *timeline = spFlipTimeline_create(timelineArray->size, x); - timeline->boneIndex = boneIndex; - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) - spFlipTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getInt(frame, field, 0)); - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size * 2 - 2]; - if (duration > animation->duration) animation->duration = duration; - - } else { - spAnimation_dispose(animation); - _spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineArray->name); - return 0; - } - } - } - } - - /* IK timelines. */ - for (ikMap = ik ? ik->child : 0; ikMap; ikMap = ikMap->next) { - spIkConstraintData* ikConstraint = spSkeletonData_findIkConstraint(skeletonData, ikMap->name); - spIkConstraintTimeline* timeline = spIkConstraintTimeline_create(ikMap->size); - for (i = 0; i < skeletonData->ikConstraintsCount; ++i) { - if (ikConstraint == skeletonData->ikConstraints[i]) { - timeline->ikConstraintIndex = i; - break; - } - } - for (frame = ikMap->child, i = 0; frame; frame = frame->next, ++i) { - spIkConstraintTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "mix", 0), - Json_getInt(frame, "bendPositive", 1) ? 1 : -1); - readCurve(SUPER(timeline), i, frame); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[ikMap->size * 3 - 3]; - if (duration > animation->duration) animation->duration = duration; - } - - /* FFD timelines. */ - for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next) { - spSkin* skin = spSkeletonData_findSkin(skeletonData, ffdMap->name); - for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next) { - int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name); - Json* timelineArray; - for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) { - Json* frame; - int verticesCount = 0; - float* tempVertices; - spFFDTimeline *timeline; - - spAttachment* attachment = spSkin_getAttachment(skin, slotIndex, timelineArray->name); - if (!attachment) { - spAnimation_dispose(animation); - _spSkeletonJson_setError(self, 0, "Attachment not found: ", timelineArray->name); - return 0; - } - if (attachment->type == SP_ATTACHMENT_MESH) - verticesCount = SUB_CAST(spMeshAttachment, attachment)->verticesCount; - else if (attachment->type == SP_ATTACHMENT_SKINNED_MESH) - verticesCount = SUB_CAST(spSkinnedMeshAttachment, attachment)->weightsCount / 3 * 2; - - timeline = spFFDTimeline_create(timelineArray->size, verticesCount); - timeline->slotIndex = slotIndex; - timeline->attachment = attachment; - - tempVertices = MALLOC(float, verticesCount); - for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { - Json* vertices = Json_getItem(frame, "vertices"); - float* frameVertices; - if (!vertices) { - if (attachment->type == SP_ATTACHMENT_MESH) - frameVertices = SUB_CAST(spMeshAttachment, attachment)->vertices; - else { - frameVertices = tempVertices; - memset(frameVertices, 0, sizeof(float) * verticesCount); - } - } else { - int v, start = Json_getInt(frame, "offset", 0); - Json* vertex; - frameVertices = tempVertices; - memset(frameVertices, 0, sizeof(float) * start); - if (self->scale == 1) { - for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v) - frameVertices[v] = vertex->valueFloat; - } else { - for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v) - frameVertices[v] = vertex->valueFloat * self->scale; - } - memset(frameVertices + v, 0, sizeof(float) * (verticesCount - v)); - if (attachment->type == SP_ATTACHMENT_MESH) { - float* meshVertices = SUB_CAST(spMeshAttachment, attachment)->vertices; - for (v = 0; v < verticesCount; ++v) - frameVertices[v] += meshVertices[v]; - } - } - spFFDTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), frameVertices); - readCurve(SUPER(timeline), i, frame); - } - FREE(tempVertices); - - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[timelineArray->size - 1]; - if (duration > animation->duration) animation->duration = duration; - } - } - } - - /* Draw order timeline. */ - if (drawOrder) { - spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotsCount); - for (frame = drawOrder->child, i = 0; frame; frame = frame->next, ++i) { - int ii; - int* drawOrder = 0; - Json* offsets = Json_getItem(frame, "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, i, Json_getFloat(frame, "time", 0), drawOrder); - FREE(drawOrder); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[drawOrder->size - 1]; - if (duration > animation->duration) animation->duration = duration; - } - - /* Event timeline. */ - if (events) { - Json* frame; - - spEventTimeline* timeline = spEventTimeline_create(events->size); - for (frame = events->child, i = 0; frame; frame = frame->next, ++i) { - spEvent* event; - const char* stringValue; - spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0)); - if (!eventData) { - spAnimation_dispose(animation); - _spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0)); - return 0; - } - event = spEvent_create(eventData); - event->intValue = Json_getInt(frame, "int", eventData->intValue); - event->floatValue = Json_getFloat(frame, "float", eventData->floatValue); - stringValue = Json_getString(frame, "string", eventData->stringValue); - if (stringValue) MALLOC_STR(event->stringValue, stringValue); - spEventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event); - } - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[events->size - 1]; - if (duration > animation->duration) animation->duration = duration; - } - - return animation; -} - -spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path) { - int length; - spSkeletonData* skeletonData; - const char* json = _spUtil_readFile(path, &length); - if (!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, *slots, *skins, *animations, *events; - - FREE(self->error); - CONST_CAST(char*, self->error) = 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) { - skeletonData->hash = Json_getString(skeleton, "hash", 0); - 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* boneData; - - 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; - } - } - - boneData = spBoneData_create(Json_getString(boneMap, "name", 0), parent); - boneData->length = Json_getFloat(boneMap, "length", 0) * self->scale; - boneData->x = Json_getFloat(boneMap, "x", 0) * self->scale; - boneData->y = Json_getFloat(boneMap, "y", 0) * self->scale; - boneData->rotation = Json_getFloat(boneMap, "rotation", 0); - boneData->scaleX = Json_getFloat(boneMap, "scaleX", 1); - boneData->scaleY = Json_getFloat(boneMap, "scaleY", 1); - boneData->inheritScale = Json_getInt(boneMap, "inheritScale", 1); - boneData->inheritRotation = Json_getInt(boneMap, "inheritRotation", 1); - - skeletonData->bones[i] = boneData; - skeletonData->bonesCount++; - } - - /* IK constraints. */ - ik = Json_getItem(root, "ik"); - if (ik) { - Json *ikMap; - skeletonData->ikConstraintsCount = ik->size; - skeletonData->ikConstraints = MALLOC(spIkConstraintData*, ik->size); - for (ikMap = ik->child, i = 0; ikMap; ikMap = ikMap->next, ++i) { - const char* targetName; - - spIkConstraintData* ikConstraintData = spIkConstraintData_create(Json_getString(ikMap, "name", 0)); - boneMap = Json_getItem(ikMap, "bones"); - ikConstraintData->bonesCount = boneMap->size; - ikConstraintData->bones = MALLOC(spBoneData*, boneMap->size); - for (boneMap = boneMap->child, ii = 0; boneMap; boneMap = boneMap->next, ++ii) { - ikConstraintData->bones[ii] = spSkeletonData_findBone(skeletonData, boneMap->valueString); - if (!ikConstraintData->bones[ii]) { - spSkeletonData_dispose(skeletonData); - _spSkeletonJson_setError(self, root, "IK bone not found: ", boneMap->valueString); - return 0; - } - } - - targetName = Json_getString(ikMap, "target", 0); - ikConstraintData->target = spSkeletonData_findBone(skeletonData, targetName); - if (!ikConstraintData->target) { - spSkeletonData_dispose(skeletonData); - _spSkeletonJson_setError(self, root, "Target bone not found: ", boneMap->name); - return 0; - } - - ikConstraintData->bendDirection = Json_getInt(ikMap, "bendPositive", 1) ? 1 : -1; - ikConstraintData->mix = Json_getFloat(ikMap, "mix", 1); - - skeletonData->ikConstraints[i] = ikConstraintData; - } - } - - /* 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* slotData; - const char* color; - Json *attachmentItem; - - 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; - } - - slotData = spSlotData_create(Json_getString(slotMap, "name", 0), boneData); - - color = Json_getString(slotMap, "color", 0); - if (color) { - slotData->r = toColor(color, 0); - slotData->g = toColor(color, 1); - slotData->b = toColor(color, 2); - slotData->a = toColor(color, 3); - } - - attachmentItem = Json_getItem(slotMap, "attachment"); - if (attachmentItem) spSlotData_setAttachmentName(slotData, attachmentItem->valueString); - - slotData->additiveBlending = Json_getInt(slotMap, "additive", 0); - - skeletonData->slots[i] = slotData; - } - } - - /* Skins. */ - skins = Json_getItem(root, "skins"); - if (skins) { - Json *slotMap; - skeletonData->skinsCount = skins->size; - skeletonData->skins = MALLOC(spSkin*, skins->size); - for (slotMap = skins->child, i = 0; slotMap; slotMap = slotMap->next, ++i) { - Json *attachmentsMap; - spSkin *skin = spSkin_create(slotMap->name); - - skeletonData->skins[i] = skin; - if (strcmp(slotMap->name, "default") == 0) skeletonData->defaultSkin = skin; - - for (attachmentsMap = slotMap->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; - int i; - 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, "skinnedmesh") == 0) - type = SP_ATTACHMENT_SKINNED_MESH; - else if (strcmp(typeString, "boundingbox") == 0) - type = SP_ATTACHMENT_BOUNDING_BOX; - else { - spSkeletonData_dispose(skeletonData); - _spSkeletonJson_setError(self, root, "Unknown attachment type: ", typeString); - return 0; - } - - attachment = spAttachmentLoader_newAttachment(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) { - region->r = toColor(color, 0); - region->g = toColor(color, 1); - region->b = toColor(color, 2); - region->a = toColor(color, 3); - } - - spRegionAttachment_updateOffset(region); - break; - } - case SP_ATTACHMENT_MESH: { - spMeshAttachment* mesh = SUB_CAST(spMeshAttachment, attachment); - - MALLOC_STR(mesh->path, path); - - entry = Json_getItem(attachmentMap, "vertices"); - mesh->verticesCount = entry->size; - mesh->vertices = MALLOC(float, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - mesh->vertices[i] = entry->valueFloat * self->scale; - - entry = Json_getItem(attachmentMap, "triangles"); - mesh->trianglesCount = entry->size; - mesh->triangles = MALLOC(int, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - mesh->triangles[i] = entry->valueInt; - - entry = Json_getItem(attachmentMap, "uvs"); - mesh->regionUVs = MALLOC(float, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - mesh->regionUVs[i] = entry->valueFloat; - - spMeshAttachment_updateUVs(mesh); - - color = Json_getString(attachmentMap, "color", 0); - if (color) { - mesh->r = toColor(color, 0); - mesh->g = toColor(color, 1); - mesh->b = toColor(color, 2); - mesh->a = toColor(color, 3); - } - - 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, i = 0; entry; entry = entry->next, ++i) - mesh->edges[i] = entry->valueInt; - } - - mesh->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; - mesh->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; - break; - } - case SP_ATTACHMENT_SKINNED_MESH: { - spSkinnedMeshAttachment* mesh = SUB_CAST(spSkinnedMeshAttachment, attachment); - int verticesCount, b, w, nn; - float* vertices; - - MALLOC_STR(mesh->path, path); - - entry = Json_getItem(attachmentMap, "uvs"); - mesh->uvsCount = entry->size; - mesh->regionUVs = MALLOC(float, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - mesh->regionUVs[i] = entry->valueFloat; - - entry = Json_getItem(attachmentMap, "vertices"); - verticesCount = entry->size; - vertices = MALLOC(float, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - vertices[i] = entry->valueFloat; - - for (i = 0; i < verticesCount;) { - int bonesCount = (int)vertices[i]; - mesh->bonesCount += bonesCount + 1; - mesh->weightsCount += bonesCount * 3; - i += 1 + bonesCount * 4; - } - mesh->bones = MALLOC(int, mesh->bonesCount); - mesh->weights = MALLOC(float, mesh->weightsCount); - - for (i = 0, b = 0, w = 0; i < verticesCount;) { - int bonesCount = (int)vertices[i++]; - mesh->bones[b++] = bonesCount; - for (nn = i + bonesCount * 4; i < nn; i += 4, ++b, w += 3) { - mesh->bones[b] = (int)vertices[i]; - mesh->weights[w] = vertices[i + 1] * self->scale; - mesh->weights[w + 1] = vertices[i + 2] * self->scale; - mesh->weights[w + 2] = vertices[i + 3]; - } - } - - FREE(vertices); - - entry = Json_getItem(attachmentMap, "triangles"); - mesh->trianglesCount = entry->size; - mesh->triangles = MALLOC(int, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - mesh->triangles[i] = entry->valueInt; - - spSkinnedMeshAttachment_updateUVs(mesh); - - color = Json_getString(attachmentMap, "color", 0); - if (color) { - mesh->r = toColor(color, 0); - mesh->g = toColor(color, 1); - mesh->b = toColor(color, 2); - mesh->a = toColor(color, 3); - } - - 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, i = 0; entry; entry = entry->next, ++i) - mesh->edges[i] = entry->valueInt; - } - - mesh->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; - mesh->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; - break; - } - case SP_ATTACHMENT_BOUNDING_BOX: { - spBoundingBoxAttachment* box = SUB_CAST(spBoundingBoxAttachment, attachment); - entry = Json_getItem(attachmentMap, "vertices"); - box->verticesCount = entry->size; - box->vertices = MALLOC(float, entry->size); - for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) - box->vertices[i] = entry->valueFloat * self->scale; - break; - } - } - - spSkin_addAttachment(skin, slotIndex, skinAttachmentName, attachment); - } - } - } - } - - /* 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) - _spSkeletonJson_readAnimation(self, animationMap, skeletonData); - } - - Json_dispose(root); - return skeletonData; -} +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.1 + * + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to install, execute and perform the Spine Runtimes + * Software (the "Software") solely for internal use. Without the written + * permission of Esoteric Software (typically granted by licensing Spine), you + * may not (a) modify, translate, adapt or otherwise create derivative works, + * improvements of the Software or develop new applications using the Software + * or (b) remove, delete, alter or obscure any trademarks or any copyright, + * trademark, patent or other intellectual property or proprietary rights + * notices on or in the Software, including any copy thereof. Redistributions + * in binary or source form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include +#include "Json.h" +#include +#include + +typedef struct { + spSkeletonJson super; + int ownsLoader; +} _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) { + if (SUB_CAST(_spSkeletonJson, self)->ownsLoader) spAttachmentLoader_dispose(self->attachmentLoader); + 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, 256 - 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) != 8) 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 (spCurveTimeline* timeline, int frameIndex, Json* frame) { + 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 spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) { + int i; + spAnimation* animation; + Json* frame; + float duration; + int timelinesCount = 0; + + Json* bones = Json_getItem(root, "bones"); + Json* slots = Json_getItem(root, "slots"); + Json* ik = Json_getItem(root, "ik"); + Json* ffd = Json_getItem(root, "ffd"); + Json* drawOrder = Json_getItem(root, "drawOrder"); + Json* events = Json_getItem(root, "events"); + Json* flipX = Json_getItem(root, "flipx"); + Json* flipY = Json_getItem(root, "flipy"); + Json *boneMap, *slotMap, *ikMap, *ffdMap; + 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; + for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next) + for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next) + timelinesCount += slotMap->size; + if (drawOrder) ++timelinesCount; + if (events) ++timelinesCount; + if (flipX) ++timelinesCount; + if (flipY) ++timelinesCount; + + animation = spAnimation_create(root->name, timelinesCount); + animation->timelinesCount = 0; + skeletonData->animations[skeletonData->animationsCount++] = animation; + + /* Slot timelines. */ + for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) { + Json *timelineArray; + + 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 (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) { + if (strcmp(timelineArray->name, "color") == 0) { + spColorTimeline *timeline = spColorTimeline_create(timelineArray->size); + timeline->slotIndex = slotIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { + const char* s = Json_getString(frame, "color", 0); + spColorTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2), + toColor(s, 3)); + readCurve(SUPER(timeline), i, frame); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size * 5 - 5]; + if (duration > animation->duration) animation->duration = duration; + + } else if (strcmp(timelineArray->name, "attachment") == 0) { + spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size); + timeline->slotIndex = slotIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { + Json* name = Json_getItem(frame, "name"); + spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), + name->type == Json_NULL ? 0 : name->valueString); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size - 1]; + if (duration > animation->duration) animation->duration = duration; + + } else { + spAnimation_dispose(animation); + _spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineArray->name); + return 0; + } + } + } + + /* Bone timelines. */ + for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) { + Json *timelineArray; + + 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 (timelineArray = boneMap->child; timelineArray; timelineArray = timelineArray->next) { + if (strcmp(timelineArray->name, "rotate") == 0) { + spRotateTimeline *timeline = spRotateTimeline_create(timelineArray->size); + timeline->boneIndex = boneIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { + spRotateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "angle", 0)); + readCurve(SUPER(timeline), i, frame); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size * 2 - 2]; + if (duration > animation->duration) animation->duration = duration; + + } else { + int isScale = strcmp(timelineArray->name, "scale") == 0; + if (isScale || strcmp(timelineArray->name, "translate") == 0) { + float scale = isScale ? 1 : self->scale; + spTranslateTimeline *timeline = + isScale ? spScaleTimeline_create(timelineArray->size) : spTranslateTimeline_create(timelineArray->size); + timeline->boneIndex = boneIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { + spTranslateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "x", 0) * scale, + Json_getFloat(frame, "y", 0) * scale); + readCurve(SUPER(timeline), i, frame); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size * 3 - 3]; + if (duration > animation->duration) animation->duration = duration; + } else if (strcmp(timelineArray->name, "flipX") == 0 || strcmp(timelineArray->name, "flipY") == 0) { + int x = strcmp(timelineArray->name, "flipX") == 0; + char* field = x ? "x" : "y"; + spFlipTimeline *timeline = spFlipTimeline_create(timelineArray->size, x); + timeline->boneIndex = boneIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) + spFlipTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getInt(frame, field, 0)); + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size * 2 - 2]; + if (duration > animation->duration) animation->duration = duration; + + } else { + spAnimation_dispose(animation); + _spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineArray->name); + return 0; + } + } + } + } + + /* IK timelines. */ + for (ikMap = ik ? ik->child : 0; ikMap; ikMap = ikMap->next) { + spIkConstraintData* ikConstraint = spSkeletonData_findIkConstraint(skeletonData, ikMap->name); + spIkConstraintTimeline* timeline = spIkConstraintTimeline_create(ikMap->size); + for (i = 0; i < skeletonData->ikConstraintsCount; ++i) { + if (ikConstraint == skeletonData->ikConstraints[i]) { + timeline->ikConstraintIndex = i; + break; + } + } + for (frame = ikMap->child, i = 0; frame; frame = frame->next, ++i) { + spIkConstraintTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "mix", 0), + Json_getInt(frame, "bendPositive", 1) ? 1 : -1); + readCurve(SUPER(timeline), i, frame); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[ikMap->size * 3 - 3]; + if (duration > animation->duration) animation->duration = duration; + } + + /* FFD timelines. */ + for (ffdMap = ffd ? ffd->child : 0; ffdMap; ffdMap = ffdMap->next) { + spSkin* skin = spSkeletonData_findSkin(skeletonData, ffdMap->name); + for (slotMap = ffdMap->child; slotMap; slotMap = slotMap->next) { + int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name); + Json* timelineArray; + for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) { + Json* frame; + int verticesCount = 0; + float* tempVertices; + spFFDTimeline *timeline; + + spAttachment* attachment = spSkin_getAttachment(skin, slotIndex, timelineArray->name); + if (!attachment) { + spAnimation_dispose(animation); + _spSkeletonJson_setError(self, 0, "Attachment not found: ", timelineArray->name); + return 0; + } + if (attachment->type == SP_ATTACHMENT_MESH) + verticesCount = SUB_CAST(spMeshAttachment, attachment)->verticesCount; + else if (attachment->type == SP_ATTACHMENT_SKINNED_MESH) + verticesCount = SUB_CAST(spSkinnedMeshAttachment, attachment)->weightsCount / 3 * 2; + + timeline = spFFDTimeline_create(timelineArray->size, verticesCount); + timeline->slotIndex = slotIndex; + timeline->attachment = attachment; + + tempVertices = MALLOC(float, verticesCount); + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) { + Json* vertices = Json_getItem(frame, "vertices"); + float* frameVertices; + if (!vertices) { + if (attachment->type == SP_ATTACHMENT_MESH) + frameVertices = SUB_CAST(spMeshAttachment, attachment)->vertices; + else { + frameVertices = tempVertices; + memset(frameVertices, 0, sizeof(float) * verticesCount); + } + } else { + int v, start = Json_getInt(frame, "offset", 0); + Json* vertex; + frameVertices = tempVertices; + memset(frameVertices, 0, sizeof(float) * start); + if (self->scale == 1) { + for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v) + frameVertices[v] = vertex->valueFloat; + } else { + for (vertex = vertices->child, v = start; vertex; vertex = vertex->next, ++v) + frameVertices[v] = vertex->valueFloat * self->scale; + } + memset(frameVertices + v, 0, sizeof(float) * (verticesCount - v)); + if (attachment->type == SP_ATTACHMENT_MESH) { + float* meshVertices = SUB_CAST(spMeshAttachment, attachment)->vertices; + for (v = 0; v < verticesCount; ++v) + frameVertices[v] += meshVertices[v]; + } + } + spFFDTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), frameVertices); + readCurve(SUPER(timeline), i, frame); + } + FREE(tempVertices); + + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size - 1]; + if (duration > animation->duration) animation->duration = duration; + } + } + } + + /* Draw order timeline. */ + if (drawOrder) { + spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotsCount); + for (frame = drawOrder->child, i = 0; frame; frame = frame->next, ++i) { + int ii; + int* drawOrder = 0; + Json* offsets = Json_getItem(frame, "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, i, Json_getFloat(frame, "time", 0), drawOrder); + FREE(drawOrder); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[drawOrder->size - 1]; + if (duration > animation->duration) animation->duration = duration; + } + + /* Event timeline. */ + if (events) { + Json* frame; + + spEventTimeline* timeline = spEventTimeline_create(events->size); + for (frame = events->child, i = 0; frame; frame = frame->next, ++i) { + spEvent* event; + const char* stringValue; + spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0)); + if (!eventData) { + spAnimation_dispose(animation); + _spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0)); + return 0; + } + event = spEvent_create(eventData); + event->intValue = Json_getInt(frame, "int", eventData->intValue); + event->floatValue = Json_getFloat(frame, "float", eventData->floatValue); + stringValue = Json_getString(frame, "string", eventData->stringValue); + if (stringValue) MALLOC_STR(event->stringValue, stringValue); + spEventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event); + } + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[events->size - 1]; + if (duration > animation->duration) animation->duration = duration; + } + + return animation; +} + +spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path) { + int length; + spSkeletonData* skeletonData; + const char* json = _spUtil_readFile(path, &length); + if (!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, *slots, *skins, *animations, *events; + + FREE(self->error); + CONST_CAST(char*, self->error) = 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* boneData; + + 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; + } + } + + boneData = spBoneData_create(Json_getString(boneMap, "name", 0), parent); + boneData->length = Json_getFloat(boneMap, "length", 0) * self->scale; + boneData->x = Json_getFloat(boneMap, "x", 0) * self->scale; + boneData->y = Json_getFloat(boneMap, "y", 0) * self->scale; + boneData->rotation = Json_getFloat(boneMap, "rotation", 0); + boneData->scaleX = Json_getFloat(boneMap, "scaleX", 1); + boneData->scaleY = Json_getFloat(boneMap, "scaleY", 1); + boneData->inheritScale = Json_getInt(boneMap, "inheritScale", 1); + boneData->inheritRotation = Json_getInt(boneMap, "inheritRotation", 1); + + skeletonData->bones[i] = boneData; + skeletonData->bonesCount++; + } + + /* IK constraints. */ + ik = Json_getItem(root, "ik"); + if (ik) { + Json *ikMap; + skeletonData->ikConstraintsCount = ik->size; + skeletonData->ikConstraints = MALLOC(spIkConstraintData*, ik->size); + for (ikMap = ik->child, i = 0; ikMap; ikMap = ikMap->next, ++i) { + const char* targetName; + + spIkConstraintData* ikConstraintData = spIkConstraintData_create(Json_getString(ikMap, "name", 0)); + boneMap = Json_getItem(ikMap, "bones"); + ikConstraintData->bonesCount = boneMap->size; + ikConstraintData->bones = MALLOC(spBoneData*, boneMap->size); + for (boneMap = boneMap->child, ii = 0; boneMap; boneMap = boneMap->next, ++ii) { + ikConstraintData->bones[ii] = spSkeletonData_findBone(skeletonData, boneMap->valueString); + if (!ikConstraintData->bones[ii]) { + spSkeletonData_dispose(skeletonData); + _spSkeletonJson_setError(self, root, "IK bone not found: ", boneMap->valueString); + return 0; + } + } + + targetName = Json_getString(ikMap, "target", 0); + ikConstraintData->target = spSkeletonData_findBone(skeletonData, targetName); + if (!ikConstraintData->target) { + spSkeletonData_dispose(skeletonData); + _spSkeletonJson_setError(self, root, "Target bone not found: ", boneMap->name); + return 0; + } + + ikConstraintData->bendDirection = Json_getInt(ikMap, "bendPositive", 1) ? 1 : -1; + ikConstraintData->mix = Json_getFloat(ikMap, "mix", 1); + + skeletonData->ikConstraints[i] = ikConstraintData; + } + } + + /* 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* slotData; + const char* color; + Json *attachmentItem; + + 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; + } + + slotData = spSlotData_create(Json_getString(slotMap, "name", 0), boneData); + + color = Json_getString(slotMap, "color", 0); + if (color) { + slotData->r = toColor(color, 0); + slotData->g = toColor(color, 1); + slotData->b = toColor(color, 2); + slotData->a = toColor(color, 3); + } + + attachmentItem = Json_getItem(slotMap, "attachment"); + if (attachmentItem) spSlotData_setAttachmentName(slotData, attachmentItem->valueString); + + slotData->additiveBlending = Json_getInt(slotMap, "additive", 0); + + skeletonData->slots[i] = slotData; + } + } + + /* Skins. */ + skins = Json_getItem(root, "skins"); + if (skins) { + Json *slotMap; + skeletonData->skinsCount = skins->size; + skeletonData->skins = MALLOC(spSkin*, skins->size); + for (slotMap = skins->child, i = 0; slotMap; slotMap = slotMap->next, ++i) { + Json *attachmentsMap; + spSkin *skin = spSkin_create(slotMap->name); + + skeletonData->skins[i] = skin; + if (strcmp(slotMap->name, "default") == 0) skeletonData->defaultSkin = skin; + + for (attachmentsMap = slotMap->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; + int i; + 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, "skinnedmesh") == 0) + type = SP_ATTACHMENT_SKINNED_MESH; + else if (strcmp(typeString, "boundingbox") == 0) + type = SP_ATTACHMENT_BOUNDING_BOX; + else { + spSkeletonData_dispose(skeletonData); + _spSkeletonJson_setError(self, root, "Unknown attachment type: ", typeString); + return 0; + } + + attachment = spAttachmentLoader_newAttachment(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) { + region->r = toColor(color, 0); + region->g = toColor(color, 1); + region->b = toColor(color, 2); + region->a = toColor(color, 3); + } + + spRegionAttachment_updateOffset(region); + break; + } + case SP_ATTACHMENT_MESH: { + spMeshAttachment* mesh = SUB_CAST(spMeshAttachment, attachment); + + MALLOC_STR(mesh->path, path); + + entry = Json_getItem(attachmentMap, "vertices"); + mesh->verticesCount = entry->size; + mesh->vertices = MALLOC(float, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + mesh->vertices[i] = entry->valueFloat * self->scale; + + entry = Json_getItem(attachmentMap, "triangles"); + mesh->trianglesCount = entry->size; + mesh->triangles = MALLOC(int, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + mesh->triangles[i] = entry->valueInt; + + entry = Json_getItem(attachmentMap, "uvs"); + mesh->regionUVs = MALLOC(float, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + mesh->regionUVs[i] = entry->valueFloat; + + spMeshAttachment_updateUVs(mesh); + + color = Json_getString(attachmentMap, "color", 0); + if (color) { + mesh->r = toColor(color, 0); + mesh->g = toColor(color, 1); + mesh->b = toColor(color, 2); + mesh->a = toColor(color, 3); + } + + 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, i = 0; entry; entry = entry->next, ++i) + mesh->edges[i] = entry->valueInt; + } + + mesh->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; + mesh->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; + break; + } + case SP_ATTACHMENT_SKINNED_MESH: { + spSkinnedMeshAttachment* mesh = SUB_CAST(spSkinnedMeshAttachment, attachment); + int verticesCount, b, w, nn; + float* vertices; + + MALLOC_STR(mesh->path, path); + + entry = Json_getItem(attachmentMap, "uvs"); + mesh->uvsCount = entry->size; + mesh->regionUVs = MALLOC(float, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + mesh->regionUVs[i] = entry->valueFloat; + + entry = Json_getItem(attachmentMap, "vertices"); + verticesCount = entry->size; + vertices = MALLOC(float, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + vertices[i] = entry->valueFloat; + + for (i = 0; i < verticesCount;) { + int bonesCount = (int)vertices[i]; + mesh->bonesCount += bonesCount + 1; + mesh->weightsCount += bonesCount * 3; + i += 1 + bonesCount * 4; + } + mesh->bones = MALLOC(int, mesh->bonesCount); + mesh->weights = MALLOC(float, mesh->weightsCount); + + for (i = 0, b = 0, w = 0; i < verticesCount;) { + int bonesCount = (int)vertices[i++]; + mesh->bones[b++] = bonesCount; + for (nn = i + bonesCount * 4; i < nn; i += 4, ++b, w += 3) { + mesh->bones[b] = (int)vertices[i]; + mesh->weights[w] = vertices[i + 1] * self->scale; + mesh->weights[w + 1] = vertices[i + 2] * self->scale; + mesh->weights[w + 2] = vertices[i + 3]; + } + } + + FREE(vertices); + + entry = Json_getItem(attachmentMap, "triangles"); + mesh->trianglesCount = entry->size; + mesh->triangles = MALLOC(int, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + mesh->triangles[i] = entry->valueInt; + + spSkinnedMeshAttachment_updateUVs(mesh); + + color = Json_getString(attachmentMap, "color", 0); + if (color) { + mesh->r = toColor(color, 0); + mesh->g = toColor(color, 1); + mesh->b = toColor(color, 2); + mesh->a = toColor(color, 3); + } + + 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, i = 0; entry; entry = entry->next, ++i) + mesh->edges[i] = entry->valueInt; + } + + mesh->width = Json_getFloat(attachmentMap, "width", 32) * self->scale; + mesh->height = Json_getFloat(attachmentMap, "height", 32) * self->scale; + break; + } + case SP_ATTACHMENT_BOUNDING_BOX: { + spBoundingBoxAttachment* box = SUB_CAST(spBoundingBoxAttachment, attachment); + entry = Json_getItem(attachmentMap, "vertices"); + box->verticesCount = entry->size; + box->vertices = MALLOC(float, entry->size); + for (entry = entry->child, i = 0; entry; entry = entry->next, ++i) + box->vertices[i] = entry->valueFloat * self->scale; + break; + } + } + + spSkin_addAttachment(skin, slotIndex, skinAttachmentName, attachment); + } + } + } + } + + /* 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) + _spSkeletonJson_readAnimation(self, animationMap, skeletonData); + } + + Json_dispose(root); + return skeletonData; +}