public void TestCompareMmo() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res\mmo"); var path = Path.Combine(resPath, "template39.json"); var path2 = Path.Combine(resPath, "crash-temp.mmo"); var mmo1 = PsbCompiler.LoadPsbFromJsonFile(path); var allpart1 = FindPart((PsbList)mmo1.Objects["objectChildren"], "body_parts"); var mmo2 = new PSB(path2); var allpart2 = FindPart((PsbList)mmo2.Objects["objectChildren"], "body_parts"); //var p1 = mmo1.Objects.FindByPath( // "/objectChildren/[3]/children/[1]/layerChildren/[0]/children/[0]/frameList/[0]/content/coord"); //var pp = ((IPsbChild) p1).Parent.Parent.Parent.Parent["label"]; PsbDictionary FindPart(PsbList col, string label) { foreach (var c in col) { if (c is PsbDictionary d) { if (d["label"] is PsbString s && s.Value == label) { return(d); } } } return(null); } //PsBuildTest.CompareValue(allpart1, allpart2); PsBuildTest.CompareValue(mmo1.Objects["metaformat"].Children("data"), mmo2.Objects["metaformat"].Children("data")); }
public void TestFindByPath() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "e-mote38_win-pure.psb.json"); PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); var obj = psb.Objects.FindByPath("/object/all_parts/motion/タイムライン構造/bounds"); var type = obj.Type; var objs = psb.Objects.FindAllByPath("/object/*/motion/*"); foreach (var psbValue in objs) { if (psbValue is PsbDictionary dic) { var s = dic.GetName(); Console.WriteLine(s); } else { Console.WriteLine($"Not a PsbObject: {psbValue}"); } } }
public void TestCompareDecompile() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var pccPsb = new PSB(Path.Combine(resPath, "c01c.txt.scn")); //var pccPsb = new PSB(Path.Combine(resPath, "ca01_l_body_1.psz.psb-pure.psb")); //var pccPsb = new PSB(Path.Combine(resPath, "ca01.psb")); //var psbuildPsb = new PSB(Path.Combine(resPath, "ca01_l_body_1.psz.psb-pure.psb.json.psbuild.psb")); //var psbuildPsb = new PSB(Path.Combine(resPath, "dx_れいなh1a1.psb.json-pure.psb")); var psbuildPsb = PsbCompiler.LoadPsbFromJsonFile(Path.Combine(resPath, "c01c.txt.json")); //foreach (var s in psbuildPsb.Strings) //{ // var pccStr = pccPsb.Strings.Find(ss => ss.Value == s.Value); // if (pccStr != null) // { // s.Index = pccStr.Index; // } // else // { // Console.WriteLine($"Can not find: {s}"); // } //} //psbuildPsb.UpdateIndexes(); //File.WriteAllBytes(Path.Combine(resPath, "ca01_build.psb"), psbuildPsb.Build()); CompareValue(pccPsb.Objects, psbuildPsb.Objects); //Console.WriteLine("============"); //CompareValue(psbuildPsb.Objects, pccPsb.Objects); }
public void TestCompileCommon() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "emote396-a8l8.pure.json"); var path2 = Path.Combine(resPath, "emote396-a8l8.pure.psb"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); var psb2 = new PSB(path2); //File.WriteAllBytes("396.psb", psb.Build()); PsbCompiler.CompileToFile(path, path + ".psbuild.psb", null, 4, null, PsbSpec.win); }
public void TestCompileEms() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "akira_guide-pure.psb.json"); var path = Path.Combine(resPath, "emote_test2-pure.psb.json"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); psb.Platform = PsbSpec.ems; psb.Merge(); File.WriteAllBytes(path + ".build.psb", psb.Build()); //PsbCompiler.CompileToFile(path, path + ".psbuild.psb", null, 3, null, PsbSpec.ems); }
public void TestPathTravel() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "e-mote38_win-pure.psb.json"); PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); var targetPath = "/object/all_parts/motion/タイムライン構造/bounds"; var obj = (PsbDictionary)psb.Objects.FindByPath(targetPath); var objPath = obj.Path; Assert.AreEqual(targetPath, objPath); }
public void TestConvertCommon2Krkr() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "akira_guide-pure.psb.json"); PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); Common2KrkrConverter converter = new Common2KrkrConverter(); converter.Convert(psb); psb.Merge(); File.WriteAllBytes("emote_test_front.psb", psb.Build()); File.WriteAllText("emote_test_front.json", PsbDecompiler.Decompile(psb)); }
public void TestNaN() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "dx_れいなh1a1.psb"); //var psb = new PSB(path); var path = Path.Combine(resPath, "dx_れいなh1a1.psb.json"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); var o = psb.Objects.FindByPath( "/object/head_parts/motion/頭部変形基礎/layer/[0]/children/[3]/children/[0]/children/[0]/children/[0]/children/[0]/children/[0]/children/[0]/children/[0]/children/[0]/children/[1]/children/[0]/children/[0]/children/[0]/children/[0]/frameList/[0]/content/coord") as PsbCollection; var num = o[0] as PsbNumber; var val = num.IntValue; var valNaN = num.FloatValue; }
public void TestPackMmo() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "template39.json"); var path2 = Path.Combine(resPath, "template39-krkr.json"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); //var psb2 = PsbCompiler.LoadPsbFromJsonFile(path2); //psb.Objects["objectChildren"] = psb2.Objects["object"]; //var collection = (PsbList)psb.Objects["objectChildren"]; //collection.RemoveAt(0); psb.Objects["metaformat"] = PsbNull.Null; psb.Merge(); psb.SaveAsMdfFile("temp.mmo"); }
public void TestFindPath() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "template39.json"); var path = Path.Combine(resPath, "mmo", "NekoCrash.json"); var mmo = PsbCompiler.LoadPsbFromJsonFile(path); var children = (PsbList)mmo.Objects["objectChildren"]; var source = (PsbList)mmo.Objects["sourceChildren"]; var obj = (PsbDictionary)children.FindByMmoPath( "all_parts/全体構造/■全体レイアウト/move_UD/move_LR/□下半身配置_le/胴体回転中心/全身調整/□頭部調整_le/act_sp"); var realPath = obj.Path; var mmoPath = obj.GetMmoPath(); //"FreeMote/all_parts/全体構造/■全体レイアウト/move_UD/move_LR/□下半身配置_le/胴体回転中心/全身調整/□頭部調整_le/act_sp" //obj = source.FindByMmoPath("face_eye_mabuta_l"); }
public void TestConvertAndBuildMmo() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "dx_e-moteショコラ小ex制服b.psb.json"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); psb.SwitchSpec(PsbSpec.krkr); psb.Merge(); //File.WriteAllBytes(Path.Combine(resPath, "neko-krkr.psb"), psb.Build()); //return; MmoBuilder mmoBuilder = new MmoBuilder(true); var psbMmo = mmoBuilder.Build(psb); psbMmo.Merge(); File.WriteAllBytes(Path.Combine(resPath, "mmo", "DxNekoCrash.mmo"), psbMmo.Build()); }
public void TestMmoGraft() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "template39.json"); var path2 = Path.Combine(resPath, "template39-krkr.json"); var mmo = PsbCompiler.LoadPsbFromJsonFile(path); var psb = PsbCompiler.LoadPsbFromJsonFile(path2); MmoBuilder mmoBuilder = new MmoBuilder(true); var psbMmo = mmoBuilder.Build(psb); //mmo.Objects["objectChildren"] = psbMmo.Objects["objectChildren"]; var data = (PsbDictionary)mmo.Objects["metaformat"].Children("data"); var data2 = (PsbDictionary)psbMmo.Objects["metaformat"].Children("data"); data["bustControlDefinitionList"] = data2["bustControlDefinitionList"]; mmo.Merge(); mmo.SaveAsMdfFile(Path.Combine(resPath, "mmo", "temp.mmo")); }
public void TestConvertKrkr2Win() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "澄怜a_裸.psb-pure.psb"); var path = Path.Combine(resPath, "澄怜a_裸.psb-pure.psb.json"); //var path = Path.Combine(resPath, "e-mote38_KRKR-pure.psb.json"); //var path = Path.Combine(resPath, "e-mote38_KRKR-pure.psb"); PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); //PSB psb = new PSB(path); psb.SwitchSpec(PsbSpec.win); psb.Merge(); File.WriteAllBytes("emote_krkr2win.psb", psb.Build()); File.WriteAllText("emote_krkr2win.json", PsbDecompiler.Decompile(psb)); RL.ConvertToImageFile(psb.Resources.First().Data, "tex-in-psb.png", 4096, 4096, PsbImageFormat.Png, PsbPixelFormat.WinRGBA8); }
public void TestBuildMmo() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "e-mote3.0ショコラパジャマa中-krkr.json"); //var path = Path.Combine(resPath, "template39-krkr.json"); var psb = PsbCompiler.LoadPsbFromJsonFile(path); MmoBuilder mmoBuilder = new MmoBuilder(true); //Add custom menu paths mmoBuilder.CustomPartMenuPaths.Add("スカート", "胴体/スカート"); mmoBuilder.CustomPartMenuPaths.Add("前髪_le1", "頭部/前髪1"); mmoBuilder.CustomPartMenuPaths.Add("後髪_le4", "頭部/後髪4"); mmoBuilder.CustomPartMenuPaths.Add("後髪_le5", "頭部/後髪5"); var psbMmo = mmoBuilder.Build(psb); psbMmo.Merge(); File.WriteAllBytes(Path.Combine(resPath, "mmo", "NekoCrash.mmo"), psbMmo.Build()); }
public void TestGraft() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "澄怜a_裸.psb-pure.psb.json"); var path = Path.Combine(resPath, "e-mote38_KRKR-pure.psb.json"); var path2 = Path.Combine(resPath, "e-mote38_win-pure.psb.json"); PSB psbKrkr = PsbCompiler.LoadPsbFromJsonFile(path); PSB psbWin = PsbCompiler.LoadPsbFromJsonFile(path2); psbWin.SwitchSpec(PsbSpec.krkr); //var metadata = (PsbDictionary)psbWin.Objects["metadata"]; //metadata["attrcomp"] = psbKrkr.Objects["metadata"].Children("attrcomp"); psbWin.Merge(); ////Graft var resKrkr = psbKrkr.CollectResources(false); var resWin = psbWin.CollectResources(false); var headWin = resWin.FirstOrDefault(r => r.Height == 186 && r.Width == 122); var headKrkr = resKrkr.FirstOrDefault(r => r.Height == 186 && r.Width == 122); if (headWin != null && headKrkr != null) { headWin.Resource.Data = headKrkr.Resource.Data; } //foreach (var resourceMetadata in resWin) //{ // var sameRes = resKrkr.FirstOrDefault(r => r.Height == resourceMetadata.Height && r.Width == resourceMetadata.Width); // if (sameRes != null) // { // Console.WriteLine($"{sameRes} {sameRes.Width}x{sameRes.Height} found."); // resourceMetadata.Resource.Data = sameRes.Resource.Data; // } //} psbWin.Merge(); File.WriteAllBytes("emote_win2krkr.psb", psbWin.Build()); //File.WriteAllText("emote_krkr2win.json", PsbDecompiler.Decompile(psb2)); }
/// <summary> /// Pack Archive PSB /// </summary> /// <param name="jsonPath">json path</param> /// <param name="key">crypt key</param> /// <param name="intersect">Only pack files which existed in info.psb.m</param> /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param> /// <param name="enableParallel">parallel process</param> /// <param name="keyLen">key length</param> /// <param name="keepRaw">Do not try to compile json or pack MDF</param> public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true, int keyLen = 131, bool keepRaw = false) { if (!File.Exists(jsonPath)) { return; } PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath); if (infoPsb.Type != PsbType.ArchiveInfo) { Console.WriteLine("Json is not an ArchiveInfo PSB."); return; } var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath); if (!resx.Context.ContainsKey(Context_ArchiveSource) || resx.Context[Context_ArchiveSource] == null) { Console.WriteLine("ArchiveSource must be specified in resx.json Context."); return; } if (keyLen > 0) { resx.Context[Context_MdfKeyLength] = keyLen; } string infoKey = null; if (resx.Context[Context_MdfKey] is string mdfKey) { infoKey = mdfKey; } List <string> sourceDirs = null; if (resx.Context[Context_ArchiveSource] is string path) { sourceDirs = new List <string> { path }; } else if (resx.Context[Context_ArchiveSource] is IList paths) { sourceDirs = new List <string>(paths.Count); sourceDirs.AddRange(from object p in paths select p.ToString()); } else { Console.WriteLine("ArchiveSource incorrect."); return; } var baseDir = Path.GetDirectoryName(jsonPath); var files = new Dictionary <string, (string Path, ProcessMethod Method)>(); var suffix = ArchiveInfoGetSuffix(infoPsb); List <string> filter = null; if (intersect) //only collect files appeared in json { filter = ArchiveInfoCollectFiles(infoPsb, suffix).ToList(); } void CollectFiles(string targetDir) { if (!Directory.Exists(targetDir)) { return; } foreach (var f in Directory.EnumerateFiles(targetDir)) { if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture)) { continue; } else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture)) //json source, need compile { var name = Path.GetFileNameWithoutExtension(f); if (preferPacked && files.ContainsKey(name) && files[name].Method != ProcessMethod.Compile) //it's always right no matter set or replace { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) //this file is not appeared in json { //ignore } else { files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.Compile); } } } else { var name = Path.GetFileName(f); if (!preferPacked && files.ContainsKey(name) && files[name].Method == ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { using var fs = File.OpenRead(f); if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF") { files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.EncodeMdf); } else { files[name] = (f, ProcessMethod.None); } } } } } } //Collect files Console.WriteLine("Collecting files ..."); foreach (var sourceDir in sourceDirs) { CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir)); } Console.WriteLine($"Packing {files.Count} files ..."); var bodyBinFileName = Path.GetFileName(jsonPath); var packageName = Path.GetFileNameWithoutExtension(bodyBinFileName); var coreName = ArchiveInfoGetPackageName(packageName); bodyBinFileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin"; //using var mmFile = // MemoryMappedFile.CreateFromFile(bodyBinFileName, FileMode.Create, coreName, ); using var bodyFs = File.OpenWrite(bodyBinFileName); var fileInfoDic = new PsbDictionary(files.Count); var fmContext = FreeMount.CreateContext(resx.Context); //byte[] bodyBin = null; if (enableParallel) { var contents = new ConcurrentBag <(string Name, Stream Content)>(); Parallel.ForEach(files, (kv) => { var fileNameWithoutSuffix = ArchiveInfoGetFileNameRemoveSuffix(kv.Key, suffix); if (kv.Value.Method == ProcessMethod.None) { contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path))); return; } var mdfContext = new Dictionary <string, object>(resx.Context); var context = FreeMount.CreateContext(mdfContext); if (!string.IsNullOrEmpty(key)) { mdfContext[Context_MdfKey] = key + kv.Key; } else if (resx.Context[Context_MdfMtKey] is string mtKey) { mdfContext[Context_MdfKey] = mtKey + kv.Key; } else { mdfContext.Remove(Context_MdfKey); } mdfContext.Remove(Context_ArchiveSource); if (kv.Value.Method == ProcessMethod.EncodeMdf) { using var mmFs = MemoryMappedFile.CreateFromFile(kv.Value.Path, FileMode.Open); //using var fs = File.OpenRead(kv.Value.Path); contents.Add((fileNameWithoutSuffix, context.PackToShell(mmFs.CreateViewStream(), "MDF"))); //disposed later } else { var content = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path); var stream = content.Psb.ToStream(); var shellType = kv.Key.DefaultShellType(); //MARK: use shellType in filename, or use suffix in info? if (!string.IsNullOrEmpty(shellType)) { stream = context.PackToShell(stream, shellType); //disposed later } contents.Add((fileNameWithoutSuffix, stream)); } });
/// <summary> /// Pack Archive PSB /// </summary> /// <param name="jsonPath">json path</param> /// <param name="key">crypt key</param> /// <param name="intersect">Only pack files which existed in info.psb.m</param> /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param> /// <param name="enableParallel">parallel process</param> /// <param name="keyLen">key length</param> public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true, int keyLen = 131) { if (!File.Exists(jsonPath)) { return; } PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath); if (infoPsb.Type != PsbType.ArchiveInfo) { Console.WriteLine("Json is not an ArchiveInfo PSB."); return; } var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath); if (!resx.Context.ContainsKey(Context_ArchiveSource) || resx.Context[Context_ArchiveSource] == null) { Console.WriteLine("ArchiveSource must be specified in resx.json Context."); return; } if (keyLen > 0) { resx.Context[Context_MdfKeyLength] = keyLen; } string infoKey = null; if (resx.Context[Context_MdfKey] is string mdfKey) { infoKey = mdfKey; } List <string> sourceDirs = null; if (resx.Context[Context_ArchiveSource] is string path) { sourceDirs = new List <string> { path }; } else if (resx.Context[Context_ArchiveSource] is IList paths) { sourceDirs = new List <string>(paths.Count); sourceDirs.AddRange(from object p in paths select p.ToString()); } else { Console.WriteLine("ArchiveSource incorrect."); return; } var baseDir = Path.GetDirectoryName(jsonPath); var files = new Dictionary <string, (string Path, ProcessMethod Method)>(); var suffix = ArchiveInfoPsbGetSuffix(infoPsb); List <string> filter = null; if (intersect) { filter = ArchiveInfoPsbCollectFiles(infoPsb, suffix); } void CollectFiles(string targetDir) { if (!Directory.Exists(targetDir)) { return; } foreach (var f in Directory.EnumerateFiles(targetDir)) { if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture)) { continue; } else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture)) { var name = Path.GetFileNameWithoutExtension(f); if (preferPacked && files.ContainsKey(name) && files[name].Method != ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { files[name] = (f, ProcessMethod.Compile); } } } else { var name = Path.GetFileName(f); if (!preferPacked && files.ContainsKey(name) && files[name].Method == ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { using var fs = File.OpenRead(f); if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF") { files[name] = (f, ProcessMethod.EncodeMdf); } else { files[name] = (f, ProcessMethod.None); } } } } } } //Collect files foreach (var sourceDir in sourceDirs) { CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir)); } var fileName = Path.GetFileName(jsonPath); var packageName = Path.GetFileNameWithoutExtension(fileName); var coreName = PsbExtension.ArchiveInfoGetPackageName(packageName); fileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin"; var fileInfoDic = new PsbDictionary(files.Count); var fmContext = FreeMount.CreateContext(resx.Context); byte[] bodyBin = null; if (enableParallel) { var contents = new ConcurrentBag <(string Name, Stream Content)>(); Parallel.ForEach(files, (kv) => { var fileNameWithoutSuffix = ArchiveInfoPsbGetFileName(kv.Key, suffix); if (kv.Value.Method == ProcessMethod.None) { contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path))); return; } var mdfContext = new Dictionary <string, object>(resx.Context); var context = FreeMount.CreateContext(mdfContext); if (!string.IsNullOrEmpty(key)) { mdfContext[Context_MdfKey] = key + fileNameWithoutSuffix + suffix; } else if (resx.Context[Context_MdfMtKey] is string mtKey) { mdfContext[Context_MdfKey] = mtKey + fileNameWithoutSuffix + suffix; } else { mdfContext.Remove(Context_MdfKey); } mdfContext.Remove(Context_ArchiveSource); if (kv.Value.Method == ProcessMethod.EncodeMdf) { contents.Add((fileNameWithoutSuffix, context.PackToShell( File.OpenRead(kv.Value.Path), "MDF"))); } else { var content = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path); var outputMdf = context.PackToShell(content.Psb.ToStream(), "MDF"); contents.Add((fileNameWithoutSuffix, outputMdf)); } });