Example #1
0
        // Yet another PSB bad design: some types do not have a type id while others do.

        public static string DefaultTypeId(this PsbType type)
        {
            switch (type)
            {
            case PsbType.Tachie:
                return("image");

            case PsbType.ArchiveInfo:
                return("archive");

            case PsbType.BmpFont:
                return("font");

            case PsbType.Motion:
                return("motion");

            case PsbType.SoundArchive:
                return("sound_archive");

            case PsbType.PSB:
            case PsbType.Mmo:
            case PsbType.Scn:
            case PsbType.Pimg:
            default:
                return("");
            }
        }
Example #2
0
        /// <summary>
        /// Get <see cref="PsbType"/>'s default extension
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string DefaultExtension(this PsbType type)
        {
            switch (type)
            {
            case PsbType.Pimg:
                return("pimg");

            case PsbType.Scn:
                return("scn");

            case PsbType.Motion:
            default:
                return("psb");
            }
        }
Example #3
0
        /// <summary>
        /// Get <see cref="PsbType"/>'s default extension
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string DefaultExtension(this PsbType type)
        {
            switch (type)
            {
            case PsbType.Mmo:
                return(".mmo");

            case PsbType.Pimg:
                return(".pimg");

            case PsbType.Scn:
                return(".scn");

            case PsbType.ArchiveInfo:
                return(".psb.m");

            case PsbType.Motion:
            default:
                return(".psb");
            }
        }
Example #4
0
        /// <summary>
        /// Decompile Pure PSB as Json
        /// </summary>
        /// <param name="path"></param>
        /// <param name="psb"></param>
        /// <param name="context"></param>
        /// <param name="psbType"></param>
        /// <returns></returns>
        public static string Decompile(string path, out PSB psb, Dictionary <string, object> context = null, PsbType psbType = PsbType.PSB)
        {
            using var fs = File.OpenRead(path);
            var    ctx    = FreeMount.CreateContext(context);
            string type   = null;
            Stream stream = fs;

            using var ms = ctx.OpenFromShell(fs, ref type);
            if (ms != null)
            {
                ctx.Context[Consts.Context_PsbShellType] = type;
                fs.Dispose();
                stream = ms;
            }

            try
            {
                psb = new PSB(stream, false);
            }
            catch (PsbBadFormatException e) when(e.Reason == PsbBadFormatReason.Header ||
                                                 e.Reason == PsbBadFormatReason.Array ||
                                                 e.Reason == PsbBadFormatReason.Body)  //maybe encrypted
            {
                stream.Position = 0;
                uint?key = null;

                if (ctx.Context.ContainsKey(Consts.Context_CryptKey))
                {
                    key = ctx.Context[Consts.Context_CryptKey] as uint?;
                }
                else
                {
                    key = ctx.GetKey(stream);
                }

                stream.Position = 0;
                if (key != null) //try use key
                {
                    try
                    {
                        using (var mms = new MemoryStream((int)stream.Length))
                        {
                            PsbFile.Encode(key.Value, EncodeMode.Decrypt, EncodePosition.Auto, stream, mms);
                            stream.Dispose();
                            psb = new PSB(mms);
                            ctx.Context[Consts.Context_CryptKey] = key;
                        }
                    }
                    catch
                    {
                        throw e;
                    }
                }
                else //key = null
                {
                    if (e.Reason == PsbBadFormatReason.Header || e.Reason == PsbBadFormatReason.Array) //now try Dullahan loading
                    {
                        psb = PSB.DullahanLoad(stream);
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            if (psbType != PsbType.PSB)
            {
                psb.Type = psbType;
            }

            return(Decompile(psb));
        }
Example #5
0
        /// <summary>
        /// Decompile to files
        /// </summary>
        /// <param name="inputPath">PSB file path</param>
        /// <param name="extractOption">whether to extract image to common format</param>
        /// <param name="extractFormat">if extract, what format do you want</param>
        /// <param name="useResx">if false, use array-based resource json (legacy)</param>
        /// <param name="key">PSB CryptKey</param>
        /// <param name="type">Specify PSB type, if not set, infer type automatically</param>
        public static void DecompileToFile(string inputPath, PsbExtractOption extractOption = PsbExtractOption.Original,
                                           PsbImageFormat extractFormat = PsbImageFormat.png, bool useResx = true, uint?key = null, PsbType type = PsbType.PSB)
        {
            var context = FreeMount.CreateContext();

            if (key != null)
            {
                context.Context[Consts.Context_CryptKey] = key;
            }

            File.WriteAllText(ChangeExtensionForOutputJson(inputPath, ".json"), Decompile(inputPath, out var psb, context.Context));

            OutputResources(psb, context, inputPath, extractOption, extractFormat, useResx);
        }
Example #6
0
        static void Main(string[] args)
        {
            Console.WriteLine("FreeMote PSB Decompiler");
            Console.WriteLine("by Ulysses, [email protected]");

            FreeMount.Init();
            Console.WriteLine($"{FreeMount.PluginsCount} Plugins Loaded.");

            InMemoryLoading = true;
            Console.WriteLine();

            var app = new CommandLineApplication();

            app.OptionsComparison = StringComparison.OrdinalIgnoreCase;

            //help
            app.HelpOption(); //do not inherit
            app.ExtendedHelpText = PrintHelp();

            //options
            var optKey = app.Option <uint>("-k|--key", "Set PSB key (uint, dec)", CommandOptionType.SingleValue);
            //var optFormat = app.Option<PsbImageFormat>("-e|--extract <FORMAT>",
            //    "Convert textures to png/bmp. Default=png", CommandOptionType.SingleValue, true);
            var optRaw = app.Option("-raw|--raw", "Output raw resources", CommandOptionType.NoValue, inherited: true);
            //メモリ足りない もうどうしよう : https://soundcloud.com/ulysses-wu/Heart-Chrome
            var optOom = app.Option("-oom|--memory-limit", "Disable In-Memory Loading", CommandOptionType.NoValue,
                                    inherited: true);
            var optNoParallel = app.Option("-1by1|--enumerate",
                                           "Disable parallel processing (can be very slow)", CommandOptionType.NoValue, inherited: true);
            var optHex   = app.Option("-hex|--json-hex", "(Json) Use hex numbers", CommandOptionType.NoValue, true);
            var optArray = app.Option("-indent|--json-array-indent", "(Json) Indent arrays", CommandOptionType.NoValue,
                                      true);
            var optDisableFlattenArray = app.Option("-dfa|--disable-flatten-array",
                                                    "Disable represent extra resource as flatten arrays", CommandOptionType.NoValue, inherited: true);
            var optType = app.Option <PsbType>("-t|--type <TYPE>", "Set PSB type manually",
                                               CommandOptionType.SingleValue, inherited: true);

            //args
            var argPath =
                app.Argument("Files", "File paths", multipleValues: true);

            //command: image
            app.Command("image", imageCmd =>
            {
                //help
                imageCmd.Description = "Extract (combined) textures from PSBs";
                imageCmd.HelpOption();
                imageCmd.ExtendedHelpText = @"
Example:
  PsbDecompile image sample.psb
  PsbDecompile image sample-resource-folder
";
                //args
                var argPsbPath = imageCmd.Argument("Path", "PSB paths or PSB directory paths").IsRequired();

                imageCmd.OnExecute(() =>
                {
                    var enableParallel = !optNoParallel.HasValue();
                    var psbPaths       = argPsbPath.Values;

                    foreach (var psbPath in psbPaths)
                    {
                        if (File.Exists(psbPath))
                        {
                            try
                            {
                                PsbDecompiler.ExtractImageFiles(psbPath);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                            }
                        }
                        else if (Directory.Exists(psbPath))
                        {
                            var files = FreeMoteExtension.GetFiles(psbPath, new[] { "*.psb", "*.pimg", "*.m", "*.bytes" });

                            if (enableParallel)
                            {
                                Parallel.ForEach(files, (s, state) =>
                                {
                                    try
                                    {
                                        PsbDecompiler.ExtractImageFiles(s);
                                    }
                                    catch (Exception e)
                                    {
                                        Console.WriteLine(e);
                                    }
                                });
                            }
                            else
                            {
                                foreach (var s in files)
                                {
                                    try
                                    {
                                        PsbDecompiler.ExtractImageFiles(s);
                                    }
                                    catch (Exception e)
                                    {
                                        Console.WriteLine(e);
                                    }
                                }
                            }
                        }
                    }
                });
            });

            //command: unlink
            app.Command("unlink", linkCmd =>
            {
                //help
                linkCmd.Description = "Unlink textures from PSBs";
                linkCmd.HelpOption();
                linkCmd.ExtendedHelpText = @"
Example:
  PsbDecompile unlink sample.psb
";
                //options
                var optOrder = linkCmd.Option <PsbLinkOrderBy>("-o|--order <ORDER>",
                                                               "Set texture unlink order (ByName/ByOrder/Convention). Default=ByName",
                                                               CommandOptionType.SingleValue);
                //args
                var argPsbPath = linkCmd.Argument("PSB", "PSB Path").IsRequired();
                //var argTexPath = linkCmd.Argument("Textures", "Texture Paths").IsRequired();

                linkCmd.OnExecute(() =>
                {
                    //PsbImageFormat format = optFormat.HasValue() ? optFormat.ParsedValue : PsbImageFormat.png;
                    var order    = optOrder.HasValue() ? optOrder.ParsedValue : PsbLinkOrderBy.Name;
                    var psbPaths = argPsbPath.Values;
                    foreach (var psbPath in psbPaths)
                    {
                        if (File.Exists(psbPath))
                        {
                            try
                            {
                                PsbDecompiler.UnlinkToFile(psbPath, order: order);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e);
                            }
                        }
                    }
                });
            });

            //info-psb
            app.Command("info-psb", archiveCmd =>
            {
                //help
                archiveCmd.Description = "Extract files from info.psb.m & body.bin (FreeMote.Plugins required)";
                archiveCmd.HelpOption();
                archiveCmd.ExtendedHelpText = @"
Example:
  PsbDecompile info-psb -k 1234567890ab sample_info.psb.m
  PsbDecompile info-psb -k 1234567890ab -l 131 -a sample_info.psb.m
  Hint: The body.bin should exist in the same folder and keep both file names correct.
";
                //options
                //var optMdfSeed = archiveCmd.Option("-s|--seed <SEED>",
                //    "Set complete seed (Key+FileName)",
                //    CommandOptionType.SingleValue);
                var optExtractAll = archiveCmd.Option("-a|--all",
                                                      "Decompile all contents in body.bin if possible (can be slow)",
                                                      CommandOptionType.NoValue);
                var optMdfKey = archiveCmd.Option("-k|--key <KEY>",
                                                  "Set key (Infer file name from path)",
                                                  CommandOptionType.SingleValue);
                var optMdfKeyLen = archiveCmd.Option <int>("-l|--length <LEN>",
                                                           "Set key length. Default=131",
                                                           CommandOptionType.SingleValue);

                //args
                var argPsbPaths = archiveCmd.Argument("PSB", "Archive Info PSB Paths", true);

                archiveCmd.OnExecute(() =>
                {
                    if (optOom.HasValue())
                    {
                        InMemoryLoading = false;
                    }

                    if (optArray.HasValue())
                    {
                        JsonArrayCollapse = false;
                    }

                    if (optHex.HasValue())
                    {
                        JsonUseHexNumber = true;
                    }

                    if (optDisableFlattenArray.HasValue())
                    {
                        FlattenArrayByDefault = false;
                    }

                    bool extractAll     = optExtractAll.HasValue();
                    var outputRaw       = optRaw.HasValue();
                    bool enableParallel = FastMode;
                    if (optNoParallel.HasValue())
                    {
                        enableParallel = false;
                    }

                    string key = optMdfKey.HasValue() ? optMdfKey.Value() : null;
                    //string seed = optMdfSeed.HasValue() ? optMdfSeed.Value() : null;
                    if (string.IsNullOrEmpty(key))
                    {
                        throw new ArgumentNullException(nameof(key), "No key or seed specified.");
                    }

                    int keyLen = optMdfKeyLen.HasValue() ? optMdfKeyLen.ParsedValue : 0x83;
                    Dictionary <string, object> context = new Dictionary <string, object>();

                    if (keyLen >= 0)
                    {
                        context[Context_MdfKeyLength] = (uint)keyLen;
                    }

                    Stopwatch sw = Stopwatch.StartNew();
                    foreach (var s in argPsbPaths.Values)
                    {
                        ExtractArchive(s, key, context, outputRaw, extractAll, enableParallel);
                    }
                    sw.Stop();
                    Console.WriteLine($"Process time: {sw.Elapsed:g}");
                });
            });

            app.OnExecute(() =>
            {
                if (optOom.HasValue())
                {
                    InMemoryLoading = false;
                }

                if (optArray.HasValue())
                {
                    JsonArrayCollapse = false;
                }

                if (optHex.HasValue())
                {
                    JsonUseHexNumber = true;
                }

                if (optDisableFlattenArray.HasValue())
                {
                    FlattenArrayByDefault = false;
                }

                bool useRaw = optRaw.HasValue();
                //PsbImageFormat format = optFormat.HasValue() ? optFormat.ParsedValue : PsbImageFormat.png;
                uint?key = optKey.HasValue() ? optKey.ParsedValue : (uint?)null;

                PsbType type = PsbType.PSB;
                if (optType.HasValue())
                {
                    type = optType.ParsedValue;
                }
                foreach (var s in argPath.Values)
                {
                    if (File.Exists(s))
                    {
                        Decompile(s, useRaw, PsbImageFormat.png, key, type);
                    }
                    else if (Directory.Exists(s))
                    {
                        foreach (var file in FreeMoteExtension.GetFiles(s,
                                                                        new[] { "*.psb", "*.mmo", "*.pimg", "*.scn", "*.dpak", "*.psz", "*.psp", "*.bytes", "*.m" }))
                        {
                            Decompile(s, useRaw, PsbImageFormat.png, key, type);
                        }
                    }
                }
            });

            if (args.Length == 0)
            {
                app.ShowHelp();
                return;
            }

            app.Execute(args);

            Console.WriteLine("Done.");
        }