using SixLabors.ImageSharp.ColorSpaces; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.IO.Pipes; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; namespace VRCX { public static class PNGHelper { public static string ReadResolution(PNGFile pngFile) { var ihdrChunk = pngFile.GetChunk(PNGChunkTypeFilter.IHDR); if (ihdrChunk != null) { var resolution = ihdrChunk.ReadIHDRChunkResolution(); return resolution.Item1 + "x" + resolution.Item2; } return "0x0"; } /// /// Reads the metadata associated with a specified keyword from text chunks within a PNG file. /// /// The unique keyword for a speicifc text chunk to search for. /// The PNG file containing the chunks to be searched. /// /// Specifies whether to search for legacy text chunks created by older VRChat mods. /// If true, the function searches from the end of the file using a reverse search bruteforce method. /// /// The text associated with the specified keyword, or null if not found. public static string? ReadTextChunk(string keyword, PNGFile pngFile, bool legacySearch = false) { // Search for legacy text chunks created by old vrchat mods if (legacySearch) { var legacyTextChunk = pngFile.GetChunkReverse(PNGChunkTypeFilter.iTXt); if (legacyTextChunk != null) { var data = legacyTextChunk.ReadITXtChunk(); if (data.Item1 == keyword) return data.Item2; } return null; } var iTXtChunk = pngFile.GetChunksOfType(PNGChunkTypeFilter.iTXt); if (iTXtChunk.Count == 0) return null; for (int i = 0; i < iTXtChunk.Count; i++) { var data = iTXtChunk[i].ReadITXtChunk(); if (data.Item1 == keyword) return data.Item2; } return null; } public static bool DeleteTextChunk(string keyword, PNGFile pngFile) { var iTXtChunk = pngFile.GetChunksOfType(PNGChunkTypeFilter.iTXt); if (iTXtChunk.Count == 0) return false; for (int i = 0; i < iTXtChunk.Count; i++) { var data = iTXtChunk[i].ReadITXtChunk(); if (data.Item1 == keyword) return pngFile.DeleteChunk(iTXtChunk[i]); } return false; } /// /// Generates a PNG text chunk ready for writing. /// /// The keyword to write to the text chunk. /// The text to write to the text chunk. /// The binary data for the text chunk. public static PNGChunk GenerateTextChunk(string keyword, string text) { byte[] textBytes = Encoding.UTF8.GetBytes(text); byte[] keywordBytes = Encoding.UTF8.GetBytes(keyword); List constructedTextChunk = new List(); constructedTextChunk.AddRange(keywordBytes); constructedTextChunk.Add(0x0); // Null separator constructedTextChunk.Add(0x0); // Compression flag constructedTextChunk.Add(0x0); // Compression method constructedTextChunk.Add(0x0); // Null separator (skipping over language tag byte) constructedTextChunk.Add(0x0); // Null separator (skipping over translated keyword byte) constructedTextChunk.AddRange(textBytes); return new PNGChunk { ChunkType = "iTXt", ChunkTypeEnum = PNGChunkTypeFilter.iTXt, Data = constructedTextChunk.ToArray(), Length = constructedTextChunk.Count }; } } }