public static HSD_FOBJ EncodeFrames(List <FOBJKey> Keys, byte TrackType, float error = 0.0001f)
        {
            HSD_FOBJ fobj = new HSD_FOBJ();

            fobj.JointTrackType = (JointTrackType)TrackType;

            if (fobj.JointTrackType == JointTrackType.HSD_A_J_PTCL)
            {
                return(fobj);
            }

            // automatically set single key interpolation type
            if (Keys.Count == 1)
            {
                Keys[0].InterpolationType = GXInterpolationType.HSD_A_OP_KEY;
            }

            // perform quantization
            FOBJQuantanizer valueQ   = new FOBJQuantanizer(error);
            FOBJQuantanizer tangentQ = new FOBJQuantanizer(error);

            foreach (FOBJKey key in Keys)
            {
                valueQ.AddValue(key.Value);
                tangentQ.AddValue(key.Tan);
            }

            fobj.ValueScale  = valueQ.GetValueScale();
            fobj.ValueFormat = valueQ.GetDataFormat();

            fobj.TanScale  = tangentQ.GetValueScale();
            fobj.TanFormat = tangentQ.GetDataFormat();

            MemoryStream o = new MemoryStream();

            using (BinaryWriterExt Writer = new BinaryWriterExt(o))
            {
                Writer.BigEndian = false;

                for (int i = 0; i < Keys.Count;)
                {
                    GXInterpolationType ip = Keys[i].InterpolationType;
                    int j;
                    for (j = 0; j < Keys.Count - i; j++)
                    {
                        if (Keys[i + j].InterpolationType != ip)
                        {
                            break;
                        }
                    }

                    if (j > 0x7FF)
                    {
                        j = 0x7FF;
                    }

                    int flag = ((j - 1) << 4) | (int)ip;
                    Writer.WritePacked(flag);

                    for (int k = i; k < i + j; k++)
                    {
                        int DeltaTime = 0;

                        if (k + 1 < Keys.Count)
                        {
                            var nextKey =
                                Keys.Find(e =>
                                          e.Frame > Keys[k].Frame &&
                                          e.InterpolationType != GXInterpolationType.HSD_A_OP_SLP);

                            if (nextKey != null)
                            {
                                DeltaTime = (int)(nextKey.Frame - Keys[k].Frame);
                            }
                        }

                        if (k == Keys.Count)
                        {
                            DeltaTime = 1;
                        }

                        switch (ip)
                        {
                        case GXInterpolationType.HSD_A_OP_CON:
                            valueQ.WriteValue(Writer, Keys[k].Value);
                            Writer.WritePacked(DeltaTime);
                            break;

                        case GXInterpolationType.HSD_A_OP_LIN:
                            valueQ.WriteValue(Writer, Keys[k].Value);
                            Writer.WritePacked(DeltaTime);
                            break;

                        case GXInterpolationType.HSD_A_OP_SPL0:
                            valueQ.WriteValue(Writer, Keys[k].Value);
                            Writer.WritePacked(DeltaTime);
                            break;

                        case GXInterpolationType.HSD_A_OP_SPL:
                            valueQ.WriteValue(Writer, Keys[k].Value);
                            tangentQ.WriteValue(Writer, Keys[k].Tan);
                            Writer.WritePacked(DeltaTime);
                            break;

                        case GXInterpolationType.HSD_A_OP_SLP:
                            tangentQ.WriteValue(Writer, Keys[k].Tan);
                            break;

                        case GXInterpolationType.HSD_A_OP_KEY:
                            valueQ.WriteValue(Writer, Keys[k].Value);
                            break;
                        }
                    }

                    i += j;
                }
            }
            fobj.Buffer = o.ToArray();
            o.Dispose();
            return(fobj);
        }