From 40e9757dbce8cfd31fdbc2c909af3963fd4e8e5c Mon Sep 17 00:00:00 2001 From: geofmigliacci Date: Tue, 5 Nov 2024 11:54:26 +0100 Subject: [PATCH] feat(ytdform): add drag-and-drop support for multiple files with uniqueness and power-of-2 checks --- .../GameFiles/Resources/Texture.cs | 33 ++++- CodeWalker/Forms/YtdForm.Designer.cs | 3 + CodeWalker/Forms/YtdForm.cs | 130 +++++++++++++++++- 3 files changed, 157 insertions(+), 9 deletions(-) diff --git a/CodeWalker.Core/GameFiles/Resources/Texture.cs b/CodeWalker.Core/GameFiles/Resources/Texture.cs index f27a96a5..adc188dd 100644 --- a/CodeWalker.Core/GameFiles/Resources/Texture.cs +++ b/CodeWalker.Core/GameFiles/Resources/Texture.cs @@ -3,16 +3,15 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; using System.Xml; namespace CodeWalker.GameFiles { - [TypeConverter(typeof(ExpandableObjectConverter))] public class TextureDictionary : ResourceFileBase + [TypeConverter(typeof(ExpandableObjectConverter))] + public class TextureDictionary : ResourceFileBase { public override long BlockLength { @@ -172,7 +171,7 @@ private void BuildDict() public void BuildFromTextureList(List textures) { textures.Sort((a, b) => a.NameHash.CompareTo(b.NameHash)); - + var texturehashes = new List(); foreach (var tex in textures) { @@ -188,7 +187,8 @@ public void BuildFromTextureList(List textures) } - [TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock + [TypeConverter(typeof(ExpandableObjectConverter))] + public class TextureBase : ResourceSystemBlock { public override long BlockLength { @@ -430,7 +430,8 @@ public override string ToString() } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class Texture : TextureBase + [TypeConverter(typeof(ExpandableObjectConverter))] + public class Texture : TextureBase { public override long BlockLength { @@ -475,6 +476,23 @@ public long MemoryUsage } } + /// + /// Checks if the and are powers of two. + /// + /// + /// It is not necessary for the and to be the same. + /// + /// + /// if the and are powers of two; otherwise, . + /// + public bool IsPowerOf2 + { + get + { + return (Width & (Width - 1)) == 0 && (Height & (Height - 1)) == 0; + } + } + public override void Read(ResourceDataReader reader, params object[] parameters) { base.Read(reader, parameters); @@ -612,7 +630,8 @@ public override string ToString() } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class TextureData : ResourceGraphicsBlock + [TypeConverter(typeof(ExpandableObjectConverter))] + public class TextureData : ResourceGraphicsBlock { public override long BlockLength { diff --git a/CodeWalker/Forms/YtdForm.Designer.cs b/CodeWalker/Forms/YtdForm.Designer.cs index 1bc0de00..46a20b3b 100644 --- a/CodeWalker/Forms/YtdForm.Designer.cs +++ b/CodeWalker/Forms/YtdForm.Designer.cs @@ -310,6 +310,7 @@ private void InitializeComponent() // // TexturesListView // + this.TexturesListView.AllowDrop = true; this.TexturesListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); @@ -326,6 +327,8 @@ private void InitializeComponent() this.TexturesListView.UseCompatibleStateImageBehavior = false; this.TexturesListView.View = System.Windows.Forms.View.Details; this.TexturesListView.SelectedIndexChanged += new System.EventHandler(this.TexturesListView_SelectedIndexChanged); + this.TexturesListView.DragDrop += new System.Windows.Forms.DragEventHandler(this.TexturesListView_DragDrop); + this.TexturesListView.DragEnter += new System.Windows.Forms.DragEventHandler(this.TexturesListView_DragEnter); // // TextureNameColumnHeader // diff --git a/CodeWalker/Forms/YtdForm.cs b/CodeWalker/Forms/YtdForm.cs index e064dea4..4f7f5472 100644 --- a/CodeWalker/Forms/YtdForm.cs +++ b/CodeWalker/Forms/YtdForm.cs @@ -200,8 +200,13 @@ private void AddTexture() var tex = OpenDDSFile(); if (tex == null) return; - var textures = new List(); - textures.AddRange(TexDict.Textures.data_items); + var textures = new List(TexDict.Textures.data_items); + if (textures.Any(t => t.Name.Equals(tex.Name, StringComparison.InvariantCultureIgnoreCase))) + { + MessageBox.Show($"A texture with the name {tex.Name} already exists in this YTD.\nAll textures must have unique names in a YTD."); + return; + } + textures.Add(tex); TexDict.BuildFromTextureList(textures); @@ -322,6 +327,12 @@ private Texture OpenDDSFile() { var dds = File.ReadAllBytes(fn); var tex = DDSIO.GetTexture(dds); + if (!tex.IsPowerOf2) + { + MessageBox.Show($"The texture is not a power of two texture.\nThe texture must have a width and height equal to a power of two e.g. 256, 512, 1024, etc..."); + return null; + } + tex.Name = Path.GetFileNameWithoutExtension(fn); tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant()); JenkIndex.Ensure(tex.Name?.ToLowerInvariant()); @@ -649,5 +660,120 @@ private void SelTextureNameTextBox_TextChanged(object sender, EventArgs e) { RenameTexture(SelTextureNameTextBox.Text); } + + /// + /// Add one or more textures to the YTD by dragging and dropping them into the YTD. + /// + /// + /// The source of the event. + /// + /// + /// The that contains the event data. + /// + private void TexturesListView_DragDrop(object sender, DragEventArgs e) + { + if (TexDict.Textures?.data_items == null) + { + return; + } + + if (!e.Data.GetDataPresent(DataFormats.FileDrop)) + { + return; + } + + string[] filePaths = e.Data.GetData(DataFormats.FileDrop) as string[]; + if (filePaths == null) + { + // Couldn't get files paths? + return; + } + + List newTextures = new List(TexDict.Textures.data_items); + foreach (string filePath in filePaths) + { + if (!filePath.EndsWith(".dds", StringComparison.InvariantCultureIgnoreCase)) + { + // Not a DDS file? + MessageBox.Show("You can only drag and drop DDS files into an YTD."); + return; + } + + if (!File.Exists(filePath)) + { + // Couldn't find file? + MessageBox.Show($"Couldn't find file {filePath}."); + return; + } + + // Check if a texture with the same name already exists in the YTD. + string textureName = Path.GetFileNameWithoutExtension(filePath); + if(newTextures.Any(t => t.Name.Equals(textureName, StringComparison.InvariantCultureIgnoreCase))) + { + MessageBox.Show($"A texture with the name {textureName} already exists in this YTD.\nAll textures must have unique names in a YTD."); + return; + } + + byte[] dds = File.ReadAllBytes(filePath); + try + { + Texture texture = DDSIO.GetTexture(dds); + if(texture == null) + { + throw new Exception("Something went wrong while loading the texture."); + } + + if(!texture.IsPowerOf2) + { + MessageBox.Show($"The texture {filePath} is not a power of two texture.\nThe texture must have a width and height equal to a power of two e.g. 256, 512, 1024, etc..."); + return; + } + + texture.Name = textureName; + texture.NameHash = JenkHash.GenHash(texture.Name?.ToLowerInvariant()); + JenkIndex.Ensure(texture.Name?.ToLowerInvariant()); + newTextures.Add(texture); + } + catch + { + MessageBox.Show($"Unable to load {filePath}.\nAre you sure it's a valid .dds file?"); + } + } + + TexDict.BuildFromTextureList(newTextures); + + Modified = true; + + LoadTexDict(TexDict, FileName); + + UpdateModelFormTextures(); + } + + /// + /// Checks if the files can be dropped into the YTD. + /// + /// + /// The source of the event. + /// + /// + /// The that contains the event data. + /// + private void TexturesListView_DragEnter(object sender, DragEventArgs e) + { + if (!e.Data.GetDataPresent(DataFormats.FileDrop)) + { + e.Effect = DragDropEffects.None; + return; + } + + string[] files = e.Data.GetData(DataFormats.FileDrop) as string[]; + if (files == null) + { + e.Effect = DragDropEffects.None; + return; + } + + e.Effect = DragDropEffects.Copy; + } } }