public static void DumpCpk(Cpk cpk, FilePath dirPath)
    {
        void HandleContent(FilePath path, object content)
        {
            Console.WriteLine($"Dumping: {path} ... ");
            try {
                switch (content)
                {
                case Xtx xtx:
                    xtx.SaveTextureTo(path);
                    break;

                case byte[] bytes:
                    File.WriteAllBytes(path, bytes);
                    break;

                default:
                    throw new InvalidDataException($"Unknonw content type: {content.GetType().FullName}");
                }
            }
            catch (NotSupportedException exc) {
                Console.WriteLine(exc.Message);
            }
        }

        Utils.CreateOrClearDirectory(dirPath);
        Parallel.ForEach(cpk.ItocEntries, itoc => {
            var itocStr = "Itoc" + itoc.Id.ToString(cpk.ItocEntries.Count > 10000 ? "000000" : "0000");

            using var ms = new MemoryStream();
            cpk.ExtractItoc(ms, itoc);
            var data = ms.ToArray();

            var files = HandleNested("", data).ToList();
            try {
                if (files.Count > 1)
                {
                    var itocDirPath = dirPath / itocStr;
                    Utils.CreateOrClearDirectory(itocDirPath);
                    foreach (var entry in files)
                    {
                        var path = itocDirPath / entry.Path;
                        HandleContent(path, entry.Content);
                    }
                }
                else
                {
                    var entry = files.First();
                    var path  = dirPath / (itocStr + "_" + entry.Path);
                    HandleContent(path, entry.Content);
                }
            }
            finally {
                foreach (var entry in files)
                {
                    entry.Dispose();
                }
            }
        });
    }
    public static void DumpSys(Cpk cpk, int index, FilePath dirPath)
    {
        Parallel.ForEach(EnumerateSysFiles(cpk, index), entry => {
            try {
                var path   = dirPath / entry.Path;
                var parent = path.Parent;
                if (!Directory.Exists(parent))
                {
                    Directory.CreateDirectory(parent);
                }
                switch (entry.Content)
                {
                case Xtx xtx:
                    xtx.SaveTextureTo(path);
                    break;

                case byte[] bytes:
                    File.WriteAllBytes(path, bytes);
                    break;

                default:
                    throw new InvalidDataException($"Unknonw content type: {entry.Content.GetType().FullName}");
                }
            }
            finally {
                entry.Dispose();
            }
        });
    }
    public static IEnumerable <SysCpkEntry> EnumerateSysFiles(Cpk cpk, int index)
    {
        if (cpk.ItocEntries.Count != 16)
        {
            throw new InvalidDataException($"{cpk.ItocEntries.Count} != 16");
        }

        byte[] data;
        using (var ms = new MemoryStream()) {
            cpk.ExtractItoc(ms, cpk.ItocEntries[index]);
            if (index != 1)   // lzss
            {
                Span <byte> span = stackalloc byte[4];
                ms.Position = 0;
                ms.Read(span);
                var size = BitConverter.ToInt32(span);
                data = LZSS.Decode(ms.StreamAsIEnumerable()).ToArray();
                if (data.Length != size)
                {
                    throw new InvalidDataException($"itoc {index} decoded size {data.Length} != {size}");
                }
            }
            else
            {
                data        = new byte[ms.Length];
                ms.Position = 0;
                ms.Read(data);
            }
        }
        return(HandleNested("", data));
    }
 public static void DumpSys(Cpk cpk, FilePath dirPath)
 {
     Utils.CreateOrClearDirectory(dirPath);
     Parallel.For(0, 16, i => {
         Console.WriteLine($"Dumping: {i:00} ... ");
         DumpSys(cpk, i, dirPath / $"{i:00}");
     });
 }
        public void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString("nominal", Nominal.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("mean", Mean.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("standardDeviation", StandardDeviation.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("lowerSpecLimit", LowerSpecLimit.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("upperSpecLimit", UpperSpecLimit.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("cp", Cp.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("cpk", Cpk.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("distribution", Distribution);
            writer.WriteAttributeString("skewness", Skewness.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("kurtosis", Kurtosis.ToString(CultureInfo.InvariantCulture));
            writer.WriteAttributeString("sampleSize", SampleSize.ToString(CultureInfo.InvariantCulture));

            writer.WriteStartElement("Sample");
            Sample.WriteXml(writer);
            writer.WriteEndElement();

            writer.WriteStartElement("Estimate");
            Estimate.WriteXml(writer);
            writer.WriteEndElement();
        }
        public ActionResult cpk()
        {
            Cpk cp = new Cpk();

            cp.cname
        }