Switch-Toolbox/Switch_FileFormatsMain/FileFormats/Texture/BNTX.cs
KillzXGaming b8c8ef8395 Viewport toggle, and many more fixes
The aamp editor now acts like the byaml one.
Fixed aamp files saving with improper strings.
Added toggle for viewport in bfres editor. The setting will apply to the config so you can always disable it for quicker previewing for bfres.
More editors for bfres now dock rather than convering the whole screen.
Plugins can have icons for the toolstrip that contains the save/update icons.
Force PTCL texture injection due to an alignment thing
2019-04-25 15:21:41 -04:00

1239 lines
No EOL
50 KiB
C#

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.ComponentModel;
using System.Text;
using System.Threading;
using Syroot.NintenTools.NSW.Bntx;
using Syroot.NintenTools.NSW.Bntx.GFX;
using System.Runtime.InteropServices;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Switch_Toolbox.Library;
using Switch_Toolbox.Library.Forms;
using Switch_Toolbox.Library.IO;
using Switch_Toolbox.Library.Animations;
namespace FirstPlugin
{
public class BNTX : TreeNodeFile, IFileFormat
{
public bool CanSave { get; set; }
public string[] Description { get; set; } = new string[] { "BNTX" };
public string[] Extension { get; set; } = new string[] { "*.bntx" };
public string FileName { get; set; }
public string FilePath { get; set; }
public IFileInfo IFileInfo { get; set; }
public BNTX()
{
ImageKey = "bntx";
SelectedImageKey = "bntx";
}
public bool Identify(System.IO.Stream stream)
{
using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true))
{
return reader.CheckSignature(4, "BNTX");
}
}
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
types.Add(typeof(MenuExt));
return types.ToArray();
}
}
class MenuExt : IFileMenuExtension
{
public STToolStripItem[] NewFileMenuExtensions => newFileExt;
public STToolStripItem[] NewFromFileMenuExtensions => null;
public STToolStripItem[] ToolsMenuExtensions => toolExt;
public STToolStripItem[] TitleBarExtensions => null;
public STToolStripItem[] CompressionMenuExtensions => null;
public STToolStripItem[] ExperimentalMenuExtensions => null;
public STToolStripItem[] EditMenuExtensions => null;
public ToolStripButton[] IconButtonMenuExtensions => null;
STToolStripItem[] toolExt = new STToolStripItem[1];
STToolStripItem[] newFileExt = new STToolStripItem[1];
public MenuExt()
{
toolExt[0] = new STToolStripItem("Extract BNTX");
toolExt[0].Click += Export;
newFileExt[0] = new STToolStripItem("BNTX");
newFileExt[0].Click += New;
}
private void New(object sender, EventArgs args)
{
BNTX bntx = new BNTX();
bntx.IFileInfo = new IFileInfo();
bntx.FileName = "textures";
bntx.Load(new MemoryStream(CreateNewBNTX("textures")));
ObjectEditor editor = new ObjectEditor();
editor.Text = "Untitled-" + 0;
editor.treeViewCustom1.Nodes.Add(bntx);
LibraryGUI.Instance.CreateMdiWindow(editor);
}
private void Export(object sender, EventArgs args)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
foreach (string file in ofd.FileNames)
{
FileReader reader = new FileReader(ofd.FileName);
reader.Seek(16, SeekOrigin.Begin);
int offsetName = reader.ReadInt32();
reader.Seek(offsetName, SeekOrigin.Begin);
string Name = reader.ReadString(Syroot.BinaryData.BinaryStringFormat.ZeroTerminated);
reader.Close();
reader.Dispose();
// System.IO.File.Move(file, Name);
}
}
}
}
public Dictionary<string, TextureData> Textures;
public PropertieGridData prop;
public BntxFile BinaryTexFile;
public string FileNameText;
private bool hasParent;
public bool HasParent
{
get
{
hasParent = Parent != null;
if (ContextMenuStrip != null)
{
ContextMenuStrip.Items[0].Enabled = hasParent;
ContextMenuStrip.Items[6].Enabled = hasParent;
}
return hasParent;
}
}
public bool CanReplace;
public bool AllGLInitialized
{
get
{
if (Textures.Any(item => item.Value.RenderableTex.GLInitialized == false))
return false;
else
return true;
}
}
public void Load(Stream stream)
{
if (IFileInfo == null)
IFileInfo = new IFileInfo();
IFileInfo.UseEditMenu = true;
CanSave = true;
FileNameText = FileName;
LoadFile(stream, Name);
PluginRuntime.bntxContainers.Add(this);
//Check if bntx is parented to determine if an archive is used
bool checkParent = HasParent;
ContextMenuStrip = new STContextMenuStrip();
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export", null, Save, Keys.Control | Keys.E));
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Replace", null, Import, Keys.Control | Keys.R));
ContextMenuStrip.Items.Add(new ToolStripSeparator());
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Import Texture", null, ImportTextureAction, Keys.Control | Keys.I));
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Replace Textures (From Folder)", null, ReplaceAll, Keys.Control | Keys.T));
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export All Textures", null, ExportAll, Keys.Control | Keys.A));
ContextMenuStrip.Items.Add(new ToolStripSeparator());
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Rename", null, Rename, Keys.Control | Keys.N));
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Sort", null, SortTextures, Keys.Control | Keys.S));
ContextMenuStrip.Items.Add(new ToolStripSeparator());
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Clear", null, Clear, Keys.Control | Keys.C));
}
public void Unload()
{
foreach (var tex in Textures)
tex.Value.DisposeRenderable();
Textures.Clear();
Nodes.Clear();
this.BinaryTexFile = null;
if (PluginRuntime.bntxContainers.Contains(this))
PluginRuntime.bntxContainers.Remove(this);
}
public static byte[] CreateNewBNTX(string Name)
{
MemoryStream mem = new MemoryStream();
BntxFile bntx = new BntxFile();
bntx.Target = new char[] { 'N', 'X', ' ', ' ' };
bntx.Name = Name;
bntx.Alignment = 0xC;
bntx.TargetAddressSize = 0x40;
bntx.VersionMajor = 0;
bntx.VersionMajor2 = 4;
bntx.VersionMinor = 0;
bntx.VersionMinor2 = 0;
bntx.Textures = new List<Texture>();
bntx.TextureDict = new ResDict();
bntx.RelocationTable = new RelocationTable();
bntx.Flag = 0;
bntx.Save(mem);
var data = mem.ToArray();
mem.Close();
mem.Dispose();
return data;
}
public void RemoveTexture(TextureData textureData)
{
Nodes.Remove(textureData);
Textures.Remove(textureData.Text);
LibraryGUI.Instance.UpdateViewport();
}
public override void OnClick(TreeView treeView)
{
if (Parent != null && Parent is BFRES)
{
((BFRES)Parent).LoadEditors(this);
return;
}
STPropertyGrid editor = (STPropertyGrid)LibraryGUI.Instance.GetActiveContent(typeof(STPropertyGrid));
if (editor == null)
{
editor = new STPropertyGrid();
LibraryGUI.Instance.LoadEditor(editor);
}
editor.Text = Text;
editor.Dock = DockStyle.Fill;
editor.LoadProperty(BinaryTexFile, OnPropertyChanged);
}
public void OnPropertyChanged(){ Text = BinaryTexFile.Name; }
//Check right click to enable/disable certain context menus
public override void OnMouseRightClick(TreeView treeview)
{
bool checkParent = HasParent;
}
public void LoadFile(Stream stream, string Name = "")
{
Textures = new Dictionary<string, TextureData>(StringComparer.InvariantCultureIgnoreCase);
BinaryTexFile = new BntxFile(stream);
Text = BinaryTexFile.Name;
foreach (Texture tex in BinaryTexFile.Textures)
{
TextureData texData = new TextureData(tex, BinaryTexFile);
Nodes.Add(texData);
Textures.Add(tex.Name, texData);
}
BinaryTexFile.Textures.Clear(); //We don't need these in memeory anymore
BinaryTexFile.TextureDict.Clear();
}
private void ImportTextureAction(object sender, EventArgs args) {
ImportTexture();
}
public void ImportTexture()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = FileFilters.GetFilter(typeof(TextureData));
ofd.DefaultExt = "bftex";
ofd.Multiselect = true;
if (ofd.ShowDialog() == DialogResult.OK)
{
ImportTexture(ofd.FileNames);
}
}
public void ImportTexture(string[] FileNames)
{
BinaryTextureImporterList importer = new BinaryTextureImporterList();
List<TextureImporterSettings> settings = new List<TextureImporterSettings>();
foreach (string name in FileNames)
{
string ext = Path.GetExtension(name);
ext = ext.ToLower();
if (ext == ".dds" || ext == ".bftex" || ext == ".astc")
{
AddTexture(name);
}
else
{
settings.Add(LoadSettings(name));
}
}
if (settings.Count == 0)
{
importer.Dispose();
return;
}
importer.LoadSettings(settings);
if (importer.ShowDialog() == DialogResult.OK)
{
ImportTexture(settings);
}
settings.Clear();
GC.Collect();
Cursor.Current = Cursors.Default;
}
public void ImportTexture(ImageKeyFrame[] Keys, string TextureName)
{
BinaryTextureImporterList importer = new BinaryTextureImporterList();
List<TextureImporterSettings> settings = new List<TextureImporterSettings>();
foreach (ImageKeyFrame key in Keys) {
settings.Add(LoadSettings(key.Image, $"{TextureName}{key.Frame}"));
}
if (settings.Count == 0) {
importer.Dispose();
return;
}
importer.LoadSettings(settings);
if (importer.ShowDialog() == DialogResult.OK)
{
ImportTexture(settings);
}
settings.Clear();
GC.Collect();
Cursor.Current = Cursors.Default;
}
private void ImportTexture(List<TextureImporterSettings> settings)
{
Cursor.Current = Cursors.WaitCursor;
foreach (var setting in settings)
{
if (setting.GenerateMipmaps)
{
setting.DataBlockOutput.Clear();
setting.DataBlockOutput.Add(setting.GenerateMips());
}
if (setting.DataBlockOutput != null)
{
Texture tex = setting.FromBitMap(setting.DataBlockOutput, setting);
if (setting.textureData == null)
setting.textureData = new TextureData(tex, BinaryTexFile);
int i = 0;
if (Textures.ContainsKey(setting.textureData.Text))
{
setting.textureData.Text = setting.textureData.Text + i++;
}
Nodes.Add(setting.textureData);
Textures.Add(setting.textureData.Text, setting.textureData);
setting.textureData.LoadOpenGLTexture();
}
else
{
MessageBox.Show("Something went wrong???");
}
}
}
//This function is an optional feature that will import a dummy texture if one is missing in the materials
public void ImportPlaceholderTexture(string TextureName)
{
if (Textures.ContainsKey(TextureName))
return;
if (TextureName == "Basic_Alb")
ImportBasicTextures("Basic_Alb");
else if (TextureName == "Basic_Nrm")
ImportBasicTextures("Basic_Nrm");
else if (TextureName == "Basic_Spm")
ImportBasicTextures("Basic_Spm");
else if (TextureName == "Basic_Sphere")
ImportBasicTextures("Basic_Sphere");
else if (TextureName == "Basic_Mtl")
ImportBasicTextures("Basic_Mtl");
else if (TextureName == "Basic_Rgh")
ImportBasicTextures("Basic_Rgh");
else if (TextureName == "Basic_MRA")
ImportBasicTextures("Basic_MRA");
else if (TextureName == "Basic_Bake_st0")
ImportBasicTextures("Basic_Bake_st0");
else if (TextureName == "Basic_Bake_st1")
ImportBasicTextures("Basic_Bake_st1");
else if (TextureName == "Basic_Emm")
ImportBasicTextures("Basic_Emm");
else
{
ImportPlaceholderTexture(Properties.Resources.InjectTexErrored, TextureName);
}
}
private void ImportPlaceholderTexture(byte[] data, string TextureName)
{
TextureImporterSettings importDDS = new TextureImporterSettings();
importDDS.LoadDDS(TextureName, data);
Texture tex = importDDS.FromBitMap(importDDS.DataBlockOutput, importDDS);
TextureData texData = new TextureData(tex, BinaryTexFile);
texData.Text = TextureName;
Nodes.Add(texData);
Textures.Add(TextureName, texData);
texData.LoadOpenGLTexture();
}
public void ImportBasicTextures(string TextureName, bool BC5Nrm = true)
{
if (Textures.ContainsKey(TextureName))
return;
if (TextureName == "Basic_Alb")
ImportPlaceholderTexture(Properties.Resources.InjectTexErrored, TextureName);
if (TextureName == "Basic_Nrm" && BC5Nrm)
ImportPlaceholderTexture(Properties.Resources.Basic_NrmBC5, TextureName);
if (TextureName == "Basic_Nrm" && BC5Nrm == false)
ImportPlaceholderTexture(Properties.Resources.Basic_Nrm, TextureName);
if (TextureName == "Basic_Spm")
ImportPlaceholderTexture(Properties.Resources.Black, TextureName);
if (TextureName == "Basic_Sphere")
ImportPlaceholderTexture(Properties.Resources.Black, TextureName);
if (TextureName == "Basic_Mtl")
ImportPlaceholderTexture(Properties.Resources.Black, TextureName);
if (TextureName == "Basic_Rgh")
ImportPlaceholderTexture(Properties.Resources.White, TextureName);
if (TextureName == "Basic_MRA")
ImportPlaceholderTexture(Properties.Resources.Black, TextureName);
if (TextureName == "Basic_Bake_st0")
ImportPlaceholderTexture(Properties.Resources.Basic_Bake_st0, TextureName);
if (TextureName == "Basic_Bake_st1")
ImportPlaceholderTexture(Properties.Resources.Basic_Bake_st1, TextureName);
}
public TextureImporterSettings LoadSettings(Image image, string Name)
{
var importer = new TextureImporterSettings();
importer.LoadBitMap(image, Name);
return importer;
}
public TextureImporterSettings LoadSettings(string name)
{
var importer = new TextureImporterSettings();
string ext = Path.GetExtension(name);
ext = ext.ToLower();
switch (ext)
{
case ".bftex":
Texture tex = new Texture();
tex.Import(name);
break;
case ".dds":
importer.LoadDDS(name);
break;
default:
importer.LoadBitMap(name);
break;
}
return importer;
}
public TextureData AddTexture(string name)
{
var importer = new TextureImporterSettings();
Texture tex = null;
TextureData texData = null;
string ext = Path.GetExtension(name);
ext = ext.ToLower();
switch (ext)
{
case ".bftex":
tex = new Texture();
tex.Import(name);
texData = new TextureData(tex, BinaryTexFile);
break;
case ".dds":
case ".dds2":
importer.LoadDDS(name);
tex = importer.FromBitMap(importer.DataBlockOutput, importer);
texData = new TextureData(tex, BinaryTexFile);
break;
case ".astc":
importer.LoadASTC(name);
tex = importer.FromBitMap(importer.DataBlockOutput, importer);
texData = new TextureData(tex, BinaryTexFile);
break;
default:
break;
}
if (texData != null)
{
List<string> keyList = new List<string>(Textures.Keys);
texData.Text = Utils.RenameDuplicateString(keyList, texData.Text);
texData.Load(texData.Texture);
Nodes.Add(texData);
Textures.Add(texData.Text, texData);
}
return texData;
}
private void Clear(object sender, EventArgs args)
{
var result = MessageBox.Show("Are you sure you want to clear this section? This cannot be undone!",
"", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
Nodes.Clear();
Textures.Clear();
GC.Collect();
}
}
private void ReplaceAll(object sender, EventArgs args)
{
FolderSelectDialog sfd = new FolderSelectDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
foreach (string file in System.IO.Directory.GetFiles(sfd.SelectedPath))
{
string FileName = System.IO.Path.GetFileNameWithoutExtension(file);
foreach (TextureData node in Textures.Values)
{
if (FileName == node.Text)
{
node.Replace(file);
}
}
}
}
}
private void ExportAll(object sender, EventArgs args)
{
List<string> Formats = new List<string>();
Formats.Add("Cafe Binary Textures (.bftex)");
Formats.Add("Microsoft DDS (.dds)");
Formats.Add("Portable Graphics Network (.png)");
Formats.Add("Joint Photographic Experts Group (.jpg)");
Formats.Add("Bitmap Image (.bmp)");
Formats.Add("Tagged Image File Format (.tiff)");
FolderSelectDialog sfd = new FolderSelectDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
string folderPath = sfd.SelectedPath;
BatchFormatExport form = new BatchFormatExport(Formats);
if (form.ShowDialog() == DialogResult.OK)
{
foreach (TextureData tex in Nodes)
{
if (form.Index == 0)
tex.SaveBinaryTexture(folderPath + '\\' + tex.Text + ".bftex");
else if (form.Index == 1)
tex.SaveDDS(folderPath + '\\' + tex.Text + ".dds");
else if (form.Index == 2)
tex.SaveBitMap(folderPath + '\\' + tex.Text + ".png");
else if (form.Index == 3)
tex.SaveBitMap(folderPath + '\\' + tex.Text + ".jpg");
else if (form.Index == 4)
tex.SaveBitMap(folderPath + '\\' + tex.Text + ".bmp");
else if (form.Index == 5)
tex.SaveBitMap(folderPath + '\\' + tex.Text + ".tiff");
}
}
}
}
bool SortedAscending;
private void SortTextures(object sender, EventArgs args)
{
}
public byte[] Save()
{
BinaryTexFile.Textures.Clear();
BinaryTexFile.TextureDict.Clear();
foreach (TextureData tex in Textures.Values)
{
if (tex.IsEdited)
{
for (int i = 0; i < tex.EditedImages.Length; i++)
{
tex.SetImageData(tex.EditedImages[i].bitmap, tex.EditedImages[i].ArrayLevel);
tex.EditedImages[i].bitmap.Dispose();
}
tex.EditedImages = new EditedBitmap[0];
}
tex.Texture.Name = tex.Text;
BinaryTexFile.Textures.Add(tex.Texture);
BinaryTexFile.TextureDict.Add(tex.Text);
}
MemoryStream mem = new MemoryStream();
BinaryTexFile.Save(mem);
return mem.ToArray();
}
public class PropertieGridData
{
[Browsable(true)]
[Category("BNTX")]
[DisplayName("Name")]
public string Name { get; set; }
[Browsable(true)]
[Category("BNTX")]
[DisplayName("Original Path")]
public string Path { get; set; }
[Browsable(true)]
[Category("BNTX")]
[DisplayName("Target")]
public string Target { get; set; }
[Browsable(true)]
[ReadOnly(true)]
[Category("Versions")]
[DisplayName("Full Version")]
public string VersionFull { get; set; }
[Browsable(true)]
[Category("Versions")]
[DisplayName("Version Major 1")]
public uint VersionMajor { get; set; }
[Browsable(true)]
[Category("Versions")]
[DisplayName("Version Major 2")]
public uint VersionMajor2 { get; set; }
[Browsable(true)]
[Category("Versions")]
[DisplayName("Version Minor 1")]
public uint VersionMinor { get; set; }
[Browsable(true)]
[Category("Versions")]
[DisplayName("Version Minor 2")]
public uint VersionMinor2 { get; set; }
}
private void Import(object sender, EventArgs args)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
LoadFile(new MemoryStream(File.ReadAllBytes(ofd.FileName)));
}
}
private void Rename(object sender, EventArgs args)
{
RenameDialog dialog = new RenameDialog();
dialog.SetString(Text);
if (dialog.ShowDialog() == DialogResult.OK)
{
Text = dialog.textBox1.Text;
}
}
private void Save(object sender, EventArgs args)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.DefaultExt = "bntx";
sfd.Filter = "Supported Formats|*.bntx;";
sfd.FileName = FileName;
if (sfd.ShowDialog() == DialogResult.OK)
{
STFileSaver.SaveFileFormat(this, sfd.FileName);
}
}
}
public class TextureData : STGenericTexture
{
public override TEX_FORMAT[] SupportedFormats
{
get
{
return new TEX_FORMAT[]
{
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.BC6H_UF16,
TEX_FORMAT.BC6H_SF16,
TEX_FORMAT.BC7_UNORM,
TEX_FORMAT.BC7_UNORM_SRGB,
TEX_FORMAT.B5G5R5A1_UNORM,
TEX_FORMAT.B5G6R5_UNORM,
TEX_FORMAT.B8G8R8A8_UNORM_SRGB,
TEX_FORMAT.B8G8R8A8_UNORM,
TEX_FORMAT.R10G10B10A2_UNORM,
TEX_FORMAT.R16_UNORM,
TEX_FORMAT.B4G4R4A4_UNORM,
TEX_FORMAT.B5_G5_R5_A1_UNORM,
TEX_FORMAT.R8G8B8A8_UNORM_SRGB,
TEX_FORMAT.R8G8B8A8_UNORM,
TEX_FORMAT.R8_UNORM,
TEX_FORMAT.R8G8_UNORM,
TEX_FORMAT.R32G8X24_FLOAT,
TEX_FORMAT.ASTC_10x10_SRGB,
TEX_FORMAT.ASTC_10x10_UNORM,
TEX_FORMAT.ASTC_10x5_SRGB,
TEX_FORMAT.ASTC_10x5_UNORM,
TEX_FORMAT.ASTC_10x6_SRGB,
TEX_FORMAT.ASTC_10x6_UNORM,
TEX_FORMAT.ASTC_10x8_SRGB,
TEX_FORMAT.ASTC_10x8_UNORM,
TEX_FORMAT.ASTC_12x10_SRGB,
TEX_FORMAT.ASTC_12x10_UNORM,
TEX_FORMAT.ASTC_12x12_SRGB,
TEX_FORMAT.ASTC_12x12_UNORM,
TEX_FORMAT.ASTC_4x4_SRGB,
TEX_FORMAT.ASTC_4x4_UNORM,
TEX_FORMAT.ASTC_5x4_SRGB,
TEX_FORMAT.ASTC_5x4_UNORM,
TEX_FORMAT.ASTC_5x5_SRGB,
TEX_FORMAT.ASTC_5x5_UNORM,
TEX_FORMAT.ASTC_6x5_SRGB,
TEX_FORMAT.ASTC_6x5_UNORM,
TEX_FORMAT.ASTC_6x6_SRGB,
TEX_FORMAT.ASTC_6x6_UNORM,
TEX_FORMAT.ASTC_8x5_SRGB,
TEX_FORMAT.ASTC_8x5_UNORM,
TEX_FORMAT.ASTC_8x6_SRGB,
TEX_FORMAT.ASTC_8x6_UNORM,
TEX_FORMAT.ASTC_8x8_SRGB,
TEX_FORMAT.ASTC_8x8_UNORM,
};
}
}
public override bool CanEdit { get; set; } = true;
public Texture Texture;
public TextureData()
{
ImageKey = "Texture";
SelectedImageKey = "Texture";
}
public TextureData(Texture tex, BntxFile bntx)
{
Load(tex);
}
public void Load(Texture tex, int target = 1)
{
ImageKey = "Texture";
SelectedImageKey = "Texture";
Texture = tex;
Text = tex.Name;
Width = tex.Width;
Height = tex.Height;
ArrayCount = (uint)tex.TextureData.Count;
MipCount = (uint)tex.TextureData[0].Count;
Format = ConvertFormat(tex.Format);
ContextMenuStrip = new STContextMenuStrip();
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Export", null, Export, Keys.Control | Keys.E));
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Replace", null, Replace, Keys.Control | Keys.R));
ContextMenuStrip.Items.Add(new ToolStripSeparator());
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Rename", null, Rename, Keys.Control | Keys.N));
ContextMenuStrip.Items.Add(new ToolStripSeparator());
ContextMenuStrip.Items.Add(new ToolStripMenuItem("Delete", null, Remove, Keys.Control | Keys.Delete));
// LoadOpenGLTexture();
}
public static SurfaceFormat GenericToBntxSurfaceFormat(TEX_FORMAT texFormat)
{
switch (texFormat)
{
case TEX_FORMAT.BC1_UNORM: return SurfaceFormat.BC1_UNORM;
case TEX_FORMAT.BC1_UNORM_SRGB: return SurfaceFormat.BC1_SRGB;
case TEX_FORMAT.BC2_UNORM: return SurfaceFormat.BC2_UNORM;
case TEX_FORMAT.BC2_UNORM_SRGB: return SurfaceFormat.BC2_SRGB;
case TEX_FORMAT.BC3_UNORM: return SurfaceFormat.BC3_UNORM;
case TEX_FORMAT.BC3_UNORM_SRGB: return SurfaceFormat.BC3_SRGB;
case TEX_FORMAT.BC4_UNORM: return SurfaceFormat.BC4_UNORM;
case TEX_FORMAT.BC4_SNORM: return SurfaceFormat.BC4_SNORM;
case TEX_FORMAT.BC5_UNORM: return SurfaceFormat.BC5_UNORM;
case TEX_FORMAT.BC5_SNORM: return SurfaceFormat.BC5_SNORM;
case TEX_FORMAT.BC6H_UF16: return SurfaceFormat.BC6_UFLOAT;
case TEX_FORMAT.BC6H_SF16: return SurfaceFormat.BC6_UFLOAT;
case TEX_FORMAT.BC7_UNORM: return SurfaceFormat.BC7_UNORM;
case TEX_FORMAT.BC7_UNORM_SRGB: return SurfaceFormat.BC7_SRGB;
case TEX_FORMAT.B5G5R5A1_UNORM: return SurfaceFormat.B5_G5_R5_A1_UNORM;
case TEX_FORMAT.B5G6R5_UNORM: return SurfaceFormat.B5_G6_R5_UNORM;
case TEX_FORMAT.B8G8R8A8_UNORM_SRGB: return SurfaceFormat.B8_G8_R8_A8_SRGB;
case TEX_FORMAT.B8G8R8A8_UNORM: return SurfaceFormat.B8_G8_R8_A8_UNORM;
case TEX_FORMAT.R10G10B10A2_UNORM: return SurfaceFormat.R10_G10_B10_A2_UNORM;
case TEX_FORMAT.R16_UNORM: return SurfaceFormat.R16_UNORM;
case TEX_FORMAT.B4G4R4A4_UNORM: return SurfaceFormat.R4_G4_B4_A4_UNORM;
case TEX_FORMAT.B5_G5_R5_A1_UNORM: return SurfaceFormat.R5_G5_B5_A1_UNORM;
case TEX_FORMAT.R8G8B8A8_UNORM_SRGB: return SurfaceFormat.R8_G8_B8_A8_SRGB;
case TEX_FORMAT.R8G8B8A8_UNORM: return SurfaceFormat.R8_G8_B8_A8_UNORM;
case TEX_FORMAT.R8_UNORM: return SurfaceFormat.R8_UNORM;
case TEX_FORMAT.R8G8_UNORM: return SurfaceFormat.R8_G8_UNORM;
case TEX_FORMAT.R32G8X24_FLOAT: return SurfaceFormat.R32_G8_X24_FLOAT;
case TEX_FORMAT.ASTC_10x10_SRGB: return SurfaceFormat.ASTC_10x10_SRGB;
case TEX_FORMAT.ASTC_10x10_UNORM: return SurfaceFormat.ASTC_10x10_UNORM;
case TEX_FORMAT.ASTC_10x5_SRGB: return SurfaceFormat.ASTC_10x5_SRGB;
case TEX_FORMAT.ASTC_10x5_UNORM: return SurfaceFormat.ASTC_10x5_UNORM;
case TEX_FORMAT.ASTC_10x6_SRGB: return SurfaceFormat.ASTC_10x6_SRGB;
case TEX_FORMAT.ASTC_10x6_UNORM: return SurfaceFormat.ASTC_10x6_UNORM;
case TEX_FORMAT.ASTC_10x8_SRGB: return SurfaceFormat.ASTC_10x8_SRGB;
case TEX_FORMAT.ASTC_10x8_UNORM: return SurfaceFormat.ASTC_10x8_UNORM;
case TEX_FORMAT.ASTC_12x10_SRGB: return SurfaceFormat.ASTC_12x10_SRGB;
case TEX_FORMAT.ASTC_12x10_UNORM: return SurfaceFormat.ASTC_12x10_UNORM;
case TEX_FORMAT.ASTC_12x12_SRGB: return SurfaceFormat.ASTC_12x12_SRGB;
case TEX_FORMAT.ASTC_12x12_UNORM: return SurfaceFormat.ASTC_12x12_UNORM;
case TEX_FORMAT.ASTC_4x4_SRGB: return SurfaceFormat.ASTC_4x4_SRGB;
case TEX_FORMAT.ASTC_4x4_UNORM: return SurfaceFormat.ASTC_4x4_UNORM;
case TEX_FORMAT.ASTC_5x4_SRGB: return SurfaceFormat.ASTC_5x4_SRGB;
case TEX_FORMAT.ASTC_5x4_UNORM: return SurfaceFormat.ASTC_5x4_UNORM;
case TEX_FORMAT.ASTC_5x5_SRGB: return SurfaceFormat.ASTC_5x5_SRGB;
case TEX_FORMAT.ASTC_5x5_UNORM: return SurfaceFormat.ASTC_5x5_UNORM;
case TEX_FORMAT.ASTC_6x5_SRGB: return SurfaceFormat.ASTC_6x5_SRGB;
case TEX_FORMAT.ASTC_6x5_UNORM: return SurfaceFormat.ASTC_6x5_UNORM;
case TEX_FORMAT.ASTC_6x6_SRGB: return SurfaceFormat.ASTC_6x6_SRGB;
case TEX_FORMAT.ASTC_6x6_UNORM: return SurfaceFormat.ASTC_6x6_UNORM;
case TEX_FORMAT.ASTC_8x5_SRGB: return SurfaceFormat.ASTC_8x5_SRGB;
case TEX_FORMAT.ASTC_8x5_UNORM: return SurfaceFormat.ASTC_8x5_UNORM;
case TEX_FORMAT.ASTC_8x6_SRGB: return SurfaceFormat.ASTC_8x6_SRGB;
case TEX_FORMAT.ASTC_8x6_UNORM: return SurfaceFormat.ASTC_8x6_UNORM;
case TEX_FORMAT.ASTC_8x8_SRGB: return SurfaceFormat.ASTC_8x8_SRGB;
case TEX_FORMAT.ASTC_8x8_UNORM: return SurfaceFormat.ASTC_8x8_UNORM;
default:
throw new Exception($"Cannot convert format {texFormat}");
}
}
public static TEX_FORMAT ConvertFormat(SurfaceFormat surfaceFormat)
{
switch (surfaceFormat)
{
case SurfaceFormat.BC1_UNORM: return TEX_FORMAT.BC1_UNORM;
case SurfaceFormat.BC1_SRGB: return TEX_FORMAT.BC1_UNORM_SRGB;
case SurfaceFormat.BC2_UNORM: return TEX_FORMAT.BC2_UNORM;
case SurfaceFormat.BC2_SRGB: return TEX_FORMAT.BC2_UNORM;
case SurfaceFormat.BC3_UNORM: return TEX_FORMAT.BC3_UNORM;
case SurfaceFormat.BC3_SRGB: return TEX_FORMAT.BC3_UNORM;
case SurfaceFormat.BC4_UNORM: return TEX_FORMAT.BC4_UNORM;
case SurfaceFormat.BC4_SNORM: return TEX_FORMAT.BC4_SNORM;
case SurfaceFormat.BC5_UNORM: return TEX_FORMAT.BC5_UNORM;
case SurfaceFormat.BC5_SNORM: return TEX_FORMAT.BC5_SNORM;
case SurfaceFormat.BC6_UFLOAT: return TEX_FORMAT.BC6H_UF16;
case SurfaceFormat.BC6_FLOAT: return TEX_FORMAT.BC6H_SF16;
case SurfaceFormat.BC7_UNORM: return TEX_FORMAT.BC7_UNORM;
case SurfaceFormat.BC7_SRGB: return TEX_FORMAT.BC7_UNORM;
case SurfaceFormat.A1_B5_G5_R5_UNORM: return TEX_FORMAT.B5G5R5A1_UNORM;
case SurfaceFormat.A4_B4_G4_R4_UNORM: return TEX_FORMAT.B4G4R4A4_UNORM;
case SurfaceFormat.B5_G5_R5_A1_UNORM: return TEX_FORMAT.B5G5R5A1_UNORM;
case SurfaceFormat.B5_G6_R5_UNORM: return TEX_FORMAT.B5G6R5_UNORM;
case SurfaceFormat.B8_G8_R8_A8_SRGB: return TEX_FORMAT.B8G8R8A8_UNORM_SRGB;
case SurfaceFormat.B8_G8_R8_A8_UNORM: return TEX_FORMAT.B8G8R8A8_UNORM;
case SurfaceFormat.R10_G10_B10_A2_UNORM: return TEX_FORMAT.R10G10B10A2_UNORM;
case SurfaceFormat.R16_UNORM: return TEX_FORMAT.R16_UNORM;
case SurfaceFormat.R4_G4_B4_A4_UNORM: return TEX_FORMAT.B4G4R4A4_UNORM;
case SurfaceFormat.R4_G4_UNORM: return TEX_FORMAT.B4G4R4A4_UNORM;
case SurfaceFormat.R5_G5_B5_A1_UNORM: return TEX_FORMAT.B5G5R5A1_UNORM;
case SurfaceFormat.R5_G6_B5_UNORM: return TEX_FORMAT.B5G6R5_UNORM;
case SurfaceFormat.R8_G8_B8_A8_SRGB: return TEX_FORMAT.R8G8B8A8_UNORM_SRGB;
case SurfaceFormat.R8_G8_B8_A8_UNORM: return TEX_FORMAT.R8G8B8A8_UNORM;
case SurfaceFormat.R8_UNORM: return TEX_FORMAT.R8_UNORM;
case SurfaceFormat.R8_G8_UNORM: return TEX_FORMAT.R8G8_UNORM;
case SurfaceFormat.R32_G8_X24_FLOAT: return TEX_FORMAT.R32G8X24_FLOAT;
case SurfaceFormat.ASTC_10x10_SRGB: return TEX_FORMAT.ASTC_10x10_SRGB;
case SurfaceFormat.ASTC_10x10_UNORM: return TEX_FORMAT.ASTC_10x10_UNORM;
case SurfaceFormat.ASTC_10x5_SRGB: return TEX_FORMAT.ASTC_10x5_SRGB;
case SurfaceFormat.ASTC_10x5_UNORM: return TEX_FORMAT.ASTC_10x5_UNORM;
case SurfaceFormat.ASTC_10x6_SRGB: return TEX_FORMAT.ASTC_10x6_SRGB;
case SurfaceFormat.ASTC_10x6_UNORM: return TEX_FORMAT.ASTC_10x6_UNORM;
case SurfaceFormat.ASTC_10x8_SRGB: return TEX_FORMAT.ASTC_10x8_SRGB;
case SurfaceFormat.ASTC_10x8_UNORM: return TEX_FORMAT.ASTC_10x8_UNORM;
case SurfaceFormat.ASTC_12x10_SRGB: return TEX_FORMAT.ASTC_12x10_SRGB;
case SurfaceFormat.ASTC_12x10_UNORM: return TEX_FORMAT.ASTC_12x10_UNORM;
case SurfaceFormat.ASTC_12x12_SRGB: return TEX_FORMAT.ASTC_12x12_SRGB;
case SurfaceFormat.ASTC_12x12_UNORM: return TEX_FORMAT.ASTC_12x12_UNORM;
case SurfaceFormat.ASTC_4x4_SRGB: return TEX_FORMAT.ASTC_4x4_SRGB;
case SurfaceFormat.ASTC_4x4_UNORM: return TEX_FORMAT.ASTC_4x4_UNORM;
case SurfaceFormat.ASTC_5x4_SRGB: return TEX_FORMAT.ASTC_5x4_SRGB;
case SurfaceFormat.ASTC_5x4_UNORM: return TEX_FORMAT.ASTC_5x4_UNORM;
case SurfaceFormat.ASTC_5x5_SRGB: return TEX_FORMAT.ASTC_5x5_SRGB;
case SurfaceFormat.ASTC_5x5_UNORM: return TEX_FORMAT.ASTC_5x5_UNORM;
case SurfaceFormat.ASTC_6x5_SRGB: return TEX_FORMAT.ASTC_6x5_SRGB;
case SurfaceFormat.ASTC_6x5_UNORM: return TEX_FORMAT.ASTC_6x5_UNORM;
case SurfaceFormat.ASTC_6x6_SRGB: return TEX_FORMAT.ASTC_6x6_SRGB;
case SurfaceFormat.ASTC_6x6_UNORM: return TEX_FORMAT.ASTC_6x6_UNORM;
case SurfaceFormat.ASTC_8x5_SRGB: return TEX_FORMAT.ASTC_8x5_SRGB;
case SurfaceFormat.ASTC_8x5_UNORM: return TEX_FORMAT.ASTC_8x5_UNORM;
case SurfaceFormat.ASTC_8x6_SRGB: return TEX_FORMAT.ASTC_8x6_SRGB;
case SurfaceFormat.ASTC_8x6_UNORM: return TEX_FORMAT.ASTC_8x6_UNORM;
case SurfaceFormat.ASTC_8x8_SRGB: return TEX_FORMAT.ASTC_8x8_SRGB;
case SurfaceFormat.ASTC_8x8_UNORM: return TEX_FORMAT.ASTC_8x8_UNORM;
case SurfaceFormat.Invalid: throw new Exception("Invalid Format");
default:
throw new Exception($"Cannot convert format {surfaceFormat}");
}
}
public override void OnClick(TreeView treeView)
{
UpdateEditor();
}
private bool IsEditorActive()
{
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.Instance.GetActiveContent(typeof(ImageEditorBase));
if (editor == null)
return false;
else
return true;
}
public void UpdateEditor()
{
if (Parent != null && Parent.Parent != null && Parent.Parent is BFRES)
{
((BFRES)Parent.Parent).LoadEditors(this);
return;
}
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.Instance.GetActiveContent(typeof(ImageEditorBase));
if (editor == null)
{
editor = new ImageEditorBase();
editor.Dock = DockStyle.Fill;
LibraryGUI.Instance.LoadEditor(editor);
}
if (Texture.UserData != null)
{
UserDataEditor userEditor = (UserDataEditor)editor.GetActiveTabEditor(typeof(UserDataEditor));
if (userEditor == null)
{
userEditor = new UserDataEditor();
userEditor.Name = "User Data";
editor.AddCustomControl(userEditor, typeof(UserDataEditor));
}
userEditor.LoadUserData(Texture.UserData.ToList());
}
editor.Text = Text;
editor.LoadProperties(Texture);
editor.LoadImage(this);
}
private void Remove(object sender, EventArgs args)
{
((BNTX)Parent).RemoveTexture(this);
}
private void Rename(object sender, EventArgs args)
{
RenameDialog dialog = new RenameDialog();
dialog.SetString(Text);
if (dialog.ShowDialog() == DialogResult.OK)
{
((BNTX)Parent).Textures.Remove(Text);
Text = dialog.textBox1.Text;
((BNTX)Parent).Textures.Add(Text, this);
}
}
private void Replace(object sender, EventArgs args)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Supported Formats|*.bftex;*.dds;*.astc; *.png;*.tga;*.jpg;*.tiff|" +
"Binary Texture |*.bftex|" +
"Microsoft DDS |*.dds|" +
"Adaptable Scalable Texture Compression |*.astc|" +
"TGA |*.tga|" +
"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)
{
Replace(ofd.FileName, MipCount, Format);
}
}
//Max mip level will be set automatically unless overwritten
//The tex format can be adjusted in the function if necessary. Will normally be set to format in settings
public void Replace(string FileName, uint MaxMipLevel = 0, TEX_FORMAT DefaultFormat = TEX_FORMAT.BC1_UNORM_SRGB)
{
string ext = Path.GetExtension(FileName);
ext = ext.ToLower();
TextureImporterSettings setting = new TextureImporterSettings();
BinaryTextureImporterList importer = new BinaryTextureImporterList();
switch (ext)
{
case ".bftex":
Texture.Import(FileName);
Texture.Name = Text;
LoadOpenGLTexture();
break;
case ".dds":
setting.LoadDDS(FileName, null, this);
ApplyImportSettings(setting);
break;
case ".astc":
setting.LoadASTC(FileName);
ApplyImportSettings(setting);
break;
default:
setting.LoadBitMap(FileName);
importer.LoadSetting(setting);
setting.Format = GenericToBntxSurfaceFormat(DefaultFormat);
if (MaxMipLevel != 0)
{
importer.ForceMipCount = true;
setting.MipCount = MaxMipLevel;
importer.SelectedMipCount = MaxMipLevel;
}
if (importer.ShowDialog() == DialogResult.OK)
{
ApplyImportSettings(setting);
}
break;
}
}
private void ApplyImportSettings(TextureImporterSettings setting)
{
Cursor.Current = Cursors.WaitCursor;
if (setting.GenerateMipmaps)
{
setting.DataBlockOutput.Clear();
setting.DataBlockOutput.Add(setting.GenerateMips());
}
Texture = setting.FromBitMap(setting.DataBlockOutput, setting);
Texture.Name = Text;
Load(Texture);
LoadOpenGLTexture();
UpdateBfresTextureMapping();
if (IsEditorActive())
UpdateEditor();
}
private void UpdateBfresTextureMapping()
{
var viewport = LibraryGUI.Instance.GetActiveViewport();
if (viewport == null)
return;
foreach (var drawable in viewport.scene.objects)
{
if (drawable is BFRESRender)
{
((BFRESRender)drawable).UpdateTextureMaps();
}
}
}
private void Export(object sender, EventArgs args)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.FileName = Texture.Name;
sfd.DefaultExt = "bftex";
sfd.Filter = "Supported Formats|*.bftex;*.dds; *.png;*.jpg;*.tiff|" +
"Binary Texture |*.bftex|" +
"Microsoft DDS |*.dds|" +
"Portable Network Graphics |*.png|" +
"Joint Photographic Experts Group |*.jpg|" +
"Bitmap Image |*.bmp|" +
"Tagged Image File Format |*.tiff|" +
"All files(*.*)|*.*";
if (sfd.ShowDialog() == DialogResult.OK)
{
Export(sfd.FileName);
}
}
public void Export(string FileName, bool ExportSurfaceLevel = false,
bool ExportMipMapLevel = false, int SurfaceLevel = 0, int MipLevel = 0)
{
string ext = Path.GetExtension(FileName);
ext = ext.ToLower();
switch (ext)
{
case ".bftex":
SaveBinaryTexture(FileName);
break;
case ".dds":
SaveDDS(FileName);
break;
case ".astc":
SaveASTC(FileName);
break;
default:
SaveBitMap(FileName);
break;
}
}
public override void SetImageData(Bitmap bitmap, int ArrayLevel)
{
if (bitmap == null)
return; //Image is likely disposed and not needed to be applied
Texture.Format = GenericToBntxSurfaceFormat(Format);
Texture.Width = (uint)bitmap.Width;
Texture.Height = (uint)bitmap.Height;
if (MipCount != 1)
{
MipCount = GenerateMipCount(bitmap.Width, bitmap.Height);
if (MipCount == 0)
MipCount = 1;
}
Texture.MipCount = MipCount;
Texture.MipOffsets = new long[MipCount];
try
{
Texture.TextureData[ArrayLevel].Clear(); //Clear previous mip maps
var mipmaps = TextureImporterSettings.SwizzleSurfaceMipMaps(Texture,
GenerateMipsAndCompress(bitmap, Format), MipCount);
Texture.TextureData[ArrayLevel] = mipmaps;
//Combine mip map data
byte[] combinedMips = Utils.CombineByteArray(mipmaps.ToArray());
Texture.TextureData[ArrayLevel][0] = combinedMips;
}
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)
{
int target = 1;
try
{
uint blkWidth = GetBlockWidth(Format);
uint blkHeight = GetBlockHeight(Format);
uint blkDepth = GetBlockDepth(Format);
int linesPerBlockHeight = (1 << (int)Texture.BlockHeightLog2) * 8;
uint bpp = GetBytesPerPixel(Format);
for (int arrayLevel = 0; arrayLevel < Texture.TextureData.Count; arrayLevel++)
{
int blockHeightShift = 0;
for (int mipLevel = 0; mipLevel < Texture.TextureData[arrayLevel].Count; mipLevel++)
{
uint width = (uint)Math.Max(1, Texture.Width >> mipLevel);
uint height = (uint)Math.Max(1, Texture.Height >> mipLevel);
uint depth = (uint)Math.Max(1, Texture.Depth >> mipLevel);
uint size = TegraX1Swizzle.DIV_ROUND_UP(width, blkWidth) * TegraX1Swizzle.DIV_ROUND_UP(height, blkHeight) * bpp;
if (TegraX1Swizzle.pow2_round_up(TegraX1Swizzle.DIV_ROUND_UP(height, blkWidth)) < linesPerBlockHeight)
blockHeightShift += 1;
Console.WriteLine($"{width} {height} {depth} {blkWidth} {blkHeight} {blkDepth} {target} {bpp} {Texture.TileMode} {(int)Math.Max(0, Texture.BlockHeightLog2 - blockHeightShift)} {Texture.TextureData[arrayLevel][mipLevel].Length}");
byte[] result = TegraX1Swizzle.deswizzle(width, height, depth, blkWidth, blkHeight, blkDepth, target, bpp, (uint)Texture.TileMode, (int)Math.Max(0, Texture.BlockHeightLog2 - blockHeightShift), Texture.TextureData[arrayLevel][mipLevel]);
//Create a copy and use that to remove uneeded data
byte[] result_ = new byte[size];
Array.Copy(result, 0, result_, 0, size);
result = null;
if (ArrayLevel == arrayLevel && MipLevel == mipLevel)
return result_;
}
}
return new byte[0];
}
catch (Exception e)
{
MessageBox.Show($"Failed to swizzle texture {Text}! Exception: {e}");
return new byte[0];
}
}
internal void SaveBinaryTexture(string FileName)
{
Texture.Export(FileName, ((BNTX)Parent).BinaryTexFile);
}
}
}