[cpp] Port cf45806b, 409bf9d6, 0e16ef9c from libgdx

This commit is contained in:
Mario Zechner 2026-03-16 17:14:15 +01:00
parent 2ba91231f3
commit 275e702bc8
12 changed files with 348 additions and 50 deletions

View File

@ -0,0 +1,74 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_DrawOrderFolderTimeline_h
#define Spine_DrawOrderFolderTimeline_h
#include <spine/Timeline.h>
namespace spine {
/// Changes a subset of a skeleton's Skeleton::getDrawOrder().
class SP_API DrawOrderFolderTimeline : public Timeline {
friend class SkeletonBinary;
friend class SkeletonJson;
RTTI_DECL
public:
DrawOrderFolderTimeline(size_t frameCount, Array<int> &slots, size_t slotCount);
virtual void apply(Skeleton &skeleton, float lastTime, float time, Array<Event *> *events, float alpha, MixBlend blend,
MixDirection direction, bool appliedPose) override;
size_t getFrameCount();
/// The Skeleton::getSlots() indices that this timeline affects, in setup order.
Array<int> &getSlots();
/// The draw order for each frame. See setFrame().
Array<Array<int>> &getDrawOrders();
/// Sets the time and draw order for the specified frame.
/// @param frame Between 0 and frameCount, inclusive.
/// @param time The frame time in seconds.
/// @param drawOrder Ordered getSlots() indices, or null to use setup pose order.
void setFrame(size_t frame, float time, Array<int> *drawOrder);
private:
Array<int> _slots;
Array<bool> _inFolder;
Array<Array<int>> _drawOrders;
void setup(Skeleton &skeleton);
void apply(Skeleton &skeleton, Array<int> &drawOrder);
};
}
#endif /* Spine_DrawOrderFolderTimeline_h */

View File

@ -88,6 +88,8 @@ namespace spine {
friend class DeformTimeline;
friend class DrawOrderFolderTimeline;
friend class DrawOrderTimeline;
friend class EventTimeline;

View File

@ -257,6 +257,8 @@ namespace spine {
void readTimeline(DataInput &input, Array<Timeline *> &timelines, BoneTimeline2 &timeline, float scale);
void readDrawOrder(DataInput &input, size_t slotCount, Array<int> &drawOrder);
void setBezier(DataInput &input, CurveTimeline &timeline, int bezier, int frame, int value, float time1, float time2, float value1,
float value2, float scale);
};

View File

@ -88,6 +88,9 @@ namespace spine {
/// @return May be NULL.
Animation *findAnimation(const String &animationName);
/// Collects animations used by slider constraints.
Array<Animation *> &findSliderAnimations(Array<Animation *> &animations);
/// The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been
/// set.
const String &getName();

View File

@ -111,6 +111,8 @@ namespace spine {
void readVertices(Json *attachmentMap, VertexAttachment *attachment, size_t verticesLength);
bool readDrawOrder(SkeletonData *skeletonData, Json *keyMap, int slotCount, const Array<int> *folderSlots, Array<int> &drawOrder);
void setError(Json *root, const String &value1, const String &value2);
int findSlotIndex(SkeletonData *skeletonData, const String &slotName, Array<Timeline *> timelines);

View File

@ -45,6 +45,7 @@ namespace spine {
/// Stores the setup pose for a Slider
class SP_API SliderData : public ConstraintDataGeneric<Slider, SliderPose> {
friend class SkeletonBinary;
friend class SkeletonData;
friend class SkeletonJson;
friend class Slider;
friend class SliderMixTimeline;

View File

@ -55,6 +55,7 @@
#include <spine/ArrayUtils.h>
#include <spine/CurveTimeline.h>
#include <spine/DeformTimeline.h>
#include <spine/DrawOrderFolderTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/Event.h>
#include <spine/EventData.h>

View File

@ -33,6 +33,7 @@
#include <spine/AttachmentTimeline.h>
#include <spine/Bone.h>
#include <spine/BoneData.h>
#include <spine/DrawOrderFolderTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/Event.h>
#include <spine/EventTimeline.h>
@ -444,7 +445,8 @@ void EventQueue::drain() {
break;
case EventType_Event:
if (!trackEntry->_listenerObject)
SP_INVOKE_ANIMATION_LISTENER(trackEntry->_listener, state, queueEntry._type, trackEntry, queueEntry._event, trackEntry->_listenerUserData);
SP_INVOKE_ANIMATION_LISTENER(trackEntry->_listener, state, queueEntry._type, trackEntry, queueEntry._event,
trackEntry->_listenerUserData);
else
trackEntry->_listenerObject->callback(&state, queueEntry._type, trackEntry, queueEntry._event);
if (!state._listenerObject)
@ -1203,7 +1205,8 @@ continue_outer:
timelineMode[i] = Subsequent;
} else {
if (to == NULL || timeline->getRTTI().isExactly(AttachmentTimeline::rtti) || timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) ||
timeline->getRTTI().isExactly(EventTimeline::rtti) || !to->_animation->hasTimeline(ids)) {
timeline->getRTTI().isExactly(DrawOrderFolderTimeline::rtti) || timeline->getRTTI().isExactly(EventTimeline::rtti) ||
!to->_animation->hasTimeline(ids)) {
timelineMode[i] = First;
} else {
for (TrackEntry *next = to->_mixingTo; next != NULL; next = next->_mixingTo) {

View File

@ -0,0 +1,115 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/DrawOrderFolderTimeline.h>
#include <spine/Animation.h>
#include <spine/Event.h>
#include <spine/Property.h>
#include <spine/Skeleton.h>
#include <spine/Slot.h>
#include <spine/SlotData.h>
using namespace spine;
RTTI_IMPL(DrawOrderFolderTimeline, Timeline)
DrawOrderFolderTimeline::DrawOrderFolderTimeline(size_t frameCount, Array<int> &slots, size_t slotCount) : Timeline(frameCount, 1) {
PropertyId ids[] = {((PropertyId) Property_DrawOrder << 32)};
setPropertyIds(ids, 1);
_slots.addAll(slots);
_drawOrders.ensureCapacity(frameCount);
_inFolder.setSize(slotCount, false);
for (size_t i = 0; i < _slots.size(); ++i) _inFolder[_slots[i]] = true;
for (size_t i = 0; i < frameCount; ++i) {
Array<int> vec;
_drawOrders.add(vec);
}
}
void DrawOrderFolderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Array<Event *> *events, float alpha, MixBlend blend,
MixDirection direction, bool appliedPose) {
SP_UNUSED(lastTime);
SP_UNUSED(events);
SP_UNUSED(alpha);
SP_UNUSED(appliedPose);
if (direction == MixDirection_Out) {
if (blend == MixBlend_Setup) setup(skeleton);
} else if (time < _frames[0]) {
if (blend == MixBlend_Setup || blend == MixBlend_First) setup(skeleton);
} else {
Array<int> &drawOrder = _drawOrders[Animation::search(_frames, time)];
if (drawOrder.size() == 0)
setup(skeleton);
else
apply(skeleton, drawOrder);
}
}
size_t DrawOrderFolderTimeline::getFrameCount() {
return _frames.size();
}
Array<int> &DrawOrderFolderTimeline::getSlots() {
return _slots;
}
Array<Array<int>> &DrawOrderFolderTimeline::getDrawOrders() {
return _drawOrders;
}
void DrawOrderFolderTimeline::setFrame(size_t frame, float time, Array<int> *drawOrder) {
_frames[frame] = time;
_drawOrders[frame].clear();
if (drawOrder != NULL) _drawOrders[frame].addAll(*drawOrder);
}
void DrawOrderFolderTimeline::setup(Skeleton &skeleton) {
Array<Slot *> &drawOrder = skeleton._drawOrder;
Array<Slot *> &allSlots = skeleton._slots;
for (size_t i = 0, found = 0, done = _slots.size();; ++i) {
if (_inFolder[drawOrder[i]->getData().getIndex()]) {
drawOrder[i] = allSlots[_slots[found]];
if (++found == done) break;
}
}
}
void DrawOrderFolderTimeline::apply(Skeleton &skeleton, Array<int> &drawOrderIndices) {
Array<Slot *> &drawOrder = skeleton._drawOrder;
Array<Slot *> &allSlots = skeleton._slots;
for (size_t i = 0, found = 0, done = _slots.size();; ++i) {
if (_inFolder[drawOrder[i]->getData().getIndex()]) {
drawOrder[i] = allSlots[_slots[drawOrderIndices[found]]];
if (++found == done) break;
}
}
}

View File

@ -47,6 +47,7 @@
#include <spine/ColorTimeline.h>
#include <spine/ArrayUtils.h>
#include <spine/DeformTimeline.h>
#include <spine/DrawOrderFolderTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/Event.h>
#include <spine/EventData.h>
@ -1304,33 +1305,33 @@ Animation *SkeletonBinary::readAnimation(DataInput &input, const String &name, S
}
// Draw order timeline.
size_t slotCount = skeletonData._slots.size();
size_t drawOrderCount = (size_t) input.readInt(true);
if (drawOrderCount > 0) {
DrawOrderTimeline *timeline = new (__FILE__, __LINE__) DrawOrderTimeline(drawOrderCount);
size_t slotCount = skeletonData._slots.size();
for (size_t i = 0; i < drawOrderCount; ++i) {
float time = input.readFloat();
size_t offsetCount = (size_t) input.readInt(true);
Array<int> drawOrder;
drawOrder.setSize(slotCount, 0);
for (int ii = (int) slotCount - 1; ii >= 0; --ii) drawOrder[ii] = -1;
Array<int> unchanged;
unchanged.setSize(slotCount - offsetCount, 0);
size_t originalIndex = 0, unchangedIndex = 0;
for (size_t ii = 0; ii < offsetCount; ++ii) {
size_t slotIndex = (size_t) input.readInt(true);
// Collect unchanged items.
while (originalIndex != slotIndex) unchanged[unchangedIndex++] = (int) originalIndex++;
// Set changed items.
size_t index = originalIndex;
drawOrder[index + (size_t) input.readInt(true)] = (int) originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount) unchanged[unchangedIndex++] = (int) originalIndex++;
// Fill in unchanged items.
for (int ii = (int) slotCount - 1; ii >= 0; --ii)
if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
timeline->setFrame(i, time, &drawOrder);
readDrawOrder(input, slotCount, drawOrder);
timeline->setFrame(i, time, drawOrder.size() == 0 ? NULL : &drawOrder);
}
timelines.add(timeline);
}
// Draw order folder timelines.
size_t folderCount = (size_t) input.readInt(true);
for (size_t i = 0; i < folderCount; ++i) {
size_t folderSlotCount = (size_t) input.readInt(true);
Array<int> folderSlots;
folderSlots.setSize(folderSlotCount, 0);
for (size_t ii = 0; ii < folderSlotCount; ++ii) folderSlots[ii] = input.readInt(true);
size_t keyCount = (size_t) input.readInt(true);
DrawOrderFolderTimeline *timeline = new (__FILE__, __LINE__) DrawOrderFolderTimeline(keyCount, folderSlots, slotCount);
for (size_t ii = 0; ii < keyCount; ++ii) {
float time = input.readFloat();
Array<int> drawOrder;
readDrawOrder(input, folderSlotCount, drawOrder);
timeline->setFrame(ii, time, drawOrder.size() == 0 ? NULL : &drawOrder);
}
timelines.add(timeline);
}
@ -1409,6 +1410,27 @@ void SkeletonBinary::readTimeline(DataInput &input, Array<Timeline *> &timelines
timelines.add(&timeline);
}
void SkeletonBinary::readDrawOrder(DataInput &input, size_t slotCount, Array<int> &drawOrder) {
size_t changeCount = (size_t) input.readInt(true);
drawOrder.clear();
if (changeCount == 0) return;
drawOrder.setSize(slotCount, 0);
for (int i = (int) slotCount - 1; i >= 0; --i) drawOrder[i] = -1;
Array<int> unchanged;
unchanged.setSize(slotCount - changeCount, 0);
size_t originalIndex = 0, unchangedIndex = 0;
for (size_t i = 0; i < changeCount; ++i) {
size_t slotIndex = (size_t) input.readInt(true);
while (originalIndex != slotIndex) unchanged[unchangedIndex++] = (int) originalIndex++;
size_t index = originalIndex;
drawOrder[index + (size_t) input.readInt(true)] = (int) originalIndex++;
}
while (originalIndex < slotCount) unchanged[unchangedIndex++] = (int) originalIndex++;
for (int i = (int) slotCount - 1; i >= 0; --i)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
}
void SkeletonBinary::setBezier(DataInput &input, CurveTimeline &timeline, int bezier, int frame, int value, float time1, float time2, float value1,
float value2, float scale) {
float cx1 = input.readFloat();

View File

@ -39,6 +39,7 @@
#include <spine/Skin.h>
#include <spine/SlotData.h>
#include <spine/TransformConstraintData.h>
#include <spine/SliderData.h>
#include <spine/ArrayUtils.h>
@ -84,6 +85,17 @@ Animation *SkeletonData::findAnimation(const String &animationName) {
return ArrayUtils::findWithName(_animations, animationName);
}
Array<Animation *> &SkeletonData::findSliderAnimations(Array<Animation *> &animations) {
for (size_t i = 0, n = _constraints.size(); i < n; i++) {
ConstraintData *constraint = _constraints[i];
if (constraint->getRTTI().instanceOf(SliderData::rtti)) {
SliderData *data = static_cast<SliderData *>(constraint);
if (data->_animation != NULL) animations.add(data->_animation);
}
}
return animations;
}
const String &SkeletonData::getName() {
return _name;
}

View File

@ -47,6 +47,7 @@
#include <spine/ColorTimeline.h>
#include <spine/ArrayUtils.h>
#include <spine/DeformTimeline.h>
#include <spine/DrawOrderFolderTimeline.h>
#include <spine/DrawOrderTimeline.h>
#include <spine/Event.h>
#include <spine/EventData.h>
@ -1242,8 +1243,13 @@ Animation *SkeletonJson::readAnimation(Json *map, SkeletonData *skeletonData) {
return NULL;
}
for (Json *slotMap = attachmentsMap->_child; slotMap; slotMap = slotMap->_next) {
int slotIndex = findSlotIndex(skeletonData, slotMap->_name, timelines);
if (slotIndex == -1) return NULL;
SlotData *slot = skeletonData->findSlot(slotMap->_name);
if (!slot) {
ArrayUtils::deleteElements(timelines);
setError(NULL, "Attachment slot not found: ", slotMap->_name);
return NULL;
}
int slotIndex = slot->getIndex();
for (Json *attachmentMap = slotMap->_child; attachmentMap; attachmentMap = attachmentMap->_next) {
Attachment *attachment = skin->getAttachment(slotIndex, attachmentMap->_name);
if (!attachment) {
@ -1327,36 +1333,48 @@ Animation *SkeletonJson::readAnimation(Json *map, SkeletonData *skeletonData) {
int frame = 0;
for (Json *keyMap = drawOrder->_child; keyMap; keyMap = keyMap->_next, ++frame) {
Array<int> drawOrder2;
Json *offsets = Json::getItem(keyMap, "offsets");
if (offsets) {
drawOrder2.setSize(slotCount, 0);
for (int i = slotCount - 1; i >= 0; i--) drawOrder2[i] = -1;
Array<int> unchanged;
unchanged.setSize(slotCount - offsets->_size, 0);
int originalIndex = 0, unchangedIndex = 0;
for (Json *offsetMap = offsets->_child; offsetMap; offsetMap = offsetMap->_next) {
SlotData *slot = skeletonData->findSlot(Json::getString(offsetMap, "slot", 0));
if (!slot) {
ArrayUtils::deleteElements(timelines);
return NULL;
}
/* Collect unchanged items. */
while (originalIndex != slot->_index) unchanged[unchangedIndex++] = originalIndex++;
/* Set changed items. */
int index = originalIndex;
drawOrder2[index + Json::getInt(offsetMap, "offset", 0)] = originalIndex++;
}
/* Collect remaining unchanged items. */
while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++;
/* Fill in unchanged items. */
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder2[i] == -1) drawOrder2[i] = unchanged[--unchangedIndex];
if (!readDrawOrder(skeletonData, keyMap, slotCount, NULL, drawOrder2)) {
ArrayUtils::deleteElements(timelines);
return NULL;
}
timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), &drawOrder2);
timeline->setFrame(frame, Json::getFloat(keyMap, "time", 0), drawOrder2.size() == 0 ? NULL : &drawOrder2);
}
timelines.add(timeline);
}
// Draw order folder timelines.
Json *drawOrderFolder = Json::getItem(map, "drawOrderFolder");
if (drawOrderFolder) {
for (Json *timelineMap = drawOrderFolder->_child; timelineMap; timelineMap = timelineMap->_next) {
Json *slotEntry = Json::getItem(timelineMap, "slots");
Array<int> folderSlots;
folderSlots.setSize(slotEntry ? slotEntry->_size : 0, 0);
int ii = 0;
for (Json *entry = slotEntry ? slotEntry->_child : NULL; entry; entry = entry->_next, ++ii) {
SlotData *slot = skeletonData->findSlot(entry->_valueString);
if (!slot) {
ArrayUtils::deleteElements(timelines);
setError(NULL, "Draw order folder slot not found: ", entry->_valueString);
return NULL;
}
folderSlots[ii] = slot->getIndex();
}
Json *keyMap = Json::getItem(timelineMap, "keys");
DrawOrderFolderTimeline *timeline = new (__FILE__, __LINE__)
DrawOrderFolderTimeline(keyMap ? keyMap->_size : 0, folderSlots, skeletonData->_slots.size());
int frame = 0;
for (Json *entry = keyMap ? keyMap->_child : NULL; entry; entry = entry->_next, ++frame) {
Array<int> folderDrawOrder;
if (!readDrawOrder(skeletonData, entry, (int) folderSlots.size(), &folderSlots, folderDrawOrder)) {
ArrayUtils::deleteElements(timelines);
return NULL;
}
timeline->setFrame(frame, Json::getFloat(entry, "time", 0), folderDrawOrder.size() == 0 ? NULL : &folderDrawOrder);
}
timelines.add(timeline);
}
}
// Event timeline.
Json *events = Json::getItem(map, "events");
if (events) {
@ -1462,6 +1480,49 @@ int SkeletonJson::findSlotIndex(SkeletonData *skeletonData, const String &slotNa
return slotIndex;
}
bool SkeletonJson::readDrawOrder(SkeletonData *skeletonData, Json *keyMap, int slotCount, const Array<int> *folderSlots, Array<int> &drawOrder) {
Json *changes = Json::getItem(keyMap, "offsets");
drawOrder.clear();
if (changes == NULL) return true;
drawOrder.setSize(slotCount, 0);
for (int i = slotCount - 1; i >= 0; i--) drawOrder[i] = -1;
Array<int> unchanged;
unchanged.setSize(slotCount - changes->_size, 0);
int originalIndex = 0, unchangedIndex = 0;
for (Json *offsetMap = changes->_child; offsetMap; offsetMap = offsetMap->_next) {
const char *slotName = Json::getString(offsetMap, "slot", 0);
SlotData *slot = skeletonData->findSlot(slotName);
if (slot == NULL) {
setError(NULL, "Draw order slot not found: ", slotName);
return false;
}
int index;
if (folderSlots == NULL) {
index = slot->getIndex();
} else {
index = -1;
for (int i = 0; i < slotCount; i++) {
if ((*folderSlots)[i] == slot->getIndex()) {
index = i;
break;
}
}
if (index == -1) {
setError(NULL, "Slot not in folder: ", slotName);
return false;
}
}
while (originalIndex != index) unchanged[unchangedIndex++] = originalIndex++;
int drawOrderIndex = originalIndex;
drawOrder[drawOrderIndex + Json::getInt(offsetMap, "offset", 0)] = originalIndex++;
}
while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++;
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
return true;
}
void SkeletonJson::setError(Json *root, const String &value1, const String &value2) {
_error = String(value1).append(value2);
delete root;