From ef0824998922823cc9b1efce0f5ecf1ae653e6cd Mon Sep 17 00:00:00 2001 From: Lunaretic Date: Sun, 30 Jun 2024 18:28:04 -0400 Subject: [PATCH 1/4] Updated modlist UI layout. --- FFXIV_TexTools/Views/ModListView.xaml | 96 ++++++++++++--------------- lib/xivModdingFramework | 2 +- 2 files changed, 43 insertions(+), 55 deletions(-) diff --git a/FFXIV_TexTools/Views/ModListView.xaml b/FFXIV_TexTools/Views/ModListView.xaml index b3b7dff5..94af72e5 100644 --- a/FFXIV_TexTools/Views/ModListView.xaml +++ b/FFXIV_TexTools/Views/ModListView.xaml @@ -63,14 +63,14 @@ - - - - - + + + - + @@ -86,66 +86,54 @@ - + + - - - - - - - + + - - - - - - - - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + diff --git a/lib/xivModdingFramework b/lib/xivModdingFramework index cf094e32..d3ed9327 160000 --- a/lib/xivModdingFramework +++ b/lib/xivModdingFramework @@ -1 +1 @@ -Subproject commit cf094e32668aa5a570280dc22fa3181d1d0b92cc +Subproject commit d3ed9327ce6ccbfce1fdac6569eee0fdcc04bdbc From b8a4b46eb93e8888b19955dd0fc228a9cfe930f1 Mon Sep 17 00:00:00 2001 From: Lunaretic Date: Sun, 30 Jun 2024 18:57:34 -0400 Subject: [PATCH 2/4] Change modlist to only deferr loading for images/mod states. --- FFXIV_TexTools/ViewModels/ModListViewModel.cs | 472 ++++++++++-------- 1 file changed, 251 insertions(+), 221 deletions(-) diff --git a/FFXIV_TexTools/ViewModels/ModListViewModel.cs b/FFXIV_TexTools/ViewModels/ModListViewModel.cs index d57cf1f8..68b284fa 100644 --- a/FFXIV_TexTools/ViewModels/ModListViewModel.cs +++ b/FFXIV_TexTools/ViewModels/ModListViewModel.cs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +using FFXIV_TexTools.Annotations; using FFXIV_TexTools.Helpers; using FFXIV_TexTools.Models; using FFXIV_TexTools.Resources; @@ -37,6 +38,7 @@ using System.Windows.Forms; using System.Windows.Media; using System.Windows.Media.Imaging; +using System.Windows.Threading; using xivModdingFramework.Cache; using xivModdingFramework.General.Enums; using xivModdingFramework.Helpers; @@ -49,6 +51,8 @@ using xivModdingFramework.Textures.DataContainers; using xivModdingFramework.Textures.Enums; using xivModdingFramework.Textures.FileTypes; +using static FFXIV_TexTools.ViewModels.ModListViewModel; +using static System.Windows.Forms.AxHost; using Application = System.Windows.Application; namespace FFXIV_TexTools.ViewModels @@ -427,266 +431,289 @@ public async Task UpdateList(Category category, CancellationTokenSource cts) ProgressValue = 0; ProgressText = string.Empty; - await Task.Run(async () => - { - var tx = MainWindow.DefaultTransaction; - var selectedItem = category.Item as IItem; - if (selectedItem == null) return; + var tx = MainWindow.DefaultTransaction; + var selectedItem = category.Item as IItem; + if (selectedItem == null) return; - var modList = await tx.GetModList(); + var modList = await tx.GetModList(); - var modItems = new List(); + var modItems = new List(); - var allMods = modList.GetMods(x => !x.IsInternal()); - if (ModPackFilter) + var allMods = modList.GetMods(x => !x.IsInternal()); + if (ModPackFilter) + { + var modPackCategory = category; + + while (!modPackCategory.ParentCategory.Name.Equals("ModPacks")) + { + modPackCategory = modPackCategory.ParentCategory; + } + + foreach (var mod in allMods) { - var modPackCategory = category; + if (!mod.ItemName.Equals(category.Name)) continue; - while (!modPackCategory.ParentCategory.Name.Equals("ModPacks")) + if (mod.ModPack == modPackCategory.Name) { - modPackCategory = modPackCategory.ParentCategory; + modItems.Add(mod); } - - foreach (var mod in allMods) + else { - if (!mod.ItemName.Equals(category.Name)) continue; - - if (mod.ModPack == modPackCategory.Name) - { - modItems.Add(mod); - } - else - { - modItems.Add(mod); - } + modItems.Add(mod); } } - else + } + else + { + var itemNames = allMods.Select(x => x.ItemName).ToArray(); + modItems = + (from mod in allMods + where mod.ItemName.Equals(category.Name) + select mod).ToList(); + } + + var modNum = 0; + + foreach (var modItem in modItems) + { + + var itemPath = modItem.FilePath; + + var modListModel = new ModListModel { - var itemNames = allMods.Select(x => x.ItemName).ToArray(); - modItems = - (from mod in allMods - where mod.ItemName.Equals(category.Name) - select mod).ToList(); + ModItem = modItem + }; + + string parent = null; + if(_modListParents.ContainsKey(modItem.FilePath) && _modListParents[modItem.FilePath] != null && _modListParents[modItem.FilePath].Count > 0) + { + parent = _modListParents[modItem.FilePath][0]; } - var modNum = 0; + var suffix = ""; + try + { + suffix = Path.GetExtension(modItem.FilePath).Substring(1); + } + catch + { + // No-op. + } - await Task.Run(async () => + // Race + modListModel.ItemName = ModViewHelpers.GetFancyName(modItem.ItemName, modItem.FilePath); + + // File Name + modListModel.FileName = Path.GetFileName(modItem.FilePath); + modListModel.FilePath = modItem.FilePath; + + // Type + modListModel.Type = ModViewHelpers.GetType(modItem.FilePath); + + // Material + if (suffix == "tex" && parent != null) { - foreach (var modItem in modItems) - { + modListModel.Material = ModViewHelpers.GetMaterialId(parent); + } + else + { + modListModel.Material = ModViewHelpers.GetMaterialId(modItem.FilePath); + } - var itemPath = modItem.FilePath; + // Race + if (suffix == "tex" && parent != null) + { + modListModel.Race = ModViewHelpers.GetRace(parent).GetDisplayName(); + } + else + { + modListModel.Race = ModViewHelpers.GetRace(modItem.FilePath).GetDisplayName(); + } - var modListModel = new ModListModel - { - ModItem = modItem - }; + ModListPreviewList.Add(modListModel); + var mod = modItem; - string parent = null; - if(_modListParents.ContainsKey(modItem.FilePath) && _modListParents[modItem.FilePath] != null && _modListParents[modItem.FilePath].Count > 0) - { - parent = _modListParents[modItem.FilePath][0]; - } + // Preload this so we don't potentially cause multi-load collisions. + await tx.GetIndexFile(mod.DataFile); - var suffix = ""; - try - { - suffix = Path.GetExtension(modItem.FilePath).Substring(1); - } - catch - { - // No-op. - } + _ = Task.Run(async () => + { + List tasks = new List(); + tasks.Add(UpdateModState(modListModel, mod, tx)); + tasks.Add(UpdateModImage(modListModel, mod, tx)); - // Race - modListModel.ItemName = ModViewHelpers.GetFancyName(modItem.ItemName, modItem.FilePath); + await Task.WhenAll(tasks); + }); + } + } - // File Name - modListModel.FileName = Path.GetFileName(modItem.FilePath); - modListModel.FilePath = modItem.FilePath; + private async Task UpdateModState(ModListModel mlm, Mod m, ModTransaction tx) + { + var state = await m.GetState(tx); + await Application.Current.Dispatcher.InvokeAsync(() => + { + if (state == EModState.Enabled) + { + mlm.ActiveBorder = Brushes.Green; + mlm.Active = Brushes.Transparent; + mlm.ActiveOpacity = 1; + } + else + { + mlm.ActiveBorder = Brushes.Red; + mlm.Active = Brushes.Gray; + mlm.ActiveOpacity = 0.5f; + } + }); + } - // Type - modListModel.Type = ModViewHelpers.GetType(modItem.FilePath); + private async Task UpdateModImage(ModListModel mlm, Mod m, ModTransaction tx) + { + var img = await GetModImage(m, tx); + await Application.Current.Dispatcher.InvokeAsync(() => + { + mlm.Image = img; + }); + } - // Material - if (suffix == "tex" && parent != null) - { - modListModel.Material = ModViewHelpers.GetMaterialId(parent); - } - else - { - modListModel.Material = ModViewHelpers.GetMaterialId(modItem.FilePath); - } + private async Task GetModImage(Mod m, ModTransaction tx) + { - // Race - if (suffix == "tex" && parent != null) - { - modListModel.Race = ModViewHelpers.GetRace(parent).GetDisplayName(); - } - else + // Added files which are currently disabled cannot be previewed, as their index file entries don't exist. + // (???) The above comment is wrong. The data still exists in the Dats, and we have the offset in the modlist file... + var state = await m.GetState(tx); + var itemPath = m.FilePath; + if (!(m.OriginalOffset8x == m.ModOffset8x && state != EModState.Enabled)) + { + // Image + if (itemPath.Contains(".mtrl")) + { + try + { + var mtrlData = await Mtrl.GetXivMtrl(m.FilePath, false, tx); + if (mtrlData != null) { - modListModel.Race = ModViewHelpers.GetRace(modItem.FilePath).GetDisplayName(); - } + var floats = Half.ConvertToFloat(mtrlData.ColorSetData.ToArray()); - // Added files which are currently disabled cannot be previewed, as their index file entries don't exist. - // (???) The above comment is wrong. The data still exists in the Dats, and we have the offset in the modlist file... - var state = await modItem.GetState(tx); - if (!(modItem.OriginalOffset8x == modItem.ModOffset8x && state != EModState.Enabled)) - { - // Image - if (itemPath.Contains(".mtrl")) + var floatArray = Utilities.ToByteArray(floats); + + if (floatArray.Length > 0) { - try + var w = 4; + var h = 16; + + if (floatArray.Length >= 1024) { - var mtrlData = await Mtrl.GetXivMtrl(modItem.FilePath, false, tx); - if (mtrlData != null) - { + w = 8; + h = 32; + } - var floats = Half.ConvertToFloat(mtrlData.ColorSetData.ToArray()); + using (var img = Image.LoadPixelData(floatArray, w, h)) + { + img.Mutate(x => x.Opacity(1)); - var floatArray = Utilities.ToByteArray(floats); - if (floatArray.Length > 0) + using (var ms = new MemoryStream()) + { + img.Save(ms, new BmpEncoder()); + + // Have to create the BMP on the main thread, for reasons. + return await Application.Current.Dispatcher.InvokeAsync(() => { - var w = 4; - var h = 16; - - if(floatArray.Length >= 1024) - { - w = 8; - h = 32; - } - - using (var img = Image.LoadPixelData(floatArray, w, h)) - { - img.Mutate(x => x.Opacity(1)); - - BitmapImage bmp; - - using (var ms = new MemoryStream()) - { - img.Save(ms, new BmpEncoder()); - - bmp = new BitmapImage(); - bmp.BeginInit(); - bmp.StreamSource = ms; - bmp.CacheOption = BitmapCacheOption.OnLoad; - bmp.EndInit(); - bmp.Freeze(); - } - - modListModel.Image = - Application.Current.Dispatcher.Invoke(() => bmp); - } - } + BitmapImage bmp = null; + bmp = new BitmapImage(); + bmp.BeginInit(); + bmp.StreamSource = ms; + bmp.CacheOption = BitmapCacheOption.OnLoad; + bmp.EndInit(); + bmp.Freeze(); + return bmp; + }); } } - catch (Exception ex) - { - // User doesn't need to be bombarded with error messages if something is broken here. - /*FlexibleMessageBox.Show( - string.Format(UIMessages.MaterialFileReadErrorMessage, modItem.FilePath, - ex.Message), - UIMessages.MaterialDataReadErrorTitle, - MessageBoxButtons.OK, MessageBoxIcon.Error);*/ - } - - } - else if (itemPath.Contains(".mdl")) - { - modListModel.Image = Application.Current.Dispatcher.Invoke(() => new BitmapImage( - new Uri("pack://application:,,,/FFXIV_TexTools;component/Resources/3DModel.png"))); } - else if (itemPath.Contains(".imc") || itemPath.Contains(".eqp") || itemPath.Contains(".eqdp") || itemPath.Contains(".meta")) - { - modListModel.Image = Application.Current.Dispatcher.Invoke(() => new BitmapImage( - new Uri("pack://application:,,,/FFXIV_TexTools;component/Resources/Metadata.png"))); - } - else if(itemPath.Contains(".tex")) - { - var ttp = new TexTypePath - { - Type = XivTexType.Diffuse, - DataFile = modItem.DataFile, - Path = modItem.FilePath - }; + } + } + catch (Exception ex) + { + // User doesn't need to be bombarded with error messages if something is broken here. + /*FlexibleMessageBox.Show( + string.Format(UIMessages.MaterialFileReadErrorMessage, modItem.FilePath, + ex.Message), + UIMessages.MaterialDataReadErrorTitle, + MessageBoxButtons.OK, MessageBoxIcon.Error);*/ + } - XivTex texData; - try - { - if(await tx.FileExists(modItem.FilePath)) - { + } + else if (itemPath.Contains(".mdl")) + { - texData = await Tex.GetXivTex(modItem.FilePath, false, tx); + // Have to create the BMP on the main thread, for reasons. + return await Application.Current.Dispatcher.InvokeAsync(() => + { + return new BitmapImage(new Uri("pack://application:,,,/FFXIV_TexTools;component/Resources/3DModel.png")); + }); + } + else if (itemPath.Contains(".imc") || itemPath.Contains(".eqp") || itemPath.Contains(".eqdp") || itemPath.Contains(".meta")) + { + // Have to create the BMP on the main thread, for reasons. + return await Application.Current.Dispatcher.InvokeAsync(() => + { + return new BitmapImage(new Uri("pack://application:,,,/FFXIV_TexTools;component/Resources/Metadata.png")); + }); + } + else if (itemPath.Contains(".tex")) + { - var mapBytes = await texData.GetRawPixels(); + XivTex texData; + try + { + if (await tx.FileExists(m.FilePath)) + { - using (var img = Image.LoadPixelData(mapBytes, texData.Width, texData.Height)) - { - img.Mutate(x => x.Opacity(1)); + texData = await Tex.GetXivTex(m.FilePath, false, tx); - BitmapImage bmp; + var mapBytes = await texData.GetRawPixels(); - using (var ms = new MemoryStream()) - { - img.Save(ms, new BmpEncoder()); + using (var img = Image.LoadPixelData(mapBytes, texData.Width, texData.Height)) + { + img.Mutate(x => x.Opacity(1)); - bmp = new BitmapImage(); - bmp.BeginInit(); - bmp.StreamSource = ms; - bmp.CacheOption = BitmapCacheOption.OnLoad; - bmp.EndInit(); - bmp.Freeze(); - } - modListModel.Image = - Application.Current.Dispatcher.Invoke(() => bmp); - } - } - } - catch (Exception ex) + using (var ms = new MemoryStream()) { - /* - FlexibleMessageBox.Show( - string.Format(UIMessages.TextureFileReadErrorMessage, ttp.Path, ex.Message), - UIMessages.TextureDataReadErrorTitle, - MessageBoxButtons.OK, MessageBoxIcon.Error);*/ + img.Save(ms, new BmpEncoder()); + + // Have to create the BMP on the main thread, for reasons. + return await Application.Current.Dispatcher.InvokeAsync(() => + { + BitmapImage bmp = null; + bmp = new BitmapImage(); + bmp.BeginInit(); + bmp.StreamSource = ms; + bmp.CacheOption = BitmapCacheOption.OnLoad; + bmp.EndInit(); + bmp.Freeze(); + + return bmp; + }); } } - - } - - // Status - if (state == EModState.Enabled) - { - modListModel.ActiveBorder = Brushes.Green; - modListModel.Active = Brushes.Transparent; - modListModel.ActiveOpacity = 1; - } - else - { - modListModel.ActiveBorder = Brushes.Red; - modListModel.Active = Brushes.Gray; - modListModel.ActiveOpacity = 0.5f; - } - - cts.Token.ThrowIfCancellationRequested(); - - lock (updateLock) - { - progress.Report((++modNum, modItems.Count)); - } - - if (!cts.IsCancellationRequested) - { - Application.Current.Dispatcher.Invoke(() => ModListPreviewList.Add(modListModel)); } } - }, cts.Token); - }, cts.Token); + catch (Exception ex) + { + /* + FlexibleMessageBox.Show( + string.Format(UIMessages.TextureFileReadErrorMessage, ttp.Path, ex.Message), + UIMessages.TextureDataReadErrorTitle, + MessageBoxButtons.OK, MessageBoxIcon.Error);*/ + } + } + } + return null; + } /// @@ -1170,10 +1197,22 @@ public SolidColorBrush ActiveBorder /// public Mod ModItem { get; set; } + + private ImageSource _Image; + /// /// The image of the modded item /// - public BitmapSource Image { get; set; } + public ImageSource Image + { + get => _Image; + + set + { + _Image = value; + OnPropertyChanged(nameof(Image)); + } + } public event PropertyChangedEventHandler PropertyChanged; @@ -1183,14 +1222,5 @@ protected virtual void OnPropertyChanged(string propertyName) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } - - /// - /// Gets the language for the application - /// - /// The application language as XivLanguage - private static XivLanguage GetLanguage() - { - return XivLanguages.GetXivLanguage(Properties.Settings.Default.Application_Language); - } } } \ No newline at end of file From 1e801b5d40b592527c4947974fe1fcd5e2a264a9 Mon Sep 17 00:00:00 2001 From: Lunaretic Date: Sun, 30 Jun 2024 20:53:01 -0400 Subject: [PATCH 3/4] IMC fixes. --- FFXIV_TexTools/FFXIV_TexTools.csproj | 4 ++-- .../Wizard/ManipulationEditors/ImcManipulationEditor.xaml.cs | 3 ++- lib/xivModdingFramework | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/FFXIV_TexTools/FFXIV_TexTools.csproj b/FFXIV_TexTools/FFXIV_TexTools.csproj index 8a9fe959..61719d34 100644 --- a/FFXIV_TexTools/FFXIV_TexTools.csproj +++ b/FFXIV_TexTools/FFXIV_TexTools.csproj @@ -23,8 +23,8 @@ FFXIV_TexTools FFXIV_TexTools Copyright © 2024 - 3.0.1.5 - 3.0.1.5 + 3.0.1.6 + 3.0.1.6 9.0 bin\$(Configuration)\ true diff --git a/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditors/ImcManipulationEditor.xaml.cs b/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditors/ImcManipulationEditor.xaml.cs index e50d4721..6bfdadf8 100644 --- a/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditors/ImcManipulationEditor.xaml.cs +++ b/FFXIV_TexTools/Views/ModPack/Wizard/ManipulationEditors/ImcManipulationEditor.xaml.cs @@ -59,7 +59,8 @@ public XivDependencyRoot Root { if (value == null) return; var id = PmpIdentifierJson.FromRoot(value.Info); - + + Manipulation.ObjectType = PMPExtensions.XivItemTypeToPenumbraObject[value.Info.PrimaryType]; Manipulation.EquipSlot = id.EquipSlot; Manipulation.BodySlot = id.BodySlot; Manipulation.PrimaryId = id.PrimaryId; diff --git a/lib/xivModdingFramework b/lib/xivModdingFramework index d3ed9327..e7cb8232 160000 --- a/lib/xivModdingFramework +++ b/lib/xivModdingFramework @@ -1 +1 @@ -Subproject commit d3ed9327ce6ccbfce1fdac6569eee0fdcc04bdbc +Subproject commit e7cb8232b723010173163910ed2aea4d585500b7 From 0ff7a30963fa1e8f687d7fd53848b22d7fcced10 Mon Sep 17 00:00:00 2001 From: Lunaretic Date: Sun, 30 Jun 2024 20:54:25 -0400 Subject: [PATCH 4/4] Update Framework Reference to Update v3.0.1.6 --- lib/xivModdingFramework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/xivModdingFramework b/lib/xivModdingFramework index e7cb8232..832314a3 160000 --- a/lib/xivModdingFramework +++ b/lib/xivModdingFramework @@ -1 +1 @@ -Subproject commit e7cb8232b723010173163910ed2aea4d585500b7 +Subproject commit 832314a3a63f3b1269f3c523ee77389b2fa8ae70