Пример #1
0
        /// <summary>
        /// Link
        /// </summary>
        /// <param name="psb"></param>
        /// <param name="resx">advanced resource json(resx.jon)</param>
        /// <param name="baseDir"></param>
        internal static void Link(this PSB psb, PsbResourceJson resx, string baseDir)
        {
            var resList = psb.CollectResources();

            foreach (var resxResource in resx.Resources)
            {
                //Scan for Resource
                var resMd = resList.FirstOrDefault(r =>
                                                   resxResource.Key == $"{r.Part}{PsbResCollector.ResourceNameDelimiter}{r.Name}");
                if (resMd == null && psb.Type == PsbType.Pimg)
                {
                    resMd = resList.FirstOrDefault(r => resxResource.Key == Path.GetFileNameWithoutExtension(r.Name));
                }
                if (resMd == null && uint.TryParse(resxResource.Key, out uint rid))
                {
                    resMd = resList.FirstOrDefault(r => r.Index == rid);
                }

                if (resMd == null)
                {
                    Console.WriteLine($"[WARN]{resxResource.Key} is not used.");
                    continue;
                }

                var fullPath = Path.IsPathRooted(resxResource.Value)
                    ? resxResource.Value
                    : Path.Combine(baseDir ?? "", resxResource.Value.Replace('/', '\\'));
                byte[] data = LoadImageBytes(fullPath, resMd);
                resMd.Resource.Data = data;
            }
        }
Пример #2
0
        public void Link(PSB psb, FreeMountContext context, IDictionary <string, string> resPaths, string baseDir = null)
        {
            var rawResList = psb.CollectResources <AudioMetadata>();

            foreach (var resPath in resPaths)
            {
                var fullPath = Path.Combine(baseDir ?? "", resPath.Value);
                var resMd    = rawResList.FirstOrDefault(r => r.Name == resPath.Key);
                if (resMd == null)
                {
                    if (uint.TryParse(resPath.Key, out var idx))
                    {
                        var resource = psb.Resources.FirstOrDefault(r => r.Index == idx);
                        if (resource != null)
                        {
                            resource.Data = File.ReadAllBytes(fullPath);
                        }
                        else
                        {
                            Console.WriteLine($"[WARN] {resPath.Key} is not used.");
                        }
                    }
                }
                else
                {
                    resMd.Link(fullPath, context);
                }
            }
        }
Пример #3
0
        public void Link(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null,
                         PsbLinkOrderBy order = PsbLinkOrderBy.Convention)
        {
            var rawResList = psb.CollectResources <AudioMetadata>();

            if (order == PsbLinkOrderBy.Order)
            {
                for (int i = 0; i < rawResList.Count; i++)
                {
                    var resMd    = rawResList[i];
                    var fullPath = Path.Combine(baseDir ?? "", resPaths[i]);
                    resMd.Link(fullPath, context);
                }

                return;
            }

            foreach (var resPath in resPaths)
            {
                var resMd = rawResList.FirstOrDefault(r => r.Name == Path.GetFileNameWithoutExtension(resPath));
                if (resMd == null)
                {
                    Console.WriteLine($"[WARN] {resPath} is not used.");
                    continue;
                }

                var fullPath = Path.Combine(baseDir ?? "", resPath);
                resMd.Link(fullPath, context);
            }
        }
Пример #4
0
        public Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath,
                                                           PsbExtractOption extractOption = PsbExtractOption.Original)
        {
            var resources = psb.CollectResources <AudioMetadata>();
            Dictionary <string, string> resDictionary = new Dictionary <string, string>();

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

                    File.WriteAllBytes(
                        Path.Combine(dirPath, relativePath),
                        psb.Resources[i].Data);
                    resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                }
            }
            else
            {
                foreach (var resource in resources)
                {
                    if (resource.ChannelList.Count == 1)
                    {
                        var bts          = resource.ChannelList[0].TryToWave(context);
                        var relativePath = resource.GetFileName(resource.ChannelList[0].WaveExtension); //WaveExtension may change after ToWave
                        if (bts != null)
                        {
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts);
                            resDictionary.Add(resource.Name, $"{name}/{relativePath}");
                        }
                    }
                    else if (resource.ChannelList.Count > 1)
                    {
                        for (var j = 0; j < resource.ChannelList.Count; j++)
                        {
                            var waveChannel  = resource.ChannelList[j];
                            var bts          = waveChannel.TryToWave(context);
                            var relativePath = resource.GetFileName($"-{j}{waveChannel.WaveExtension}");
                            if (bts != null)
                            {
                                File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts);
                                resDictionary.Add(resource.Name, $"{name}/{relativePath}");
                            }
                        }
                    }
                }
            }

            return(resDictionary);
        }
Пример #5
0
        public void Convert(PSB psb)
        {
            if (!FromSpec.Contains(psb.Platform))
            {
                throw new FormatException("Can not convert Spec for this PSB");
            }

            var asSpec        = EmsAsCommon ? PsbSpec.ems : PsbSpec.common;
            var toSpec        = psb.Platform == PsbSpec.win ? asSpec : PsbSpec.win;
            var toPixelFormat = toSpec == asSpec ? PsbPixelFormat.CommonRGBA8 : PsbPixelFormat.WinRGBA8;
            var resList       = psb.CollectResources <ImageMetadata>(false);

            foreach (var resMd in resList)
            {
                var resourceData = resMd.Resource.Data;
                if (resourceData == null)
                {
                    continue;
                }
                if (resMd.Compress == PsbCompressType.RL)
                {
                    resourceData = RL.Decompress(resourceData);
                }
                if (resMd.PixelFormat == PsbPixelFormat.DXT5)
                {
                    resourceData = RL.GetPixelBytesFromImage(
                        DxtUtil.Dxt5Decode(resourceData, resMd.Width, resMd.Height), toPixelFormat);
                    resMd.TypeString.Value = toPixelFormat.ToStringForPsb();
                }
                else
                {
                    RL.Switch_0_2(ref resourceData);
                    if (UseRL)
                    {
                        resourceData = RL.Compress(resourceData);
                    }
                }
                resMd.Resource.Data = resourceData;
            }
            psb.Platform = toSpec;
        }
Пример #6
0
        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));
        }
Пример #7
0
        // ReSharper disable once InvalidXmlDocComment
        /// <summary>
        /// Inlined PSB -> External Texture PSB. Inverse of <seealso cref="PsbCompiler.Link"/>
        /// </summary>
        /// <param name="psb"></param>
        /// <param name="order">To make a regular external texture PSB you should set it to <see cref="PsbLinkOrderBy.Name"/>.</param>
        /// <param name="disposeResInPsb">Whether to remove resources in PSB. To make a real external texture PSB you should set it to true.</param>
        /// <returns>Ordered textures</returns>
        public static List <Bitmap> Unlink(this PSB psb, PsbLinkOrderBy order = PsbLinkOrderBy.Name, bool disposeResInPsb = true)
        {
            var           resources = psb.CollectResources();
            List <Bitmap> texs      = new List <Bitmap>();

            for (int i = 0; i < resources.Count; i++)
            {
                var resource = resources[i];
                var tex      = RL.ConvertToImage(resource.Data, resource.Height, resource.Width, resource.PixelFormat);

                switch (order)
                {
                case PsbLinkOrderBy.Convention:
                    tex.Tag = resource.GetFriendlyName(psb.Type);
                    break;

                default:
                    var tId = resource.TextureIndex;
                    if (tId == null)
                    {
                        throw new FormatException(
                                  "Unable to unlink with texture names since they can't be recognized.");
                    }

                    tex.Tag = $"tex{tId.Value:D3}";
                    break;
                }

                texs.Add(tex);

                //Finally, dispose
                if (disposeResInPsb)
                {
                    resource.Data = null;
                }
            }

            return(texs);
        }
Пример #8
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);
            }
        }
Пример #9
0
        /// <summary>
        /// Link
        /// </summary>
        /// <param name="psb"></param>
        /// <param name="resPaths">resource paths</param>
        /// <param name="baseDir"></param>
        /// <param name="order">how to arrange images</param>
        /// <param name="isExternal">Whether this is an external texture PSB</param>
        public static void Link(this PSB psb, IList <string> resPaths, string baseDir = null,
                                PsbLinkOrderBy order = PsbLinkOrderBy.Convention, bool isExternal = false)
        {
            if (isExternal)
            {
                psb.MotionResourceInstrument();
            }
            var resList = psb.CollectResources();
            var context = FreeMount.CreateContext();

            if (order == PsbLinkOrderBy.Order)
            {
                for (int i = 0; i < resList.Count; i++)
                {
                    var    resMd    = resList[i];
                    var    fullPath = Path.Combine(baseDir ?? "", resPaths[i]);
                    byte[] data     = LoadImageBytes(fullPath, resMd, context);
                    resMd.Resource.Data = data;
                }
                return;
            }

            if (order == PsbLinkOrderBy.Name)
            {
                if (psb.Platform == PsbSpec.krkr)
                {
                    throw new InvalidOperationException(
                              $"Can not link by file name for krkr PSB. Please consider using {PsbLinkOrderBy.Convention}");
                }

                resList.Sort((md1, md2) => (int)(md1.TextureIndex ?? 0) - (int)(md2.TextureIndex ?? 0));
            }

            for (var i = 0; i < resPaths.Count; i++)
            {
                var resPath = resPaths[i];
                var resName = Path.GetFileNameWithoutExtension(resPath);
                //var resMd = uint.TryParse(resName, out uint rid)
                //    ? resList.FirstOrDefault(r => r.Index == rid)
                //    : resList.FirstOrDefault(r =>
                //        resName == $"{r.Part}{PsbResCollector.ResourceNameDelimiter}{r.Name}");

                //Scan for Resource
                ResourceMetadata resMd = null;
                if (order == PsbLinkOrderBy.Name)
                {
                    if (resName == null)
                    {
                        continue;
                    }

                    if (resList.Count == 1 && resPaths.Count == 1)
                    {
                        //If there is only one resource and one texture, we won't care about file name.
                        resMd = resList[0];
                    }
                    else
                    {
                        var texIdx = ResourceMetadata.GetTextureIndex(resName);

                        if (texIdx == null)
                        {
                            Console.WriteLine($"[WARN]{resPath} is not used since the file name cannot be recognized.");
                            continue;
                        }

                        if (resList.Count <= texIdx.Value)
                        {
                            Console.WriteLine($"[WARN]{resPath} is not used since the tex No. is too large.");
                            continue;
                        }

                        resMd = resList[(int)texIdx.Value];
                    }
                }
                else //if (order == PsbLinkOrderBy.Convention)
                {
                    resMd = resList.FirstOrDefault(r =>
                                                   resName == $"{r.Part}{PsbResCollector.ResourceNameDelimiter}{r.Name}");
                    if (resMd == null && uint.TryParse(resName, out uint rid))
                    {
                        resMd = resList.FirstOrDefault(r => r.Index == rid);
                    }

                    if (resMd == null && psb.Type == PsbType.Pimg)
                    {
                        resMd = resList.FirstOrDefault(r => resName == Path.GetFileNameWithoutExtension(r.Name));
                    }
                }


                if (resMd == null)
                {
                    Console.WriteLine($"[WARN]{resPath} is not used.");
                    continue;
                }

                var    fullPath = Path.Combine(baseDir ?? "", resPath.Replace('/', '\\'));
                byte[] data     = LoadImageBytes(fullPath, resMd, context);
                resMd.Resource.Data = data;
            }
        }
Пример #10
0
        public Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath,
                                                           PsbExtractOption extractOption = PsbExtractOption.Original)
        {
            var resources = psb.CollectResources <AudioMetadata>();
            Dictionary <string, string> resDictionary = new Dictionary <string, string>();

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

                    File.WriteAllBytes(
                        Path.Combine(dirPath, relativePath),
                        psb.Resources[i].Data);
                    resDictionary.Add(Path.GetFileNameWithoutExtension(relativePath), $"{name}/{relativePath}");
                }
            }
            else
            {
                foreach (var resource in resources)
                {
                    if (resource.ChannelList.Count == 1)
                    {
                        var bts          = resource.ChannelList[0].TryToWave(context);
                        var relativePath = resource.GetFileName(resource.ChannelList[0].Extension + resource.ChannelList[0].WaveExtension); //WaveExtension may change after ToWave
                        if (bts != null)
                        {
                            File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts);
                            resDictionary.Add(resource.Name, $"{name}/{relativePath}");
                        }
                    }
                    else if (resource.ChannelList.Count > 1)
                    {
                        if (resource.Pan == PsbAudioPan.LeftRight) //load audio.vag.l.wav & audio.vag.r.wav
                        {
                            var left          = resource.GetLeftChannel();
                            var relativePathL = resource.GetFileName($"{left.Extension}.l{left.WaveExtension}");
                            var btsL          = left.TryToWave(context);
                            if (btsL != null)
                            {
                                File.WriteAllBytes(Path.Combine(dirPath, relativePathL), btsL);
                            }
                            else
                            {
                                relativePathL = resource.GetFileName($"{left.Extension}.l{left.Extension}");
                                File.WriteAllBytes(Path.Combine(dirPath, relativePathL), left.Data.Data);
                                resDictionary.Add(resource.Name, $"{name}/{relativePathL}");
                            }

                            var right         = resource.GetRightChannel();
                            var relativePathR = resource.GetFileName($"{right.Extension}.r{right.WaveExtension}");
                            var btsR          = right.TryToWave(context);
                            if (btsR != null)
                            {
                                File.WriteAllBytes(Path.Combine(dirPath, relativePathR), btsR);
                            }
                            else
                            {
                                relativePathR = resource.GetFileName($"{right.Extension}.r{right.Extension}");
                                File.WriteAllBytes(Path.Combine(dirPath, relativePathR), right.Data.Data);
                                resDictionary.Add(resource.Name, $"{name}/{relativePathR}");
                            }

                            if (btsL != null && btsR != null)
                            {
                                var relativePath = resource.GetFileName($"{left.Extension}{left.WaveExtension}");
                                resDictionary.Add(resource.Name, $"{name}/{relativePath}"); //a virtual file path
                            }
                        }
                        else //not LeftRight
                        {
                            for (var j = 0; j < resource.ChannelList.Count; j++) //load audio.vag.1.wav etc.
                            {
                                var waveChannel = resource.ChannelList[j];
                                if (waveChannel.Data.Index == null)
                                {
                                    Console.WriteLine($"[WARN] Channel {j} is not linked with a Resource.");
                                    continue;
                                }
                                var bts          = waveChannel.TryToWave(context);
                                var noStr        = waveChannel.Data.Index == null ? $".@{j}" : $".#{waveChannel.Data.Index.Value}"; //TODO: handle @ - channel internal No.
                                var relativePath = resource.GetFileName($"{waveChannel.Extension}{noStr}{waveChannel.WaveExtension}");
                                if (bts != null)
                                {
                                    File.WriteAllBytes(Path.Combine(dirPath, relativePath), bts);
                                    resDictionary.Add(resource.Name, $"{name}/{relativePath}");
                                }
                            }
                        }
                    }
                }
            }

            return(resDictionary);
        }