512 lines
17 KiB
C#
512 lines
17 KiB
C#
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<ImageEditorForm>, IFileFormat
|
|
{
|
|
public FileType FileType { get; set; } = FileType.Image;
|
|
|
|
public override TEX_FORMAT[] SupportedFormats
|
|
{
|
|
get
|
|
{
|
|
return new TEX_FORMAT[]
|
|
{
|
|
TEX_FORMAT.ETC1,
|
|
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();
|
|
}
|
|
}
|
|
|
|
ImageEditorForm form;
|
|
public ImageEditorForm 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 ImageEditorForm(IsDialog);
|
|
form.editorBase.Text = Text;
|
|
form.editorBase.Dock = DockStyle.Fill;
|
|
form.editorBase.AddFileContextEvent("Save", Save);
|
|
form.editorBase.AddFileContextEvent("Replace", Replace);
|
|
form.editorBase.LoadProperties(prop);
|
|
form.editorBase.LoadImage(this);
|
|
|
|
return form;
|
|
}
|
|
|
|
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.editorBase.LoadProperties(prop);
|
|
form.editorBase.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;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|