/// <summary> /// Compile Json to PSB /// </summary> /// <param name="inputJson">Json text</param> /// <param name="inputResJson">Resource Json text</param> /// <param name="baseDir">If resource Json uses relative paths (usually it does), specify the base dir</param> /// <param name="version">PSB version</param> /// <param name="cryptKey">CryptKey, if you need to use it outside FreeMote</param> /// <param name="spec">PSB Platform</param> /// <returns></returns> public static byte[] Compile(string inputJson, string inputResJson, string baseDir = null, ushort?version = null, uint?cryptKey = null, PsbSpec?spec = null) { //Parse PSB psb = Parse(inputJson, version ?? 3); //Link if (!string.IsNullOrWhiteSpace(inputResJson)) { if (inputResJson.Trim().StartsWith("{")) //resx.json { PsbResourceJson resx = JsonConvert.DeserializeObject <PsbResourceJson>(inputResJson); if (resx.PsbType != null) { psb.Type = resx.PsbType.Value; } if (resx.PsbVersion != null && version == null) { psb.Header.Version = resx.PsbVersion.Value; } if (resx.Platform != null && spec == null) { spec = resx.Platform; } if (resx.CryptKey != null & cryptKey == null) { cryptKey = resx.CryptKey; } if (resx.ExternalTextures) { Console.WriteLine("[INFO] External Texture mode ON, no resource will be compiled."); } else { psb.Link(resx, baseDir); } } else { List <string> resources = JsonConvert.DeserializeObject <List <string> >(inputResJson); psb.Link(resources, baseDir); } } //Build psb.Merge(); if (spec != null && spec != psb.Platform) { psb.SwitchSpec(spec.Value, spec.Value.DefaultPixelFormat()); psb.Merge(); } var bytes = psb.Build(); //Convert return(cryptKey != null?PsbFile.EncodeToBytes(cryptKey.Value, bytes, EncodeMode.Encrypt, EncodePosition.Auto) : bytes); }
public void TestWin2Krkr2Win() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var pathGood = Path.Combine(resPath, "goodstr.freemote.psb"); var psb = new PSB(pathGood); psb.SwitchSpec(PsbSpec.krkr, PsbSpec.krkr.DefaultPixelFormat()); psb.Merge(); psb.SwitchSpec(PsbSpec.win, PsbSpec.win.DefaultPixelFormat()); psb.Merge(); psb.BuildToFile("convert2.psb"); }
/// <summary> /// Convert a PSB to External Texture PSB. /// </summary> /// <param name="inputPath"></param> /// <param name="outputUnlinkedPsb">output unlinked PSB if you need</param> /// <param name="order"></param> /// <param name="format"></param> public static void UnlinkToFile(string inputPath, bool outputUnlinkedPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name, PsbImageFormat format = PsbImageFormat.Png) { if (!File.Exists(inputPath)) { return; } var name = Path.GetFileNameWithoutExtension(inputPath); var dirPath = Path.Combine(Path.GetDirectoryName(inputPath), name); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { Directory.CreateDirectory(dirPath); } var psb = new PSB(inputPath); var texs = psb.Unlink(); if (outputUnlinkedPsb) { psb.Merge(); var psbSavePath = Path.ChangeExtension(inputPath, ".unlinked.psb"); File.WriteAllBytes(psbSavePath, psb.Build()); } var texExt = format == PsbImageFormat.Bmp ? ".bmp" :".png"; var texFormat = format == PsbImageFormat.Bmp ? ImageFormat.Bmp : ImageFormat.Png; switch (order) { case PsbLinkOrderBy.Convention: foreach (var tex in texs) { tex.Save(Path.Combine(dirPath, tex.Tag + texExt), texFormat); } break; case PsbLinkOrderBy.Name: foreach (var tex in texs) { tex.Save(Path.Combine(dirPath, $"{name}_{tex.Tag}{texExt}"), texFormat); } break; case PsbLinkOrderBy.Order: for (var i = 0; i < texs.Count; i++) { var tex = texs[i]; tex.Save(Path.Combine(dirPath, $"{i}{texExt}"), texFormat); } break; } }
public void TestConvertWin2Krkr() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); //var path = Path.Combine(resPath, "e-mote38_win-pure.psb.json"); var path = Path.Combine(resPath, "e-mote38_win-pure.psb"); //var path = Path.Combine(resPath, "dx_e-mote3.0ショコラパジャマa-pure.psb.json"); //PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); PSB psb = new PSB(path); psb.SwitchSpec(PsbSpec.krkr); //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)); psb.SwitchSpec(PsbSpec.win); psb.Merge(); File.WriteAllBytes("emote_2x.psb", psb.Build()); }
public void TestConvertCommon2Win() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "akira_guide-pure.psb"); //PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); PSB psb = new PSB(path); psb.SwitchSpec(PsbSpec.win); psb.Merge(); File.WriteAllBytes("emote_common2win.psb", psb.Build()); }
public void TestConvertWin2Ems() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "ca01_s_body_2.psz.psb-pure.psb"); //PSB psb = PsbCompiler.LoadPsbFromJsonFile(path); PSB psb = new PSB(path); psb.SwitchSpec(PsbSpec.ems); psb.Merge(); File.WriteAllBytes("emote_win2ems.psb", psb.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)); }
public void TestInline() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var path = Path.Combine(resPath, "2001010400_00_mid.pure.psb"); var texPath = Path.Combine(resPath, "2001010400_00_mid_tex000.png"); PSB psb = new PSB(path); psb.Link(new List <string> { texPath }, order: PsbLinkOrderBy.Order); psb.Merge(); File.WriteAllBytes("inline.psb", psb.Build()); PSB p2 = new PSB("inline.psb"); }
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 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); }
/// <summary> /// Convert a PSB to External Texture PSB. /// </summary> /// <param name="inputPath"></param> /// <param name="outputUnlinkedPsb">output unlinked PSB; otherwise only output textures</param> /// <param name="order"></param> /// <param name="format"></param> /// <returns>The unlinked PSB path</returns> public static string UnlinkToFile(string inputPath, bool outputUnlinkedPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name, PsbImageFormat format = PsbImageFormat.png) { if (!File.Exists(inputPath)) { return(null); } var name = Path.GetFileNameWithoutExtension(inputPath); var dirPath = Path.Combine(Path.GetDirectoryName(inputPath), name); var psbSavePath = inputPath; if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { Directory.CreateDirectory(dirPath); } var context = FreeMount.CreateContext(); context.ImageFormat = format; var psb = new PSB(inputPath); if (psb.TypeHandler is BaseImageType imageType) { imageType.UnlinkToFile(psb, context, name, dirPath, outputUnlinkedPsb, order); } psb.TypeHandler.UnlinkToFile(psb, context, name, dirPath, outputUnlinkedPsb, order); if (outputUnlinkedPsb) { psb.Merge(); psbSavePath = Path.ChangeExtension(inputPath, ".unlinked.psb"); //unlink only works with motion.psb so no need for ext rename File.WriteAllBytes(psbSavePath, psb.Build()); } return(psbSavePath); }
private static void Port(string s, PsbSpec portSpec) { var name = Path.GetFileNameWithoutExtension(s); var ext = Path.GetExtension(s); Console.WriteLine($"Converting {name} to {portSpec} platform..."); PSB psb = new PSB(s); if (psb.Platform == portSpec) { Console.WriteLine("Already at the same platform, Skip."); } else { psb.SwitchSpec(portSpec); psb.Merge(); File.WriteAllBytes(Path.ChangeExtension(s, $".{portSpec}.psb"), psb.Build()); Console.WriteLine($"Convert {name} succeed."); } }
public void TestGraft2() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var pathGood = Path.Combine(resPath, "goodstr.freemote.psb"); var pathBad = Path.Combine(resPath, "goodStr.psb"); var psbGood = new PSB(pathGood); var psbBad = new PSB(pathBad); dynamic texGood = (PsbDictionary)psbGood.Objects["source"].Children("tex"); dynamic texBad = (PsbDictionary)psbBad.Objects["source"].Children("tex#000"); var badIcon = texBad["icon"]; PsbDictionary newIcon = new PsbDictionary(); foreach (var part in texGood["icon"]) { var content = part.Value; var bi = ((PsbDictionary)badIcon).FirstOrDefault(i => ((PsbNumber)i.Value.Children("width")).AsInt == content["width"].AsInt && ((PsbNumber)i.Value.Children("height")).AsInt == content["height"].AsInt && ((PsbNumber)i.Value.Children("originX")).AsFloat == content["originX"].AsFloat && ((PsbNumber)i.Value.Children("originY")).AsFloat == content["originY"].AsFloat); if (bi.Key != null) { newIcon[bi.Key] = content; } } texGood["icon"] = newIcon; dynamic badSource = psbBad.Objects["source"]; badSource["tex#000"] = texGood; psbBad.Merge(); psbBad.BuildToFile("graft.psb"); }
private static void Link(string psbPath, List <string> texPaths, PsbLinkOrderBy order) { if (!File.Exists(psbPath)) { return; } var name = Path.GetFileNameWithoutExtension(psbPath); var ext = Path.GetExtension(psbPath); try { List <string> texs = new List <string>(); foreach (var texPath in texPaths) { if (File.Exists(texPath)) { texs.Add(texPath); } else if (Directory.Exists(texPath)) { texs.AddRange(Directory.EnumerateFiles(texPath)); } } PSB psb = new PSB(psbPath); psb.Link(texs, order: order, isExternal: true); psb.Merge(); File.WriteAllBytes(Path.ChangeExtension(psbPath, "linked" + ext), psb.Build()); } catch (Exception e) { Console.WriteLine(e); } Console.WriteLine($"Link {name} succeed."); }
/// <summary> /// Load PSB From Json file /// </summary> /// <param name="inputPath">Json file path</param> /// <param name="inputResPath">Resource Json file</param> /// <param name="version">PSB version</param> /// <returns></returns> public static PSB LoadPsbFromJsonFile(string inputPath, string inputResPath = null, ushort?version = null) { if (string.IsNullOrEmpty(inputPath)) { throw new FileNotFoundException("Can not find input json file."); } if (string.IsNullOrEmpty(inputResPath) || !File.Exists(inputResPath)) { inputResPath = Path.ChangeExtension(inputPath, ".resx.json"); if (!File.Exists(inputResPath)) { inputResPath = Path.ChangeExtension(inputPath, ".res.json"); } } string inputResJson = null; string baseDir = Path.GetDirectoryName(inputPath); if (File.Exists(inputResPath)) { inputResJson = File.ReadAllText(inputResPath); baseDir = Path.GetDirectoryName(inputPath); } //Parse PSB psb = Parse(File.ReadAllText(inputPath), version ?? 3); //Link if (!string.IsNullOrWhiteSpace(inputResJson)) { if (inputResJson.Trim().StartsWith("{")) //resx.json { PsbResourceJson resx = JsonConvert.DeserializeObject <PsbResourceJson>(inputResJson); if (resx.PsbType != null) { psb.Type = resx.PsbType.Value; } if (resx.PsbVersion != null && version == null) { psb.Header.Version = resx.PsbVersion.Value; } if (resx.ExternalTextures) { Console.WriteLine("[INFO] External Texture mode ON, no resource will be compiled."); } else { psb.Link(resx, baseDir); } if (resx.Platform != null) { psb.SwitchSpec(resx.Platform.Value, resx.Platform.Value.DefaultPixelFormat()); } } else { List <string> resources = JsonConvert.DeserializeObject <List <string> >(inputResJson); psb.Link(resources, baseDir); } } if (version != null) { psb.Header.Version = version.Value; } psb.Merge(); return(psb); }
static void Main(string[] args) { Console.WriteLine("FreeMote MMO Decompiler (Preview)"); Console.WriteLine("by Ulysses, [email protected]"); FreeMount.Init(); Console.WriteLine(); Console.WriteLine("This is a preview version. If it crashes, send the PSB to me."); Console.WriteLine("All output files from this tool should follow [CC BY-NC-SA 4.0] license. Agree this license by pressing Enter:"); Console.ReadLine(); if (args.Length < 1 || !File.Exists(args[0])) { return; } PSB psb = null; try { psb = new PSB(args[0]); } catch (Exception e) { Console.WriteLine("Input PSB is invalid."); } if (psb != null) { psb.FixMotionMetadata(); //Fix for partial exported PSB if (psb.Platform != PsbSpec.krkr) { if (psb.Platform == PsbSpec.common || psb.Platform == PsbSpec.win) { psb.SwitchSpec(PsbSpec.krkr); psb.Merge(); } else { Console.WriteLine( $"EmtMake do not support {psb.Platform} PSB. Please use pure krkr PSB."); goto END; } } #if !DEBUG try #endif { MmoBuilder builder = new MmoBuilder(); var output = builder.Build(psb); output.Merge(); File.WriteAllBytes(Path.ChangeExtension(args[0], ".FreeMote.mmo"), output.Build()); } #if !DEBUG catch (Exception e) { Console.WriteLine(e); } #endif } END: Console.WriteLine("Done."); Console.ReadLine(); }
/// <summary> /// Compile Json to PSB /// </summary> /// <param name="inputJson">Json text</param> /// <param name="inputResJson">Resource Json text</param> /// <param name="baseDir">If resource Json uses relative paths (usually it does), specify the base dir</param> /// <param name="version">PSB version</param> /// <param name="cryptKey">CryptKey, use null for pure PSB</param> /// <param name="spec">PSB Platform</param> /// <param name="keepShell">If true, try to compress PSB to shell type (MDF/LZ4 etc.) specified in resx.json; otherwise just output PSB</param> /// <returns></returns> public static byte[] Compile(string inputJson, string inputResJson, string baseDir = null, ushort?version = null, uint?cryptKey = null, PsbSpec?spec = null, bool keepShell = true) { var context = FreeMount.CreateContext(); //Parse PSB psb = Parse(inputJson, version ?? 3); //Link if (!string.IsNullOrWhiteSpace(inputResJson)) { if (inputResJson.Trim().StartsWith("{")) //resx.json { PsbResourceJson resx = JsonConvert.DeserializeObject <PsbResourceJson>(inputResJson); if (resx.PsbType != null) { psb.Type = resx.PsbType.Value; } if (resx.PsbVersion != null && version == null) { psb.Header.Version = resx.PsbVersion.Value; } if (resx.Platform != null && spec == null) { spec = resx.Platform; } if (resx.CryptKey != null & cryptKey == null) { cryptKey = resx.CryptKey; } context = FreeMount.CreateContext(resx.Context); if (resx.HasExtraResources) { PsbResHelper.LinkExtraResources(psb, context, resx.ExtraResources, resx.ExtraFlattenArrays, baseDir); } if (resx.ExternalTextures) { #if DEBUG Console.WriteLine("[INFO] External Texture mode ON, no resource will be compiled."); #endif } else { psb.Link(resx, baseDir); } } else { List <string> resources = JsonConvert.DeserializeObject <List <string> >(inputResJson); psb.Link(resources, baseDir); } } //Build psb.Merge(); if (spec != null && spec != psb.Platform) { psb.SwitchSpec(spec.Value, spec.Value.DefaultPixelFormat()); psb.Merge(); } var bytes = psb.Build(); //Convert if (cryptKey != null) { bytes = PsbFile.EncodeToBytes(cryptKey.Value, bytes, EncodeMode.Encrypt, EncodePosition.Auto); } if (context.HasShell && keepShell) { using var outStream = context.PackToShell(new MemoryStream(bytes)); bytes = outStream.ToArray(); } return(bytes); }