/// <summary> /// <see cref="PsbResHelper.CollectResources"/> for packed-texture specs, like <see cref="PsbSpec.win"/> /// </summary> /// <param name="psb"></param> /// <returns></returns> public static List <ImageMetadata> CollectSplitResources(this PSB psb) { List <ImageMetadata> resList = new List <ImageMetadata>(); var source = (PsbDictionary)psb.Objects["source"]; foreach (var tex in source) { if (tex.Value is PsbDictionary texDic) { var typeStr = (PsbString)texDic["texture"].Children("type"); typeStr = PsbPixelFormat.WinRGBA8.ToStringForPsb().ToPsbString(); var bmps = SplitTexture(texDic, psb.Platform); var icons = (PsbDictionary)texDic["icon"]; foreach (var iconPair in icons) { var res = new PsbResource { Data = RL.GetPixelBytesFromImage(bmps[iconPair.Key], typeStr.Value.ToPsbPixelFormat(psb.Platform)) }; var icon = (PsbDictionary)iconPair.Value; var md = PsbResHelper.GenerateImageMetadata(icon, res); md.Spec = psb.Platform; md.TypeString = typeStr; resList.Add(md); } } } return(resList); }
/// <summary> /// Combine Image texture parts /// </summary> /// <param name="psb">Image (image) type PSB</param> /// <returns></returns> public static Dictionary <string, Bitmap> CombineTachie(PSB psb) { if (psb.Type != PsbType.Tachie) { throw new NotSupportedException("PSB is not image type"); } Dictionary <string, Bitmap> images = new Dictionary <string, Bitmap>(); PsbList imageList = psb.Objects["imageList"] as PsbList; if (imageList == null) { return(images); } foreach (var psbValue in imageList) { var imageItem = psbValue as PsbDictionary; var texture = imageItem?["texture"] as PsbList; if (texture == null) { continue; } var height = imageItem["height"].GetInt(); var width = imageItem["width"].GetInt(); var label = imageItem["label"].ToString(); Bitmap img = new Bitmap(width, height, PixelFormat.Format32bppArgb); using (var f = img.FastLock()) { foreach (var texObj in texture) { var tex = (PsbDictionary)texObj; var md = PsbResHelper.GenerateImageMetadata(tex.Children("image") as PsbDictionary); md.Spec = psb.Platform; var left = tex["left"].GetInt(); var top = tex["top"].GetInt(); var tHeight = tex["height"].GetInt(); var tWidth = tex["width"].GetInt(); f.CopyRegion(md.ToImage(), new Rectangle(0, 0, md.Width, md.Height), new Rectangle(left, top, tWidth, tHeight)); } } images.Add(label, img); } return(images); }
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); }
/// <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) { if (resx.Resources == null) { return; } var context = FreeMount.CreateContext(resx.Context); if (psb.TypeHandler != null) { psb.TypeHandler.Link(psb, context, resx.Resources, baseDir); } else { PsbResHelper.LinkImages(psb, context, resx.Resources, baseDir); } }
/// <summary> /// Save (most user friendly) images /// </summary> /// <param name="inputPath"></param> /// <param name="format"></param> public static void ExtractImageFiles(string inputPath, PsbImageFormat format = PsbImageFormat.png) { if (!File.Exists(inputPath)) { return; } var name = Path.GetFileNameWithoutExtension(inputPath); var dirPath = Path.Combine(Path.GetDirectoryName(inputPath), name); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { Directory.CreateDirectory(dirPath); } var texExt = format == PsbImageFormat.bmp ? ".bmp" : ".png"; var texFormat = format.ToImageFormat(); var psb = new PSB(inputPath); if (psb.Type == PsbType.Tachie) { var bitmaps = TextureCombiner.CombineTachie(psb); foreach (var kv in bitmaps) { kv.Value.Save(Path.Combine(dirPath, $"{kv.Key}{texExt}"), texFormat); } return; } var texs = PsbResHelper.UnlinkImages(psb); foreach (var tex in texs) { tex.Save(Path.Combine(dirPath, tex.Tag + texExt), texFormat); } }
/// <summary> /// Link Textures /// </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) { var context = FreeMount.CreateContext(); if (psb.Type == PsbType.Motion) { PsbResHelper.LinkImages(psb, context, resPaths, baseDir, order, isExternal); return; } if (psb.TypeHandler != null) { psb.TypeHandler.Link(psb, context, resPaths, baseDir, order); } else { PsbResHelper.LinkImages(psb, context, resPaths, baseDir, order); } }
public List <ImageMetadata> FindFontResources(PSB psb, bool deDuplication = true) { List <ImageMetadata> resList = new List <ImageMetadata>(psb.Resources.Count); if (psb.Objects == null || !psb.Objects.ContainsKey(Source) || !(psb.Objects[Source] is PsbList list)) { return(resList); } foreach (var item in list) { if (!(item is PsbDictionary obj)) { continue; } //TODO: deDuplication for resource (besides pal) var md = PsbResHelper.GenerateImageMetadata(obj, null); resList.Add(md); } return(resList); }
public virtual void Link(PSB psb, FreeMountContext context, IList <string> resPaths, string baseDir = null, PsbLinkOrderBy order = PsbLinkOrderBy.Convention) { PsbResHelper.LinkImages(psb, context, resPaths, baseDir, order); }
public virtual Dictionary <string, string> OutputResources(PSB psb, FreeMountContext context, string name, string dirPath, PsbExtractOption extractOption = PsbExtractOption.Original) { return(PsbResHelper.OutputImageResources(psb, context, name, dirPath, extractOption)); }
public virtual void UnlinkToFile(PSB psb, FreeMountContext context, string name, string dirPath, bool outputUnlinkedPsb = true, PsbLinkOrderBy order = PsbLinkOrderBy.Name) { PsbResHelper.UnlinkImagesToFile(psb, context, name, dirPath, outputUnlinkedPsb, order); }
public virtual List <Bitmap> Unlink(PSB psb, PsbLinkOrderBy order = PsbLinkOrderBy.Name, bool disposeResInPsb = true) { return(PsbResHelper.UnlinkImages(psb, order, disposeResInPsb)); }
public virtual void Link(PSB psb, FreeMountContext context, IDictionary <string, string> resPaths, string baseDir = null) { PsbResHelper.LinkImages(psb, context, resPaths, baseDir); }
internal static void OutputResources(PSB psb, FreeMountContext context, string filePath, PsbExtractOption extractOption = PsbExtractOption.Original, PsbImageFormat extractFormat = PsbImageFormat.png, bool useResx = true) { var name = Path.GetFileNameWithoutExtension(filePath); var dirPath = Path.Combine(Path.GetDirectoryName(filePath), name); PsbResourceJson resx = new PsbResourceJson(psb, context.Context); if (File.Exists(dirPath)) { name += "-resources"; dirPath += "-resources"; } var extraDir = Path.Combine(dirPath, Consts.ExtraResourceFolderName); if (!Directory.Exists(dirPath)) //ensure there is no file with same name! { if (psb.Resources.Count != 0) { Directory.CreateDirectory(dirPath); } } if (psb.ExtraResources.Count > 0) { var extraDic = PsbResHelper.OutputExtraResources(psb, context, name, extraDir, out var flattenArrays, extractOption); resx.ExtraResources = extraDic; if (flattenArrays != null && flattenArrays.Count > 0) { resx.ExtraFlattenArrays = flattenArrays; } } var resDictionary = psb.TypeHandler.OutputResources(psb, context, name, dirPath, extractOption); //MARK: We use `.resx.json` to distinguish from psbtools' `.res.json` if (useResx) { resx.Resources = resDictionary; resx.Context = context.Context; string json; if (Consts.JsonArrayCollapse) { json = ArrayCollapseJsonTextWriter.SerializeObject(resx); } else { json = JsonConvert.SerializeObject(resx, Formatting.Indented); } File.WriteAllText(ChangeExtensionForOutputJson(filePath, ".resx.json"), json); } else { if (psb.ExtraResources.Count > 0) { throw new NotSupportedException("PSBv4 cannot use legacy res.json format."); } File.WriteAllText(ChangeExtensionForOutputJson(filePath, ".res.json"), JsonConvert.SerializeObject(resDictionary.Values.ToList(), Formatting.Indented)); } }
/// <summary> /// Split textures into parts and save to files /// </summary> /// <param name="psb">PSB</param> /// <param name="path">Save directory</param> /// <param name="option">Save option</param> /// <param name="imageFormat">Save format</param> /// <param name="pixelFormat">When save to PSB special formats, specific pixel format to use</param> public static void SplitTextureToFiles(this PSB psb, string path, PsbExtractOption option = PsbExtractOption.Extract, PsbImageFormat imageFormat = PsbImageFormat.png, PsbPixelFormat pixelFormat = PsbPixelFormat.None) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } var source = (PsbDictionary)psb.Objects["source"]; foreach (var texPair in source) { if (!(texPair.Value is PsbDictionary tex)) { continue; } var name = texPair.Key; if (!Directory.Exists(Path.Combine(path, name))) { Directory.CreateDirectory(Path.Combine(path, name)); } var icon = (PsbDictionary)tex["icon"]; var texture = (PsbDictionary)tex["texture"]; //var mipmap = (PsbDictionary)texture["mipMap"]; //TODO: Mipmap? 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; } var md = PsbResHelper.GenerateImageMetadata(texture, pixelRes); md.Spec = psb.Platform; //Important Bitmap bmp = md.ToImage(); foreach (var iconPair in icon) { var savePath = Path.Combine(path, name, iconPair.Key); 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"]; var attr = (int)(PsbNumber)info["attr"]; 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 switch (option) { case PsbExtractOption.Decompress: File.WriteAllBytes(savePath + ".raw", RL.GetPixelBytesFromImage(b, pixelFormat)); break; case PsbExtractOption.Compress: File.WriteAllBytes(savePath + ".rl", RL.CompressImage(b, pixelFormat)); break; case PsbExtractOption.Original: case PsbExtractOption.Extract: default: switch (imageFormat) { case PsbImageFormat.bmp: b.Save(savePath + ".bmp", ImageFormat.Bmp); break; case PsbImageFormat.png: default: b.Save(savePath + ".png", ImageFormat.Png); //b.Save(savePath + $"_{attr}.png", ImageFormat.png); break; } break; } } bmp.Dispose(); } }
/// <summary> /// Compile Json to PSB /// </summary> /// <param name="inputJson">Json text</param> /// <param name="inputResJson">Resource Json text</param> /// <param name="baseDir">If resource Json uses relative paths (usually it does), specify the base dir</param> /// <param name="version">PSB version</param> /// <param name="cryptKey">CryptKey, use null for pure PSB</param> /// <param name="spec">PSB Platform</param> /// <param name="keepShell">If true, try to compress PSB to shell type (MDF/LZ4 etc.) specified in resx.json; otherwise just output PSB</param> /// <returns></returns> public static byte[] Compile(string inputJson, string inputResJson, string baseDir = null, ushort?version = null, uint?cryptKey = null, PsbSpec?spec = null, bool keepShell = true) { var context = FreeMount.CreateContext(); //Parse PSB psb = Parse(inputJson, version ?? 3); //Link if (!string.IsNullOrWhiteSpace(inputResJson)) { if (inputResJson.Trim().StartsWith("{")) //resx.json { PsbResourceJson resx = JsonConvert.DeserializeObject <PsbResourceJson>(inputResJson); if (resx.PsbType != null) { psb.Type = resx.PsbType.Value; } if (resx.PsbVersion != null && version == null) { psb.Header.Version = resx.PsbVersion.Value; } if (resx.Platform != null && spec == null) { spec = resx.Platform; } if (resx.CryptKey != null & cryptKey == null) { cryptKey = resx.CryptKey; } context = FreeMount.CreateContext(resx.Context); if (resx.HasExtraResources) { PsbResHelper.LinkExtraResources(psb, context, resx.ExtraResources, resx.ExtraFlattenArrays, baseDir); } if (resx.ExternalTextures) { #if DEBUG Console.WriteLine("[INFO] External Texture mode ON, no resource will be compiled."); #endif } else { psb.Link(resx, baseDir); } } else { List <string> resources = JsonConvert.DeserializeObject <List <string> >(inputResJson); psb.Link(resources, baseDir); } } //Build psb.Merge(); if (spec != null && spec != psb.Platform) { psb.SwitchSpec(spec.Value, spec.Value.DefaultPixelFormat()); psb.Merge(); } var bytes = psb.Build(); //Convert if (cryptKey != null) { bytes = PsbFile.EncodeToBytes(cryptKey.Value, bytes, EncodeMode.Encrypt, EncodePosition.Auto); } if (context.HasShell && keepShell) { using var outStream = context.PackToShell(new MemoryStream(bytes)); bytes = outStream.ToArray(); } return(bytes); }