private float[] readFloatArray(DataInput input, float scale) throws IOException {
   int n = input.readInt(true);
   float[] array = new float[n];
   if (scale == 1) {
     for (int i = 0; i < n; i++) array[i] = input.readFloat();
   } else {
     for (int i = 0; i < n; i++) array[i] = input.readFloat() * scale;
   }
   return array;
 }
 private Skin readSkin(DataInput input, String skinName, boolean nonessential) throws IOException {
   int slotCount = input.readInt(true);
   if (slotCount == 0) return null;
   Skin skin = new Skin(skinName);
   for (int i = 0; i < slotCount; i++) {
     int slotIndex = input.readInt(true);
     for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
       String name = input.readString();
       skin.addAttachment(slotIndex, name, readAttachment(input, skin, name, nonessential));
     }
   }
   return skin;
 }
 private void readCurve(DataInput input, int frameIndex, CurveTimeline timeline)
     throws IOException {
   switch (input.readByte()) {
     case CURVE_STEPPED:
       timeline.setStepped(frameIndex);
       break;
     case CURVE_BEZIER:
       setCurve(
           timeline,
           frameIndex,
           input.readFloat(),
           input.readFloat(),
           input.readFloat(),
           input.readFloat());
       break;
   }
 }
  public SkeletonData readSkeletonData(FileHandle file) {
    if (file == null) throw new IllegalArgumentException("file cannot be null.");

    float scale = this.scale;

    SkeletonData skeletonData = new SkeletonData();
    skeletonData.name = file.nameWithoutExtension();

    DataInput input = new DataInput(file.read(512));
    try {
      boolean nonessential = input.readBoolean();
      // Bones.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        String name = input.readString();
        BoneData parent = null;
        int parentIndex = input.readInt(true) - 1;
        if (parentIndex != -1) parent = skeletonData.bones.get(parentIndex);
        BoneData boneData = new BoneData(name, parent);
        boneData.x = input.readFloat() * scale;
        boneData.y = input.readFloat() * scale;
        boneData.scaleX = input.readFloat();
        boneData.scaleY = input.readFloat();
        boneData.rotation = input.readFloat();
        boneData.length = input.readFloat() * scale;
        boneData.inheritScale = input.readBoolean();
        boneData.inheritRotation = input.readBoolean();
        if (nonessential) Color.rgba8888ToColor(boneData.getColor(), input.readInt());
        skeletonData.addBone(boneData);
      }

      // Slots.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        String slotName = input.readString();
        BoneData boneData = skeletonData.bones.get(input.readInt(true));
        SlotData slotData = new SlotData(slotName, boneData);
        Color.rgba8888ToColor(slotData.getColor(), input.readInt());
        slotData.attachmentName = input.readString();
        slotData.additiveBlending = input.readBoolean();
        skeletonData.addSlot(slotData);
      }

      // Default skin.
      Skin defaultSkin = readSkin(input, "default", nonessential);
      if (defaultSkin != null) {
        skeletonData.defaultSkin = defaultSkin;
        skeletonData.addSkin(defaultSkin);
      }

      // Skins.
      for (int i = 0, n = input.readInt(true); i < n; i++)
        skeletonData.addSkin(readSkin(input, input.readString(), nonessential));

      // Events.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        EventData eventData = new EventData(input.readString());
        eventData.intValue = input.readInt(false);
        eventData.floatValue = input.readFloat();
        eventData.stringValue = input.readString();
        skeletonData.addEvent(eventData);
      }

      // Animations.
      for (int i = 0, n = input.readInt(true); i < n; i++)
        readAnimation(input.readString(), input, skeletonData);

    } catch (IOException ex) {
      throw new SerializationException("Error reading skeleton file.", ex);
    } finally {
      try {
        input.close();
      } catch (IOException ignored) {
      }
    }

    skeletonData.bones.shrink();
    skeletonData.slots.shrink();
    skeletonData.skins.shrink();
    return skeletonData;
  }
  private void readAnimation(String name, DataInput input, SkeletonData skeletonData) {
    Array<Timeline> timelines = new Array();
    float scale = this.scale;
    float duration = 0;

    try {
      // Slot timelines.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        int slotIndex = input.readInt(true);
        for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
          int timelineType = input.readByte();
          int frameCount = input.readInt(true);
          switch (timelineType) {
            case TIMELINE_COLOR:
              {
                ColorTimeline timeline = new ColorTimeline(frameCount);
                timeline.slotIndex = slotIndex;
                for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
                  float time = input.readFloat();
                  Color.rgba8888ToColor(tempColor, input.readInt());
                  timeline.setFrame(
                      frameIndex, time, tempColor.r, tempColor.g, tempColor.b, tempColor.a);
                  if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
                }
                timelines.add(timeline);
                duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]);
                break;
              }
            case TIMELINE_ATTACHMENT:
              AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
              timeline.slotIndex = slotIndex;
              for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
                timeline.setFrame(frameIndex, input.readFloat(), input.readString());
              timelines.add(timeline);
              duration = Math.max(duration, timeline.getFrames()[frameCount - 1]);
              break;
          }
        }
      }

      // Bone timelines.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        int boneIndex = input.readInt(true);
        for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
          int timelineType = input.readByte();
          int frameCount = input.readInt(true);
          switch (timelineType) {
            case TIMELINE_ROTATE:
              {
                RotateTimeline timeline = new RotateTimeline(frameCount);
                timeline.boneIndex = boneIndex;
                for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
                  timeline.setFrame(frameIndex, input.readFloat(), input.readFloat());
                  if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
                }
                timelines.add(timeline);
                duration = Math.max(duration, timeline.getFrames()[frameCount * 2 - 2]);
                break;
              }
            case TIMELINE_TRANSLATE:
            case TIMELINE_SCALE:
              TranslateTimeline timeline;
              float timelineScale = 1;
              if (timelineType == TIMELINE_SCALE) timeline = new ScaleTimeline(frameCount);
              else {
                timeline = new TranslateTimeline(frameCount);
                timelineScale = scale;
              }
              timeline.boneIndex = boneIndex;
              for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
                timeline.setFrame(
                    frameIndex,
                    input.readFloat(),
                    input.readFloat() * timelineScale,
                    input.readFloat() * timelineScale);
                if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
              }
              timelines.add(timeline);
              duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]);
              break;
          }
        }
      }

      // FFD timelines.
      for (int i = 0, n = input.readInt(true); i < n; i++) {
        Skin skin = skeletonData.getSkins().get(input.readInt(true) + 1);
        for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
          int slotIndex = input.readInt(true);
          for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
            Attachment attachment = skin.getAttachment(slotIndex, input.readString());
            int frameCount = input.readInt(true);
            FfdTimeline timeline = new FfdTimeline(frameCount);
            timeline.slotIndex = slotIndex;
            timeline.attachment = attachment;
            for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
              float time = input.readFloat();

              float[] vertices;
              int vertexCount;
              if (attachment instanceof MeshAttachment)
                vertexCount = ((MeshAttachment) attachment).getVertices().length;
              else vertexCount = ((SkinnedMeshAttachment) attachment).getWeights().length / 3 * 2;

              int end = input.readInt(true);
              if (end == 0) {
                if (attachment instanceof MeshAttachment)
                  vertices = ((MeshAttachment) attachment).getVertices();
                else vertices = new float[vertexCount];
              } else {
                vertices = new float[vertexCount];
                int start = input.readInt(true);
                end += start;
                if (scale == 1) {
                  for (int v = start; v < end; v++) vertices[v] = input.readFloat();
                } else {
                  for (int v = start; v < end; v++) vertices[v] = input.readFloat() * scale;
                }
                if (attachment instanceof MeshAttachment) {
                  float[] meshVertices = ((MeshAttachment) attachment).getVertices();
                  for (int v = 0, vn = vertices.length; v < vn; v++) vertices[v] += meshVertices[v];
                }
              }

              timeline.setFrame(frameIndex, time, vertices);
              if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
            }
            timelines.add(timeline);
            duration = Math.max(duration, timeline.getFrames()[frameCount - 1]);
          }
        }
      }

      // Draw order timeline.
      int drawOrderCount = input.readInt(true);
      if (drawOrderCount > 0) {
        DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount);
        int slotCount = skeletonData.slots.size;
        for (int i = 0; i < drawOrderCount; i++) {
          int offsetCount = input.readInt(true);
          int[] drawOrder = new int[slotCount];
          for (int ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1;
          int[] unchanged = new int[slotCount - offsetCount];
          int originalIndex = 0, unchangedIndex = 0;
          for (int ii = 0; ii < offsetCount; ii++) {
            int slotIndex = input.readInt(true);
            // Collect unchanged items.
            while (originalIndex != slotIndex) unchanged[unchangedIndex++] = originalIndex++;
            // Set changed items.
            drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
          }
          // Collect remaining unchanged items.
          while (originalIndex < slotCount) unchanged[unchangedIndex++] = originalIndex++;
          // Fill in unchanged items.
          for (int ii = slotCount - 1; ii >= 0; ii--)
            if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
          timeline.setFrame(i, input.readFloat(), drawOrder);
        }
        timelines.add(timeline);
        duration = Math.max(duration, timeline.getFrames()[drawOrderCount - 1]);
      }

      // Event timeline.
      int eventCount = input.readInt(true);
      if (eventCount > 0) {
        EventTimeline timeline = new EventTimeline(eventCount);
        for (int i = 0; i < eventCount; i++) {
          float time = input.readFloat();
          EventData eventData = skeletonData.events.get(input.readInt(true));
          Event event = new Event(eventData);
          event.intValue = input.readInt(false);
          event.floatValue = input.readFloat();
          event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
          timeline.setFrame(i, time, event);
        }
        timelines.add(timeline);
        duration = Math.max(duration, timeline.getFrames()[eventCount - 1]);
      }
    } catch (IOException ex) {
      throw new SerializationException("Error reading skeleton file.", ex);
    }

    timelines.shrink();
    skeletonData.addAnimation(new Animation(name, timelines, duration));
  }
 private int[] readIntArray(DataInput input) throws IOException {
   int n = input.readInt(true);
   int[] array = new int[n];
   for (int i = 0; i < n; i++) array[i] = input.readInt(true);
   return array;
 }
  private Attachment readAttachment(
      DataInput input, Skin skin, String attachmentName, boolean nonessential) throws IOException {
    float scale = this.scale;

    String name = input.readString();
    if (name == null) name = attachmentName;

    switch (AttachmentType.values()[input.readByte()]) {
      case region:
        {
          String path = input.readString();
          if (path == null) path = name;
          RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
          if (region == null) return null;
          region.setPath(path);
          region.setX(input.readFloat() * scale);
          region.setY(input.readFloat() * scale);
          region.setScaleX(input.readFloat());
          region.setScaleY(input.readFloat());
          region.setRotation(input.readFloat());
          region.setWidth(input.readFloat() * scale);
          region.setHeight(input.readFloat() * scale);
          Color.rgba8888ToColor(region.getColor(), input.readInt());
          region.updateOffset();
          return region;
        }
      case boundingbox:
        {
          BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
          if (box == null) return null;
          box.setVertices(readFloatArray(input, scale));
          return box;
        }
      case mesh:
        {
          String path = input.readString();
          if (path == null) path = name;
          MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
          if (mesh == null) return null;
          mesh.setPath(path);
          float[] uvs = readFloatArray(input, 1);
          short[] triangles = readShortArray(input);
          float[] vertices = readFloatArray(input, scale);
          mesh.setVertices(vertices);
          mesh.setTriangles(triangles);
          mesh.setRegionUVs(uvs);
          mesh.updateUVs();
          Color.rgba8888ToColor(mesh.getColor(), input.readInt());
          mesh.setHullLength(input.readInt(true) * 2);
          if (nonessential) {
            mesh.setEdges(readIntArray(input));
            mesh.setWidth(input.readFloat() * scale);
            mesh.setHeight(input.readFloat() * scale);
          }
          return mesh;
        }
      case skinnedmesh:
        {
          String path = input.readString();
          if (path == null) path = name;
          SkinnedMeshAttachment mesh = attachmentLoader.newSkinnedMeshAttachment(skin, name, path);
          if (mesh == null) return null;
          mesh.setPath(path);
          float[] uvs = readFloatArray(input, 1);
          short[] triangles = readShortArray(input);

          int vertexCount = input.readInt(true);
          FloatArray weights = new FloatArray(uvs.length * 3 * 3);
          IntArray bones = new IntArray(uvs.length * 3);
          for (int i = 0; i < vertexCount; i++) {
            int boneCount = (int) input.readFloat();
            bones.add(boneCount);
            for (int nn = i + boneCount * 4; i < nn; i += 4) {
              bones.add((int) input.readFloat());
              weights.add(input.readFloat() * scale);
              weights.add(input.readFloat() * scale);
              weights.add(input.readFloat());
            }
          }
          mesh.setBones(bones.toArray());
          mesh.setWeights(weights.toArray());
          mesh.setTriangles(triangles);
          mesh.setRegionUVs(uvs);
          mesh.updateUVs();
          Color.rgba8888ToColor(mesh.getColor(), input.readInt());
          mesh.setHullLength(input.readInt(true) * 2);
          if (nonessential) {
            mesh.setEdges(readIntArray(input));
            mesh.setWidth(input.readFloat() * scale);
            mesh.setHeight(input.readFloat() * scale);
          }
          return mesh;
        }
    }
    return null;
  }