1
0
mirror of synced 2024-12-01 02:27:22 +01:00
Switch-Toolbox/File_Format_Library/FileFormats/Grezzo/CTXB.cs

449 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Toolbox;
using System.Windows.Forms;
using Toolbox.Library;
using Toolbox.Library.IO;
using Toolbox.Library.Forms;
namespace FirstPlugin
{
public class CTXB : TreeNodeFile, IFileFormat, ITextureContainer, IContextMenuNode
{
public FileType FileType { get; set; } = FileType.Archive;
public bool CanSave { get; set; }
public string[] Description { get; set; } = new string[] { "CTXB" };
public string[] Extension { get; set; } = new string[] { "*.ctxb" };
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 Toolbox.Library.IO.FileReader(stream, true))
{
return reader.CheckSignature(4, "ctxb");
}
}
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
return types.ToArray();
}
}
public bool DisplayIcons => true;
public List<STGenericTexture> TextureList
{
get
{
List<STGenericTexture> textures = new List<STGenericTexture>();
foreach (STGenericTexture node in Nodes)
textures.Add(node);
return textures;
}
set { }
}
public override void OnAfterAdded()
{
if (Nodes.Count > 0 && this.TreeView != null)
this.TreeView.SelectedNode = Nodes[0];
}
public Header header;
public void Load(System.IO.Stream stream)
{
Text = FileName;
CanSave = true;
header = new Header();
header.Read(new FileReader(stream), this);
}
public ToolStripItem[] GetContextMenuItems()
{
return new ToolStripItem[]
{
new ToolStripMenuItem("Save", null, Save, Keys.Control | Keys.S),
new ToolStripMenuItem("Export All", null, ExportAllAction, Keys.Control | Keys.E),
new ToolStripMenuItem("Import", null, ImportAction, Keys.Control | Keys.I),
};
}
private void Save(object sender, EventArgs args)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.DefaultExt = "ctxb";
sfd.Filter = "Supported Formats|*.ctxb;";
sfd.FileName = FileName;
if (sfd.ShowDialog() == DialogResult.OK)
{
STFileSaver.SaveFileFormat(this, sfd.FileName);
}
}
protected void ExportAllAction(object sender, EventArgs e)
{
if (Nodes.Count <= 0)
return;
string formats = FileFilters.GTX;
string[] forms = formats.Split('|');
List<string> Formats = new List<string>();
for (int i = 0; i < forms.Length; i++)
{
if (i > 1 || i == (forms.Length - 1)) //Skip lines with all extensions
{
if (!forms[i].StartsWith("*"))
Formats.Add(forms[i]);
}
}
FolderSelectDialog sfd = new FolderSelectDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
string folderPath = sfd.SelectedPath;
BatchFormatExport form = new BatchFormatExport(Formats);
if (form.ShowDialog() == DialogResult.OK)
{
string extension = form.GetSelectedExtension();
extension.Replace(" ", string.Empty);
foreach (STGenericTexture node in Nodes)
{
((STGenericTexture)node).Export($"{folderPath}\\{node.Text}{extension}");
}
}
}
}
private void ImportAction(object sender, EventArgs e)
{
}
public void Unload()
{
}
public void Save(System.IO.Stream stream) {
header.Write(new FileWriter(stream));
}
public class Header
{
public List<Chunk> Chunks = new List<Chunk>();
public void Read(FileReader reader, CTXB ctxb)
{
string Magic = reader.ReadSignature(4, "ctxb");
uint FileSize = reader.ReadUInt32();
uint ChunkCount = reader.ReadUInt32();
reader.ReadUInt32(); //padding
uint ChunkOffset = reader.ReadUInt32();
uint TextureDataOffset = reader.ReadUInt32();
for (int i = 0; i < ChunkCount; i++)
Chunks.Add(new Chunk(reader));
for (int i = 0; i < ChunkCount; i++)
{
for (int t = 0; t < Chunks[i].Textures.Count; t++)
{
var texWrapper = new TextureWrapper(Chunks[i].Textures[t]);
texWrapper.Text = $"Texture_{t}";
texWrapper.ImageKey = "texture";
texWrapper.SelectedImageKey = texWrapper.ImageKey;
if (Chunks[i].Textures[t].Name != string.Empty)
texWrapper.Text = Chunks[i].Textures[t].Name;
texWrapper.Width = Chunks[i].Textures[t].Width;
texWrapper.Height = Chunks[i].Textures[t].Height;
texWrapper.Format = CTR_3DS.ConvertPICAToGenericFormat(Chunks[i].Textures[t].PicaFormat);
reader.SeekBegin(TextureDataOffset + Chunks[i].Textures[t].DataOffset);
Chunks[i].Textures[t].ImageData = reader.ReadBytes((int)Chunks[i].Textures[t].ImageSize);
ctxb.Nodes.Add(texWrapper);
}
}
}
public void Write(FileWriter writer)
{
writer.WriteSignature("ctxb");
writer.Write(uint.MaxValue);
writer.Write(Chunks.Count);
writer.Write(0);
writer.Write(24);
writer.Write(24 + Chunks.Count * 8 + (Chunks.Sum(x => x.Textures.Count) * 40));
foreach (var chunk in Chunks) {
long pos = writer.Position;
writer.WriteSignature(chunk.Magic);
writer.Write(uint.MaxValue);
writer.Write(chunk.Textures.Count);
foreach (var tex in chunk.Textures)
tex.Write(writer);
writer.WriteSectionSizeU32(pos + 4, pos, writer.Position);
}
uint dataOffset = 0;
foreach (var chunk in Chunks)
{
foreach (var tex in chunk.Textures) {
tex.DataOffset = dataOffset;
writer.Write(tex.ImageData);
dataOffset += (uint)tex.ImageData.Length;
}
}
using (writer.TemporarySeek(4, System.IO.SeekOrigin.Begin)) {
writer.Write((uint)writer.BaseStream.Length);
}
}
public class Chunk
{
public readonly string Magic = "tex ";
public List<Texture> Textures = new List<Texture>();
public Chunk(FileReader reader)
{
reader.ReadSignature(4, Magic);
uint SectionSize = reader.ReadUInt32();
uint TextureCount = reader.ReadUInt32();
for (int i = 0; i < TextureCount; i++)
Textures.Add(new Texture(reader));
}
}
}
public class Texture
{
public ushort MaxLevel { get; set; }
public ushort Unknown { get; set; }
public ushort Width { get; set; }
public ushort Height { get; set; }
public string Name { get; set; }
public uint ImageSize { get; set; }
public uint DataOffset { get; set; }
public CTR_3DS.PICASurfaceFormat PicaFormat;
public byte[] ImageData;
public enum TextureFormat : uint
{
ETC1 = 0x0000675A,
ETC1A4 = 0x0000675B,
RGBA8 = 0x14016752,
RGBA4444 = 0x80336752,
RGBA5551 = 0x80346752,
RGB565 = 0x83636754,
RGB8 = 0x14016754,
A8 = 0x14016756,
L8 = 0x14016757,
L4 = 0x67616757,
LA8 = 0x14016758,
}
public Texture() { }
public Texture(FileReader reader)
{
ImageSize = reader.ReadUInt32();
MaxLevel = reader.ReadUInt16();
Unknown = reader.ReadUInt16();
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
TextureFormat Format = reader.ReadEnum<TextureFormat>(true);
DataOffset = reader.ReadUInt32();
Name = reader.ReadString(16).TrimEnd('\0');
PicaFormat = FormatList[Format];
}
public void Write(FileWriter writer)
{
TextureFormat format = FormatList.FirstOrDefault(x => x.Value == PicaFormat).Key;
writer.Write(ImageData.Length);
writer.Write(MaxLevel);
writer.Write(Unknown);
writer.Write((ushort)Width);
writer.Write((ushort)Height);
writer.Write(format, true);
writer.Write(DataOffset);
writer.WriteString(Name, 16);
}
public static Dictionary<TextureFormat, CTR_3DS.PICASurfaceFormat> FormatList =
new Dictionary<TextureFormat, CTR_3DS.PICASurfaceFormat>()
{
{ TextureFormat.A8, CTR_3DS.PICASurfaceFormat.A8 },
{ TextureFormat.ETC1, CTR_3DS.PICASurfaceFormat.ETC1 },
{ TextureFormat.ETC1A4, CTR_3DS.PICASurfaceFormat.ETC1A4 },
{ TextureFormat.L4, CTR_3DS.PICASurfaceFormat.L4 },
{ TextureFormat.L8, CTR_3DS.PICASurfaceFormat.L8 },
{ TextureFormat.LA8, CTR_3DS.PICASurfaceFormat.LA8 },
{ TextureFormat.RGB565, CTR_3DS.PICASurfaceFormat.RGB565 },
{ TextureFormat.RGBA4444, CTR_3DS.PICASurfaceFormat.RGBA4 },
{ TextureFormat.RGBA5551, CTR_3DS.PICASurfaceFormat.RGBA5551 },
{ TextureFormat.RGBA8, CTR_3DS.PICASurfaceFormat.RGBA8 },
{ TextureFormat.RGB8, CTR_3DS.PICASurfaceFormat.RGB8 },
};
}
public class TextureWrapper : STGenericTexture
{
public Texture TextureInfo;
public byte[] ImageData
{
get { return TextureInfo.ImageData; }
set { TextureInfo.ImageData = value; }
}
public override bool CanEdit { get; set; } = true;
public override TEX_FORMAT[] SupportedFormats
{
get
{
return new TEX_FORMAT[]
{
TEX_FORMAT.B5G6R5_UNORM,
TEX_FORMAT.R8G8_UNORM,
TEX_FORMAT.B5G5R5A1_UNORM,
TEX_FORMAT.B4G4R4A4_UNORM,
TEX_FORMAT.LA8,
TEX_FORMAT.HIL08,
TEX_FORMAT.L8,
TEX_FORMAT.A8_UNORM,
TEX_FORMAT.LA4,
TEX_FORMAT.A4,
TEX_FORMAT.ETC1_UNORM,
TEX_FORMAT.ETC1_A4,
};
}
}
public TextureWrapper(Texture textureInfo)
{
TextureInfo = textureInfo;
PlatformSwizzle = PlatformSwizzle.Platform_3DS;
CanReplace = true;
CanRename = true;
CanDelete = true;
}
public override void OnClick(TreeView treeview)
{
UpdateEditor();
}
private void UpdateEditor()
{
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.GetActiveContent(typeof(ImageEditorBase));
if (editor == null)
{
editor = new ImageEditorBase();
editor.Dock = DockStyle.Fill;
LibraryGUI.LoadEditor(editor);
}
editor.Text = Text;
editor.LoadProperties(GenericProperties);
editor.LoadImage(this);
}
public void CreateNew(string fileName)
{
TextureInfo = new Texture();
Replace(fileName);
}
public override void Replace(string FileName)
{
CTR_3DSTextureImporter importer = new CTR_3DSTextureImporter();
CTR_3DSImporterSettings settings = new CTR_3DSImporterSettings();
if (Utils.GetExtension(FileName) == ".dds" ||
Utils.GetExtension(FileName) == ".dds2")
{
settings.LoadDDS(FileName);
importer.LoadSettings(new List<CTR_3DSImporterSettings>() { settings, });
ApplySettings(settings);
UpdateEditor();
}
else
{
settings.LoadBitMap(FileName);
settings.Format = CTR_3DS.ConvertToPICAFormat(Format);
if (MipCount == 1)
settings.MipCount = 1;
importer.LoadSettings(new List<CTR_3DSImporterSettings>() { settings, });
if (importer.ShowDialog() == DialogResult.OK)
{
if (settings.GenerateMipmaps && !settings.IsFinishedCompressing)
{
settings.DataBlockOutput.Clear();
settings.DataBlockOutput.Add(settings.GenerateMips());
}
Console.WriteLine($"ImageSize {this.ImageData.Length} {settings.DataBlockOutput[0]}");
ApplySettings(settings);
UpdateEditor();
}
}
}
private void ApplySettings(CTR_3DSImporterSettings settings)
{
this.ImageData = settings.DataBlockOutput[0];
this.Width = settings.TexWidth;
this.Height = settings.TexHeight;
this.Format = settings.GenericFormat;
this.MipCount = settings.MipCount;
this.Depth = settings.Depth;
this.ArrayCount = (uint)settings.DataBlockOutput.Count;
}
public override void SetImageData(System.Drawing.Bitmap bitmap, int ArrayLevel)
{
}
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0, int DepthLevel = 0)
{
return ImageData;
}
}
}
}