/// <summary>
        /// XMLファイルからプリセット情報をロードする
        /// 旧版との互換性用メソッド
        /// </summary>
        /// <param name="fileName">XMLファイル名</param>
        /// <returns>ロードされたプリセット情報</returns>
        public Dictionary<string, PresetData> LoadXML(string fileName) {

            var xdoc = XDocument.Load(fileName);
            var presetNodes = xdoc.Descendants("preset");
            if (!presetNodes.Any()) {
                return null;
            }
            var presets = new Dictionary<string, PresetData>();
            try {
                foreach (var presetNode in presetNodes) {
                    var preset = new PresetData();
                    preset.name = GetAttribute(presetNode, "name");
                    
                    var slotsNode = presetNode.Element("slots");
                    if (slotsNode != null) {
                        var slotNodes = slotsNode.Elements("slot");
                        foreach (var slotNode in slotNodes) {
                            CCSlot slot = null;
                            var slotName = GetAttribute(slotNode, "slotname");
                            try {
                                slot = new CCSlot(slotName);
                            } catch(Exception e) {
                                LogUtil.Log("未対応のスロットをスキップします.", slotName, e);
                                continue;
                            }
                            var materialNodes = slotNode.Elements("material");
                            foreach (var materialNode in materialNodes) {
                                var cmat = new CCMaterial();
                                cmat.name = GetElementVal(materialNode, "name");
                                cmat.shader = GetElementVal(materialNode, "shader");
                                var colorNode = materialNode.Element("color");
                                if (colorNode != null) cmat.color = loadColor(colorNode);
                                colorNode = materialNode.Element("shadowColor");
                                if (colorNode != null) cmat.shadowColor = loadColor(colorNode);
                                colorNode = materialNode.Element("rimColor");
                                if (colorNode != null) cmat.rimColor = loadColor(colorNode);
                                colorNode = materialNode.Element("outlineColor");
                                if (colorNode != null) cmat.outlineColor = loadColor(colorNode);
                                var f = materialNode.Element("shininess");
                                if (f != null) cmat.shininess = (float)f;
                                f = materialNode.Element("outlineWidth");
                                if (f != null)  cmat.outlineWidth = (float)f;
                                f = materialNode.Element("rimPower");
                                if (f != null)  cmat.rimPower = (float)f;
                                f = materialNode.Element("rimShift");
                                if (f != null)  cmat.rimShift = (float)f;
                                f = materialNode.Element("hiRate");
                                if (f != null) cmat.hiRate = (float)f;
                                f = materialNode.Element("hiPow");
                                if (f != null) cmat.hiPow = (float)f;
                                f = materialNode.Element("floatValue1");
                                if (f != null) cmat.floatVal1 = (float)f;
                                f = materialNode.Element("floatValue2");
                                if (f != null) cmat.floatVal2 = (float)f;
                                f = materialNode.Element("floatValue3");
                                if (f != null) cmat.floatVal3 = (float)f;
                                slot.Add(cmat);
                            }
                            //preset.slots.Add(slot.name, slot);
                            preset.slots.Add(slot);
                        }
                    }
                    var mpnsNode = presetNode.Element("mpns");
                    if (mpnsNode != null) {
                        var mpnNodes = mpnsNode.Elements("mpn");
                        foreach (var mpnNode in mpnNodes) {
                            preset.mpns.Add(new CCMPN(GetAttribute(mpnNode, "name"), mpnNode.Value));
                        }
                    }
                    var nodesNode = presetNode.Element("nodes");
                    if (nodesNode != null) {
                        var nodes = nodesNode.Elements("node");
                        if (nodes.Any()) {
                            var delNodes = new Dictionary<string, bool>();
                            foreach (var node in nodes) {
                                var nodeName = GetAttribute(node, "name");
                                if (nodeName != null) {
                                    bool v = GetBoolAttribute(node, "visible");
                                    // 対応するノード名をそのまま使用
                                    if (ACConstants.NodeNames.ContainsKey(nodeName)) {
                                        delNodes.Add(nodeName, v);
                                    }
                                }
                            }
                            preset.delNodes = delNodes;
                        }
                    }
                    
                    presets.Add(preset.name, preset);
                }
            } catch(Exception e) {
                LogUtil.Error("failed to load presets. file=",fileName, ". ", e);
                return null; 
            }
            
            return presets;
        }
        public void Save(string fileName,  string presetName, Dictionary<string, bool> dDelNodes) {
            Maid maid = holder.currentMaid;
            // カレントのメイドデータからプリセットデータを抽出
            var preset = new PresetData();
            preset.name = presetName;
            foreach (SlotInfo slotInfo in ACConstants.SlotNames.Values) {
                if (!slotInfo.enable) continue;

                TBodySkin slot = maid.body0.GetSlot((int)slotInfo.Id);
                // マスク情報を抽出
                SlotState maskState;
                if (slot.obj == null) {
                    maskState = SlotState.NotLoaded;
                } else if (!slot.boVisible) {
                    maskState = SlotState.Masked;
                } else {
                    maskState = SlotState.Displayed;
                }

                Material[] materialList = holder.GetMaterials(slot);
                if (materialList.Length == 0) continue;

                var slotItem = new CCSlot(slotInfo.Id);
                slotItem.mask = maskState;

                foreach (Material material in materialList) {
                    var type = ShaderType.Resolve(material.shader.name);
                    if (type == ShaderType.UNKNOWN) continue;
                    var cmat = new CCMaterial(material, type);
                    slotItem.Add(cmat);
                    foreach (var texProp in type.texProps) {
                        var tex2d = material.GetTexture(texProp.propId) as Texture2D;
                        if (tex2d == null || string.IsNullOrEmpty(tex2d.name)) continue;

                        var ti = new TextureInfo();
                        cmat.Add(ti);
                        ti.propName = texProp.keyName;
                        ti.texFile = tex2d.name;
                        var fp = texModifier.GetFilter(maid, slotInfo.Id.ToString(), material.name, tex2d.name);
                        if (fp != null && !fp.hasNotChanged()) ti.filter = new TexFilter(fp);
                    }
                }
                preset.slots.Add(slotItem);
            }

            for (int i = TypeUtil.BODY_START; i <= TypeUtil.BODY_END; i++) {
                var mpn = (MPN)Enum.ToObject(typeof(MPN), i);
                MaidProp mp = maid.GetProp(mpn);
                if (mp != null) {
                    if (!String.IsNullOrEmpty(mp.strFileName)) {
                        preset.mpns.Add(new CCMPN(mpn, mp.strFileName));
                    } else {
                        preset.mpnvals.Add(new CCMPNValue(mpn, mp.value, mp.min, mp.max));
                    }
                }
            }

            for (int i = TypeUtil.WEAR_START; i <= TypeUtil.WEAR_END; i++) {
                var mpn = (MPN)Enum.ToObject(typeof(MPN), i);
                MaidProp mp = maid.GetProp(mpn);
                if (mp != null && !String.IsNullOrEmpty(mp.strFileName)) {
                    preset.mpns.Add(new CCMPN(mpn, mp.strFileName));
                }
            }
//            for (int i = (int)MPN_TYPE_RANGE.FOLDER_BODY_START; i <= (int)MPN_TYPE_RANGE.FOLDER_BODY_END; i++) {
//                var mpn = (MPN)Enum.ToObject(typeof(MPN), i);
//                MaidProp mp = maid.GetProp(mpn);
//                if (mp != null) {
//                    LogUtil.Debug(mpn,":", mp.type, ", value=", mp.value, ", temp=", mp.temp_value, ", file=", mp.strFileName);
//                }
//            }

            // 無限色
            for (int j = TypeUtil.PARTSCOLOR_START; j <= TypeUtil.PARTSCOLOR_END; j++) {
                var pcEnum = (MaidParts.PARTS_COLOR)j;
                MaidParts.PartsColor part = maid.Parts.GetPartsColor(pcEnum);
                preset.partsColors[pcEnum.ToString()] = new CCPartsColor(part);
            }

            // 表示ノード
            preset.delNodes = new Dictionary<string, bool>(dDelNodes);
            
            LogUtil.Debug("create preset...", fileName);
            SavePreset(fileName, preset);
        }