using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Switch_Toolbox.Library; using Switch_Toolbox.Library.IO; using Switch_Toolbox.Library.Forms; using System.ComponentModel; using Bfres.Structs; namespace FirstPlugin { public class BCLIM : STGenericTexture, IEditor<ImageEditorBase>, IFileFormat { public FileType FileType { get; set; } = FileType.Image; public override TEX_FORMAT[] SupportedFormats { get { return new TEX_FORMAT[] { TEX_FORMAT.ETC1_UNORM, TEX_FORMAT.ETC1_A4, TEX_FORMAT.BC1_UNORM, TEX_FORMAT.BC1_UNORM_SRGB, TEX_FORMAT.BC2_UNORM, TEX_FORMAT.BC2_UNORM_SRGB, TEX_FORMAT.BC3_UNORM, TEX_FORMAT.BC3_UNORM_SRGB, TEX_FORMAT.BC4_UNORM, TEX_FORMAT.BC4_SNORM, TEX_FORMAT.BC5_UNORM, TEX_FORMAT.BC5_SNORM, TEX_FORMAT.R8G8B8A8_UNORM_SRGB, TEX_FORMAT.R8G8B8A8_UNORM, }; } } public BCLIMFormat ConvertFormatGenericToBflim(TEX_FORMAT Format) { switch (Format) { case TEX_FORMAT.A8_UNORM: return BCLIMFormat.L8_UNORM; case TEX_FORMAT.R8G8_UNORM: return BCLIMFormat.LA8; case TEX_FORMAT.B5G6R5_UNORM: return BCLIMFormat.RGB565; case TEX_FORMAT.R8G8B8A8_UNORM: return BCLIMFormat.RGBA8; case TEX_FORMAT.R8G8B8A8_UNORM_SRGB: return BCLIMFormat.RGBA8_SRGB; case TEX_FORMAT.R10G10B10A2_UNORM: return BCLIMFormat.RGB10A2_UNORM; case TEX_FORMAT.B4G4R4A4_UNORM: return BCLIMFormat.RGBA4; case TEX_FORMAT.BC1_UNORM: return BCLIMFormat.BC1_UNORM; case TEX_FORMAT.BC1_UNORM_SRGB: return BCLIMFormat.BC1_SRGB; case TEX_FORMAT.BC2_UNORM: return BCLIMFormat.BC2_UNORM; case TEX_FORMAT.BC2_UNORM_SRGB: return BCLIMFormat.BC2_SRGB; case TEX_FORMAT.BC3_UNORM: return BCLIMFormat.BC3_UNORM; case TEX_FORMAT.BC3_UNORM_SRGB: return BCLIMFormat.BC3_SRGB; case TEX_FORMAT.BC4_UNORM: return BCLIMFormat.BC4A_UNORM; case TEX_FORMAT.BC4_SNORM: return BCLIMFormat.BC4L_UNORM; case TEX_FORMAT.BC5_UNORM: return BCLIMFormat.BC5_UNORM; default: throw new Exception("Unsupported format " + Format); } } public override bool CanEdit { get; set; } = true; public bool CanSave { get; set; } public string[] Description { get; set; } = new string[] { "Layout Image" }; public string[] Extension { get; set; } = new string[] { "*.bclim" }; public string FileName { get; set; } public string FilePath { get; set; } public IFileInfo IFileInfo { get; set; } public bool Identify(System.IO.Stream stream) { using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true)) { return reader.CheckSignature(4, "CLIM", reader.BaseStream.Length - 0x28); } } public Type[] Types { get { List<Type> types = new List<Type>(); return types.ToArray(); } } ImageEditorBase form; public ImageEditorBase OpenForm() { bool IsDialog = IFileInfo != null && IFileInfo.InArchive; Properties prop = new Properties(); prop.Width = Width; prop.Height = Height; prop.Depth = Depth; prop.MipCount = MipCount; prop.ArrayCount = ArrayCount; prop.ImageSize = (uint)ImageData.Length; prop.Format = Format; form = new ImageEditorBase(); form.Text = Text; form.Dock = DockStyle.Fill; form.AddFileContextEvent("Save", Save); form.AddFileContextEvent("Replace", Replace); form.LoadProperties(prop); form.LoadImage(this); return form; } public void FillEditor(UserControl control) { UpdateForm(); } private void UpdateForm() { if (form != null && image != null) { Properties prop = new Properties(); prop.Width = Width; prop.Height = Height; prop.Depth = Depth; prop.MipCount = MipCount; prop.ArrayCount = ArrayCount; prop.ImageSize = (uint)ImageData.Length; prop.Format = Format; form.LoadProperties(prop); form.LoadImage(this); } } private void Replace(object sender, EventArgs args) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Supported Formats|*.dds; *.png;*.tga;*.jpg;*.tiff|" + "Microsoft DDS |*.dds|" + "Portable Network Graphics |*.png|" + "Joint Photographic Experts Group |*.jpg|" + "Bitmap Image |*.bmp|" + "Tagged Image File Format |*.tiff|" + "All files(*.*)|*.*"; ofd.Multiselect = false; if (ofd.ShowDialog() == DialogResult.OK) { FTEX ftex = new FTEX(); ftex.ReplaceTexture(ofd.FileName, Format, 1, SupportedFormats, true, true, false); if (ftex.texture != null) { image.BCLIMFormat = ConvertFormatGenericToBflim(ftex.Format); image.Height = (ushort)ftex.texture.Height; image.Width = (ushort)ftex.texture.Width; Format = GetFormat(image.BCLIMFormat); Width = image.Width; Height = image.Height; ImageData = ftex.texture.Data; UpdateForm(); } } } private void Save(object sender, EventArgs args) { List<IFileFormat> formats = new List<IFileFormat>(); formats.Add(this); SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = Utils.GetAllFilters(formats); sfd.FileName = FileName; if (sfd.ShowDialog() == DialogResult.OK) { STFileSaver.SaveFileFormat(this, sfd.FileName); } } public class Properties { [Browsable(true)] [ReadOnly(true)] [Description("Height of the image.")] [Category("Image Info")] public uint Height { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("Width of the image.")] [Category("Image Info")] public uint Width { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("Format of the image.")] [Category("Image Info")] public TEX_FORMAT Format { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("Depth of the image (3D type).")] [Category("Image Info")] public uint Depth { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("Mip map count of the image.")] [Category("Image Info")] public uint MipCount { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("Array count of the image for multiple surfaces.")] [Category("Image Info")] public uint ArrayCount { get; set; } [Browsable(true)] [ReadOnly(true)] [Description("The image size in bytes.")] [Category("Image Info")] public uint ImageSize { get; set; } } Header header; Image image; byte[] ImageData; public void Load(System.IO.Stream stream) { CanSave = true; Text = FileName; using (var reader = new FileReader(stream)) { reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; uint FileSize = (uint)reader.BaseStream.Length; reader.Seek(FileSize - 0x28, SeekOrigin.Begin); header = new Header(); header.Read(reader); reader.Seek(header.HeaderSize + FileSize - 0x28, SeekOrigin.Begin); image = new Image(); image.Read(reader); Format = GetFormat(image.BCLIMFormat); Width = image.Width; Height = image.Height; LoadComponents(Format); reader.Position = 0; ImageData = reader.ReadBytes((int)image.ImageSize); PlatformSwizzle = PlatformSwizzle.Platform_3DS; } } private void LoadComponents(TEX_FORMAT Format) { switch (Format) { case TEX_FORMAT.BC5_SNORM: case TEX_FORMAT.BC5_UNORM: RedChannel = STChannelType.Red; GreenChannel = STChannelType.Red; BlueChannel = STChannelType.Red; AlphaChannel = STChannelType.Green; break; case TEX_FORMAT.BC4_SNORM: case TEX_FORMAT.BC4_UNORM: RedChannel = STChannelType.Red; GreenChannel = STChannelType.Red; BlueChannel = STChannelType.Red; AlphaChannel = STChannelType.Red; break; } } private TEX_FORMAT GetFormat(BCLIMFormat format) { switch (format) { case BCLIMFormat.L8_UNORM: return TEX_FORMAT.L8; case BCLIMFormat.A8: return TEX_FORMAT.A8_UNORM; case BCLIMFormat.LA4: return TEX_FORMAT.LA4; case BCLIMFormat.LA8: return TEX_FORMAT.LA8; case BCLIMFormat.HILO8: return TEX_FORMAT.HIL08; case BCLIMFormat.ETC1: return TEX_FORMAT.ETC1_UNORM; case BCLIMFormat.ETC1A4: return TEX_FORMAT.ETC1_A4; case BCLIMFormat.RGB565: return TEX_FORMAT.B5G6R5_UNORM; case BCLIMFormat.RGBX8: case BCLIMFormat.RGBA8: return TEX_FORMAT.R8G8B8A8_UNORM; case BCLIMFormat.RGBA8_SRGB: return TEX_FORMAT.R8G8B8A8_UNORM_SRGB; case BCLIMFormat.RGB10A2_UNORM: return TEX_FORMAT.R10G10B10A2_UNORM; case BCLIMFormat.RGB5A1: return TEX_FORMAT.B5G5R5A1_UNORM; case BCLIMFormat.RGBA4: return TEX_FORMAT.B4G4R4A4_UNORM; case BCLIMFormat.BC1_UNORM: return TEX_FORMAT.BC1_UNORM; case BCLIMFormat.BC1_SRGB: return TEX_FORMAT.BC1_UNORM_SRGB; case BCLIMFormat.BC2_UNORM: return TEX_FORMAT.BC2_UNORM_SRGB; case BCLIMFormat.BC3_UNORM: return TEX_FORMAT.BC3_UNORM; case BCLIMFormat.BC3_SRGB: return TEX_FORMAT.BC3_UNORM_SRGB; case BCLIMFormat.BC4L_UNORM: case BCLIMFormat.BC4A_UNORM: return TEX_FORMAT.BC4_UNORM; case BCLIMFormat.BC5_UNORM: return TEX_FORMAT.BC5_UNORM; default: throw new Exception("Unsupported format " + format); } } public enum BCLIMFormat : byte { L8_UNORM = 0, A8 = 1, LA4 = 2, LA8 = 3, HILO8 = 4, RGB565 = 5, RGBX8 = 6, RGB5A1 = 7, RGBA4 = 8, RGBA8 = 9, ETC1 = 10, ETC1A4 = 11, BC1_UNORM, BC2_UNORM, BC3_UNORM, BC4L_UNORM, BC4A_UNORM, BC5_UNORM, L4_UNORM, A4_UNORM, RGBA8_SRGB, BC1_SRGB, BC2_SRGB, BC3_SRGB, RGB10A2_UNORM, RGB565_Indirect_UNORM, } public override void SetImageData(System.Drawing.Bitmap bitmap, int ArrayLevel) { if (bitmap == null || image == null) return; //Image is likely disposed and not needed to be applied MipCount = 1; var CtrFormat = CTR_3DS.ConvertToPICAFormat(Format); try { //Create image block from bitmap first var data = GenerateMipsAndCompress(bitmap, MipCount, Format); //Swizzle and create surface /* var surface = GX2.CreateGx2Texture(data, Text, (uint)image.TileMode, (uint)0, (uint)image.Width, (uint)image.Height, (uint)1, (uint)Gx2Format, (uint)0, (uint)1, (uint)MipCount ); image.Swizzle = (byte)surface.swizzle; image.BCLIMFormat = ConvertFormatGenericToBflim(Format); image.Height = (ushort)surface.height; image.Width = (ushort)surface.width;*/ Width = image.Width; Height = image.Height; // ImageData = surface.data; IsEdited = true; LoadOpenGLTexture(); LibraryGUI.Instance.UpdateViewport(); } catch (Exception ex) { STErrorDialog.Show("Failed to swizzle and compress image " + Text, "Error", ex.ToString()); } } public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0) { return ImageData; } public void Unload() { } public byte[] Save() { MemoryStream mem = new MemoryStream(); using (var writer = new FileWriter(mem)) { writer.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; writer.Write(ImageData); long headerPos = writer.Position; header.Write(writer); image.Write(writer); writer.Write(ImageData.Length); writer.Seek(headerPos + 0x0C, SeekOrigin.Begin); writer.Write((uint)writer.BaseStream.Length); } return mem.ToArray(); } public class Header { public ushort ByteOrderMark; public ushort HeaderSize; public uint Version; public ushort blockount; public ushort padding; public void Read(FileReader reader) { reader.ReadSignature(4, "CLIM"); ByteOrderMark = reader.ReadUInt16(); reader.CheckByteOrderMark(ByteOrderMark); HeaderSize = reader.ReadUInt16(); Version = reader.ReadUInt32(); uint fileSize = reader.ReadUInt32(); blockount = reader.ReadUInt16(); padding = reader.ReadUInt16(); } public void Write(FileWriter writer) { writer.WriteSignature("FLIM"); writer.Write(ByteOrderMark); writer.Write(HeaderSize); writer.Write(Version); writer.Write(uint.MaxValue); writer.Write(blockount); writer.Write(padding); } } public class Image { public uint Size; public ushort Width; public ushort Height; public ushort Alignment; public BCLIMFormat BCLIMFormat; public byte Flags; public uint ImageSize; public void Read(FileReader reader) { reader.ReadSignature(4, "imag"); Size = reader.ReadUInt32(); Width = reader.ReadUInt16(); Height = reader.ReadUInt16(); BCLIMFormat = reader.ReadEnum<BCLIMFormat>(true); Flags = reader.ReadByte(); Alignment = reader.ReadUInt16(); ImageSize = reader.ReadUInt32(); } public void Write(FileWriter writer) { writer.WriteSignature("imag"); writer.Write(Size); writer.Write(Width); writer.Write(Height); writer.Write(BCLIMFormat, true); writer.Write(Flags); writer.Write(Alignment); writer.Write(ImageSize); } } } }