public void Save(Stream output) { ImageSettings modSettings = Settings; modSettings.Width = Image.Width; modSettings.Height = Image.Height; byte[] data = Common.Save(Image, modSettings); using (BinaryWriterX br = new BinaryWriterX(new MemoryStream())) { JTEXRawHeader.width = (ushort)Image.Width; JTEXRawHeader.height = (ushort)Image.Height; br.WriteStruct <RawHeader>(JTEXRawHeader); br.BaseStream.Position = JTEXRawHeader.dataStart; br.Write(data); br.BaseStream.Position = 0; if (lz11_compressed) { byte[] comp = LZ11.Compress(br.BaseStream); output.Write(comp, 0, comp.Length); } else { output.Write(new BinaryReaderX(br.BaseStream).ReadBytes((int)br.BaseStream.Length), 0, (int)br.BaseStream.Length); } output.Close(); } }
public XI(Stream input) { using (var br = new BinaryReaderX(input)) { var header = br.ReadStruct <Header>(); Settings = new ImageSettings { Width = header.width, Height = header.height, Format = ImageSettings.ConvertFormat(header.imageFormat), Orientation = header.orientation, PadToPowerOf2 = false }; CombineFormat = header.combineFormat; if (CombineFormat != 1) { throw new Exception($"Unknown combine format {header.combineFormat}"); } var buf1 = Level5.Decompress(input); while (input.Position % 4 != 0) { input.ReadByte(); } var buf2 = Level5.Decompress(input); var ms = new MemoryStream(); for (int i = 0; i < buf1.Length / 2; i++) { int index = BitConverter.ToInt16(buf1, 2 * i); ms.Write(buf2, index * header.bytesPerTile, header.bytesPerTile); } Image = Common.Load(ms.ToArray(), Settings); } }
public static Bitmap Load(byte[] tex, ImageSettings settings) { int width = settings.Width, height = settings.Height; var colors = GetColorsFromTexture(tex, settings); var points = GetPointSequence(settings); // Now we just need to merge the points with the colors var bmp = new Bitmap(width, height); var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); unsafe { var ptr = (int *)data.Scan0; foreach (var pair in points.Zip(colors, Tuple.Create)) { int x = pair.Item1.X, y = pair.Item1.Y; if (0 <= x && x < width && 0 <= y && y < height) { var color = pair.Item2; if (settings.PixelShader != null) { color = settings.PixelShader(color); } ptr[data.Stride * y / 4 + x] = color.ToArgb(); } } } bmp.UnlockBits(data); return(bmp); }
public BXLIM(Stream input) { using (var br = new BinaryReaderX(input)) { var tex = br.ReadBytes((int)br.BaseStream.Length - 40); sections = br.ReadSections(); switch (sections.Header.magic) { case "CLIM": BCLIMHeader = sections[0].Data.BytesToStruct <BCLIMImageHeader>(br.ByteOrder); Settings = new ImageSettings { Width = BCLIMHeader.width, Height = BCLIMHeader.height, Format = ImageSettings.ConvertFormat(BCLIMHeader.format), Orientation = BCLIMHeader.orientation }; break; case "FLIM": BFLIMHeader = sections[0].Data.BytesToStruct <BFLIMImageHeader>(br.ByteOrder); Settings = new ImageSettings { Width = BFLIMHeader.width, Height = BFLIMHeader.height, Format = ImageSettings.ConvertFormat(BFLIMHeader.format), Orientation = BFLIMHeader.orientation }; break; default: throw new NotSupportedException($"Unknown image format {sections.Header.magic}"); } Image = Common.Load(tex, Settings); } }
public RawJTEX(Stream input) { using (var br = new BinaryReaderX(input)) { Stream stream; if (br.ReadByte() == 0x11) { br.BaseStream.Position = 0; uint size = br.ReadUInt32() >> 8; br.BaseStream.Position = 0; lz11_compressed = true; byte[] decomp = LZ11.Decompress(br.BaseStream); stream = new MemoryStream(decomp); } else { br.BaseStream.Position = 0; stream = br.BaseStream; } //File.OpenWrite("test.decomp").Write(new BinaryReaderX(stream).ReadBytes((int)stream.Length), 0, (int)stream.Length); using (BinaryReaderX br2 = new BinaryReaderX(stream)) { JTEXRawHeader = br2.ReadStruct <RawHeader>(); br2.BaseStream.Position = JTEXRawHeader.dataStart; Settings = new ImageSettings { Width = JTEXRawHeader.width, Height = JTEXRawHeader.height, Format = JTEXRawHeader.format }; Image = Common.Load(br2.ReadBytes((int)(br2.BaseStream.Length - br2.BaseStream.Position)), Settings); } } }
static IEnumerable <Point> GetPointSequence(ImageSettings settings) { int strideWidth = (settings.Width + 7) & ~7; int strideHeight = (settings.Height + 7) & ~7; if (settings.PadToPowerOf2) { strideWidth = 2 << (int)Math.Log(strideWidth - 1, 2); strideHeight = 2 << (int)Math.Log(strideHeight - 1, 2); } int stride = (int)settings.Orientation < 4 ? strideWidth : strideHeight; for (int i = 0; i < strideWidth * strideHeight; i++) { int x_out = 0, y_out = 0, x_in = 0, y_in = 0; if (settings.ZOrder) { x_out = (i / 64 % (stride / 8)) * 8; y_out = (i / 64 / (stride / 8)) * 8; x_in = (i / 4 & 4) | (i / 2 & 2) | (i & 1); y_in = (i / 8 & 4) | (i / 4 & 2) | (i / 2 & 1); } else { x_out = (i / 64 % (stride / 8)) * 8; y_out = (i / 64 / (stride / 8)) * 8; x_in = i % 64 % 8; y_in = i % 64 / 8; } switch (settings.Orientation) { case Orientation.Default: yield return(new Point(x_out + x_in, y_out + y_in)); break; case Orientation.TransposeTile: yield return(new Point(x_out + y_in, y_out + x_in)); break; case Orientation.Rotate90: yield return(new Point(y_out + y_in, stride - 1 - (x_out + x_in))); break; case Orientation.Transpose: yield return(new Point(y_out + y_in, x_out + x_in)); break; default: throw new NotSupportedException($"Unknown orientation format {settings.Orientation}"); } } }
public BXLIM(Stream input) { using (var br = new BinaryReaderX(input)) { var tex = br.ReadBytes((int)br.BaseStream.Length - 40); sections = br.ReadSections(); byteOrder = br.ByteOrder; switch (sections.Header.magic) { case "CLIM": BCLIMHeader = sections[0].Data.BytesToStruct <BCLIMImageHeader>(byteOrder); Settings = new ImageSettings { Width = BCLIMHeader.width, Height = BCLIMHeader.height, Format = ImageSettings.ConvertFormat(BCLIMHeader.format), Orientation = ImageSettings.ConvertOrientation(BCLIMHeader.orientation) }; Image = Common.Load(tex, Settings); break; case "FLIM": if (byteOrder == ByteOrder.LittleEndian) { BFLIMHeaderLE = sections[0].Data.BytesToStruct <BFLIMImageHeaderLE>(byteOrder); Settings = new ImageSettings { Width = BFLIMHeaderLE.width, Height = BFLIMHeaderLE.height, Format = ImageSettings.ConvertFormat(BFLIMHeaderLE.format), Orientation = ImageSettings.ConvertOrientation(BFLIMHeaderLE.orientation), }; Image = Common.Load(tex, Settings); } else { BFLIMHeaderBE = sections[0].Data.BytesToStruct <BFLIMImageHeaderBE>(byteOrder); var padWidth = 2 << (int)Math.Log(BFLIMHeaderBE.width - 1, 2); var padHeight = 2 << (int)Math.Log(BFLIMHeaderBE.height - 1, 2); Settings = new ImageSettings { Width = padWidth, Height = padHeight, Format = ImageSettings.ConvertFormat(BFLIMHeaderBE.format), Orientation = Cetera.Image.Orientation.Default, PadToPowerOf2 = true, ZOrder = (br.ByteOrder == ByteOrder.LittleEndian) ? true : false }; Image = SwizzleTiles(Common.Load(tex, Settings), padWidth, padHeight, BFLIMHeaderBE.width, BFLIMHeaderBE.height, 8, BFLIMHeaderBE.tileMode); } break; default: throw new NotSupportedException($"Unknown image format {sections.Header.magic}"); } } }
public void Save(Stream output) { using (var bw = new BinaryWriterX(output)) { var settings = new ImageSettings(); byte[] texture; switch (sections.Header.magic) { case "CLIM": settings.Width = BCLIMHeader.width; settings.Height = BCLIMHeader.height; settings.Orientation = ImageSettings.ConvertOrientation(BCLIMHeader.orientation); settings.Format = ImageSettings.ConvertFormat(BCLIMHeader.format); texture = Common.Save(Image, settings); bw.Write(texture); // We can now change the image width/height/filesize! BCLIMHeader.width = (short)Image.Width; BCLIMHeader.height = (short)Image.Height; BCLIMHeader.datasize = texture.Length; sections[0].Data = BCLIMHeader.StructToBytes(); sections.Header.file_size = texture.Length + 40; bw.WriteSections(sections); break; case "FLIM": if (byteOrder == ByteOrder.LittleEndian) { settings.Width = BFLIMHeaderLE.width; settings.Height = BFLIMHeaderLE.height; settings.Orientation = ImageSettings.ConvertOrientation(BFLIMHeaderLE.orientation); settings.Format = ImageSettings.ConvertFormat(BFLIMHeaderLE.format); texture = Common.Save(Image, settings); bw.Write(texture); // We can now change the image width/height/filesize! BFLIMHeaderLE.width = (short)Image.Width; BFLIMHeaderLE.height = (short)Image.Height; BFLIMHeaderLE.datasize = texture.Length; sections[0].Data = BFLIMHeaderLE.StructToBytes(); sections.Header.file_size = texture.Length + 40; bw.WriteSections(sections); } else { throw new NotSupportedException($"Big Endian FLIM isn't savable yet!"); } break; default: throw new NotSupportedException($"Unknown image format {sections.Header.magic}"); } } }
public void Save(Stream output) { using (var bw = new BinaryWriterX(output)) { var settings = new ImageSettings(); byte[] texture; switch (sections.Header.magic) { case "CLIM": settings.Width = BCLIMHeader.width; settings.Height = BCLIMHeader.height; settings.Orientation = BCLIMHeader.orientation; settings.Format = ImageSettings.ConvertFormat(BCLIMHeader.format); texture = Common.Save(Image, settings); bw.Write(texture); // We can now change the image width/height/filesize! var modifiedBCLIMHeader = BCLIMHeader; modifiedBCLIMHeader.width = (short)Image.Width; modifiedBCLIMHeader.height = (short)Image.Height; modifiedBCLIMHeader.datasize = texture.Length; BCLIMHeader = modifiedBCLIMHeader; sections[0].Data = BCLIMHeader.StructToBytes(); sections.Header.file_size = texture.Length + 40; bw.WriteSections(sections); break; case "FLIM": settings.Width = BFLIMHeader.width; settings.Height = BFLIMHeader.height; settings.Orientation = BFLIMHeader.orientation; settings.Format = ImageSettings.ConvertFormat(BFLIMHeader.format); texture = Common.Save(Image, settings); bw.Write(texture); // We can now change the image width/height/filesize! var modifiedBFLIMHeader = BFLIMHeader; modifiedBFLIMHeader.width = (short)Image.Width; modifiedBFLIMHeader.height = (short)Image.Height; modifiedBFLIMHeader.datasize = texture.Length; BFLIMHeader = modifiedBFLIMHeader; sections[0].Data = BFLIMHeader.StructToBytes(); sections.Header.file_size = texture.Length + 40; bw.WriteSections(sections); break; default: throw new NotSupportedException($"Unknown image format {sections.Header.magic}"); } } }
public JTEX(Stream input) { using (var br = new BinaryReaderX(input)) { JTEXHeader = br.ReadStruct <Header>(); Settings = new ImageSettings { Width = JTEXHeader.width, Height = JTEXHeader.height }; Settings.SetFormat(JTEXHeader.format); var texture2 = br.ReadBytes(JTEXHeader.unk3[0]); // bytes to read? Image = Common.Load(texture2, Settings); } }
public void Save(Stream output) { using (var bw = new BinaryWriterX(output)) { var modifiedJTEXHeader = JTEXHeader; modifiedJTEXHeader.width = (short)Image.Width; modifiedJTEXHeader.height = (short)Image.Height; var settings = new ImageSettings(); settings.Width = modifiedJTEXHeader.width; settings.Height = modifiedJTEXHeader.height; settings.Format = ImageSettings.ConvertFormat(modifiedJTEXHeader.format); byte[] texture = Common.Save(Image, settings); modifiedJTEXHeader.unk3[0] = texture.Length; JTEXHeader = modifiedJTEXHeader; bw.WriteStruct(JTEXHeader); bw.Write(texture); } }
public static byte[] Save(Bitmap bmp, ImageSettings settings) { settings.Width = bmp.Width; settings.Height = bmp.Height; var points = GetPointSequence(settings); var ms = new MemoryStream(); var etc1encoder = new ETC1.Encoder(); Enum.TryParse <DXT.Formats>(settings.Format.ToString(), false, out var dxtFormat); var dxtencoder = new DXT.Encoder(dxtFormat); using (var bw = new BinaryWriterX(ms)) { foreach (var point in points) { int x = Clamp(point.X, 0, bmp.Width); int y = Clamp(point.Y, 0, bmp.Height); var color = bmp.GetPixel(x, y); if (settings.PixelShader != null) { color = settings.PixelShader(color); } switch (settings.Format) { case Format.L8: bw.Write(color.G); break; case Format.A8: bw.Write(color.A); break; case Format.LA44: bw.WriteNibble(color.A / 16); bw.WriteNibble(color.G / 16); break; case Format.LA88: bw.Write(color.A); bw.Write(color.G); break; case Format.HL88: bw.Write(color.G); bw.Write(color.R); break; case Format.RGB565: bw.Write((short)((color.R / 8 << 11) | (color.G / 4 << 5) | (color.B / 8))); break; case Format.RGB888: bw.Write(color.B); bw.Write(color.G); bw.Write(color.R); break; case Format.RGBA5551: bw.Write((short)((color.R / 8 << 11) | (color.G / 8 << 6) | (color.B / 8 << 1) | color.A / 128)); break; case Format.RGBA4444: bw.WriteNibble(color.A / 16); bw.WriteNibble(color.B / 16); bw.WriteNibble(color.G / 16); bw.WriteNibble(color.R / 16); break; case Format.RGBA8888: bw.Write(color.A); bw.Write(color.B); bw.Write(color.G); bw.Write(color.R); break; case Format.ETC1: case Format.ETC1A4: etc1encoder.Set(color, data => { if (settings.Format == Format.ETC1A4) { bw.Write(data.Alpha); } bw.WriteStruct(data.Block); }); break; case Format.DXT1: case Format.DXT5: dxtencoder.Set(color, data => { if (settings.Format == Format.DXT5) { bw.Write(data.alpha); } bw.Write(data.block); }); break; case Format.L4: bw.WriteNibble(color.G / 16); break; case Format.A4: bw.WriteNibble(color.A / 16); break; default: throw new NotSupportedException(); } } } return(ms.ToArray()); }
static IEnumerable <Point> GetPointSequence(ImageSettings settings) { switch (settings.Format) { case Format.ATI1A: case Format.ATI1L: case Format.ATI2: case Format.ETC1: case Format.ETC1A4: case Format.DXT1: case Format.DXT3: case Format.DXT5: settings.TileSize = settings.TileSize + 3 & ~0x3; break; } int strideWidth = (settings.Width + 7) & ~7; int strideHeight = (settings.Height + 7) & ~7; if (settings.PadToPowerOf2) { strideWidth = 2 << (int)Math.Log(strideWidth - 1, 2); strideHeight = 2 << (int)Math.Log(strideHeight - 1, 2); } //stride TileSize var tileSize = 0; if (settings.ZOrder) { tileSize = 2 << (int)(Math.Log(((settings.TileSize + 7) & ~7) - 1, 2)); } else { tileSize = settings.TileSize; } int powTileSize = (int)Math.Pow(tileSize, 2); int stride = strideWidth; switch (settings.Orientation) { case Orientation.Rotate90: case Orientation.Transpose: stride = strideHeight; break; } for (int i = 0; i < strideWidth * strideHeight; i++) { //in == order inside a tile //out == order of tiles themselves int x_out = 0, y_out = 0, x_in = 0, y_in = 0; if (settings.ZOrder) { x_out = (i / powTileSize % (stride / tileSize)) * tileSize; y_out = (i / powTileSize / (stride / tileSize)) * tileSize; x_in = ZOrderX(tileSize, i); y_in = ZOrderY(tileSize, i); } else { x_out = (i / powTileSize % (stride / tileSize)) * tileSize; y_out = (i / powTileSize / (stride / tileSize)) * tileSize; switch (settings.Format) { case Format.ATI1A: case Format.ATI1L: case Format.ATI2: case Format.ETC1: case Format.ETC1A4: case Format.DXT1: case Format.DXT3: case Format.DXT5: x_in = (i % 4 + i % powTileSize / 16 * 4) % tileSize; y_in = (i % 16 / 4 + i / (tileSize * 4) * 4) % tileSize; break; default: x_in = i % powTileSize % tileSize; y_in = i % powTileSize / tileSize; break; } } switch (settings.Orientation) { case Orientation.Default: yield return(new Point(x_out + x_in, y_out + y_in)); break; case Orientation.HorizontalFlip: yield return(new Point(stride - 1 - (x_out + x_in), y_out + y_in)); break; case Orientation.Rotate90: yield return(new Point(y_out + y_in, stride - 1 - (x_out + x_in))); break; case Orientation.Transpose: yield return(new Point(y_out + y_in, x_out + x_in)); break; case Orientation.TransposeTile: yield return(new Point(x_out + y_in, y_out + x_in)); break; default: throw new NotSupportedException($"Unknown orientation format {settings.Orientation}"); } } }
static IEnumerable <Color> GetColorsFromTexture(byte[] tex, ImageSettings settings) { var format = settings.Format; using (var br = new BinaryReaderX(new MemoryStream(tex))) { var etc1decoder = new ETC1.Decoder(); Enum.TryParse <DXT.Formats>(format.ToString(), false, out var dxtFormat); var dxtdecoder = new DXT.Decoder(dxtFormat); Enum.TryParse <ATI.Formats>(format.ToString(), false, out var atiFormat); var atidecoder = new ATI.Decoder(atiFormat); while (true) { int a = 255, r = 255, g = 255, b = 255; switch (format) { case Format.L8: b = g = r = br.ReadByte(); break; case Format.A8: a = br.ReadByte(); break; case Format.LA44: a = br.ReadNibble() * 17; b = g = r = br.ReadNibble() * 17; break; case Format.LA88: if (settings.ByteOrder == ByteOrder.LittleEndian) { a = br.ReadByte(); b = g = r = br.ReadByte(); } else { b = g = r = br.ReadByte(); a = br.ReadByte(); } break; case Format.HL88: g = br.ReadByte(); r = br.ReadByte(); break; case Format.RGB565: var s = br.ReadUInt16(); b = (s % 32) * 33 / 4; g = (s >> 5) % 64 * 65 / 16; r = (s >> 11) * 33 / 4; break; case Format.RGB888: b = br.ReadByte(); g = br.ReadByte(); r = br.ReadByte(); break; case Format.RGBA5551: var s2 = br.ReadUInt16(); a = (s2 & 1) * 255; b = (s2 >> 1) % 32 * 33 / 4; g = (s2 >> 6) % 32 * 33 / 4; r = (s2 >> 11) % 32 * 33 / 4; break; case Format.RGBA4444: a = br.ReadNibble() * 17; b = br.ReadNibble() * 17; g = br.ReadNibble() * 17; r = br.ReadNibble() * 17; break; case Format.RGBA8888: if (settings.ByteOrder == ByteOrder.LittleEndian) { a = br.ReadByte(); b = br.ReadByte(); g = br.ReadByte(); r = br.ReadByte(); } else { r = br.ReadByte(); g = br.ReadByte(); b = br.ReadByte(); a = br.ReadByte(); } break; case Format.RGBA1010102: var pack = br.ReadUInt32(); r = (int)((pack >> 22) / 4); g = (int)(((pack >> 12) & 0x3FF) / 4); b = (int)(((pack >> 2) & 0x3FF) / 4); a = (int)((pack & 0x3) * 85); break; case Format.ETC1: case Format.ETC1A4: yield return(etc1decoder.Get(() => { var etc1Alpha = format == Format.ETC1A4 ? br.ReadUInt64() : ulong.MaxValue; return new ETC1.PixelData { Alpha = etc1Alpha, Block = br.ReadStruct <ETC1.Block>() }; })); continue; case Format.DXT1: case Format.DXT3: case Format.DXT5: yield return(dxtdecoder.Get(() => { if (br.BaseStream.Position == br.BaseStream.Length) { return (0, 0); } var dxt5Alpha = format == Format.DXT3 || format == Format.DXT5 ? br.ReadUInt64() : 0; return (dxt5Alpha, br.ReadUInt64()); })); continue; case Format.ATI1L: case Format.ATI1A: case Format.ATI2: yield return(atidecoder.Get(() => { if (br.BaseStream.Position == br.BaseStream.Length) { return (0, 0); } return (br.ReadUInt64(), format == Format.ATI2 ? br.ReadUInt64() : 0); })); continue; case Format.L4: b = g = r = br.ReadNibble() * 17; break; case Format.A4: a = br.ReadNibble() * 17; break; case Format.PVRTC: var bmp = PVRTC.PvrtcDecompress.DecodeRgb4Bpp(tex, settings.Width); for (int y = 0; y < settings.Height; y++) { for (int x = 0; x < settings.Width; x++) { yield return(bmp.GetPixel(x, y)); } } continue; case Format.PVRTCA: bmp = PVRTC.PvrtcDecompress.DecodeRgba4Bpp(tex, settings.Width); for (int y = 0; y < settings.Height; y++) { for (int x = 0; x < settings.Width; x++) { yield return(bmp.GetPixel(x, y)); } } continue; default: throw new NotSupportedException($"Unknown image format {format}"); } yield return(Color.FromArgb(a, r, g, b)); } } }
public static byte[] Save(Bitmap bmp, ImageSettings settings) { settings.Width = bmp.Width; settings.Height = bmp.Height; var points = GetPointSequence(settings); var ms = new MemoryStream(); var etc1encoder = new ETC1.Encoder(); using (var bw = new BinaryWriterX(ms)) { foreach (var point in points) { int x = Clamp(point.X, 0, bmp.Width); int y = Clamp(point.Y, 0, bmp.Height); var color = bmp.GetPixel(x, y); //if (color.A == 0) color = default(Color); // daigasso seems to need this switch (settings.Format) { case Format.L8: bw.Write(color.G); break; case Format.A8: bw.Write(color.A); break; case Format.LA44: bw.WriteNibble(color.A / 16); bw.WriteNibble(color.G / 16); break; case Format.LA88: bw.Write(color.A); bw.Write(color.G); break; case Format.HL88: bw.Write(color.G); bw.Write(color.R); break; case Format.RGB565: bw.Write((short)((color.R / 8 << 11) | (color.G / 4 << 5) | (color.B / 8))); break; case Format.RGB888: bw.Write(color.B); bw.Write(color.G); bw.Write(color.R); break; case Format.RGBA5551: bw.Write((short)((color.R / 8 << 11) | (color.G / 8 << 6) | (color.B / 8 << 1) | color.A / 128)); break; case Format.RGBA4444: bw.WriteNibble(color.A / 16); bw.WriteNibble(color.B / 16); bw.WriteNibble(color.G / 16); bw.WriteNibble(color.R / 16); break; case Format.RGBA8888: bw.Write(color.A); bw.Write(color.B); bw.Write(color.G); bw.Write(color.R); break; case Format.ETC1: case Format.ETC1A4: etc1encoder.Set(color, data => { if (settings.Format == Format.ETC1A4) { bw.Write(data.Alpha); } bw.WriteStruct(data.Block); }); break; case Format.L4: bw.WriteNibble(color.G / 16); break; case Format.A4: bw.WriteNibble(color.A / 16); break; default: throw new NotSupportedException(); } } } return(ms.ToArray()); }