feat(ytdform): add drag-and-drop support for multiple files with uniqueness and power-of-2 checks

This commit is contained in:
geofmigliacci 2024-11-05 11:54:26 +01:00
parent c45ea83733
commit 40e9757dbc
3 changed files with 157 additions and 9 deletions

View File

@ -3,16 +3,15 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Xml; using System.Xml;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureDictionary : ResourceFileBase [TypeConverter(typeof(ExpandableObjectConverter))]
public class TextureDictionary : ResourceFileBase
{ {
public override long BlockLength public override long BlockLength
{ {
@ -188,7 +187,8 @@ namespace CodeWalker.GameFiles
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock [TypeConverter(typeof(ExpandableObjectConverter))]
public class TextureBase : ResourceSystemBlock
{ {
public override long BlockLength public override long BlockLength
{ {
@ -430,7 +430,8 @@ namespace CodeWalker.GameFiles
} }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class Texture : TextureBase [TypeConverter(typeof(ExpandableObjectConverter))]
public class Texture : TextureBase
{ {
public override long BlockLength public override long BlockLength
{ {
@ -475,6 +476,23 @@ namespace CodeWalker.GameFiles
} }
} }
/// <summary>
/// Checks if the <see cref="Texture.Width"/> and <see cref="Texture.Height"/> are powers of two.
/// </summary>
/// <remarks>
/// It is not necessary for the <see cref="Texture.Width"/> and <see cref="Texture.Height"/> to be the same.
/// </remarks>
/// <value>
/// <see langword="true"/> if the <see cref="Texture.Width"/> and <see cref="Texture.Height"/> are powers of two; otherwise, <see langword="false"/>.
/// </value>
public bool IsPowerOf2
{
get
{
return (Width & (Width - 1)) == 0 && (Height & (Height - 1)) == 0;
}
}
public override void Read(ResourceDataReader reader, params object[] parameters) public override void Read(ResourceDataReader reader, params object[] parameters)
{ {
base.Read(reader, parameters); base.Read(reader, parameters);
@ -612,7 +630,8 @@ namespace CodeWalker.GameFiles
} }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureData : ResourceGraphicsBlock [TypeConverter(typeof(ExpandableObjectConverter))]
public class TextureData : ResourceGraphicsBlock
{ {
public override long BlockLength public override long BlockLength
{ {

View File

@ -310,6 +310,7 @@
// //
// TexturesListView // TexturesListView
// //
this.TexturesListView.AllowDrop = true;
this.TexturesListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 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.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
@ -326,6 +327,8 @@
this.TexturesListView.UseCompatibleStateImageBehavior = false; this.TexturesListView.UseCompatibleStateImageBehavior = false;
this.TexturesListView.View = System.Windows.Forms.View.Details; this.TexturesListView.View = System.Windows.Forms.View.Details;
this.TexturesListView.SelectedIndexChanged += new System.EventHandler(this.TexturesListView_SelectedIndexChanged); 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 // TextureNameColumnHeader
// //

View File

@ -200,8 +200,13 @@ namespace CodeWalker.Forms
var tex = OpenDDSFile(); var tex = OpenDDSFile();
if (tex == null) return; if (tex == null) return;
var textures = new List<Texture>(); var textures = new List<Texture>(TexDict.Textures.data_items);
textures.AddRange(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); textures.Add(tex);
TexDict.BuildFromTextureList(textures); TexDict.BuildFromTextureList(textures);
@ -322,6 +327,12 @@ namespace CodeWalker.Forms
{ {
var dds = File.ReadAllBytes(fn); var dds = File.ReadAllBytes(fn);
var tex = DDSIO.GetTexture(dds); 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.Name = Path.GetFileNameWithoutExtension(fn);
tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant()); tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant());
JenkIndex.Ensure(tex.Name?.ToLowerInvariant()); JenkIndex.Ensure(tex.Name?.ToLowerInvariant());
@ -649,5 +660,120 @@ namespace CodeWalker.Forms
{ {
RenameTexture(SelTextureNameTextBox.Text); RenameTexture(SelTextureNameTextBox.Text);
} }
/// <summary>
/// Add one or more textures to the YTD by dragging and dropping them into the YTD.
/// </summary>
/// <param name="sender">
/// The source of the event.
/// </param>
/// <param name="e">
/// The <see cref="DragEventArgs"/> that contains the event data.
/// </param>
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<Texture> newTextures = new List<Texture>(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();
}
/// <summary>
/// Checks if the files can be dropped into the YTD.
/// </summary>
/// <param name="sender">
/// The source of the event.
/// </param>
/// <param name="e">
/// The <see cref="DragEventArgs"/> that contains the event data.
/// </param>
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;
}
} }
} }