private static bool ApplyDefaultMotionMetadata(PSB psb, PsbDictionary metadata) { List <(string Chara, string Motion)> knownMotions = new List <(string Chara, string Motion)> { ("body_parts", "全身変形基礎"), ("body_parts", "下半身変形基礎"), ("head_parts", "頭部変形基礎"), ("head_parts", "頭部全体変形"), ("all_parts", "タイムライン構造"), ("all_parts", "全体構造") }; //Find a known pattern foreach (var knownMotion in knownMotions) { if (psb.Objects.FindByPath($"/object/{knownMotion.Chara}") is PsbDictionary knownDic && knownDic["motion"].Children(knownMotion.Motion) is PsbDictionary motionDic && motionDic.Count > 0) { metadata["chara"] = knownMotion.Chara.ToPsbString(); metadata["motion"] = knownMotion.Motion.ToPsbString(); return(true); } } return(false); }
public bool TryGetArchData(PSB psb, PsbDictionary dic, out IArchData data) { data = null; //if (psb.Platform != PsbSpec.nx) //{ // return false; //} if (dic.Count != 1 || !(dic["archData"] is PsbDictionary archDic)) { return(false); } if (archDic["body"] is PsbDictionary body && body["data"] is PsbResource aData && body["sampleCount"] is PsbNumber sampleCount && archDic["ext"] is PsbString ext && ext.Value == Extensions[0] && archDic["samprate"] is PsbNumber sampRate) { data = new OpusArchData { Data = aData, SampRate = sampRate.IntValue, SampleCount = sampleCount.IntValue }; return(true); } return(false); }
/// <summary> /// Get name /// </summary> /// <param name="c"></param> /// <param name="parent"></param> /// <returns></returns> public static string GetName(this IPsbSingleton c, PsbDictionary parent = null) { var source = parent ?? c?.Parents.FirstOrDefault(p => p is PsbDictionary) as PsbDictionary; var result = source?.FirstOrDefault(pair => Equals(pair.Value, c)); return(result?.Value == null ? null : result.Value.Key); }
public static IPsbValue FindByPath(this PsbDictionary psbObj, string path) { if (psbObj == null) { return(null); } if (path.StartsWith("/")) { path = new string(path.SkipWhile(c => c == '/').ToArray()); } if (path.Contains("/")) { var pos = path.IndexOf('/'); var current = path.Substring(0, pos); if (pos == path.Length - 1) { return(psbObj[current]); } var currentObj = psbObj[current]; if (currentObj is PsbDictionary collection) { path = path.Substring(pos); return(collection.FindByPath(path)); } } return(psbObj[path]); }
internal AudioMetadata GenerateAudioMetadata(PSB psb, string name, PsbDictionary voice, FreeMountContext context) { var md = new AudioMetadata(); md.Name = name; if (voice["file"] is PsbString fileStr) { md.FileString = fileStr; } if (voice["loopStr"] is PsbString loopStr) { md.LoopStr = loopStr; } if (voice["loop"] is PsbNumber loopNum) { md.Loop = loopNum.IntValue; } if (voice["quality"] is PsbNumber qualityNum) { md.Quality = qualityNum.IntValue; } if (voice["type"] is PsbNumber typeNum) { md.Type = typeNum.IntValue; } if (voice["device"] is PsbNumber deviceNum) { md.Device = deviceNum.IntValue; } if (voice["channelList"] is PsbList channelList) { foreach (var channel in channelList) { if (channel is PsbDictionary channelDic) { if (context.TryGetArchData(psb, channelDic, out var archData)) { archData.PsbArchData = channelDic; md.ChannelList.Add(archData); } } } } return(md); }
public static Dictionary <string, Bitmap> SplitTexture(PsbDictionary tex, PsbSpec spec) { var icon = (PsbDictionary)tex["icon"]; var texture = (PsbDictionary)tex["texture"]; //var mipmap = (PsbDictionary)texture["mipMap"]; //TODO: Mipmap? Dictionary <string, Bitmap> textures = new Dictionary <string, Bitmap>(icon.Count); var pixel = texture[Consts.ResourceKey]; if (pixel == null || !(pixel is PsbResource pixelRes)) { throw new PsbBadFormatException(PsbBadFormatReason.Resources, "External Texture PSB is not supported. Please Link textures into PSB."); return(textures); } var md = PsbResHelper.GenerateImageMetadata(texture, pixelRes); md.Spec = spec; //Important Bitmap bmp = md.ToImage(); foreach (var iconPair in icon) { var info = (PsbDictionary)iconPair.Value; var width = (int)(PsbNumber)info["width"]; var height = (int)(PsbNumber)info["height"]; var top = (int)(PsbNumber)info["top"]; var left = (int)(PsbNumber)info["left"]; Bitmap b = new Bitmap(width, height, PixelFormat.Format32bppArgb); #if USE_FASTBITMAP using (FastBitmap f = b.FastLock()) { f.CopyRegion(bmp, new Rectangle(left, top, width, height), new Rectangle(0, 0, b.Width, b.Height)); } #else Graphics g = Graphics.FromImage(b); //g.InterpolationMode = InterpolationMode.NearestNeighbor; //g.PixelOffsetMode = PixelOffsetMode.Half; g.DrawImage(bmp, new Rectangle(0, 0, b.Width, b.Height), new Rectangle(left, top, width, height), GraphicsUnit.Pixel); g.Dispose(); #endif textures.Add(iconPair.Key, b); } bmp.Dispose(); return(textures); }
public bool TryGetArchData(PSB psb, PsbDictionary dic, out IArchData data) { data = null; if (psb.Platform == PsbSpec.ps4 || psb.Platform == PsbSpec.vita) { if (dic.Count == 1 && dic["archData"] is PsbResource res) { data = new Atrac9ArchData { Data = res }; return(true); } return(false); } return(false); }
private static ResourceMetadata GenerateTachieResMetadata(PsbDictionary d, PsbResource r, string label = "") { int width = 1, height = 1; int top = 0, left = 0; var dd = d.Parent as PsbDictionary ?? d; if ((d["width"] ?? d["truncated_width"] ?? dd["width"]) is PsbNumber nw) { width = (int)nw; } if ((d["height"] ?? d["truncated_height"] ?? dd["height"]) is PsbNumber nh) { height = (int)nh; } if ((dd["top"] ?? d["top"]) is PsbNumber nx) { top = nx.AsInt; } if ((dd["left"] ?? d["left"]) is PsbNumber ny) { left = ny.AsInt; } var md = new ResourceMetadata() { Top = top, Left = left, TypeString = d["type"] as PsbString, Width = width, Height = height, Name = r.Index.ToString(), Part = label, Resource = r, }; return(md); }
public void TestGraft2() { var resPath = Path.Combine(Environment.CurrentDirectory, @"..\..\Res"); var pathGood = Path.Combine(resPath, "goodstr.freemote.psb"); var pathBad = Path.Combine(resPath, "goodStr.psb"); var psbGood = new PSB(pathGood); var psbBad = new PSB(pathBad); dynamic texGood = (PsbDictionary)psbGood.Objects["source"].Children("tex"); dynamic texBad = (PsbDictionary)psbBad.Objects["source"].Children("tex#000"); var badIcon = texBad["icon"]; PsbDictionary newIcon = new PsbDictionary(); foreach (var part in texGood["icon"]) { var content = part.Value; var bi = ((PsbDictionary)badIcon).FirstOrDefault(i => ((PsbNumber)i.Value.Children("width")).AsInt == content["width"].AsInt && ((PsbNumber)i.Value.Children("height")).AsInt == content["height"].AsInt && ((PsbNumber)i.Value.Children("originX")).AsFloat == content["originX"].AsFloat && ((PsbNumber)i.Value.Children("originY")).AsFloat == content["originY"].AsFloat); if (bi.Key != null) { newIcon[bi.Key] = content; } } texGood["icon"] = newIcon; dynamic badSource = psbBad.Objects["source"]; badSource["tex#000"] = texGood; psbBad.Merge(); psbBad.BuildToFile("graft.psb"); }
public static Dictionary <string, Bitmap> SplitTexture(PsbDictionary tex, PsbSpec spec) { var icon = (PsbDictionary)tex["icon"]; var texture = (PsbDictionary)tex["texture"]; //var mipmap = (PsbDictionary)texture["mipMap"]; //TODO: Mipmap? Dictionary <string, Bitmap> textures = new Dictionary <string, Bitmap>(icon.Count); var md = PsbResCollector.GenerateMotionResMetadata(texture, (PsbResource)texture["pixel"]); md.Spec = spec; //Important Bitmap bmp = md.ToImage(); foreach (var iconPair in icon) { var info = (PsbDictionary)iconPair.Value; var width = (int)(PsbNumber)info["width"]; var height = (int)(PsbNumber)info["height"]; var top = (int)(PsbNumber)info["top"]; var left = (int)(PsbNumber)info["left"]; Bitmap b = new Bitmap(width, height, PixelFormat.Format32bppArgb); #if USE_FASTBITMAP using (FastBitmap f = b.FastLock()) { f.CopyRegion(bmp, new Rectangle(left, top, width, height), new Rectangle(0, 0, b.Width, b.Height)); } #else Graphics g = Graphics.FromImage(b); //g.InterpolationMode = InterpolationMode.NearestNeighbor; //g.PixelOffsetMode = PixelOffsetMode.Half; g.DrawImage(bmp, new Rectangle(0, 0, b.Width, b.Height), new Rectangle(left, top, width, height), GraphicsUnit.Pixel); g.Dispose(); #endif textures.Add(iconPair.Key, b); } bmp.Dispose(); return(textures); }
public bool TryGetArchData(PSB psb, PsbDictionary dic, out IArchData data) { data = null; if (psb.Platform == PsbSpec.win) { if (dic.Count == 1 && dic["archData"] is PsbDictionary archDic && archDic["data"] is PsbResource aData && archDic["dpds"] is PsbResource aDpds && archDic["fmt"] is PsbResource aFmt && archDic["wav"] is PsbString aWav) { data = new XwmaArchData() { Data = aData, Fmt = aFmt, Dpds = aDpds, Wav = aWav.Value }; return(true); } return(false); } return(false); }
private void Add(PSB psb) { //add `easing` if (!psb.Objects.ContainsKey("easing")) { psb.Objects.Add("easing", new PsbCollection(0)); } //add `/object/*/motion/*/bounds` //add `/object/*/motion/*/layerIndexMap` var obj = (PsbDictionary)psb.Objects["object"]; foreach (var o in obj) { //var name = o.Key; foreach (var m in (PsbDictionary)((PsbDictionary)o.Value)["motion"]) { if (m.Value is PsbDictionary mDic) { if (!mDic.ContainsKey("bounds")) { var bounds = new PsbDictionary(4) { { "top", PsbNumber.Zero }, { "left", PsbNumber.Zero }, { "right", PsbNumber.Zero }, { "bottom", PsbNumber.Zero } }; mDic.Add("bounds", bounds); } if (!(mDic["layer"] is PsbCollection col)) { continue; } if (!mDic.ContainsKey("layerIndexMap")) { var layerIndexList = new List <string>(); LayerTravel(col, layerIndexList); var layerIndexMap = new PsbDictionary(layerIndexList.Count); int index = 0; foreach (var layerName in layerIndexList) { if (layerIndexMap.ContainsKey(layerName)) { continue; } layerIndexMap.Add(layerName, new PsbNumber(index)); index++; } mDic.Add("layerIndexMap", layerIndexMap); } } } } void LayerTravel(PsbCollection collection, List <string> indexList) { foreach (var col in collection) { if (col is PsbDictionary dic && dic.ContainsKey("children")) { if (dic["label"] is PsbString str) { indexList.Add(str.Value); } if (dic["children"] is PsbCollection childrenCollection) { LayerTravel(childrenCollection, indexList); } } } } }
private Dictionary <string, (string Tex, string IconName)> TranslateResources(PSB psb) { Dictionary <string, (string Tex, string IconName)> iconInfos = new Dictionary <string, (string, string)>(); Dictionary <string, Image> textures = new Dictionary <string, Image>(); var source = (PsbDictionary)psb.Objects["source"]; int maxSideLength = 2048; long area = 0; //Collect textures foreach (var tex in source) { var texName = tex.Key; var icons = (PsbDictionary)((PsbDictionary)tex.Value)["icon"]; foreach (var icon in icons) { var iconName = icon.Key; var info = (PsbDictionary)icon.Value; var width = (int)(PsbNumber)info["width"]; var height = (int)(PsbNumber)info["height"]; var res = (PsbResource)info["pixel"]; var bmp = info["compress"]?.ToString().ToUpperInvariant() == "RL" ? RL.DecompressToImage(res.Data, height, width, psb.Platform.DefaultPixelFormat()) : RL.ConvertToImage(res.Data, height, width, psb.Platform.DefaultPixelFormat()); bmp.Tag = iconName; textures.Add($"{texName}{Delimiter}{icon.Key}", bmp); //estimate area and side length area += width * height; if (width >= maxSideLength || height >= maxSideLength) { maxSideLength = 4096; } } } //Pack textures int size = 2048; if (maxSideLength > size || (area > 2048 * 2048 && psb.Header.Version > 2)) { size = 4096; } int padding = TexturePadding >= 0 && TexturePadding <= 100 ? TexturePadding : 1; TexturePacker packer = new TexturePacker { FitHeuristic = FitHeuristic }; packer.Process(textures, TextureSideLength ?? size, padding); //Add packed textures to source List <PsbDictionary> texs = new List <PsbDictionary>(); for (var i = 0; i < packer.Atlasses.Count; i++) { var atlas = packer.Atlasses[i]; var data = UseRL ? RL.CompressImage((Bitmap)atlas.ToImage(), TargetPixelFormat) : RL.GetPixelBytesFromImage(atlas.ToImage(), TargetPixelFormat); var texDic = new PsbDictionary(4); //metadata texDic.Add("metadata", new PsbString(i.ToString("D3"))); var texName = $"tex#{texDic["metadata"]}"; //icon var icons = new PsbDictionary(atlas.Nodes.Count); texDic.Add("icon", icons); int id = 0; foreach (var node in atlas.Nodes) { if (node.Texture == null) { continue; } var paths = node.Texture.Source.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries); var icon = (PsbDictionary)source[paths[0]].Children("icon").Children(paths[1]); icon.Remove("compress"); icon.Remove("pixel"); icon["attr"] = PsbNumber.Zero; icon["left"] = new PsbNumber(node.Bounds.Left); icon["top"] = new PsbNumber(node.Bounds.Top); icon.Parent = icons; var iconName = UseMeaningfulName ? node.Texture.Source : id.ToString(); icons.Add(iconName, icon); iconInfos.Add(node.Texture.Source, (texName, iconName)); id++; } //texture //TODO: support truncated var texture = new PsbDictionary(6) { { "height", new PsbNumber(atlas.Height) }, { "width", new PsbNumber(atlas.Width) }, { "truncated_height", new PsbNumber(atlas.Height) }, { "truncated_width", new PsbNumber(atlas.Width) }, { "type", new PsbString(TargetPixelFormat.ToStringForPsb()) } }; texture.Add("pixel", new PsbResource { Data = data, Parents = new List <IPsbCollection> { texture } }); texDic.Add("texture", texture); //type texDic.Add("type", PsbNumber.Zero); texs.Add(texDic); } source.Clear(); foreach (var t in texs) { source.Add($"tex#{t["metadata"]}", t); t["metadata"] = PsbNull.Null; } return(iconInfos); }
/// <summary> /// Pack Archive PSB /// </summary> /// <param name="jsonPath">json path</param> /// <param name="key">crypt key</param> /// <param name="intersect">Only pack files which existed in info.psb.m</param> /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param> /// <param name="enableParallel">parallel process</param> /// <param name="keyLen">key length</param> /// <param name="keepRaw">Do not try to compile json or pack MDF</param> public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true, int keyLen = 131, bool keepRaw = false) { if (!File.Exists(jsonPath)) { return; } PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath); if (infoPsb.Type != PsbType.ArchiveInfo) { Console.WriteLine("Json is not an ArchiveInfo PSB."); return; } var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath); if (!resx.Context.ContainsKey(Context_ArchiveSource) || resx.Context[Context_ArchiveSource] == null) { Console.WriteLine("ArchiveSource must be specified in resx.json Context."); return; } if (keyLen > 0) { resx.Context[Context_MdfKeyLength] = keyLen; } string infoKey = null; if (resx.Context[Context_MdfKey] is string mdfKey) { infoKey = mdfKey; } List <string> sourceDirs = null; if (resx.Context[Context_ArchiveSource] is string path) { sourceDirs = new List <string> { path }; } else if (resx.Context[Context_ArchiveSource] is IList paths) { sourceDirs = new List <string>(paths.Count); sourceDirs.AddRange(from object p in paths select p.ToString()); } else { Console.WriteLine("ArchiveSource incorrect."); return; } var baseDir = Path.GetDirectoryName(jsonPath); var files = new Dictionary <string, (string Path, ProcessMethod Method)>(); var suffix = ArchiveInfoGetSuffix(infoPsb); List <string> filter = null; if (intersect) //only collect files appeared in json { filter = ArchiveInfoCollectFiles(infoPsb, suffix).ToList(); } void CollectFiles(string targetDir) { if (!Directory.Exists(targetDir)) { return; } foreach (var f in Directory.EnumerateFiles(targetDir)) { if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture)) { continue; } else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture)) //json source, need compile { var name = Path.GetFileNameWithoutExtension(f); if (preferPacked && files.ContainsKey(name) && files[name].Method != ProcessMethod.Compile) //it's always right no matter set or replace { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) //this file is not appeared in json { //ignore } else { files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.Compile); } } } else { var name = Path.GetFileName(f); if (!preferPacked && files.ContainsKey(name) && files[name].Method == ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { using var fs = File.OpenRead(f); if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF") { files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.EncodeMdf); } else { files[name] = (f, ProcessMethod.None); } } } } } } //Collect files Console.WriteLine("Collecting files ..."); foreach (var sourceDir in sourceDirs) { CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir)); } Console.WriteLine($"Packing {files.Count} files ..."); var bodyBinFileName = Path.GetFileName(jsonPath); var packageName = Path.GetFileNameWithoutExtension(bodyBinFileName); var coreName = ArchiveInfoGetPackageName(packageName); bodyBinFileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin"; //using var mmFile = // MemoryMappedFile.CreateFromFile(bodyBinFileName, FileMode.Create, coreName, ); using var bodyFs = File.OpenWrite(bodyBinFileName); var fileInfoDic = new PsbDictionary(files.Count); var fmContext = FreeMount.CreateContext(resx.Context); //byte[] bodyBin = null; if (enableParallel) { var contents = new ConcurrentBag <(string Name, Stream Content)>(); Parallel.ForEach(files, (kv) => { var fileNameWithoutSuffix = ArchiveInfoGetFileNameRemoveSuffix(kv.Key, suffix); if (kv.Value.Method == ProcessMethod.None) { contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path))); return; } var mdfContext = new Dictionary <string, object>(resx.Context); var context = FreeMount.CreateContext(mdfContext); if (!string.IsNullOrEmpty(key)) { mdfContext[Context_MdfKey] = key + kv.Key; } else if (resx.Context[Context_MdfMtKey] is string mtKey) { mdfContext[Context_MdfKey] = mtKey + kv.Key; } else { mdfContext.Remove(Context_MdfKey); } mdfContext.Remove(Context_ArchiveSource); if (kv.Value.Method == ProcessMethod.EncodeMdf) { using var mmFs = MemoryMappedFile.CreateFromFile(kv.Value.Path, FileMode.Open); //using var fs = File.OpenRead(kv.Value.Path); contents.Add((fileNameWithoutSuffix, context.PackToShell(mmFs.CreateViewStream(), "MDF"))); //disposed later } else { var content = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path); var stream = content.Psb.ToStream(); var shellType = kv.Key.DefaultShellType(); //MARK: use shellType in filename, or use suffix in info? if (!string.IsNullOrEmpty(shellType)) { stream = context.PackToShell(stream, shellType); //disposed later } contents.Add((fileNameWithoutSuffix, stream)); } });
/// <summary> /// Extract resource info /// </summary> /// <param name="d">PsbObject which contains "pixel"</param> /// <param name="r">Resource</param> /// <returns></returns> internal static ResourceMetadata GenerateMotionResMetadata(PsbDictionary d, PsbResource r = null) { if (r == null) { r = d.Values.FirstOrDefault(v => v is PsbResource) as PsbResource; } bool is2D = false; var part = d.GetPartName(); var name = d.GetName(); RectangleF clip = RectangleF.Empty; if (d["clip"] is PsbDictionary clipDic && clipDic.Count > 0) { is2D = true; clip = RectangleF.FromLTRB( left: clipDic["left"] == null ? 0f : (float)(PsbNumber)clipDic["left"], top: clipDic["top"] == null ? 0f : (float)(PsbNumber)clipDic["top"], right: clipDic["right"] == null ? 1f : (float)(PsbNumber)clipDic["right"], bottom: clipDic["bottom"] == null ? 1f : (float)(PsbNumber)clipDic["bottom"] ); } var compress = PsbCompressType.None; if (d["compress"] is PsbString sc) { is2D = true; if (sc.Value.ToUpperInvariant() == "RL") { compress = PsbCompressType.RL; } } int width = 1, height = 1; float originX = 0, originY = 0; if (d["width"] is PsbNumber nw) { is2D = true; width = (int)nw; } if (d["height"] is PsbNumber nh) { is2D = true; height = (int)nh; } if (d["originX"] is PsbNumber nx) { is2D = true; originX = (float)nx; } if (d["originY"] is PsbNumber ny) { is2D = true; originY = (float)ny; } PsbString typeString = null; if (d["type"] is PsbString typeStr) { typeString = typeStr; } int top = 0, left = 0; if (d["top"] is PsbNumber nt) { is2D = true; top = (int)nt; } if (d["left"] is PsbNumber nl) { is2D = true; left = (int)nl; } var md = new ResourceMetadata() { Index = r.Index ?? int.MaxValue, Compress = compress, Name = name, Part = part, Clip = clip, Is2D = is2D, OriginX = originX, OriginY = originY, Top = top, Left = left, Width = width, Height = height, TypeString = typeString, Resource = r, }; return(md); }
/// <summary> /// Pack Archive PSB /// </summary> /// <param name="jsonPath">json path</param> /// <param name="key">crypt key</param> /// <param name="intersect">Only pack files which existed in info.psb.m</param> /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param> /// <param name="enableParallel">parallel process</param> /// <param name="keyLen">key length</param> public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true, int keyLen = 131) { if (!File.Exists(jsonPath)) { return; } PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath); if (infoPsb.Type != PsbType.ArchiveInfo) { Console.WriteLine("Json is not an ArchiveInfo PSB."); return; } var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath); if (!resx.Context.ContainsKey(Context_ArchiveSource) || resx.Context[Context_ArchiveSource] == null) { Console.WriteLine("ArchiveSource must be specified in resx.json Context."); return; } if (keyLen > 0) { resx.Context[Context_MdfKeyLength] = keyLen; } string infoKey = null; if (resx.Context[Context_MdfKey] is string mdfKey) { infoKey = mdfKey; } List <string> sourceDirs = null; if (resx.Context[Context_ArchiveSource] is string path) { sourceDirs = new List <string> { path }; } else if (resx.Context[Context_ArchiveSource] is IList paths) { sourceDirs = new List <string>(paths.Count); sourceDirs.AddRange(from object p in paths select p.ToString()); } else { Console.WriteLine("ArchiveSource incorrect."); return; } var baseDir = Path.GetDirectoryName(jsonPath); var files = new Dictionary <string, (string Path, ProcessMethod Method)>(); var suffix = ArchiveInfoPsbGetSuffix(infoPsb); List <string> filter = null; if (intersect) { filter = ArchiveInfoPsbCollectFiles(infoPsb, suffix); } void CollectFiles(string targetDir) { if (!Directory.Exists(targetDir)) { return; } foreach (var f in Directory.EnumerateFiles(targetDir)) { if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture)) { continue; } else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture)) { var name = Path.GetFileNameWithoutExtension(f); if (preferPacked && files.ContainsKey(name) && files[name].Method != ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { files[name] = (f, ProcessMethod.Compile); } } } else { var name = Path.GetFileName(f); if (!preferPacked && files.ContainsKey(name) && files[name].Method == ProcessMethod.Compile) { //ignore } else { if (intersect && filter != null && !filter.Contains(name)) { //ignore } else { using var fs = File.OpenRead(f); if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF") { files[name] = (f, ProcessMethod.EncodeMdf); } else { files[name] = (f, ProcessMethod.None); } } } } } } //Collect files foreach (var sourceDir in sourceDirs) { CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir)); } var fileName = Path.GetFileName(jsonPath); var packageName = Path.GetFileNameWithoutExtension(fileName); var coreName = PsbExtension.ArchiveInfoGetPackageName(packageName); fileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin"; var fileInfoDic = new PsbDictionary(files.Count); var fmContext = FreeMount.CreateContext(resx.Context); byte[] bodyBin = null; if (enableParallel) { var contents = new ConcurrentBag <(string Name, Stream Content)>(); Parallel.ForEach(files, (kv) => { var fileNameWithoutSuffix = ArchiveInfoPsbGetFileName(kv.Key, suffix); if (kv.Value.Method == ProcessMethod.None) { contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path))); return; } var mdfContext = new Dictionary <string, object>(resx.Context); var context = FreeMount.CreateContext(mdfContext); if (!string.IsNullOrEmpty(key)) { mdfContext[Context_MdfKey] = key + fileNameWithoutSuffix + suffix; } else if (resx.Context[Context_MdfMtKey] is string mtKey) { mdfContext[Context_MdfKey] = mtKey + fileNameWithoutSuffix + suffix; } else { mdfContext.Remove(Context_MdfKey); } mdfContext.Remove(Context_ArchiveSource); if (kv.Value.Method == ProcessMethod.EncodeMdf) { contents.Add((fileNameWithoutSuffix, context.PackToShell( File.OpenRead(kv.Value.Path), "MDF"))); } else { var content = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path); var outputMdf = context.PackToShell(content.Psb.ToStream(), "MDF"); contents.Add((fileNameWithoutSuffix, outputMdf)); } });
/// <summary> /// Extract resource info /// </summary> /// <param name="d">PsbObject which contains "pixel"</param> /// <param name="r">Resource</param> /// <param name="duplicatePalette">When set to true, Pal.Data may not be set!</param> /// <returns></returns> internal static ImageMetadata GenerateImageMetadata(PsbDictionary d, PsbResource r = null, bool duplicatePalette = false) { if (r == null) { if (d.ContainsKey(Consts.ResourceKey) && d[Consts.ResourceKey] is PsbResource rr) { r = rr; } else //this may find Pal { r = d.Values.FirstOrDefault(v => v is PsbResource) as PsbResource; } } bool is2D = false; var part = GetPartName(d); var name = d.GetName(); RectangleF clip = RectangleF.Empty; if (d["clip"] is PsbDictionary clipDic && clipDic.Count > 0) { is2D = true; clip = RectangleF.FromLTRB( left: clipDic["left"] == null ? 0f : (float)(PsbNumber)clipDic["left"], top: clipDic["top"] == null ? 0f : (float)(PsbNumber)clipDic["top"], right: clipDic["right"] == null ? 1f : (float)(PsbNumber)clipDic["right"], bottom: clipDic["bottom"] == null ? 1f : (float)(PsbNumber)clipDic["bottom"] ); } var compress = PsbCompressType.None; if (d["compress"] is PsbString sc) { is2D = true; if (sc.Value.ToUpperInvariant() == "RL") { compress = PsbCompressType.RL; } } int width = 1, height = 1; float originX = 0, originY = 0; if (d["width"] is PsbNumber nw) { is2D = true; width = (int)nw; } if (d["height"] is PsbNumber nh) { is2D = true; height = (int)nh; } if (d["originX"] is PsbNumber nx) { is2D = true; originX = (float)nx; } if (d["originY"] is PsbNumber ny) { is2D = true; originY = (float)ny; } PsbString typeString = null; if (d["type"] is PsbString typeStr) { typeString = typeStr; } int top = 0, left = 0; if (d["top"] is PsbNumber nt) { is2D = true; top = (int)nt; } if (d["left"] is PsbNumber nl) { is2D = true; left = (int)nl; } PsbResource palResource = null; PsbString palTypeString = null; if (d["pal"] is PsbResource palRes) { if (duplicatePalette) { palResource = new PsbResource(palRes.Index); d["pal"] = palResource; } else { palResource = palRes; } palTypeString = d["palType"] as PsbString; } var md = new ImageMetadata() { Index = r.Index ?? int.MaxValue, Compress = compress, Name = name, Part = part, Clip = clip, Is2D = is2D, OriginX = originX, OriginY = originY, Top = top, Left = left, Width = width, Height = height, TypeString = typeString, Resource = r, Palette = palResource, PaletteTypeString = palTypeString }; return(md); }
internal IPsbValue ConvertToken(JToken token, Dictionary <string, PsbString> context) { switch (token.Type) { case JTokenType.Null: return(PsbNull.Null); case JTokenType.Integer: var l = token.Value <long>(); if (l > Int32.MaxValue || l < Int32.MinValue) { return(new PsbNumber(l)); } return(new PsbNumber(token.Value <int>())); case JTokenType.Float: //var numberStr = token.Value<string>(); var d = token.Value <double>(); if (UseDoubleOnly) { return(new PsbNumber(d)); } var f = token.Value <float>(); if (Math.Abs(f - d) < 1E-08) //float //pcc: 1E-05 //if (Math.Abs(f - d) < float.Epsilon) { return(new PsbNumber(f)); } //if (d < float.MaxValue && d > float.MinValue) //{ // return new PsbNumber(token.Value<float>()); //} return(new PsbNumber(d)); case JTokenType.Boolean: return(new PsbBool(token.Value <bool>())); case JTokenType.String: string str = token.Value <string>(); if (str.StartsWith(Consts.NumberStringPrefix)) { var prefixLen = Consts.NumberStringPrefix.Length; if (str.EndsWith("f")) { return(new PsbNumber(int.Parse(str.Substring(prefixLen, 8), NumberStyles.AllowHexSpecifier)) { NumberType = PsbNumberType.Float }); } if (str.EndsWith("d")) { return(new PsbNumber(long.Parse(str.Substring(prefixLen, 16), NumberStyles.AllowHexSpecifier)) { NumberType = PsbNumberType.Double }); } return(new PsbNumber(long.Parse(str.Substring(prefixLen), NumberStyles.AllowHexSpecifier))); } if (str.StartsWith(Consts.ResourceIdentifier)) { return(new PsbResource(uint.Parse(str.Replace(Consts.ResourceIdentifier, "")))); } var psbStr = new PsbString(str, (uint)context.Count); if (context.ContainsKey(str)) { return(context[str]); } else { context.Add(str, psbStr); } return(psbStr); case JTokenType.Array: var array = (JArray)token; var collection = new PsbList(array.Count); foreach (var val in array) { var o = ConvertToken(val, context); if (o is IPsbChild c) { c.Parent = collection; } if (o is IPsbSingleton s) { s.Parents.Add(collection); } collection.Add(o); } return(collection); case JTokenType.Object: var obj = (JObject)token; var dictionary = new PsbDictionary(obj.Count); foreach (var val in obj) { var o = ConvertToken(val.Value, context); if (o is IPsbChild c) { c.Parent = dictionary; } if (o is IPsbSingleton s) { s.Parents.Add(dictionary); } dictionary.Add(val.Key, o); } return(dictionary); default: throw new FormatException("Unsupported json element"); } }
public static IEnumerable <IPsbValue> FindAllByPath(this PsbDictionary psbObj, string path) { if (psbObj == null) { yield break; } if (path.StartsWith("/")) { path = new string(path.SkipWhile(c => c == '/').ToArray()); } if (path.Contains("/")) { var pos = path.IndexOf('/'); var current = path.Substring(0, pos); if (current == "*") { if (pos == path.Length - 1) //end { if (psbObj is PsbDictionary dic) { foreach (var dicValue in dic.Values) { yield return(dicValue); } } } path = new string(path.SkipWhile(c => c == '*').ToArray()); foreach (var val in psbObj.Values) { if (val is PsbDictionary dic) { foreach (var dicValue in dic.FindAllByPath(path)) { yield return(dicValue); } } } } if (pos == path.Length - 1 && psbObj[current] != null) { yield return(psbObj[current]); } var currentObj = psbObj[current]; if (currentObj is PsbDictionary collection) { path = path.Substring(pos); foreach (var dicValue in collection.FindAllByPath(path)) { yield return(dicValue); } } } if (path == "*") { foreach (var value in psbObj.Values) { yield return(value); } } else if (psbObj[path] != null) { yield return(psbObj[path]); } }
internal IPsbValue ConvertToken(JToken token, List <PsbString> context) { switch (token.Type) { case JTokenType.Null: return(PsbNull.Null); case JTokenType.Integer: return(new PsbNumber(token.Value <int>())); case JTokenType.Float: var d = token.Value <double>(); var f = token.Value <float>(); if (Math.Abs(f - d) < 1E-05) //float //pcc: 1E-05 { return(new PsbNumber(token.Value <float>())); } //if (d < float.MaxValue && d > float.MinValue) //{ // return new PsbNumber(token.Value<float>()); //} return(new PsbNumber(d)); case JTokenType.Boolean: return(new PsbBool(token.Value <bool>())); case JTokenType.String: string str = token.Value <string>(); if (str.StartsWith(PsbResCollector.ResourceIdentifier)) { return(new PsbResource(uint.Parse(str.Replace(PsbResCollector.ResourceIdentifier, "")))); } var psbStr = new PsbString(str, (uint)context.Count); if (context.Contains(psbStr)) { return(context.Find(ps => ps.Value == str)); } else { context.Add(psbStr); } return(psbStr); case JTokenType.Array: var array = (JArray)token; var collection = new PsbCollection(array.Count); foreach (var val in array) { var o = ConvertToken(val, context); if (o is IPsbChild c) { c.Parent = collection; } if (o is IPsbSingleton s) { s.Parents.Add(collection); } collection.Add(o); } return(collection); case JTokenType.Object: var obj = (JObject)token; var dictionary = new PsbDictionary(obj.Count); foreach (var val in obj) { var o = ConvertToken(val.Value, context); if (o is IPsbChild c) { c.Parent = dictionary; } if (o is IPsbSingleton s) { s.Parents.Add(dictionary); } dictionary.Add(val.Key, o); } return(dictionary); default: throw new FormatException("Unsupported json element"); } }