public bool SaveFiles(ACCMenu menu) {
            var outDir = fileUtil.GetACCDirectory(trgtMenu.editfile);
            if (!Directory.Exists(outDir)) Directory.CreateDirectory(outDir);

            LogUtil.Debug("output path:", outDir);
            string filepath = Path.Combine(outDir, menu.EditFileName());

            // menu出力
            ACCMenu.WriteMenuFile(filepath, menu);

            var writeFiles = new HashSet<string>();

            // filterがあれば、適用しアイコンを変更
            string iconfilepath = Path.Combine(outDir, menu.EditIconFileName());
            writeFiles.Add(iconfilepath);
            string icontxt = settings.txtPrefixTex + iconfilepath;
            fileUtil.CopyTex(menu.icon, iconfilepath, icontxt, null);
            LogUtil.Debug("tex file:", iconfilepath);

            // model 出力 (additem)
            foreach (var pair in menu.itemFiles) {
                string infile = pair.Key;
                Item item = pair.Value;
                if (item.needUpdate) {
                    string filename = item.EditFileName();
                    if (HasAlreadyWritten(writeFiles, filename)) continue;

                    string modelfilepath = Path.Combine(outDir, filename);
    
                    // modelファイルのスロットのマテリアル/テクスチャ情報を抽出
                    SlotMaterials slotMat = menu.slotMaterials[item.slot];
                    // onlyModelでない場合はshader変更のみとしておく
                    // material slotとmatNoで特定
                    // texture  propName
                    // 必要に応じてtex出力
                    fileUtil.WriteModelFile(infile, modelfilepath, slotMat);
                    LogUtil.Debug("model file:", modelfilepath);
                }
            }

            // mate出力
            foreach (var tm in menu.slotMaterials.Values) {
                foreach(var trgtMat in tm.materials) {
                    // マテリアル変更が指定された場合 => TODO 切り替え可能とする場合:menuにマテリアル変更を追加する必要あり
                    if (!trgtMat.onlyModel) {
                        var filename = trgtMat.EditFileName();
                        if (HasAlreadyWritten(writeFiles, filename)) continue;
    
                        string matefilepath = Path.Combine(outDir, filename);
                        fileUtil.WriteMateFile(trgtMat.filename, matefilepath, trgtMat);
                        LogUtil.Debug("mate file:", matefilepath);

                        if (trgtMat.needPmatChange) {
                            var name = Path.GetFileNameWithoutExtension(filename);
                            var pmatfile = name + FileConst.EXT_PMAT;
                            string pmatfilepath = Path.Combine(outDir, pmatfile);
                            fileUtil.WritePmat(pmatfilepath, trgtMat.editname, 
                                              trgtMat.RenderQueue(), trgtMat.ShaderName());
                            LogUtil.Debug("pmat file:", pmatfilepath);
                        }
                    }

                    // テクスチャ出力
                    foreach (var trgtTex in trgtMat.texDic.Values) {
                        if (!trgtTex.needOutput) continue;

                        var tex2d = trgtTex.tex as Texture2D;
                        if (tex2d == null) {
                            LogUtil.Debug("tex is not Texture2D", trgtTex.editname);
                            continue;
                            // TODO RenderTexの場合は無理やりTexture2Dに変換も可能だが…
                        }
                        var texfilename = trgtTex.EditFileName();
                        if (HasAlreadyWritten(writeFiles, texfilename)) continue;
                            
                        string texfilepath = Path.Combine(outDir, texfilename);
                        fileUtil.WriteTexFile(texfilepath, trgtTex.EditTxtPath(), tex2d.EncodeToPNG());
                        LogUtil.Debug("tex file:", texfilepath);
                    }
                }
            }

            // めくれ、ずらし用menuファイルの出力
            foreach (var res in menu.resFiles.Values) {

                // 各モデルファイルで出力が必要となるファイルについて、
                // 元が同名のファイルを参照している場合でも関係なく出力
                // 設定が違う場合もある上、editnameはすべて別名になるはず
                var filename = res.EditFileName();
                if (HasAlreadyWritten(writeFiles, filename)) continue;


                string menufilepath = Path.Combine(outDir, filename);
                var toCreateFiles = fileUtil.WriteMenuFile(res.filename, menufilepath, res);
                LogUtil.Debug("menu file:", menufilepath);

                foreach (var toCreate in toCreateFiles) {
                    // modelを出力
                    if (toCreate.item != null) {
                        var filename0 = toCreate.replaced;
                        if (HasAlreadyWritten(writeFiles, filename0)) continue;

                        string modelfilepath = Path.Combine(outDir, filename0);
                        SlotMaterials slotMat = menu.slotMaterials[toCreate.item.slot];
                        // TODO リプレースが想定される情報であるかチェック 
                        fileUtil.WriteModelFile(toCreate.source, modelfilepath, slotMat);
                        
                    // .mate出力
                    } else if (toCreate.material != null) {
                        TargetMaterial trgtMat = toCreate.material;
                        var filename0 = toCreate.replaced;
                        if (HasAlreadyWritten(writeFiles, filename0)) continue;

                        // mate出力==別のtexファイルを出力する可能性有り
                        string matefilepath = Path.Combine(outDir, filename0);
                        fileUtil.WriteMateFile(toCreate.source, matefilepath, trgtMat);
                        // マテリアル名は上位と同じにして、同一pmatを使用する
                        //if (trgtMat.needPmatChange) {
                        //    var name = Path.GetFileNameWithoutExtension(filename);
                        //    var pmatfile = name + FileConst.EXT_PMAT;
                        //    string pmatfilepath = Path.Combine(outDir, pmatfile);
                        //    outUtil.WritePmat(pmatfilepath, trgtMat.editname, 
                        //                      trgtMat.RenderQueue(), trgtMat.ShaderName());
                        //    LogUtil.DebugLog("pmatファイルを出力しました。", pmatfilepath);
                        //}

                        foreach (var tex in trgtMat.texDic.Values) {
                            if (!tex.needOutput) continue;

                            var tex2d = tex.tex as Texture2D;
                            if (tex2d == null) {
                                LogUtil.Debug("tex is not 2D", tex.editname);
                                continue;
                            }
                            var texfilename = tex.EditFileName();
                            if (HasAlreadyWritten(writeFiles, texfilename)) continue;

                            // テクスチャをロードし、フィルタを適用
                            Texture2D loadedTex   = null;
                            Texture2D filteredTex = null;
                            if (!tex.fileChanged) { // texファイル変更済の場合はロードされたデータ済みから出力(そのままtex2dを使用)
                                loadedTex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
                                string texfile = tex.workfilename + FileConst.EXT_TEXTURE;
                                if (!fileUtil.Exists(texfile)) {
                                    LogUtil.LogF("リソース参照で使用されているtexファイル({0})が見つかりません。texファイルを出力できません。", texfile);
                                    continue;
                                }
                                loadedTex.LoadImage( ImportCM.LoadTexture(texfile) );
                                tex2d = loadedTex;
                            }

                            if (tex.colorChanged) {
                                filteredTex = ACCTexturesView.Filter(tex2d, tex.filter);
                                tex2d = filteredTex;
                            }
                            string texfilepath = Path.Combine(outDir, texfilename);
                            fileUtil.WriteTexFile(texfilepath, tex.EditTxtPath(), tex2d.EncodeToPNG());
                            LogUtil.Debug("tex file:", texfilepath);

                            if (loadedTex != null)   UnityEngine.Object.DestroyImmediate(loadedTex);
                            if (filteredTex != null) UnityEngine.Object.DestroyImmediate(filteredTex);
                        }
                    }
                }
            }
            return true;
        }
        // TODO ファイル名となりうるフィールドの文字列検証(入力フィールドの変更時に行う)
        //private bool isSavable;
        // private void CheckFileName(string filename) {
        //     isSavable &= filename.IndexOfAny(INVALID_FILENAMECHARS) < 0;
        //}

        // ファイルの重複確認
        private bool IsWritable(ACCMenu menu, bool ignoreExists) {
            if (menu.editfile.Length == 0) return false;

            bool registed = false;;

            string outDir = fileUtil.GetACCDirectory();
            outDir = Path.Combine(outDir, trgtMenu.editfile);
            if (!ignoreExists && Directory.Exists(outDir)) {
                LogUtil.Debug("output directory already exist :", outDir);
                menu.editfileExist = true;
                return false;
            }
            
            bool hasDuplicate = false;
            // 別々の情報ファイルが重複しないか + 登録済みファイルが存在するかをチェック 
            var writeFiles = new HashSet<string> ();

            var menufile = menu.EditFileName();
            writeFiles.Add(menufile);
            menu.editfileExist = false;
            if (fileUtil.Exists(menufile)) {
                LogUtil.Debug("already exist:", menufile);
                registed = true;
                menu.editfileExist = true;
            }

            // icon
            menu.editiconExist = false;
            string iconfilepath = menu.EditIconFileName();
            if (fileUtil.Exists(iconfilepath)) {
                LogUtil.Debug("already exist:", iconfilepath);
                registed = true;
                menu.editiconExist = true;
            }
            writeFiles.Add(iconfilepath);

            // modelファイル
            foreach (var pair in menu.itemFiles) {
                Item item = pair.Value;
                if (item.needUpdate) {
                    item.editnameExist = false; // clear
                    string filename = item.EditFileName();
                    if (HasAlreadyWritten(writeFiles, filename)) {
                        hasDuplicate = true;
                        item.editnameExist = true; // フラグを分けるべきか?
                        continue;
                    }
                    if (fileUtil.Exists(filename)) {
                        LogUtil.Debug("already exist:", filename);
                        registed = true;
                        item.editnameExist = true;
                    }
                }
            }
            // mateファイル
            foreach (var tm in menu.slotMaterials.Values) {
                foreach(var trgtMat in tm.materials) {
                    // modelファイルのみの場合は好み ⇒変更する場合はmenuにマテリアル変更を追加する必要あり
                    if (trgtMat.onlyModel) continue;

                    trgtMat.editfileExist = false;
                    var filename = trgtMat.EditFileName();
                    if (HasAlreadyWritten(writeFiles, filename)) {
                        hasDuplicate = true;
                        trgtMat.editfileExist = true; // フラグを分けるべきか?
                        continue;                        
                    }
                    if (fileUtil.Exists(filename)) {
                        LogUtil.Debug("already exist:", filename);
                        registed = true;
                        trgtMat.editfileExist = true;
                    }
                    // pmat
                    if (trgtMat.needPmatChange) {
                        var name = Path.GetFileNameWithoutExtension(filename);
                        var pmatfile = name + FileConst.EXT_PMAT;
                        if (HasAlreadyWritten(writeFiles, pmatfile)) {
                            hasDuplicate = true;
                            trgtMat.editfileExist = true; // フラグを分けるべきか?
                            continue;                        
                        }
                        if (fileUtil.Exists(pmatfile)) {
                            LogUtil.Debug("already exist:", pmatfile);
                            registed = true;
                            trgtMat.editfileExist = true;
                        }
                    }

                    // texファイル
                    foreach (var trgtTex in trgtMat.texDic.Values) {
                        if (!trgtTex.needOutput) continue;
                        trgtTex.editnameExist = false;

                        var texfilename = trgtTex.EditFileName();
                        if (HasAlreadyWritten(writeFiles, texfilename)) {
                            hasDuplicate = true;
                            trgtTex.editnameExist = true; // フラグを分けるべきか?
                            continue;                        
                        }
                        if (fileUtil.Exists(texfilename)) {
                            LogUtil.Debug("already exist:", texfilename);
                            registed = true;
                            trgtTex.editnameExist = true;
                        }
                    }
                }
            }

            // めくれ、ずらし用menuファイルの出力
            foreach (var res in menu.resFiles.Values) {

                res.editfileExist = false;
                var filename = res.EditFileName();
                if (HasAlreadyWritten(writeFiles, filename)) {
                    hasDuplicate = true;
                    res.editfileExist = true; // フラグを分けるべきか?
                    continue;                        
                }
                if (fileUtil.Exists(filename)) {
                    LogUtil.Debug("already exist:", filename);
                    registed = true;
                    res.editfileExist = true;
                }
            }
            // 出力ファイルの重複は無視できない
            if (hasDuplicate) return false;
            return ignoreExists || !registed;
        }