diff --git a/CUE4Parse b/CUE4Parse index 11a92870..2aed4da1 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 11a92870024a088888aae79c74d8ae0c6c8af3e5 +Subproject commit 2aed4da1ee440cd421222526bdc501031f685be6 diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index ee169896..7c2b154b 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using CUE4Parse.UE4.Assets.Exports; using FModel.Creator.Bases; @@ -30,7 +31,7 @@ public class CreatorPackage : IDisposable } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryConstructCreator(out UCreator creator) + public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator) { switch (_exportType) { diff --git a/FModel/Extensions/CUE4ParseExtensions.cs b/FModel/Extensions/CUE4ParseExtensions.cs new file mode 100644 index 00000000..463a668e --- /dev/null +++ b/FModel/Extensions/CUE4ParseExtensions.cs @@ -0,0 +1,70 @@ +using System; +using CUE4Parse.FileProvider; +using CUE4Parse.FileProvider.Objects; +using CUE4Parse.UE4.Assets; +using CUE4Parse.UE4.Objects.UObject; +using FModel.Settings; + +namespace FModel.Extensions; + +public static class CUE4ParseExtensions +{ + public class LoadPackageResult + { + // more than 1 export per page currently break the inner package navigation feature + // if you have 1k exports per page, at page 2, you click on export index 932 + // it will find the export index 932 in the current page, which would realistically be 1932 + // fix would be to use InclusiveStart and ExclusiveEnd to determine the page the export index is in + // giving the document access to this would fix the issue and we could re-use Package instead of reloading it but it's quite a bit of work atm + + private const int PaginationThreshold = 5000; + private const int MaxExportPerPage = 1; + + public IPackage Package; + public int RequestedIndex; + + public bool IsPaginated => Package.ExportMapLength >= PaginationThreshold; + + /// + /// index of the first export on the current page + /// this index is the starting point for additional data preview + /// + /// it can be >0 even if is false if we want to focus data preview on a specific export + /// in this case, we will display all exports but only the focused one will be checked for data preview + /// + public int InclusiveStart => Math.Max(0, RequestedIndex - RequestedIndex % MaxExportPerPage); + /// + /// last exclusive export index of the current page + /// + public int ExclusiveEnd => IsPaginated + ? Math.Min(InclusiveStart + MaxExportPerPage, Package.ExportMapLength) + : Package.ExportMapLength; + public int PageSize => ExclusiveEnd - InclusiveStart; + + public string TabTitleExtra => IsPaginated ? $"Export{(PageSize > 1 ? "s" : "")} {InclusiveStart}{(PageSize > 1 ? $"-{ExclusiveEnd - 1}" : "")} of {Package.ExportMapLength - 1}" : null; + + /// + /// display all exports unless paginated + /// + /// if we save the data we will display all exports even if is true + /// + public object GetDisplayData(bool save = false) => !save && IsPaginated + ? Package.GetExports(InclusiveStart, PageSize) + : Package.GetExports(); + } + + public static LoadPackageResult GetLoadPackageResult(this IFileProvider provider, GameFile file, string objectName = null) + { + var result = new LoadPackageResult { Package = provider.LoadPackage(file) }; + if (result.IsPaginated || (result.Package.HasFlags(EPackageFlags.PKG_ContainsMap) && UserSettings.Default.PreviewWorlds)) // focus on UWorld if it's a map we want to preview + { + result.RequestedIndex = result.Package.GetExportIndex(file.NameWithoutExtension); + if (objectName != null) + { + result.RequestedIndex = int.TryParse(objectName, out var index) ? index : result.Package.GetExportIndex(objectName); + } + } + + return result; + } +} diff --git a/FModel/Extensions/StringExtensions.cs b/FModel/Extensions/StringExtensions.cs index 07d6873c..cc8b523c 100644 --- a/FModel/Extensions/StringExtensions.cs +++ b/FModel/Extensions/StringExtensions.cs @@ -6,14 +6,14 @@ using ICSharpCode.AvalonEdit.Document; namespace FModel.Extensions; -public static class StringExtensions +public static partial class StringExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetReadableSize(double size) { if (size == 0) return "0 B"; - string[] sizes = { "B", "KB", "MB", "GB", "TB" }; + string[] sizes = ["B", "KB", "MB", "GB", "TB"]; var order = 0; while (size >= 1024 && order < sizes.Length - 1) { @@ -27,21 +27,22 @@ public static class StringExtensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetNameLineNumber(this string s, string lineToFind) { + if (KismetRegex().IsMatch(lineToFind)) + return s.GetKismetLineNumber(lineToFind); if (int.TryParse(lineToFind, out var index)) return s.GetLineNumber(index); lineToFind = $" \"Name\": \"{lineToFind}\","; using var reader = new StringReader(s); var lineNum = 0; - string line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine() is { } line) { lineNum++; if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase)) return lineNum; } - return 1; + return -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -64,18 +65,17 @@ public static class StringExtensions } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetKismetLineNumber(this string s, string input) + private static int GetKismetLineNumber(this string s, string input) { - var match = Regex.Match(input, @"^(.+)\[(\d+)\]$"); + var match = KismetRegex().Match(input); var name = match.Groups[1].Value; int index = int.Parse(match.Groups[2].Value); var lineToFind = $" \"Name\": \"{name}\","; var offset = $"\"StatementIndex\": {index}"; using var reader = new StringReader(s); var lineNum = 0; - string line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine() is { } line) { lineNum++; if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase)) @@ -91,7 +91,7 @@ public static class StringExtensions } } - return 1; + return -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -99,8 +99,7 @@ public static class StringExtensions { using var reader = new StringReader(s); var lineNum = 0; - string line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine() is { } line) { lineNum++; if (line.Equals(" {")) @@ -110,6 +109,9 @@ public static class StringExtensions return lineNum + 1; } - return 1; + return -1; } + + [GeneratedRegex(@"^(.+)\[(\d+)\]$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] + private static partial Regex KismetRegex(); } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index d85febbe..198d8b59 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -506,7 +506,7 @@ diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index b4200026..6e469d96 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -85,7 +85,7 @@ public partial class MainWindow #if DEBUG // await _threadWorkerView.Begin(cancellationToken => // _applicationView.CUE4Parse.Extract(cancellationToken, - // "Marvel/Content/Marvel/Characters/1016/1016501/Meshes/SK_1016_1016501.uasset")); + // _applicationView.CUE4Parse.Provider["Marvel/Content/Marvel/Characters/1050/1050300/Meshes/SK_1050_1050300_Lobby.uasset"])); // await _threadWorkerView.Begin(cancellationToken => // _applicationView.CUE4Parse.Extract(cancellationToken, // "RED/Content/Chara/ABA/Costume01/Animation/Charaselect/body/stand_body01.uasset")); diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index fdcc086d..2d668df1 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -133,17 +133,18 @@ public class CUE4ParseViewModel : ViewModel customVersions: new FCustomVersionContainer(currentDir.Versioning.CustomVersions), optionOverrides: currentDir.Versioning.Options, mapStructTypesOverrides: currentDir.Versioning.MapStructTypes); + var pathComparer = StringComparer.OrdinalIgnoreCase; switch (gameDirectory) { case Constants._FN_LIVE_TRIGGER: { - Provider = new StreamedFileProvider("FortniteLive", true, versionContainer); + Provider = new StreamedFileProvider("FortniteLive", versionContainer, pathComparer); break; } case Constants._VAL_LIVE_TRIGGER: { - Provider = new StreamedFileProvider("ValorantLive", true, versionContainer); + Provider = new StreamedFileProvider("ValorantLive", versionContainer, pathComparer); break; } default: @@ -155,12 +156,12 @@ public class CUE4ParseViewModel : ViewModel [ new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"), new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks") - ], SearchOption.AllDirectories, true, versionContainer), + ], SearchOption.AllDirectories, versionContainer, pathComparer), "eFootball" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), [ new(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\KONAMI\\eFootball\\ST\\Download") - ], SearchOption.AllDirectories, true, versionContainer), - _ => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, true, versionContainer) + ], SearchOption.AllDirectories, versionContainer, pathComparer), + _ => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer) }; break; @@ -571,16 +572,18 @@ public class CUE4ParseViewModel : ViewModel case "uasset": case "umap": { - var pkg = Provider.LoadPackage(entry); + var result = Provider.GetLoadPackageResult(entry); + TabControl.SelectedTab.TitleExtra = result.TabTitleExtra; + if (saveProperties || updateUi) { - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), saveProperties, updateUi); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(result.GetDisplayData(saveProperties), Formatting.Indented), saveProperties, updateUi); if (saveProperties) break; // do not search for viewable exports if we are dealing with jsons } - for (var i = 0; i < pkg.ExportMapLength; i++) + for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++) { - if (CheckExport(cancellationToken, pkg, i, bulk)) + if (CheckExport(cancellationToken, result.Package, i, bulk)) break; } @@ -743,13 +746,15 @@ public class CUE4ParseViewModel : ViewModel TabControl.AddTab(entry, parentExportType); TabControl.SelectedTab.ScrollTrigger = objectName; - var pkg = Provider.LoadPackage(entry); - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); // json - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(pkg.GetExports(), Formatting.Indented), false, false); + var result = Provider.GetLoadPackageResult(entry, objectName); - for (var i = 0; i < pkg.ExportMapLength; i++) + TabControl.SelectedTab.TitleExtra = result.TabTitleExtra; + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); // json + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(result.GetDisplayData(), Formatting.Indented), false, false); + + for (var i = result.InclusiveStart; i < result.ExclusiveEnd; i++) { - if (CheckExport(cancellationToken, pkg, i)) + if (CheckExport(cancellationToken, result.Package, i)) break; } } @@ -907,7 +912,10 @@ public class CUE4ParseViewModel : ViewModel { var package = Provider.LoadPackage(entry); - TabControl.AddTab($"{entry.Name} (Metadata)"); + if (TabControl.CanAddTabs) TabControl.AddTab(entry); + else TabControl.SelectedTab.SoftReset(entry); + + TabControl.SelectedTab.TitleExtra = "Metadata"; TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(""); TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(package, Formatting.Indented), false, false); diff --git a/FModel/ViewModels/Commands/ImageCommand.cs b/FModel/ViewModels/Commands/ImageCommand.cs index 264255ac..0201c989 100644 --- a/FModel/ViewModels/Commands/ImageCommand.cs +++ b/FModel/ViewModels/Commands/ImageCommand.cs @@ -14,34 +14,34 @@ public class ImageCommand : ViewModelCommand { } - public override void Execute(TabItem contextViewModel, object parameter) + public override void Execute(TabItem tabViewModel, object parameter) { - if (parameter == null || !contextViewModel.HasImage) return; + if (parameter == null || !tabViewModel.HasImage) return; switch (parameter) { case "Open": { - Helper.OpenWindow(contextViewModel.SelectedImage.ExportName + " (Image)", () => + Helper.OpenWindow(tabViewModel.SelectedImage.ExportName + " (Image)", () => { var popout = new ImagePopout { - Title = contextViewModel.SelectedImage.ExportName + " (Image)", - Width = contextViewModel.SelectedImage.Image.Width, - Height = contextViewModel.SelectedImage.Image.Height, - WindowState = contextViewModel.SelectedImage.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal, - ImageCtrl = { Source = contextViewModel.SelectedImage.Image } + Title = tabViewModel.SelectedImage.ExportName + " (Image)", + Width = tabViewModel.SelectedImage.Image.Width, + Height = tabViewModel.SelectedImage.Image.Height, + WindowState = tabViewModel.SelectedImage.Image.Height > 1000 ? WindowState.Maximized : WindowState.Normal, + ImageCtrl = { Source = tabViewModel.SelectedImage.Image } }; - RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, BoolToRenderModeConverter.Instance.Convert(contextViewModel.SelectedImage.RenderNearestNeighbor)); + RenderOptions.SetBitmapScalingMode(popout.ImageCtrl, BoolToRenderModeConverter.Instance.Convert(tabViewModel.SelectedImage.RenderNearestNeighbor)); popout.Show(); }); break; } case "Copy": - ClipboardExtensions.SetImage(contextViewModel.SelectedImage.ImageBuffer, $"{contextViewModel.SelectedImage.ExportName}.png"); + ClipboardExtensions.SetImage(tabViewModel.SelectedImage.ImageBuffer, $"{tabViewModel.SelectedImage.ExportName}.png"); break; case "Save": - contextViewModel.SaveImage(); + tabViewModel.SaveImage(); break; } } diff --git a/FModel/ViewModels/Commands/TabCommand.cs b/FModel/ViewModels/Commands/TabCommand.cs index 5abbc4e3..a7911782 100644 --- a/FModel/ViewModels/Commands/TabCommand.cs +++ b/FModel/ViewModels/Commands/TabCommand.cs @@ -15,7 +15,7 @@ public class TabCommand : ViewModelCommand { } - public override async void Execute(TabItem contextViewModel, object parameter) + public override async void Execute(TabItem tabViewModel, object parameter) { switch (parameter) { @@ -23,53 +23,53 @@ public class TabCommand : ViewModelCommand _applicationView.CUE4Parse.TabControl.RemoveTab(mdlClick); break; case "Close_Tab": - _applicationView.CUE4Parse.TabControl.RemoveTab(contextViewModel); + _applicationView.CUE4Parse.TabControl.RemoveTab(tabViewModel); break; case "Close_All_Tabs": _applicationView.CUE4Parse.TabControl.RemoveAllTabs(); break; case "Close_Other_Tabs": - _applicationView.CUE4Parse.TabControl.RemoveOtherTabs(contextViewModel); + _applicationView.CUE4Parse.TabControl.RemoveOtherTabs(tabViewModel); break; case "Asset_Export_Data": - await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(contextViewModel.Entry)); + await _threadWorkerView.Begin(_ => _applicationView.CUE4Parse.ExportData(tabViewModel.Entry)); break; case "Asset_Save_Properties": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Entry, false, EBulkType.Properties); + _applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Properties); }); break; case "Asset_Save_Textures": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Entry, false, EBulkType.Textures); + _applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Textures); }); break; case "Asset_Save_Models": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Entry, false, EBulkType.Meshes); + _applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Meshes); }); break; case "Asset_Save_Animations": await _threadWorkerView.Begin(cancellationToken => { - _applicationView.CUE4Parse.Extract(cancellationToken, contextViewModel.Entry, false, EBulkType.Animations); + _applicationView.CUE4Parse.Extract(cancellationToken, tabViewModel.Entry, false, EBulkType.Animations); }); break; case "Open_Properties": - if (contextViewModel.Entry.Name == "New Tab" || contextViewModel.Document == null) return; - Helper.OpenWindow(contextViewModel.Entry.Name + " (Properties)", () => + if (tabViewModel.Header == "New Tab" || tabViewModel.Document == null) return; + Helper.OpenWindow(tabViewModel.Header + " (Properties)", () => { - new PropertiesPopout(contextViewModel) + new PropertiesPopout(tabViewModel) { - Title = contextViewModel.Entry.Name + " (Properties)" + Title = tabViewModel.Header + " (Properties)" }.Show(); }); break; case "Copy_Asset_Path": - Clipboard.SetText(contextViewModel.Entry.Path); + Clipboard.SetText(tabViewModel.Entry.Path); break; } } diff --git a/FModel/ViewModels/TabControlViewModel.cs b/FModel/ViewModels/TabControlViewModel.cs index 1eda1e1c..39edaced 100644 --- a/FModel/ViewModels/TabControlViewModel.cs +++ b/FModel/ViewModels/TabControlViewModel.cs @@ -97,7 +97,22 @@ public class TabItem : ViewModel public GameFile Entry { get => _entry; - set => SetProperty(ref _entry, value); + set + { + SetProperty(ref _entry, value); + RaisePropertyChanged(nameof(Header)); + } + } + + private string _titleExtra; + public string TitleExtra + { + get => _titleExtra; + set + { + SetProperty(ref _titleExtra, value); + RaisePropertyChanged(nameof(Header)); + } } private bool _hasSearchOpen; @@ -194,6 +209,8 @@ public class TabItem : ViewModel } } + public string Header => $"{Entry.Name}{(string.IsNullOrEmpty(TitleExtra) ? "" : $" ({TitleExtra})")}"; + public bool HasImage => SelectedImage != null; public bool HasMultipleImages => _images.Count > 1; public string Page => $"{_images.IndexOf(_selectedImage) + 1} / {_images.Count}"; @@ -219,6 +236,7 @@ public class TabItem : ViewModel public void SoftReset(GameFile entry) { Entry = entry; + TitleExtra = string.Empty; ParentExportType = string.Empty; ScrollTrigger = null; Application.Current.Dispatcher.Invoke(() => @@ -389,7 +407,7 @@ public class TabControlViewModel : ViewModel public void AddTab(string title) => AddTab(new FakeGameFile(title)); public void AddTab(GameFile entry, string parentExportType = null) { - if (SelectedTab?.Entry.Name == "New Tab") + if (SelectedTab?.Header == "New Tab") { SelectedTab.Entry = entry; return; diff --git a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs index 86fdb4d4..ea19954c 100644 --- a/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs +++ b/FModel/Views/Resources/Controls/Aed/GamePathVisualLineText.cs @@ -70,34 +70,24 @@ public class GamePathVisualLineText : VisualLineText { var obj = gamePath.SubstringAfterLast('.'); var package = gamePath.SubstringBeforeLast('.'); - var fullPath = _applicationView.CUE4Parse.Provider.FixPath(package, StringComparison.Ordinal); + var fullPath = _applicationView.CUE4Parse.Provider.FixPath(package); var firstLine = a.ParentVisualLine.Document.GetLineByNumber(2); if (a.ParentVisualLine.Document.FileName.Equals(fullPath.SubstringBeforeLast('.'), StringComparison.OrdinalIgnoreCase) && !a.ParentVisualLine.Document.GetText(firstLine.Offset, firstLine.Length).Equals(" \"Summary\": {")) // Show Metadata case { - int lineNumber; - DocumentLine line; - - if (Regex.IsMatch(obj, @"^(.+)\[(\d+)\]$")) + var lineNumber = a.ParentVisualLine.Document.Text.GetNameLineNumber(obj); + if (lineNumber > -1) { - lineNumber = a.ParentVisualLine.Document.Text.GetKismetLineNumber(obj); - line = a.ParentVisualLine.Document.GetLineByNumber(lineNumber); - } - else - { - lineNumber = a.ParentVisualLine.Document.Text.GetNameLineNumber(obj); - line = a.ParentVisualLine.Document.GetLineByNumber(lineNumber); + var line = a.ParentVisualLine.Document.GetLineByNumber(lineNumber); + AvalonEditor.YesWeEditor.Select(line.Offset, line.Length); + AvalonEditor.YesWeEditor.ScrollToLine(lineNumber); + return; } + } - AvalonEditor.YesWeEditor.Select(line.Offset, line.Length); - AvalonEditor.YesWeEditor.ScrollToLine(lineNumber); - } - else - { - await _threadWorkerView.Begin(cancellationToken => - _applicationView.CUE4Parse.ExtractAndScroll(cancellationToken, fullPath, obj, parentExportType)); - } + await _threadWorkerView.Begin(cancellationToken => + _applicationView.CUE4Parse.ExtractAndScroll(cancellationToken, fullPath, obj, parentExportType)); }; return a; } diff --git a/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs b/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs index bc5fdab8..cdf50ae0 100644 --- a/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs +++ b/FModel/Views/Resources/Controls/AvalonEditor.xaml.cs @@ -127,6 +127,8 @@ public partial class AvalonEditor if (!tabItem.ShouldScroll) return; var lineNumber = avalonEditor.Document.Text.GetNameLineNumber(tabItem.ScrollTrigger); + if (lineNumber == -1) lineNumber = 1; + var line = avalonEditor.Document.GetLineByNumber(lineNumber); avalonEditor.Select(line.Offset, line.Length); avalonEditor.ScrollToLine(lineNumber); diff --git a/FModel/Views/Resources/Controls/ImagePopout.xaml b/FModel/Views/Resources/Controls/ImagePopout.xaml index 31bf8301..b28330aa 100644 --- a/FModel/Views/Resources/Controls/ImagePopout.xaml +++ b/FModel/Views/Resources/Controls/ImagePopout.xaml @@ -15,7 +15,9 @@ - - + + + + diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 0a4af432..1d1500ed 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -651,7 +651,7 @@ - + @@ -858,7 +858,7 @@ - + diff --git a/FModel/Views/SearchView.xaml b/FModel/Views/SearchView.xaml index 3a454383..bf5a59e8 100644 --- a/FModel/Views/SearchView.xaml +++ b/FModel/Views/SearchView.xaml @@ -159,7 +159,7 @@ diff --git a/FModel/Views/Snooper/Options.cs b/FModel/Views/Snooper/Options.cs index b081de94..d4201392 100644 --- a/FModel/Views/Snooper/Options.cs +++ b/FModel/Views/Snooper/Options.cs @@ -190,6 +190,7 @@ public class Options { var guid = o.LightingGuid; if (Textures.TryGetValue(guid, out texture)) return texture != null; + if (o.Format == EPixelFormat.PF_BC6H) return false; // BC6H is not supported by Decode thus randomly crashes the app SKBitmap bitmap = o switch { diff --git a/FModel/Views/Snooper/Shading/Material.cs b/FModel/Views/Snooper/Shading/Material.cs index 05d7e354..a3e58d49 100644 --- a/FModel/Views/Snooper/Shading/Material.cs +++ b/FModel/Views/Snooper/Shading/Material.cs @@ -80,7 +80,7 @@ public class Material : IDisposable Normals = [new Texture(new FLinearColor(0.5f, 0.5f, 1f, 1f))]; SpecularMasks = [new Texture(new FLinearColor(1f, 0.5f, 0.5f, 1f))]; Emissive = new Texture[1]; - DiffuseColor = [Vector4.One]; + DiffuseColor = FillColors(1, Diffuse, CMaterialParams2.DiffuseColors, Vector4.One); EmissiveColor = [Vector4.One]; } else