Exemple #1
0
        public void TestTlgNative()
        {
            var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res");
            //var path = Path.Combine(resPath, "title-pimg");
            var path   = Path.Combine(resPath, "title-pimg", "566.tlg");
            var bmp    = TlgImageFormatter.LoadTlg(File.ReadAllBytes(path), out int ver);
            var width  = bmp.Width;
            var height = bmp.Height;

            bmp.Save("tlg.png", ImageFormat.Png);

            path = Path.Combine(resPath, "emote_test.pure", "tex#000-texture.png");
            Bitmap            bmp2      = new Bitmap(path);
            var               bts       = TlgImageFormatter.SaveTlg(bmp2);
            TlgImageConverter converter = new TlgImageConverter();

            using (var ms = new MemoryStream(bts))
            {
                using (var br = new BinaryReader(ms))
                {
                    var bmp3 = converter.Read(br);
                    bmp3.Save("tlg2.png", ImageFormat.Png);
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Load TLG
        /// </summary>
        /// <param name="tlgData"></param>
        /// <param name="version">TLG version, can be 0(unknown),5,6</param>
        /// <returns></returns>
        public static Bitmap LoadTlg(byte[] tlgData, out int version)
        {
            if (!PreferManaged)
            {
                try
                {
                    return(TlgNative.ToBitmap(tlgData, out version));
                }
                catch (Exception)
                {
                    //ignored, fallback to managed decoder
                }
            }

            if (_managedConverter == null)
            {
                _managedConverter = new TlgImageConverter();
            }

            using (var ms = new MemoryStream(tlgData))
            {
                using (var br = new BinaryReader(ms))
                {
                    var bmp = _managedConverter.ReadAndGetMetaData(br, out var md);
                    version = md.Version;
                    return(bmp);
                }
            }
        }
Exemple #3
0
        public void ManagedBenchmark()
        {
            TlgImageConverter converter = new TlgImageConverter();

            using (BinaryReader br = new BinaryReader(new MemoryStream(_tlgBytes), Encoding.UTF8, false))
            {
                Bitmap b = converter.Read(br);
                int    w = b.Width;
                b.Dispose();
            }
        }
Exemple #4
0
        static void Main(string[] args)
        {
            //MemoryTest();

            var target = File.Exists("test.tlg") ? "test.tlg" : "NewGame5.tlg";
            TlgImageConverter converter = new TlgImageConverter();
            var original = File.ReadAllBytes(target);

            byte[] converted = null;
            using (var fs = File.Open(target, FileMode.Open))
            {
                using (var br = new BinaryReader(fs))
                {
                    var bmp = converter.Read(br);
                    converted = bmp.ToTlg6();
                    if (converted == null)
                    {
                        Console.WriteLine("Conversion failed.");
                    }
                    //else
                    //{
                    //    Console.WriteLine($"Totally equal: {(original.SequenceEqual(converted) ? "Yes" : "No")}");
                    //}
                }
            }

            if (converted != null)
            {
                using (var ms = new MemoryStream(converted))
                {
                    using (var br = new BinaryReader(ms))
                    {
                        var bmp = converter.Read(br);
                        bmp.Save("output.png", ImageFormat.Png);
                    }
                }
            }

            Console.WriteLine($"IsTLG: {(TlgNative.CheckTlg(original) ? "Yes" : "No")}");
            if (TlgNative.GetInfoTlg(original, out int w, out int h, out int v))
            {
                Console.WriteLine($"TLGv{v} Size: {w} x {h}");
            }
            //var bmp2 = TlgNative.ToBitmap(original, out _);
            var t = TlgNative.ToBitmap(original);

            Console.WriteLine(t.Item2);
            t.Item1.Save("output2.png", ImageFormat.Png);

            Console.WriteLine("Done.");
            Console.ReadLine();
        }
Exemple #5
0
        public void TestTlgDecode()
        {
            var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res");
            //var path = Path.Combine(resPath, "title-pimg");
            var path = Path.Combine(resPath, "conf");
            TlgImageConverter image = new TlgImageConverter();

            foreach (var tlg in Directory.EnumerateFiles(path, "*.tlg"))
            {
                using (var stream = File.OpenRead(tlg))
                {
                    var br  = new BinaryReader(stream);
                    var img = image.Read(br);
                    img.Save($"{tlg}.png", ImageFormat.Png);
                }
            }
        }
Exemple #6
0
        /// <summary>
        /// Load TLG
        /// </summary>
        /// <param name="tlgData"></param>
        /// <param name="version">TLG version, can be 0(unknown),5,6</param>
        /// <returns></returns>
        public static Bitmap LoadTlg(byte[] tlgData, out int version)
        {
            if (_managedConverter == null)
            {
                _managedConverter = new TlgImageConverter();
            }

            using (var ms = new MemoryStream(tlgData))
            {
                using (var br = new BinaryReader(ms))
                {
                    var bmp = _managedConverter.ReadAndGetMetaData(br, out var md);
                    version = md.Version;
                    return(bmp);
                }
            }
        }
Exemple #7
0
        static void MemoryTest()
        {
            var target = File.Exists("test.tlg") ? "test.tlg" : "NewGame5.tlg";
            TlgImageConverter converter = new TlgImageConverter();
            var original = File.ReadAllBytes(target);

            Console.WriteLine("TLG bytes loaded.");
            Console.ReadLine();

            //Pure managed!
            using (var ms = new MemoryStream(original))
            {
                using (BinaryReader br = new BinaryReader(ms))
                {
                    Bitmap b = converter.Read(br);
                    b.Dispose();
                }
            }

            Console.WriteLine("Managed done.");
            Console.ReadLine();
            GC.Collect();

            using (TlgLoader ldr = new TlgLoader(original))
            {
                Bitmap b = ldr.Bitmap;
                b.Dispose();
            }
            //You can still access b's properties here (out of `using` scope) if you don't dispose it,
            //but you can not call `Save` or `LockBits` etc. because the data is deleted.
            Console.WriteLine("NativeLoader done.");
            Console.ReadLine();
            GC.Collect();

            //By default `ToBitmap` copies data to managed side so you can call `Save` as you want.
            //Set `useUnmanagedScan0 = true` to make it behave like TlgLoader but no one deletes the data!!
            Bitmap b2 = TlgNative.ToBitmap(original, out _);

            b2.Dispose();
            Console.WriteLine("NativeCopy done.");
            Console.ReadLine();
            GC.Collect();

            Console.WriteLine("All done.");
            Console.ReadLine();
        }
Exemple #8
0
        public static Dictionary <string, string> OutputImageResources(PSB psb, FreeMountContext context, string name,
                                                                       string dirPath,
                                                                       PsbExtractOption extractOption = PsbExtractOption.Original,
                                                                       PsbImageFormat extractFormat   = PsbImageFormat.png)
        {
            var resources = psb.CollectResources <ImageMetadata>();

            Dictionary <string, string> resDictionary = new Dictionary <string, string>();

            ImageFormat pixelFormat;

            switch (extractFormat)
            {
            case PsbImageFormat.png:
                pixelFormat = ImageFormat.Png;
                break;

            default:
                pixelFormat = ImageFormat.Bmp;
                break;
            }

            if (extractOption == PsbExtractOption.Original)
            {
                for (int i = 0; i < psb.Resources.Count; i++)
                {
                    var relativePath = psb.Resources[i].Index == null ? $"#{Consts.ResourceIdentifierChar}{i}.bin" : $"{psb.Resources[i].Index}.bin";

                    File.WriteAllBytes(
                        Path.Combine(dirPath, relativePath),
                        psb.Resources[i].Data);
                    resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                }
            }
            else
            {
                for (int i = 0; i < resources.Count; i++)
                {
                    var resource = resources[i];
                    //Generate Friendly Name
                    var    friendlyName = resource.GetFriendlyName(psb.Type);
                    string relativePath = friendlyName;
                    if (string.IsNullOrWhiteSpace(friendlyName))
                    {
                        relativePath = resource.Resource.Index?.ToString() ?? $"({i})";
                        friendlyName = i.ToString();
                    }

                    var currentExtractOption = extractOption;
                    if (resource.Compress != PsbCompressType.Tlg && resource.Compress != PsbCompressType.ByName && (resource.Width <= 0 || resource.Height <= 0)) //impossible to extract, just keep raw
                    {
                        if (currentExtractOption == PsbExtractOption.Extract)
                        {
                            currentExtractOption = PsbExtractOption.Original;
                        }
                    }

                    switch (currentExtractOption)
                    {
                    case PsbExtractOption.Extract:
                        switch (extractFormat)
                        {
                        case PsbImageFormat.png:
                            relativePath += ".png";
                            break;

                        default:
                            relativePath += ".bmp";
                            break;
                        }

                        relativePath = CheckPath(relativePath, i);
                        if (resource.Compress == PsbCompressType.RL)
                        {
                            RL.DecompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                     resource.Height, resource.Width, extractFormat, resource.PixelFormat);
                        }
                        else if (resource.Compress == PsbCompressType.Tlg ||
                                 resource.Compress == PsbCompressType.ByName)
                        {
                            var bmp = context.ResourceToBitmap(resource.Compress == PsbCompressType.Tlg
                                    ? ".tlg"
                                    : Path.GetExtension(resource.Name), resource.Data);
                            if (bmp == null)
                            {
                                if (resource.Compress == PsbCompressType.Tlg)     //Fallback to managed TLG decoder
                                {
                                    using var ms = new MemoryStream(resource.Data);
                                    using var br = new BinaryReader(ms);
                                    bmp          = new TlgImageConverter().Read(br);
                                    bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat);
                                    bmp.Dispose();
                                }

                                relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else
                            {
                                bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat);
                                bmp.Dispose();
                            }
                        }
                        //else if (resource.Compress == PsbCompressType.ByName)
                        //{
                        //    relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                        //    File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        //}
                        else
                        {
                            RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                  resource.Height, resource.Width, extractFormat, resource.PixelFormat, resource.PalData,
                                                  resource.PalettePixelFormat);
                        }

                        break;

                    case PsbExtractOption.Original:
                        if (resources[i].Compress == PsbCompressType.RL)
                        {
                            relativePath += ".rl";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else if (resource.Compress == PsbCompressType.Tlg)
                        {
                            relativePath += ".tlg";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else
                        {
                            relativePath += ".raw";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }

                        break;

                    //case PsbExtractOption.Decompress:
                    //    relativePath += ".raw";
                    //    relativePath = CheckPath(relativePath, i);
                    //    File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                    //        resources[i].Compress == PsbCompressType.RL
                    //            ? RL.Decompress(resource.Data)
                    //            : resource.Data);
                    //    break;
                    //case PsbExtractOption.Compress:
                    //    relativePath += ".rl";
                    //    relativePath = CheckPath(relativePath, i);
                    //    File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                    //        resources[i].Compress != PsbCompressType.RL
                    //            ? RL.Compress(resource.Data)
                    //            : resource.Data);
                    //    break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(currentExtractOption), currentExtractOption, null);
                    }

                    //Determine save name
                    try
                    {
                        bool indexConflict   = false;
                        uint conflictedIndex = 0;
                        if (resource.Resource.Index != null)                          //try index as name first
                        {
                            if (resDictionary.ContainsKey(resource.Index.ToString())) //index is used before
                            {
                                Console.WriteLine(
                                    $"[WARN] Resource Index {resource.Index} conflict. May be resource sharing, but may also be something wrong.");
                                //continue;
                                indexConflict           = true;
                                conflictedIndex         = resource.Resource.Index.Value;
                                resource.Resource.Index = null; //have another try on friendly name
                            }
                        }
                        if (resource.Resource.Index == null)
                        {
                            if (resDictionary.ContainsKey(friendlyName)) // friendly name is also used (most likely its the same res reused), no name can be used to save
                            {
                                Console.WriteLine(
                                    $"[WARN] Resource Name {friendlyName} conflict. May be resource sharing, but may also be something wrong.");
                                continue; //just skip
                            }

                            if (indexConflict) //index is used but friendly name is not (maybe they are different?), save using friendly name
                            {
                                Console.WriteLine($"[FIXED] Resource {friendlyName} is sharing same data with Index {conflictedIndex}");
                            }
                        }

                        resDictionary.Add(resource.Resource.Index == null ? friendlyName : resource.Index.ToString(),
                                          $"{name}/{relativePath}");
                    }
                    catch (ArgumentException e)
                    {
                        throw new PsbBadFormatException(PsbBadFormatReason.Resources,
                                                        "Resource Export Error: Name conflict, or Index is not specified. Try Raw export mode.", e);
                    }
                }
            }

            string CheckPath(string rPath, int id)
            {
                var k = Path.GetFileNameWithoutExtension(rPath);

                if (resDictionary.ContainsKey(k))
                {
                    return($"{id}{Path.GetExtension(rPath)}");
                }

                return(rPath);
            }

            return(resDictionary);
        }
Exemple #9
0
        internal static void OutputResources(PSB psb, FreeMountContext context, string filePath, PsbImageOption imageOption = PsbImageOption.Original,
                                             PsbImageFormat extractFormat = PsbImageFormat.Png, bool useResx = true)
        {
            var name    = Path.GetFileNameWithoutExtension(filePath);
            var dirPath = Path.Combine(Path.GetDirectoryName(filePath), name);

            var             resources = psb.CollectResources();
            PsbResourceJson resx      = new PsbResourceJson(psb, context.Context);

            if (File.Exists(dirPath))
            {
                name    += "-resources";
                dirPath += "-resources";
            }

            if (!Directory.Exists(dirPath)) //ensure there is no file with same name!
            {
                if (psb.Resources.Count != 0 || resources.Count != 0)
                {
                    Directory.CreateDirectory(dirPath);
                }
            }

            Dictionary <string, string> resDictionary = new Dictionary <string, string>();

            if (imageOption == PsbImageOption.Original)
            {
                for (int i = 0; i < psb.Resources.Count; i++)
                {
                    var relativePath = psb.Resources[i].Index == null ? $"#{i}.bin" : $"{psb.Resources[i].Index}.bin";

                    File.WriteAllBytes(
                        Path.Combine(dirPath, relativePath),
                        psb.Resources[i].Data);
                    resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                }
            }
            else
            {
                for (int i = 0; i < resources.Count; i++)
                {
                    var resource = resources[i];
                    //Generate Friendly Name
                    string relativePath = resource.GetFriendlyName(psb.Type);

                    switch (imageOption)
                    {
                    case PsbImageOption.Extract:
                        ImageFormat pixelFormat;
                        switch (extractFormat)
                        {
                        case PsbImageFormat.Png:
                            relativePath += ".png";
                            pixelFormat   = ImageFormat.Png;
                            break;

                        default:
                            relativePath += ".bmp";
                            pixelFormat   = ImageFormat.Bmp;
                            break;
                        }

                        relativePath = CheckPath(relativePath, i);
                        if (resource.Compress == PsbCompressType.RL)
                        {
                            RL.DecompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                     resource.Height, resource.Width, extractFormat, resource.PixelFormat);
                        }
                        else if (resource.Compress == PsbCompressType.Tlg ||
                                 resource.Compress == PsbCompressType.ByName)
                        {
                            var bmp = context.ResourceToBitmap(resource.Compress == PsbCompressType.Tlg
                                    ? ".tlg"
                                    : Path.GetExtension(resource.Name), resource.Data);
                            if (bmp == null)
                            {
                                if (resource.Compress == PsbCompressType.Tlg)     //Fallback to managed TLG decoder
                                {
                                    using (var ms = new MemoryStream(resource.Data))
                                        using (var br = new BinaryReader(ms))
                                        {
                                            bmp = new TlgImageConverter().Read(br);
                                            bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat);
                                            bmp.Dispose();
                                        }
                                }

                                relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else
                            {
                                bmp.Save(Path.Combine(dirPath, relativePath), pixelFormat);
                                bmp.Dispose();
                            }
                        }
                        //else if (resource.Compress == PsbCompressType.ByName)
                        //{
                        //    relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                        //    File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        //}
                        else
                        {
                            RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                  resource.Height, resource.Width, extractFormat, resource.PixelFormat);
                        }

                        break;

                    case PsbImageOption.Original:
                        if (resources[i].Compress == PsbCompressType.RL)
                        {
                            relativePath += ".rl";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else if (resource.Compress == PsbCompressType.Tlg)
                        {
                            relativePath += ".tlg";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else
                        {
                            relativePath += ".raw";
                            relativePath  = CheckPath(relativePath, i);
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }

                        break;

                    case PsbImageOption.Decompress:
                        relativePath += ".raw";
                        relativePath  = CheckPath(relativePath, i);
                        File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                                           resources[i].Compress == PsbCompressType.RL
                                    ? RL.Decompress(resource.Data)
                                    : resource.Data);
                        break;

                    case PsbImageOption.Compress:
                        relativePath += ".rl";
                        relativePath  = CheckPath(relativePath, i);
                        File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                                           resources[i].Compress != PsbCompressType.RL
                                    ? RL.Compress(resource.Data)
                                    : resource.Data);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(imageOption), imageOption, null);
                    }

                    try
                    {
                        resDictionary.Add(i.ToString(), $"{name}/{relativePath}");
                    }
                    catch (ArgumentException e)
                    {
                        throw new PsbBadFormatException(PsbBadFormatReason.Resources,
                                                        "There are resources with same names! Try Raw export mode.", e);
                    }
                }
            }

            //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json`
            if (useResx)
            {
                resx.Resources = resDictionary;
                resx.Context   = context.Context;
                File.WriteAllText(Path.ChangeExtension(filePath, ".resx.json"),
                                  JsonConvert.SerializeObject(resx, Formatting.Indented));
            }
            else
            {
                File.WriteAllText(Path.ChangeExtension(filePath, ".res.json"),
                                  JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented));
            }

            string CheckPath(string rPath, int id)
            {
                var k = Path.GetFileNameWithoutExtension(rPath);

                if (resDictionary.ContainsKey(k))
                {
                    return($"{id}{Path.GetExtension(rPath)}");
                }

                return(rPath);
            }
        }
Exemple #10
0
        /// <summary>
        /// Decompile to files
        /// </summary>
        /// <param name="inputPath">PSB file path</param>
        /// <param name="imageOption">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>
        public static void DecompileToFile(string inputPath, PsbImageOption imageOption = PsbImageOption.Original, PsbImageFormat extractFormat = PsbImageFormat.Png, bool useResx = true)
        {
            var name    = Path.GetFileNameWithoutExtension(inputPath);
            var dirPath = Path.Combine(Path.GetDirectoryName(inputPath), name);

            File.WriteAllText(inputPath + ".json", Decompile(inputPath, out var psb));
            var             resources = psb.CollectResources();
            PsbResourceJson resx      = new PsbResourceJson
            {
                PsbVersion       = psb.Header.Version,
                PsbType          = psb.Type,
                Platform         = psb.Platform,
                ExternalTextures = psb.Type == PsbType.Motion && psb.Resources.Count <= 0
            };

            if (!Directory.Exists(dirPath)) //ensure no file with same name!
            {
                Directory.CreateDirectory(dirPath);
            }
            Dictionary <string, string> resDictionary = new Dictionary <string, string>();

            if (imageOption == PsbImageOption.Original)
            {
                for (int i = 0; i < psb.Resources.Count; i++)
                {
                    var relativePath = psb.Resources[i].Index == null ? $"#{i}.bin" : $"{psb.Resources[i].Index}.bin";

                    File.WriteAllBytes(
                        Path.Combine(dirPath, relativePath),
                        psb.Resources[i].Data);
                    resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                }
            }
            else
            {
                for (int i = 0; i < resources.Count; i++)
                {
                    var resource = resources[i];
                    //Generate Friendly Name
                    string relativePath;
                    if (psb.Type == PsbType.Pimg && !string.IsNullOrWhiteSpace(resource.Name))
                    {
                        relativePath = Path.GetFileNameWithoutExtension(resource.Name);
                    }
                    else if (string.IsNullOrWhiteSpace(resource.Name) || string.IsNullOrWhiteSpace(resource.Part))
                    {
                        relativePath = resource.Index.ToString();
                    }
                    else
                    {
                        relativePath = $"{resource.Part}{PsbResCollector.ResourceNameDelimiter}{resource.Name}";
                    }

                    switch (imageOption)
                    {
                    case PsbImageOption.Extract:
                        //var pixelFormat = resource.Spec.DefaultPixelFormat(); //MARK: PixelFormat should refer `type`
                        switch (extractFormat)
                        {
                        case PsbImageFormat.Png:
                            relativePath += ".png";
                            if (resource.Compress == PsbCompressType.RL)
                            {
                                RL.UncompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                         resource.Height, resource.Width, PsbImageFormat.Png, resource.PixelFormat);
                            }
                            else if (resource.Compress == PsbCompressType.Tlg ||
                                     resource.Compress == PsbCompressType.ByName && resource.Name.EndsWith(".tlg", true, null))
                            {
                                TlgImageConverter converter = new TlgImageConverter();
                                using (var ms = new MemoryStream(resource.Data))
                                {
                                    BinaryReader br = new BinaryReader(ms);
                                    converter.Read(br).Save(Path.Combine(dirPath, relativePath), ImageFormat.Png);
                                }
                                //WARN: tlg is kept and recorded in resource json for compile
                                relativePath = Path.ChangeExtension(relativePath, ".tlg");
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else if (resource.Compress == PsbCompressType.ByName)
                            {
                                relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else
                            {
                                RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                      resource.Height, resource.Width, extractFormat, resource.PixelFormat);
                            }
                            break;

                        default:
                            relativePath += ".bmp";
                            if (resource.Compress == PsbCompressType.RL)
                            {
                                RL.UncompressToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                         resource.Height, resource.Width, PsbImageFormat.Bmp, resource.PixelFormat);
                            }
                            else if (resource.Compress == PsbCompressType.Tlg ||
                                     resource.Compress == PsbCompressType.ByName && resource.Name.EndsWith(".tlg", true, null))
                            {
                                TlgImageConverter converter = new TlgImageConverter();
                                using (var ms = new MemoryStream(resource.Data))
                                {
                                    BinaryReader br = new BinaryReader(ms);
                                    converter.Read(br).Save(Path.Combine(dirPath, relativePath), ImageFormat.Bmp);
                                }
                                //WARN: tlg is kept and recorded in resource json for compile
                                relativePath = Path.ChangeExtension(relativePath, ".tlg");
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else if (resource.Compress == PsbCompressType.ByName)
                            {
                                relativePath = Path.ChangeExtension(relativePath, Path.GetExtension(resource.Name));
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                            }
                            else
                            {
                                RL.ConvertToImageFile(resource.Data, Path.Combine(dirPath, relativePath),
                                                      resource.Height, resource.Width, extractFormat, resource.PixelFormat);
                            }
                            break;
                        }
                        break;

                    case PsbImageOption.Original:
                        if (resources[i].Compress == PsbCompressType.RL)
                        {
                            relativePath += ".rl";
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else if (resource.Compress == PsbCompressType.Tlg)
                        {
                            relativePath += ".tlg";
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        else
                        {
                            relativePath += ".raw";
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), resource.Data);
                        }
                        break;

                    case PsbImageOption.Uncompress:
                        File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                                           resources[i].Compress == PsbCompressType.RL
                                    ? RL.Uncompress(resource.Data)
                                    : resource.Data);
                        relativePath += ".raw";
                        break;

                    case PsbImageOption.Compress:
                        File.WriteAllBytes(Path.Combine(dirPath, relativePath),
                                           resources[i].Compress != PsbCompressType.RL
                                    ? RL.Compress(resource.Data)
                                    : resource.Data);
                        relativePath += ".rl";
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(imageOption), imageOption, null);
                    }

                    try
                    {
                        resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                    }
                    catch (ArgumentException e)
                    {
                        throw new BadImageFormatException("There are resources with same names! Try Raw export mode.", e);
                    }
                }
            }

            //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json`
            if (useResx)
            {
                resx.Resources = resDictionary;
                File.WriteAllText(inputPath + ".resx.json", JsonConvert.SerializeObject(resx, Formatting.Indented));
            }
            else
            {
                File.WriteAllText(inputPath + ".res.json", JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented));
            }
        }