Example #1
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);
            }
        }
Example #2
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);
        }
Example #3
0
        internal static byte[] LoadImageBytes(string path, ResourceMetadata metadata, FreeMountContext context)
        {
            byte[] data;
            Bitmap image = null;
            var    ext   = Path.GetExtension(path)?.ToLowerInvariant();

            if (metadata.Compress == PsbCompressType.ByName && ext != null && metadata.Name != null &&
                metadata.Name.EndsWith(ext, true, null))
            {
                return(File.ReadAllBytes(path));
            }

            switch (ext)
            {
            //tlg
            case ".tlg" when metadata.Compress == PsbCompressType.Tlg:
                return(File.ReadAllBytes(path));

            case ".tlg":
                image = context.ResourceToBitmap(".tlg", File.ReadAllBytes(path));
                break;

            //rl
            case ".rl" when metadata.Compress == PsbCompressType.RL:
                return(File.ReadAllBytes(path));

            case ".rl" when metadata.Compress == PsbCompressType.None:
                return(RL.Decompress(File.ReadAllBytes(path)));

            case ".rl":
                image = RL.DecompressToImage(File.ReadAllBytes(path), metadata.Height, metadata.Width,
                                             metadata.PixelFormat);
                break;

            //raw
            case ".raw" when metadata.Compress == PsbCompressType.None:
                return(File.ReadAllBytes(path));

            case ".raw" when metadata.Compress == PsbCompressType.RL:
                return(RL.Compress(File.ReadAllBytes(path)));

            case ".raw":
                image = RL.ConvertToImage(File.ReadAllBytes(path), metadata.Height, metadata.Width,
                                          metadata.PixelFormat);
                break;

            //bin
            case ".bin":
                return(File.ReadAllBytes(path));

            //image
            default:
                if (SupportedImageExt.Contains(ext))
                {
                    image = new Bitmap(path);
                }
                else if (context.SupportImageExt(ext))
                {
                    image = context.ResourceToBitmap(ext, File.ReadAllBytes(path));
                }
                else
                {
                    //MARK: No longer try to read files we don't know
                    //return File.ReadAllBytes(path);
                    return(null);
                }

                break;
            }

            switch (metadata.Compress)
            {
            case PsbCompressType.RL:
                data = RL.CompressImage(image, metadata.PixelFormat);
                break;

            case PsbCompressType.Tlg:
                data = context.BitmapToResource(".tlg", image);
                if (data == null)
                {
                    var tlgPath = Path.ChangeExtension(path, ".tlg");
                    if (File.Exists(tlgPath))
                    {
                        Console.WriteLine($"[WARN] Can not encode TLG, using {tlgPath}");
                        data = File.ReadAllBytes(tlgPath);
                    }
                    else
                    {
                        Console.WriteLine($"[WARN] Can not convert image to TLG: {path}");
                        data = File.ReadAllBytes(path);
                    }
                }

                break;

            case PsbCompressType.ByName:
                var imgExt = Path.GetExtension(metadata.Name);
                if (context.SupportImageExt(imgExt))
                {
                    data = context.BitmapToResource(imgExt, image);
                }
                else
                {
                    Console.WriteLine($"[WARN] Unsupported image: {path}");
                    data = File.ReadAllBytes(path);
                }

                break;

            case PsbCompressType.None:
            default:
                data = RL.GetPixelBytesFromImage(image, metadata.PixelFormat);
                break;
            }

            return(data);
        }