using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;

namespace TMC_Tool
{
  public class HeaderData
  {
    public HeaderData(uint Start, byte[] bin)
    {
      this.Start = Start;
      this.Offsets = new List<uint>(0);
      this.Sizes = new List<uint>(0);

      char[] charsToTrim = { '\0' };
      this.Name = Encoding.ASCII.GetString(bin, (int)Start, 8).TrimEnd(charsToTrim);
      this.Flg1 = BitConverter.ToUInt32(bin, (int)Start + 0x08);
      this.HSize = BitConverter.ToUInt32(bin, (int)Start + 0x0C);
      this.Size = BitConverter.ToUInt32(bin, (int)Start + 0x10);
      this.Count1 = BitConverter.ToUInt32(bin, (int)Start + 0x14);
      this.Count2 = BitConverter.ToUInt32(bin, (int)Start + 0x18);
      this.Count3 = BitConverter.ToUInt32(bin, (int)Start + 0x1C);
      this.Offset1 = BitConverter.ToUInt32(bin, (int)Start + 0x20);
      this.Offset2 = BitConverter.ToUInt32(bin, (int)Start + 0x24);
      this.Offset3 = BitConverter.ToUInt32(bin, (int)Start + 0x28);
      this.Flg2 = BitConverter.ToUInt32(bin, (int)Start + 0x2C);

      for (int i = 0; i < this.Count1; i++)
      {
        this.Offsets.Add(BitConverter.ToUInt32(bin, (int)(Start + this.Offset1 + (i * 0x04))));
      }

      if (this.Offset2 > 0)
      {
        for (int i = 0; i < this.Count1; i++)
        {
          this.Sizes.Add(BitConverter.ToUInt32(bin, (int)(Start + this.Offset2 + (i * 0x04))));
        }
      }
    }

    public uint Start { get; set; }
    public string Name { get; set; }
    public uint Flg1 { get; set; }
    public uint HSize { get; set; }
    public uint Size { get; set; }
    public uint Count1 { get; set; }
    public uint Count2 { get; set; }
    public uint Count3 { get; set; }
    public uint Offset1 { get; set; }
    public uint Offset2 { get; set; }
    public uint Offset3 { get; set; }
    public uint Flg2 { get; set; }

    public List<uint> Offsets { get; private set; }
    public List<uint> Sizes { get; private set; }
  }

  public class TMCData
  {
    public TMCData(byte[] bin)
    {
      this.H = new HeaderData(0, bin);

      this.ParseGlobalMatrices(bin);
      this.ParseNodeLayer(bin);
    }

    public void ParseObjBaseData(byte[] bin)
    {
      if (this.H.Offsets[11] != 0) this.ParseCpf(bin);
      this.PreParseVertexData(bin);
      this.PreParseIndexData(bin);

      this.ObjGrp = new List<ObjectGroup>();
      this.ParseObjectData(bin);
    }

    public void ParseObjectData(byte[] bin)
    {
      uint start = this.H.Offsets[0];
      var mdlGeoH = new HeaderData(start, bin);
      byte[] tempBn;

      for (int i = 0; i < mdlGeoH.Count1; i++)
      {
        var h = new HeaderData(mdlGeoH.Start + mdlGeoH.Offsets[i], bin);
        var newGrp = new ObjectGroup(h, mdlGeoH.Offsets[i], bin);

        int idx = Array.FindIndex(this.Node.ToArray(), node => node.ObjIndex == i);
        if (idx == -1)
        {
          idx = Array.FindIndex(this.Node.ToArray(), node => node.Name == newGrp.Name);
          if (idx == -1)
          {
            string tempname = newGrp.Name.Replace("*", "");
            idx = Array.FindIndex(this.Node.ToArray(), node => node.Name.Length > tempname.Length && node.Name.Substring(node.Name.Length - tempname.Length) == tempname);
            newGrp.Name = this.Node[idx].Name;
          }
        }
        else
        {
          newGrp.Name = this.Node[idx].Name;
        }
        newGrp.Node = idx;

        if (this.NodeCp != null)
        {
          newGrp.NodeCp = Array.FindIndex(this.NodeCp.ToArray(), nodecp => nodecp.Index == newGrp.Node);
        }

        newGrp.Matrix = new Matrix3D();
        newGrp.Matrix.Append(this.MtxGrp[this.Node[idx].Index].Matrix);

        var declH = new HeaderData(h.Start + h.Offset3, bin);

        var Offsets = new uint[declH.Count1];
        for (int j = 0; j < declH.Count1; j++)
        {
          var newDecl = new DeclPart();

          newDecl.Offset = declH.Offsets[j];
          newDecl.Start = declH.Start + declH.Offsets[j];
          newDecl.IdxGrpIndex = BitConverter.ToInt32(bin, (int)(newDecl.Start + 0x0C));
          newDecl.IdxCount = BitConverter.ToUInt32(bin, (int)(newDecl.Start + 0x10));
          this.IdxGrp[newDecl.IdxGrpIndex].AddIdx(newDecl.IdxCount, bin);
          newDecl.VtxCount = BitConverter.ToUInt16(bin, (int)(newDecl.Start + 0x14));
          newDecl.VtxGrpIndex = BitConverter.ToInt32(bin, (int)(newDecl.Start + 0x30));
          newDecl.DataSize = BitConverter.ToUInt32(bin, (int)(newDecl.Start + 0x34));
          this.VtxGrp[newDecl.VtxGrpIndex].DataSize = newDecl.DataSize;
          int vtxDataLayersCount = BitConverter.ToInt32(bin, (int)(newDecl.Start + 0x38));
          ParseVertexDataLayers(bin, this.VtxGrp[newDecl.VtxGrpIndex], newDecl.Start + 0x40, vtxDataLayersCount);

          newGrp.Decl.Add(newDecl);
        }

        newGrp.MaxObjID = 0;
        tempBn = new byte[4];
        for (int j = 0; j < newGrp.ChildCount; j++)
        {
          var newObj = new ObjectPart();
          newObj.Offset = BitConverter.ToUInt32(bin, (int)(newGrp.Start + h.Offset1 + (j * 4)));
          newObj.Start = newGrp.Start + newObj.Offset;
          newObj.ID = BitConverter.ToInt32(bin, (int)newObj.Start);
          newObj.MtrColor = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x4));
          newObj.TexCount = BitConverter.ToUInt32(bin, (int)(newObj.Start + 0xC));
          newObj.DeclIndex = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x38));

          newObj.Transparent1 = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x40));
          newObj.Transparent2 = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x48));
          newObj.DoubleSided = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x6C));

          newObj.VtxGrpIndex = newGrp.Decl[newObj.DeclIndex].VtxGrpIndex;
          newObj.IdxGrpIndex = newGrp.Decl[newObj.DeclIndex].IdxGrpIndex;
          newObj.IdxStartIndex = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x70));
          newObj.IdxCount = BitConverter.ToUInt32(bin, (int)(newObj.Start + 0x74));
          newObj.VtxStartIndex = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x78));
          newObj.VtxCount = BitConverter.ToUInt16(bin, (int)(newObj.Start + 0x7C));

          newObj.TexParams = bin.Skip((int)(newObj.Start + 0xCC)).Take(4).ToArray();

          for (int k = 0; k < newObj.TexCount; k++)
          {
            var newTex = new ObjectTextureData();
            newTex.Offset = BitConverter.ToInt32(bin, (int)(newObj.Start + 0x10 + (k * 4)));
            newTex.ID = BitConverter.ToInt32(bin, (int)(newObj.Start + newTex.Offset));
            newTex.Type = BitConverter.ToInt32(bin, (int)(newObj.Start + newTex.Offset + 4));
            newTex.Num = BitConverter.ToInt32(bin, (int)(newObj.Start + newTex.Offset + 8));
            for (int l = 0; l < 25; l++)
            {
              if (l == 3 || l == 6 || l == 18 || l == 19)
                newTex.Data[l] = BitConverter.ToSingle(bin, (int)(newObj.Start + newTex.Offset + 0x0C + (4 * l)));
              else if (l == 7)
                newTex.Data[l] = BitConverter.ToInt32(bin, (int)(newObj.Start + newTex.Offset + 0x0C + (4 * l)));
              else
                newTex.Data[l] = BitConverter.ToUInt32(bin, (int)(newObj.Start + newTex.Offset + 0x0C + (4 * l)));
            }
            newObj.Tex.Add(newTex);
          }

          if (newObj.IdxStartIndex + newObj.IdxCount > newGrp.Decl[newObj.DeclIndex].IdxCount)
          {
            newGrp.Decl[newObj.DeclIndex].IdxCount = (uint)newObj.IdxStartIndex + newObj.IdxCount;
            this.IdxGrp[newObj.IdxGrpIndex].AddIdx(newGrp.Decl[newObj.DeclIndex].IdxCount, bin);
          }

          if (this.MateCp != null)
          {
            int index = this.Itable[i].Indices[j];
            if (index < this.MateCp.Count)
            {
              if (Array.FindIndex(this.MateCp[index].cp.ToArray(), cp => cp.Data1 == 0xEB53FEEF) != -1)
              {
                newObj.MaterialType = "Skin";
              }
              else if (Array.FindIndex(this.MateCp[index].cp.ToArray(), cp => cp.Data1 == 0x60121B1E) != -1)
              {
                newObj.MaterialType = "WetTex";
              }
            }
          }

          if (newObj.ID > newGrp.MaxObjID) newGrp.MaxObjID = newObj.ID;
          newGrp.Obj.Add(newObj);
        }
        this.ObjGrp.Add(newGrp);
      }
    }

    public static void ParseVertexDataLayers(byte[] bin, VertexGroup grp, uint offset, int count)
    {
      for (int i = 0; i < count; i++)
      {
        byte[] bs = new byte[8];
        Array.Copy(bin, offset + (i * 8), bs, 0, 8);

        if (bs[6] == 0)
        {
          grp.Position = true;
          grp.PositionOffset = bs[2];
          grp.PositionType = bs[4];
        }
        else if (bs[6] == 3)
        {
          grp.Normal = true;
          grp.NormalOffset = bs[2];
          grp.NormalType = bs[4];
        }
        else if (bs[6] == 1)
        {
          grp.BlendWeight = true;
          grp.BlendWeightOffset = bs[2];
          grp.BlendWeightType = bs[4];
        }
        else if (bs[6] == 2)
        {
          grp.BlendIndices = true;
          grp.BlendIndicesOffset = bs[2];
          grp.BlendIndicesType = bs[4];
        }
        else if (bs[6] == 6)
        {
          grp.Tangent = true;
          grp.TangentOffset = bs[2];
          grp.TangentType = bs[4];
        }
        else if (bs[6] == 5 && bs[4] == 0x0B)
        {
          grp.UVCount += 2;
          grp.TexCoord.Add(2);
          grp.TexCoordOffsets.Add(bs[2]);
        }
        else if (bs[6] == 5 && bs[4] == 0x0A)
        {
          grp.UVCount += 1;
          grp.TexCoord.Add(1);
          grp.TexCoordOffsets.Add(bs[2]);
        }
      }
    }

    public void ParseTextureData(byte[] bin)
    {
      uint start = this.H.Offsets[1];
      this.Tex = new List<Textures>();

      var ttdmH = new HeaderData(start, bin);
      var ttdhH = new HeaderData(start + ttdmH.HSize, bin);
      var ttdlH = new HeaderData(start + ttdmH.Offset3, bin);

      for (int i = 0; i < ttdhH.Count1; i++)
      {
        var newTex = new Textures();

        newTex.ID = i;
        newTex.InL = (BitConverter.ToInt32(bin, (int)(ttdhH.Start + ttdhH.Offsets[i])) == 1) ? true : false;
        newTex.RelativeID = BitConverter.ToInt32(bin, (int)(ttdhH.Start + ttdhH.Offsets[i] + 4));

        if (newTex.InL)
        {
          newTex.Offset = ttdlH.Offsets[newTex.RelativeID];
          newTex.Size = ttdlH.Sizes[newTex.RelativeID];
        }
        else
        {
          newTex.Offset = ttdmH.Offsets[newTex.RelativeID];
          newTex.Size = ttdmH.Sizes[newTex.RelativeID];
        }

        this.Tex.Add(newTex);
      }
    }

    public void PreParseVertexData(byte[] bin)
    {
      uint start = this.H.Offsets[2];
      this.VtxGrp = new List<VertexGroup>();

      var h = new HeaderData(start, bin);
      for (int i = 0; i < h.Count1; i++)
      {
        var newGrp = new VertexGroup(h.Start, h.Offsets[i], h.Sizes[i]);
        this.VtxGrp.Add(newGrp);
      }
    }

    public void ParseVertexData(byte[] bin)
    {

      for (int i = 0; i < this.VtxGrp.Count; i++)
      {
        // Declとリンクしていないものはスキップ
        if (this.VtxGrp[i].DataSize == 0) continue;

        if (i == 0)
        {
          this.VtxGrp[i].StartIndex = 0;
        }
        else
        {
          this.VtxGrp[i].StartIndex = this.VtxGrp[i - 1].Vtx.Count;
        }

        ParseVertexGrp(bin, this.VtxGrp[i], i);
      }
    }

    public static void ParseVertexGrp(byte[] bin, VertexGroup grp, int index)
    {
      byte[] tempBn = new byte[grp.DataSize];
      for (uint i = 0; i < (grp.Size) / grp.DataSize; i++)
      {
        var newVtx = new Vertex();
        newVtx.Group = index;
        newVtx.Address = grp.Start + (i * grp.DataSize);
        Array.Copy(bin, newVtx.Address, tempBn, 0, grp.DataSize);

        var newUV = new UV();

        if (grp.Position)
        {
          newVtx.X = BitConverter.ToSingle(tempBn, grp.PositionOffset);
          newVtx.Y = BitConverter.ToSingle(tempBn, grp.PositionOffset + 0x04);
          newVtx.Z = BitConverter.ToSingle(tempBn, grp.PositionOffset + 0x08);
          if (grp.PositionType == 3) newVtx.W = BitConverter.ToSingle(tempBn, grp.PositionOffset + 0x0C);
        }

        if (grp.Normal)
        {
          newVtx.N1 = BitConverter.ToSingle(tempBn, grp.NormalOffset);
          newVtx.N2 = BitConverter.ToSingle(tempBn, grp.NormalOffset + 0x04);
          newVtx.N3 = BitConverter.ToSingle(tempBn, grp.NormalOffset + 0x08);
          if (grp.NormalType == 3) newVtx.N4 = BitConverter.ToSingle(tempBn, grp.NormalOffset + 0x0C);
        }

        if (grp.BlendWeight)
        {
          if (grp.BlendWeightType == 3)
          {
            newVtx.WeightsF = new float[4];
            for (int j = 0; j < 4; j++)
            {
              newVtx.WeightsF[j] = BitConverter.ToSingle(tempBn, grp.BlendWeightOffset + (4 * j));
            }
          }
          else
          {
            newVtx.Weights = new byte[4];
            Array.Copy(bin, newVtx.Address + grp.BlendWeightOffset, newVtx.Weights, 0, 4);
          }
        }

        if (grp.BlendIndices)
        {
          newVtx.BlendIdx = new byte[4];
          Array.Copy(bin, newVtx.Address + grp.BlendIndicesOffset, newVtx.BlendIdx, 0, 4);
        }

        if (grp.Tangent)
        {
          newVtx.T1 = BitConverter.ToSingle(tempBn, grp.TangentOffset);
          newVtx.T2 = BitConverter.ToSingle(tempBn, grp.TangentOffset + 0x04);
          newVtx.T3 = BitConverter.ToSingle(tempBn, grp.TangentOffset + 0x08);
          newVtx.T4 = BitConverter.ToSingle(tempBn, grp.TangentOffset + 0x0C);
        }

        for (int j = 0; j < grp.TexCoord.Count; j++)
        {
          if (grp.TexCoord[j] == 2)
          {
            newUV = new UV();
            newUV.U = Half.ToHalf(tempBn, grp.TexCoordOffsets[j]);
            newUV.V = Half.ToHalf(tempBn, grp.TexCoordOffsets[j] + 0x02);
            newVtx.UV.Add(newUV);
            newUV = new UV();
            newUV.U = Half.ToHalf(tempBn, grp.TexCoordOffsets[j] + 0x04);
            newUV.V = Half.ToHalf(tempBn, grp.TexCoordOffsets[j] + 0x06);
            newVtx.UV.Add(newUV);
          }
          else if (grp.TexCoord[j] == 1)
          {
            newUV = new UV();
            newUV.U = Half.ToHalf(tempBn, grp.TexCoordOffsets[j]);
            newUV.V = Half.ToHalf(tempBn, grp.TexCoordOffsets[j] + 0x02);
            newVtx.UV.Add(newUV);
          }
        }

        grp.Vtx.Add(newVtx);
      }
    }

    public void PreParseIndexData(byte[] bin)
    {
      uint start = this.H.Offsets[3];
      this.IdxGrp = new List<IndexGroup>();

      var h = new HeaderData(start, bin);
      for (int i = 0; i < h.Count1; i++)
      {
        var newGrp = new IndexGroup(h.Start, h.Offsets[i], h.Sizes[i], bin);
        this.IdxGrp.Add(newGrp);
      }
    }

    public void ParseMtrColor(byte[] bin)
    {
      uint start = this.H.Offsets[4];
      HeaderData h = new HeaderData(start, bin);
      this.ColGrp = new List<MtrColorGroup>();
      foreach (uint offset in h.Offsets)
      {
        var newGrp = new MtrColorGroup(h.Start, offset, bin);
        this.ColGrp.Add(newGrp);
      }
    }

    public void ParseMdlInfo(byte[] bin)
    {
      uint start = this.H.Offsets[5];
      HeaderData h = new HeaderData(start, bin);
      this.ObjInfo = new List<ObjInfo>();
      foreach (uint offset in h.Offsets)
      {
        var newObjInfo = new ObjInfo(h.Start, offset, bin);
        this.ObjInfo.Add(newObjInfo);
      }
    }

    public void ParseHieLayer(byte[] bin)
    {
      uint start = this.H.Offsets[6];
      HeaderData h = new HeaderData(start, bin);
      this.maxBoneLevel = 0;

      this.Hie = new List<Hies>();
      for (int i = 0; i < h.Count1; i++)
      {
        var newHie = new Hies(h.Start, h.Offsets[i], bin);

        if (newHie.BoneLevel > this.maxBoneLevel) this.maxBoneLevel = newHie.BoneLevel;

        this.Hie.Add(newHie);
      }

      foreach (var hie in this.Hie)
      {
        if (hie.BoneLevel == 0) SetMatrixLayered(hie);
      }
    }

    public void SetMatrixLayered(Hies hie)
    {
      if (hie.Parent == -1)
        hie.MatrixLayered = hie.Matrix;
      else
        hie.MatrixLayered = hie.Matrix * this.Hie[hie.Parent].MatrixLayered;
      foreach (var child in hie.Children)
      {
        SetMatrixLayered(this.Hie[child]);
      }
    }

    public void ParseNodeLayer(byte[] bin)
    {
      uint start = this.H.Offsets[8];
      HeaderData h = new HeaderData(start, bin);

      this.Node = new List<Nodes>();
      this.BlendIdxGrp = new Dictionary<int, BlendIndexGroup>();

      char[] charsToTrim = { '\0' };
      for (int i = 0; i < h.Count1; i++)
      {
        HeaderData nodeH = new HeaderData(h.Start + h.Offsets[i], bin);
        var newNode = new Nodes(this, nodeH, charsToTrim, bin);
        this.Node.Add(newNode);
      }
    }

    public void ParseGlobalMatrices(byte[] bin)
    {
      uint start = this.H.Offsets[9];
      HeaderData h = new HeaderData(start, bin);

      this.MtxGrp = new List<MatrixGroup>();
      for (int i = 0; i < h.Count1; i++)
      {
        var newGrp = new MatrixGroup(h.Start, h.Offsets[i], bin);
        this.MtxGrp.Add(newGrp);
      }
    }

    public void ParseBoneOffsetMatrices(byte[] bin)
    {
      uint start = this.H.Offsets[10];
      HeaderData h = new HeaderData(start, bin);

      this.BnOfsMtxGrp = new List<MatrixGroup>();
      for (int i = 0; i < h.Count1; i++)
      {
        var newGrp = new MatrixGroup(h.Start, h.Offsets[i], bin);
        this.BnOfsMtxGrp.Add(newGrp);
      }
    }

    public void ParseCpf(byte[] bin)
    {
      uint start = this.H.Offsets[11];
      HeaderData h = new HeaderData(start, bin);
      this.cpfOffsets = h.Offsets.Select(elem => elem).ToArray();

      // nodecp
      HeaderData nodecpH = new HeaderData(start + h.Offsets[0], bin);
      this.NodeCp = new List<Customp>();
      for (int i = 0; i < nodecpH.Count1; i++)
      {
        if (nodecpH.Offsets[i] != 0)
        {
          var newNodeCp = new Customp(nodecpH.Start, nodecpH.Offsets[i], bin);
          newNodeCp.Index = i;
          this.NodeCp.Add(newNodeCp);
        }
      }

      if (h.Count2 == 1) return;

      // matecp
      HeaderData matecpH = new HeaderData(start + h.Offsets[1], bin);
      this.MateCp = new List<Customp>();
      for (int i = 0; i < matecpH.Count1; i++)
      {
        if (matecpH.Offsets[i] != 0)
        {
          var newMateCp = new Customp(matecpH.Start, matecpH.Offsets[i], bin);
          newMateCp.Index = i;
          this.MateCp.Add(newMateCp);
        }
      }

      //itable
      this.Itable = new List<ItableData>();
      this.itableOffset = matecpH.Offset3;
      HeaderData itableH = new HeaderData(matecpH.Start + matecpH.Offset3, bin);
      for (int i = 0; i < itableH.Count1; i++)
      {
        var newItable = new ItableData(itableH.Start, itableH.Offsets[i], bin);
        this.Itable.Add(newItable);
      }

    }

    public void ParseMCAPack(byte[] bin)
    {
      uint start = this.H.Offsets[12];
      if (start == 0) return;

      var mcaPackH = new HeaderData(start, bin);
      var mcaIndexH = new HeaderData(mcaPackH.Start + mcaPackH.Offset3, bin);
      this.McaindexOffset = mcaPackH.Offset3;

      this.McaIdx = new List<MCAIndex>();
      for (int i = 0; i < mcaIndexH.Count1; i++)
      {
        var newIdx = new MCAIndex(mcaIndexH.Start, mcaIndexH.Offsets[i], bin, this.ObjGrp[i]);
        this.McaIdx.Add(newIdx);
      }

      this.Mat = new List<Material>();
      for (int i = 0; i < mcaPackH.Count1; i++)
      {
        var newMat = new Material(mcaPackH.Start, mcaPackH.Offsets[i], bin);
        newMat.ID = i;
        this.Mat.Add(newMat);
      }
    }

    public void ParseACSCLS(byte[] bin)
    {
      uint start = this.H.Offsets[16];
      if (start == 0) return;

      var h = new HeaderData(start, bin);

      this.Physics = new List<PhysicsData>();
      if (h.Offsets[0] != 0)
      {
        var physicsH = new HeaderData(h.Start + h.Offsets[0], bin);
        var physicsSubH = new HeaderData(physicsH.Start + physicsH.HSize, bin);
        for (int i = 0; i < physicsH.Count1; i++)
        {
          var newPhysics = new PhysicsData((uint)(physicsSubH.Start + physicsSubH.Offset1 + (0x0C * i)), physicsH.Start, physicsH.Offsets[i], bin);
          this.Physics.Add(newPhysics);
        }
      }

      this.Collisions = new List<Collision>();
      if (h.Offsets[1] != 0)
      {
        var collisionsH = new HeaderData(h.Start + h.Offsets[1], bin);
        var collisionsSubH = new HeaderData(collisionsH.Start + collisionsH.HSize, bin);
        for (int i = 0; i < collisionsH.Count1; i++)
        {
          var newCollision = new Collision((uint)(collisionsSubH.Start + collisionsSubH.Offset1 + (0x0C * i)), collisionsH.Start + collisionsH.Offsets[i], bin);
          newCollision.Index = i;
          this.Collisions.Add(newCollision);
        }
      }

      this.AcsclsIndices = new List<int[]>();
      for (int i = 2; i < h.Count1; i++)
      {
        int count = 0;
        HeaderData childH = null;
        if (h.Offsets[i] != 0)
        {
          childH = new HeaderData(h.Start + h.Offsets[i], bin);
          count = (int)childH.Count1;
        }
        var newIndices = new int[count];

        for (int j = 0; j < count; j++)
        {
          newIndices[j] = BitConverter.ToInt32(bin, (int)(childH.Start + childH.Offset1 + (4 * j)));
        }

        this.AcsclsIndices.Add(newIndices);
      }
    }


    public string Path { get; set; }
    public DateTime WriteTime { get; set; }

    public int maxBoneLevel { get; set; }
    public uint[] cpfOffsets { get; set; }
    public uint itableOffset { get; set; }
    public uint McaindexOffset { get; set; }

    public HeaderData H { get; private set; }

    public List<Hies> Hie { get; private set; }
    public List<MatrixGroup> MtxGrp { get; private set; }
    public List<MatrixGroup> BnOfsMtxGrp { get; private set; }
    public List<Nodes> Node { get; private set; }
    public Dictionary<int, BlendIndexGroup> BlendIdxGrp { get; private set; }

    public List<ObjectGroup> ObjGrp { get; private set; }
    public List<Textures> Tex { get; private set; }
    public List<VertexGroup> VtxGrp { get; private set; }
    public List<IndexGroup> IdxGrp { get; private set; }
    public List<MtrColorGroup> ColGrp { get; private set; }
    public List<ObjInfo> ObjInfo { get; private set; }
    public List<Customp> NodeCp { get; private set; }
    public List<Customp> MateCp { get; private set; }
    public List<ItableData> Itable { get; private set; }
    public List<MCAIndex> McaIdx { get; private set; }
    public List<Material> Mat { get; private set; }
    public List<PhysicsData> Physics { get; private set; }
    public List<Collision> Collisions { get; private set; }
    public List<int[]> AcsclsIndices { get; private set; }
  }




  // MdlGeo
  public class ObjectGroup
  {
    public ObjectGroup(HeaderData hData, uint offset, byte[] bin)
    {
      this.Decl = new List<DeclPart>();
      this.Obj = new List<ObjectPart>();
      this.Matrix = new Matrix3D();

      this.Offset = offset;
      this.Start = hData.Start;
      this.Name = hData.Name;
      this.ChildCount = hData.Count1;
      this.DeclOffset = hData.Offset3;

      this.unknown0x30 = BitConverter.ToInt16(bin, (int)(this.Start + 0x30));
      this.unknown0x32 = BitConverter.ToInt16(bin, (int)(this.Start + 0x32));
      this.ID = BitConverter.ToInt32(bin, (int)(this.Start + 0x34));
      this.unknown0x38 = BitConverter.ToInt32(bin, (int)(this.Start + 0x38));

      char[] charsToTrim = { '\0' };
      this.Name = Encoding.ASCII.GetString(bin, (int)Start + 0x50, 16).TrimEnd(charsToTrim);

      this.ChildCount = hData.Count1;
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public uint ChildCount { get; set; }
    public uint DeclOffset { get; set; }

    public int ID { get; set; }
    public string Name { get; set; }
    public int Node { get; set; }
    public int NodeCp { get; set; }
    public int MaxObjID { get; set; }

    public short unknown0x30 { get; set; }
    public short unknown0x32 { get; set; }
    public int unknown0x38 { get; set; }

    public List<DeclPart> Decl { get; private set; }
    public List<ObjectPart> Obj { get; private set; }
    public Matrix3D Matrix { get; set; }
  }

  public class DeclPart
  {
    public DeclPart()
    {
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public uint DataSize { get; set; }
    public int IdxGrpIndex { get; set; }
    public uint IdxCount { get; set; }
    public int VtxGrpIndex { get; set; }
    public ushort VtxCount { get; set; }
  }

  public class ObjectPart
  {
    public ObjectPart()
    {
      this.Tex = new List<ObjectTextureData>();
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public int ID { get; set; }
    public int MtrColor { get; set; }

    public int Transparent1 { get; set; }
    public int Transparent2 { get; set; }
    public int DoubleSided { get; set; }

    public int VtxGrpIndex { get; set; }
    public int VtxStartIndex { get; set; }
    public ushort VtxCount { get; set; }
    public int IdxGrpIndex { get; set; }
    public int IdxStartIndex { get; set; }
    public uint IdxCount { get; set; }
    public uint TexCount { get; set; }
    public int DeclIndex { get; set; }

    public string MaterialType { get; set; }

    public int VtxFree { get; set; }
    public int IdxFree { get; set; }

    public byte[] TexParams { get; set; }

    public List<ObjectTextureData> Tex { get; private set; }
  }

  public class ObjectTextureData
  {
    public ObjectTextureData()
    {
      this.Data = new dynamic[25];
    }

    public int Offset { get; set; }
    public int ID { get; set; }
    public int Type { get; set; }
    public int Num { get; set; }

    public dynamic[] Data { get; set; }
  }

  // TTDM/TTDH/TTDL
  public class Textures
  {
    public Textures()
    {

    }

    public uint Offset { get; set; }
    public uint Size { get; set; }
    public int ID { get; set; }
    public bool InL { get; set; }
    public int RelativeID { get; set; }

    public byte[] Data { get; set; }
  }

  // VtxLay
  public class VertexGroup
  {
    public VertexGroup(uint start, uint offset, uint size)
    {
      this.Vtx = new List<Vertex>();
      this.TexCoord = new List<int>();
      this.TexCoordOffsets = new List<int>();
      //this.DataLayers = new List<byte[]>();

      this.Offset = offset;
      this.Start = start + offset;
      this.Size = size;

      PositionType = -1;
      NormalType = -1;
      BlendWeightType = -1;
      BlendIndicesType = -1;
      TangentType = -1;
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public uint Size { get; set; }
    public int StartIndex { get; set; }

    public uint DataSize { get; set; }
    public uint UVCount { get; set; }

    public bool Position { get; set; }
    public bool Normal { get; set; }
    public bool BlendWeight { get; set; }
    public bool BlendIndices { get; set; }
    public bool Tangent { get; set; }
    public List<int> TexCoord { get; set; }

    public int PositionType { get; set; }
    public int NormalType { get; set; }
    public int BlendWeightType { get; set; }
    public int BlendIndicesType { get; set; }
    public int TangentType { get; set; }

    public int PositionOffset { get; set; }
    public int NormalOffset { get; set; }
    public int BlendWeightOffset { get; set; }
    public int BlendIndicesOffset { get; set; }
    public int TangentOffset { get; set; }
    public List<int> TexCoordOffsets { get; set; }

    //public List<byte[]> DataLayers { get; set; }

    public List<Vertex> Vtx { get; private set; }
  }

  public class Vertex
  {
    public Vertex()
    {
      this.UV = new List<UV>();
      this.BlendIdx = new byte[0];
      this.W = null;
      this.N4 = null;
    }

    public uint Address { get; set; }
    public int Group { get; set; }

    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }
    public float? W { get; set; }

    public float N1 { get; set; }
    public float N2 { get; set; }
    public float N3 { get; set; }
    public float? N4 { get; set; }
    public byte[] Weights { get; set; }
    public float[] WeightsF { get; set; }
    public byte[] BlendIdx { get; set; }
    public float T1 { get; set; }
    public float T2 { get; set; }
    public float T3 { get; set; }
    public float T4 { get; set; }

    public List<UV> UV { get; private set; }
  }

  public class UV
  {
    public UV()
    {
    }

    public Half U { get; set; }
    public Half V { get; set; }
  }

  // IdxLay
  public class IndexGroup
  {
    public IndexGroup(uint start, uint offset, uint size, byte[] bin)
    {
      this.Offset = offset;
      this.Start = start + offset;
      this.Size = size;
    }

    public void AddIdx(uint count, byte[] bin)
    {
      this.Idx = new ushort[count];
      for (int i = 0; i < count; i++)
      {
        this.Idx[i] = BitConverter.ToUInt16(bin, (int)(this.Start + (i * 2)));
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public uint Size { get; set; }
    public ushort[] Idx { get; set; }
  }

  // MtrCol
  public class MtrColorGroup
  {
    public MtrColorGroup(uint start, uint offset, byte[] bin)
    {
      this.Color = new List<float[]>();
      this.Link = new Dictionary<int, int>();

      if (start == 0 && offset == 0) return;

      this.Offset = offset;
      this.Start = start + offset;
      for (int i = 0; i < 13; i++)
      {
        float[] color = {
          BitConverter.ToSingle(bin, (int)(this.Start + (0x10 * i))),
          BitConverter.ToSingle(bin, (int)(this.Start + (0x10 * i) + 0x04)),
          BitConverter.ToSingle(bin, (int)(this.Start + (0x10 * i) + 0x08)),
          BitConverter.ToSingle(bin, (int)(this.Start + (0x10 * i) + 0x0C))
        };
        this.Color.Add(color);
      }
      this.ID = bin[this.Start + 0xD0];
      for (int i = 0; i < bin[this.Start + 0xD4]; i++)
      {
        this.Link[bin[this.Start + 0xD8 + (8 * i)]] = bin[this.Start + 0xDC + (8 * i)];
      }
    }

    public int ID { get; set; }
    public uint Offset { get; set; }
    public uint Start { get; set; }
    public List<float[]> Color { get; set; }
    public Dictionary<int, int> Link { get; set; } // objindex, subobjnum_seq
  }

  // MdlInfo
  public class ObjInfo
  {
    public ObjInfo(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      this.Start = start + offset;
      var h = new HeaderData(start + offset, bin);

      this.Data1 = BitConverter.ToUInt32(bin, (int)(h.Start + h.HSize));
      this.ID = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 4));
      this.Data2 = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 8));
      this.Data3 = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x0C));
      this.Data4 = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x10));
      this.Z1 = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x14));
      this.Data5 = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + 0x18));
      this.Data6 = new int[3];
      for (int i = 0; i < 3; i++)
        this.Data6[i] = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x1C + (4 * i)));
      this.Z2 = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x28));
      this.Data7 = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + 0x2C));

      this.Params = new dynamic[32];
      for (int i = 0; i < 32; i++)
      {
        if (i == 15 || i == 27)
          this.Params[i] = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + 0x30 + (4 * i)));
        else
          this.Params[i] = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x30 + (4 * i)));
      }
      this.FParams = new float[28];
      for (int i = 0; i < 28; i++)
        this.FParams[i] = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + 0xB0 + (4 * i)));

      this.Info = new List<Info>();
      for (int i = 0; i < h.Count1; i++)
      {
        var newInfo = new Info(h.Start, h.Offsets[i], bin);
        this.Info.Add(newInfo);
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }

    public uint Data1 { get; set; }
    public int ID { get; private set; }
    public int Data2 { get; private set; }
    public int Data3 { get; private set; }
    public int Data4 { get; private set; }
    public int Z1 { get; private set; }
    public float Data5 { get; private set; }
    public int[] Data6 { get; private set; }
    public int Z2 { get; private set; }
    public float Data7 { get; private set; }

    public dynamic[] Params { get; set; }
    public float[] FParams { get; set; }

    public List<Info> Info { get; set; }
  }

  public class Info
  {
    public Info(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      this.Start = start + offset;

      this.ID = BitConverter.ToInt32(bin, (int)this.Start);
      this.Z1 = BitConverter.ToInt32(bin, (int)this.Start + 4);
      this.Data1 = BitConverter.ToInt32(bin, (int)this.Start + 8);
      this.Data2 = BitConverter.ToSingle(bin, (int)this.Start + 0x0C);
      this.Data3 = BitConverter.ToInt32(bin, (int)this.Start + 0x10);
      this.Data4 = BitConverter.ToInt32(bin, (int)this.Start + 0x14);
      this.Z2 = BitConverter.ToInt32(bin, (int)this.Start + 0x18);
      this.Data5 = BitConverter.ToInt32(bin, (int)this.Start + 0x1C);

      this.Params = new dynamic[8];
      for (int i = 0; i < 8; i++)
      {
        if (i == 1 || i == 5)
          this.Params[i] = BitConverter.ToSingle(bin, (int)(this.Start + 0x20 + (4 * i)));
        else
          this.Params[i] = BitConverter.ToInt32(bin, (int)(this.Start + 0x20 + (4 * i)));
      }
      this.FParams = new float[28];
      for (int i = 0; i < 28; i++)
        this.FParams[i] = BitConverter.ToSingle(bin, (int)(this.Start + 0x40 + (4 * i)));
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }

    public int ID { get; private set; }
    public int Z1 { get; private set; }
    public int Data1 { get; set; }
    public float Data2 { get; private set; }
    public int Data3 { get; set; }
    public int Data4 { get; set; }
    public int Z2 { get; private set; }
    public int Data5 { get; set; }

    public dynamic[] Params { get; set; }
    public float[] FParams { get; set; }
  }

  // HieLay
  public class Hies
  {
    public Hies(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      this.Start = start + offset;

      double[] m = new double[16];
      for (int i = 0; i < 16; i++)
      {
        m[i] = BitConverter.ToSingle(bin, (int)(this.Start + (0x04 * i)));
      }
      this.Matrix = new Matrix3D(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]);

      this.Parent = BitConverter.ToInt32(bin, (int)(this.Start + 0x40));
      this.ChildCount = BitConverter.ToInt32(bin, (int)(this.Start + 0x44));
      this.BoneLevel = BitConverter.ToInt32(bin, (int)(this.Start + 0x48));

      this.Children = new int[this.ChildCount];
      for (int i = 0; i < this.ChildCount; i++)
      {
        this.Children[i] = BitConverter.ToInt32(bin, (int)(this.Start + 0x50 + (0x04 * i)));
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }

    public int Parent { get; set; }
    public int ChildCount { get; set; }
    public int BoneLevel { get; set; }

    public Matrix3D Matrix { get; set; }
    public Matrix3D MatrixLayered { get; set; }
    public int[] Children { get; set; }
  }

  // NodeLay
  public class Nodes
  {
    public Nodes(dynamic tmcdata, HeaderData nodeH, char[] charsToTrim, byte[] bin)
    {
      this.Matrix = new Matrix3D();
      this.ObjIndex = -1;

      this.Start = nodeH.Start;
      this.Index = BitConverter.ToInt32(bin, (int)nodeH.Start + 0x38);

      uint length = (nodeH.Offset1 == 0) ? nodeH.Size - 0x40 : nodeH.Offset1 - 0x40;
      this.Name = Encoding.ASCII.GetString(bin, (int)nodeH.Start + 0x40, (int)length).TrimEnd(charsToTrim);
      if (nodeH.Offset1 > 0)
      {
        this.Offset = BitConverter.ToUInt32(bin, (int)(nodeH.Start + nodeH.Offset1));
        this.ObjIndex = BitConverter.ToInt32(bin, (int)(nodeH.Start + this.Offset));
        this.ChildCount = BitConverter.ToInt32(bin, (int)(nodeH.Start + this.Offset + 0x04));

        double[] m = new double[16];
        for (int i = 0; i < 16; i++)
        {
          m[i] = BitConverter.ToSingle(bin, (int)(nodeH.Start + this.Offset + 0x10 + (0x04 * i)));
        }
        this.Matrix = new Matrix3D(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]);

        BlendIndexGroup newGrp = new BlendIndexGroup(tmcdata, this, nodeH.Start + this.Offset, bin);
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }

    public string Name { get; set; }
    public int Index { get; set; }
    public int ObjIndex { get; set; }
    public int ChildCount { get; set; }

    public Matrix3D Matrix { get; set; }
  }

  public class BlendIndexGroup
  {
    public BlendIndexGroup(TMCData tmcdata, Nodes node, uint start, byte[] bin)
    {
      //this.Name = node.Name;
      this.Idx = new int[node.ChildCount];
      for (int i = 0; i < node.ChildCount; i++)
      {
        this.Idx[i] = BitConverter.ToInt32(bin, (int)(start + 0x50 + (0x04 * i)));
      }
      if (node.ChildCount == 0)
      {
        this.Idx = new int[1];
        this.Idx[0] = node.Index;
      }
      tmcdata.BlendIdxGrp[node.ObjIndex] = this;

    }

    //public string Name { get; set; }
    public int[] Idx { get; set; }
  }

  // GlblMtx / BnOfsMtx
  public class MatrixGroup
  {
    public MatrixGroup(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      this.Start = start + offset;

      double[] m = new double[16];
      for (int j = 0; j < 16; j++)
      {
        m[j] = BitConverter.ToSingle(bin, (int)(this.Start + (0x04 * j)));
      }
      this.Matrix = new Matrix3D(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]);
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }

    public Matrix3D Matrix { get; set; }
  }

  // cpf
  public class Customp
  {
    public Customp(uint start, uint offset, byte[] bin)
    {
      this.cp = new List<CustompParam>();

      if (start == 0 && offset == 0) return;

      var cpH = new HeaderData(start + offset, bin);
      this.Offset = offset;
      this.Start = start + offset;
      this.Size = cpH.Size;
      for (int i = 0; i < cpH.Count1; i++)
      {
        var newCp = new CustompParam();
        newCp.Index = BitConverter.ToInt32(bin, (int)(cpH.Start + cpH.Offset3 + (8 * i) + 4));
        newCp.Data1 = BitConverter.ToUInt32(bin, (int)(cpH.Start + cpH.Offset3 + (8 * i)));
        newCp.Data2 = BitConverter.ToInt32(bin, (int)(cpH.Start + cpH.Offsets[i]));

        switch (newCp.Data2)
        {
          case 2:
            newCp.Param = BitConverter.ToSingle(bin, (int)(cpH.Start + cpH.Offsets[i] + 4));
            break;
          case 3:
            uint datasize = 0;
            if (i == cpH.Count1 - 1)
              datasize = cpH.Size - cpH.Offsets[i] - 4;
            else
              datasize = cpH.Offsets[i + 1] - cpH.Offsets[i] - 4;
            newCp.Param = System.Text.Encoding.ASCII.GetString(bin.Skip((int)(cpH.Start + cpH.Offsets[i] + 4)).Take((int)datasize).ToArray());
            break;
          default:
            newCp.Param = BitConverter.ToInt32(bin, (int)(cpH.Start + cpH.Offsets[i] + 4));
            break;
        }

        this.cp.Add(newCp);
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public uint Size { get; set; }
    public int Index { get; set; }

    public List<CustompParam> cp { get; private set; }
  }

  public class CustompParam
  {
    public CustompParam() { }

    public int Index { get; set; }
    public uint Data1 { get; set; }
    public int Data2 { get; set; }
    public dynamic Param { get; set; } // Data2が02ならfloat、03ならstring、それ以外ならint
  }

  public class ItableData
  {
    public ItableData(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      int count = BitConverter.ToInt32(bin, (int)(start + offset));
      this.Indices = new int[count];
      for (int i = 0; i < count; i++)
      {
        this.Indices[i] = BitConverter.ToInt32(bin, (int)(start + offset + (i + 1) * 4));
      }
    }

    public uint Offset { get; set; }
    public int[] Indices { get; set; }
  }

  // MCAPACK
  public class MCAIndex
  {
    public MCAIndex(uint start, uint offset, byte[] bin, ObjectGroup grp)
    {
      if (offset == 0) return;

      this.Offset = offset;
      this.MatID = new int[grp.MaxObjID + 1];
      for (int i = 0; i < grp.MaxObjID + 1; i++)
      {
        this.MatID[i] = BitConverter.ToInt16(bin, (int)(start + offset + (i * 4)));
      }
    }

    public uint Offset { get; set; }
    public int[] MatID { get; set; }
  }

  public class Material
  {
    public Material(uint start, uint offset, byte[] bin)
    {
      this.Param = new List<MaterialParameter>();
      this.Offset = offset;
      this.Start = start + offset;

      var h = new HeaderData(start + offset, bin);
      for (int i = 0; i < h.Count1; i++)
      {
        var newParam = h.Offsets[i] != 0 ? new MaterialParameter(h.Start, h.Offsets[i], bin) : null;
        if (newParam != null)
        {
          newParam.ID = i;
          this.Param.Add(newParam);
        }
      }
    }

    public uint Offset { get; set; }
    public uint Start { get; set; }
    public int ID { get; set; }
    public List<MaterialParameter> Param { get; private set; }
  }

  public class MaterialParameter
  {
    public MaterialParameter(uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      var h = new HeaderData(start + offset, bin);
      this.Count = h.Count1;
      this.Data1 = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize));
      this.Data2 = bin[(int)(h.Start + h.HSize + 4)];

      if (h.Offset3 != 0)
      {
        this.Data3 = new dynamic[8];
        for (int i = 0; i < 8; i++)
        {
          if (i == 2)
          {
            this.Data3[i] = BitConverter.ToInt32(bin, (int)(h.Start + h.Offset3 + (4 * i)));
          }
          else
          {
            this.Data3[i] = BitConverter.ToSingle(bin, (int)(h.Start + h.Offset3 + (4 * i)));
          }
        }
      }

      if (h.Offset1 != 0)
      {
        this.Params = new List<dynamic[]>();
        for (int i = 0; i < h.Count1; i++)
        {
          dynamic[] nums = new dynamic[8];
          nums[0] = BitConverter.ToInt32(bin, (int)(h.Start + h.Offset1 + (0x20 * i)));
          for (int j = 1; j < 8; j++)
          {
            nums[j] = BitConverter.ToSingle(bin, (int)(h.Start + h.Offset1 + (0x20 * i) + (4 * j)));
          }
          this.Params.Add(nums);
        }
      }

    }

    public uint Offset { get; set; }
    public int ID { get; set; }
    public uint Count { get; set; }
    public float Data1 { get; set; }
    public byte Data2 { get; set; }
    public dynamic[] Data3 { get; set; }
    public List<dynamic[]> Params { get; set; }
  }

  // ACSCLS
  public class PhysicsData
  {
    public PhysicsData(uint startSub, uint start, uint offset, byte[] bin)
    {
      this.Offset = offset;
      this.Type = BitConverter.ToInt32(bin, (int)startSub);
      this.Node = BitConverter.ToInt32(bin, (int)startSub + 4);
      this.Param = BitConverter.ToUInt32(bin, (int)startSub + 8);

      var h = new HeaderData(start + offset, bin);
      //HeaderData[] childHs = new HeaderData[h.Count1];

      //this.Count1 = h.Count1;
      //this.Count2 = h.Count2;

      //if (h.Count1 != 7)
      if (this.Type != 2)
      {
        //this.HeaderFs = new float[24];
        //for (int i = 0; i < 24; i++)
        //{
        //  this.HeaderFs[i] = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + (4 * i)));
        //}
        this.RootNode = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x60));
        this.HeaderIdx1 = BitConverter.ToInt16(bin, (int)(h.Start + h.HSize + 0x64));
        this.HeaderIdx2 = BitConverter.ToInt16(bin, (int)(h.Start + h.HSize + 0x66));
        this.Root = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x68));

        // Block1
        this.Block1 = new List<PhysicsBlock1Data>();
        this.Block1H1 = new HeaderData(h.Start + h.Offsets[0], bin);
        this.Block1Offset = h.Offsets[0];
        //HeaderData block1H2 = new HeaderData(this.Block1H1.Start + this.Block1H1.Offset3, bin);
        //if (block1H2.Offsets[0] != 0)
        //{
        //  HeaderData block1H3 = new HeaderData(block1H2.Start + block1H2.Offsets[0], bin);
        //  this.Block1Indices = new short[block1H3.Count1];
        //  for (int i = 0; i < block1H3.Count1; i++)
        //  {
        //    this.Block1Indices[i] = BitConverter.ToInt16(bin, (int)(block1H3.Start + block1H3.Offset1) + (2 * i));
        //  }
        //}
        for (int i = 0; i < this.Block1H1.Count1; i++)
        {
          var block1data = new PhysicsBlock1Data((uint)(this.Block1H1.Start + this.Block1H1.Offset1 + (0xA0 * i)), bin);
          this.Block1.Add(block1data);
        }
      }
      else
      {
        //this.HeaderFs = new float[28];
        //for (int i = 0; i < 28; i++)
        //{
        //  this.HeaderFs[i] = BitConverter.ToSingle(bin, (int)(h.Start + h.HSize + (4 * i)));
        //}
        this.RootNode = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x70));
        this.HeaderIdx1 = -1;
        this.HeaderIdx2 = -1;
        this.Root = BitConverter.ToInt32(bin, (int)(h.Start + h.HSize + 0x74));

        this.Type2 = new List<List<PhysicsType2Data>>();
        for (int i = 0; i < h.Count1; i++)
        {
          var type2 = new List<PhysicsType2Data>();
          var type2H = new HeaderData(h.Start + h.Offsets[i], bin);
          for (int j = 0; j < type2H.Count1; j++)
          {
            var type2data = new PhysicsType2Data((uint)(type2H.Start + type2H.HSize + (0x40 * j)), bin);
            type2.Add(type2data);
          }
          this.Type2.Add(type2);
        }

        //this.Params = new uint[0];
      }
    }

    public uint Offset { get; set; }
    public uint Block1Offset { get; set; }

    //public uint Count1 { get; set; }
    //public uint Count2 { get; set; }

    public int Type { get; set; }
    public int Node { get; set; }
    public uint Param { get; set; }

    public int RootNode { get; set; }
    public short HeaderIdx1 { get; set; }
    public short HeaderIdx2 { get; set; }
    public int Root { get; set; }

    //public float[] HeaderFs { get; set; }

    public HeaderData Block1H1 { get; set; }
    //public short[] Block1Indices { get; set; }
    public List<PhysicsBlock1Data> Block1 { get; set; }

    //public short[] Block2Indices { get; set; }
    //public List<PhysicsBlock2Data> Block2 { get; set; }

    //public short[] Indices { get; set; }

    //public uint[] Params { get; set; }



    public List<List<PhysicsType2Data>> Type2 { get; set; }
  }

  public class PhysicsBlock1Data
  {
    public PhysicsBlock1Data(uint start, byte[] bin)
    {
      double[] m = new double[16];
      for (int i = 0; i < 16; i++)
      {
        m[i] = BitConverter.ToSingle(bin, (int)(start + (4 * i)));
      }
      this.Matrix = new Matrix3D(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]);

      this.Spring = new float[4];
      for (int i = 0; i < 4; i++)
      {
        this.Spring[i] = BitConverter.ToSingle(bin, (int)start + 0x40 + (4 * i));
      }
      this.Elastic = new float[4];
      for (int i = 0; i < 4; i++)
      {
        this.Elastic[i] = BitConverter.ToSingle(bin, (int)start + 0x50 + (4 * i));
      }
      this.Friction = new float[4];
      for (int i = 0; i < 4; i++)
      {
        this.Friction[i] = BitConverter.ToSingle(bin, (int)start + 0x60 + (4 * i));
      }
      this.Unknown = new float[4];
      for (int i = 0; i < 4; i++)
      {
        this.Unknown[i] = BitConverter.ToSingle(bin, (int)start + 0x70 + (4 * i));
      }
      this.Bending = new float[2];
      for (int i = 0; i < 2; i++)
      {
        this.Bending[i] = BitConverter.ToSingle(bin, (int)start + 0x80 + (4 * i));
      }
      this.Node = BitConverter.ToInt32(bin, (int)(start + 0x88));
      this.IndexInGroup = BitConverter.ToInt16(bin, (int)(start + 0x8C));
      this.Parent = BitConverter.ToInt16(bin, (int)(start + 0x8E));
      this.Index = BitConverter.ToInt32(bin, (int)(start + 0x90));
    }

    public Matrix3D Matrix { get; set; }
    public float[] Spring { get; set; }
    public float[] Elastic { get; set; }
    public float[] Friction { get; set; }
    public float[] Unknown { get; set; }
    public float[] Bending { get; set; }
    public int Node { get; set; }
    public short IndexInGroup { get; set; }
    public short Parent { get; set; }
    public int Index { get; set; }
  }

  public class PhysicsType2Data
  {
    public PhysicsType2Data(uint start, byte[] bin)
    {
      this.Fs = new float[12];
      for (int i = 0; i < this.Fs.Length; i++)
      {
        this.Fs[i] = BitConverter.ToSingle(bin, (int)start + (4 * i));
      }
      this.Index1 = BitConverter.ToInt32(bin, (int)start + 0x30);
      this.Index2 = BitConverter.ToInt32(bin, (int)start + 0x34);
    }

    public int Index1 { get; set; }
    public int Index2 { get; set; }
    public float[] Fs { get; set; }
  }

  public class Collision
  {
    public Collision(uint startSub, uint start, byte[] bin)
    {
      this.Type = BitConverter.ToInt32(bin, (int)startSub);
      this.Node = BitConverter.ToInt32(bin, (int)startSub + 4);
      this.Code = BitConverter.ToUInt32(bin, (int)startSub + 8);

      this.Fs = new float[12];
      for (int i = 0; i < 12; i++)
      {
        this.Fs[i] = BitConverter.ToSingle(bin, (int)(start + (4 * i)));
      }
      this.Node2 = BitConverter.ToInt32(bin, (int)(start + 0x30));
      this.Code2 = BitConverter.ToUInt32(bin, (int)(start + 0x34));
    }

    public int Index { get; set; }
    public int Type { get; set; }
    public int Node { get; set; }
    public uint Code { get; set; }

    public float[] Fs { get; private set; }
    public int Node2 { get; set; }
    public uint Code2 { get; set; }
  }
}
