diff --git a/CUE4Parse b/CUE4Parse index 024b005c..8d145bb8 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 024b005c4d15e8082ecebfb202700d59bb6113c0 +Subproject commit 8d145bb84efee7dfc1efe9ed2b2d9e53d8b463c3 diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs index 31da19b9..2dd04be6 100644 --- a/FModel/App.xaml.cs +++ b/FModel/App.xaml.cs @@ -11,7 +11,9 @@ using CUE4Parse; using FModel.Framework; using FModel.Services; using FModel.Settings; +using FModel.Views.Snooper; using Newtonsoft.Json; +using Serilog.Events; using Serilog.Sinks.SystemConsole.Themes; using MessageBox = AdonisUI.Controls.MessageBox; using MessageBoxImage = AdonisUI.Controls.MessageBoxImage; @@ -110,25 +112,39 @@ public partial class App Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Logs")); Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")); - const string template = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Enriched}: {Message:lj}{NewLine}{Exception}"; +#if DEBUG + var filePath = Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Debug-Log-{DateTime.Now:yyyy-MM-dd}.log"); +#else + var filePath = Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.log"); +#endif + const string template1 = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Enriched}: {Message:lj}{NewLine}{Exception}"; + const string template2 = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{ClassName}] {ObjectPath}: {Message:lj}{NewLine}{Exception}"; Log.Logger = new LoggerConfiguration() #if DEBUG .Enrich.With() .MinimumLevel.Verbose() - .WriteTo.Console(outputTemplate: template, theme: AnsiConsoleTheme.Literate) - .WriteTo.File(outputTemplate: template, - path: Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Debug-Log-{DateTime.Now:yyyy-MM-dd}.log")) #else .Enrich.With() - .WriteTo.File(outputTemplate: template, - path: Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.log")) #endif + .WriteTo.Logger(lc => lc + .Filter.ByExcluding(IsConversionLibrary) + .WriteTo.Console(outputTemplate: template1, theme: AnsiConsoleTheme.Literate) + .WriteTo.File(outputTemplate: template1, path: filePath)) + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(IsConversionLibrary) + .WriteTo.Console(outputTemplate: template2, theme: AnsiConsoleTheme.Literate) + .WriteTo.File(outputTemplate: template2, path: filePath)) + .MinimumLevel.Override("CUE4Parse_Conversion", LogEventLevel.Verbose).WriteTo.Sink(ImGuiSink.Instance) .CreateLogger(); Log.Information("Version {Version} ({CommitId})", Constants.APP_VERSION, Constants.APP_COMMIT_ID); Log.Information("{OS}", GetOperatingSystemProductName()); Log.Information("{RuntimeVer}", RuntimeInformation.FrameworkDescription); Log.Information("Culture {SysLang}", CultureInfo.CurrentCulture); + + static bool IsConversionLibrary(LogEvent e) => + e.Properties.TryGetValue("SourceContext", out var sc) && + sc.ToString().Contains("CUE4Parse_Conversion"); } private void AppExit(object sender, ExitEventArgs e) diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index edf73fce..cd57824c 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -56,8 +56,13 @@ public class BaseIcon : UCreator Preview = Utils.GetBitmap(otherPreview); else if (Object.TryGetValue(out UMaterialInstanceConstant materialInstancePreview, "EventCalloutImage")) Preview = Utils.GetBitmap(materialInstancePreview); - else if (Object.TryGetValue(out FStructFallback brush, "IconBrush") && brush.TryGetValue(out UTexture2D res, "ResourceObject")) + else if (Object.TryGetValue(out FStructFallback brush, "IconBrush", "BuildingSymbolNormal") && brush.TryGetValue(out UTexture2D res, "ResourceObject")) Preview = Utils.GetBitmap(res); + else if (Object.TryGetValue(out FStructFallback mission, "MissionIcons", "PopupWidgetData")) + { + if (mission.TryGetValue(out FStructFallback brushsize, "Brush_XL", "Brush_L", "Brush_M", "Brush_S", "Brush_XS", "Brush_XXS", "AvailableIcon", "UnavailableIcon") && brushsize.TryGetValue(out UTexture2D res2, "ResourceObject")) + Preview = Utils.GetBitmap(res2); + } } // text diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index 4369fdd4..82f81427 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -51,6 +51,11 @@ public class CreatorPackage : IDisposable case "CosmeticShoesItemDefinition": case "CosmeticCompanionItemDefinition": case "CosmeticCompanionReactFXItemDefinition": + case "MagpieEntitlementRewardDefinition": + case "FortDeferredItemGrantDefinition": + case "BattleLabDeviceItemDefinition": + case "PiggybackDanceItemDefinition": + case "MyTownBuildingDefinitionData": case "AthenaPickaxeItemDefinition": case "AthenaGadgetItemDefinition": case "AthenaGliderItemDefinition": @@ -64,6 +69,7 @@ public class CreatorPackage : IDisposable case "FortTokenType": case "FortAbilityKit": case "FortWorkerType": + case "FortMissionInfo": case "RewardGraphToken": case "JunoKnowledgeBundle": case "FortBannerTokenType": diff --git a/FModel/Enums.cs b/FModel/Enums.cs index dfbeeece..9d0ae65e 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -109,6 +109,7 @@ public enum EBulkType Audio = 1 << 5, Code = 1 << 6, Raw = 1 << 7, + Worlds = 1 << 8, } public enum EAssetCategory : uint diff --git a/FModel/Extensions/LogEventExtensions.cs b/FModel/Extensions/LogEventExtensions.cs new file mode 100644 index 00000000..620d6798 --- /dev/null +++ b/FModel/Extensions/LogEventExtensions.cs @@ -0,0 +1,11 @@ +using Serilog.Events; + +namespace FModel.Extensions; + +public static class LogEventExtensions +{ + public static string GetContext(this LogEvent log, string propertyName) + { + return log.Properties.TryGetValue(propertyName, out var value) ? value.ToString().Trim('"') : string.Empty; + } +} diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index af992309..1bfc1c33 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -121,6 +121,9 @@ + + + @@ -149,6 +152,9 @@ + + + diff --git a/FModel/Framework/ImGuiController.cs b/FModel/Framework/ImGuiController.cs index b62fde21..0768de97 100644 --- a/FModel/Framework/ImGuiController.cs +++ b/FModel/Framework/ImGuiController.cs @@ -66,18 +66,69 @@ public class ImGuiController : IDisposable var iniFileNamePtr = Marshal.StringToCoTaskMemUTF8(Path.Combine(UserSettings.Default.OutputDirectory, ".data", "imgui.ini")); io.NativePtr->IniFilename = (byte*)iniFileNamePtr; } - - // If not found, Fallback to default ImGui Font - var normalPath = @"C:\Windows\Fonts\segoeui.ttf"; - var boldPath = @"C:\Windows\Fonts\segoeuib.ttf"; - var semiBoldPath = @"C:\Windows\Fonts\seguisb.ttf"; - if (File.Exists(normalPath)) - FontNormal = io.Fonts.AddFontFromFileTTF(normalPath, 16 * DpiScale); - if (File.Exists(boldPath)) - FontBold = io.Fonts.AddFontFromFileTTF(boldPath, 16 * DpiScale); - if (File.Exists(semiBoldPath)) - FontSemiBold = io.Fonts.AddFontFromFileTTF(semiBoldPath, 16 * DpiScale); + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var assemblyName = assembly.GetName().Name; + byte[] LoadFont(string name) + { + using var stream = assembly.GetManifestResourceStream($"{assemblyName}.Resources.{name}") + ?? throw new FileNotFoundException($"Embedded font '{name}' not found."); + using var ms = new MemoryStream(); + stream.CopyTo(ms); + return ms.ToArray(); + } + + var faSolid = LoadFont("fa-solid-900.otf"); + var faRegular = LoadFont("fa-regular-400.otf"); + var faBrands = LoadFont("fa-brands-400.otf"); + + unsafe + { + // FA5 icons live in E000–F8FF (brands/regular/solid) + var iconRanges = stackalloc ushort[] { 0xe000, 0xf8ff, 0 }; + + var cfg = ImGuiNative.ImFontConfig_ImFontConfig(); + cfg->MergeMode = 1; + cfg->PixelSnapH = 1; + cfg->GlyphMinAdvanceX = 16f; + // FontDataOwnedByAtlas = 0 because we manage the GCHandle lifetime ourselves + cfg->FontDataOwnedByAtlas = 0; + + void MergeFontAwesome(byte[] solid, byte[] regular, byte[] brands) + { + fixed (byte* pSolid = solid) + fixed (byte* pRegular = regular) + fixed (byte* pBrands = brands) + { + io.Fonts.AddFontFromMemoryTTF((IntPtr)pSolid, solid.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); + io.Fonts.AddFontFromMemoryTTF((IntPtr)pRegular, regular.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); + io.Fonts.AddFontFromMemoryTTF((IntPtr)pBrands, brands.Length, 14, (IntPtr)cfg, (IntPtr)iconRanges); + } + } + + // If not found, Fallback to default ImGui Font + var normalPath = @"C:\Windows\Fonts\segoeui.ttf"; + var boldPath = @"C:\Windows\Fonts\segoeuib.ttf"; + var semiBoldPath = @"C:\Windows\Fonts\seguisb.ttf"; + + if (File.Exists(normalPath)) + { + FontNormal = io.Fonts.AddFontFromFileTTF(normalPath, 16 * DpiScale); + MergeFontAwesome(faSolid, faRegular, faBrands); + } + if (File.Exists(boldPath)) + { + FontBold = io.Fonts.AddFontFromFileTTF(boldPath, 16 * DpiScale); + MergeFontAwesome(faSolid, faRegular, faBrands); + } + if (File.Exists(semiBoldPath)) + { + FontSemiBold = io.Fonts.AddFontFromFileTTF(semiBoldPath, 16 * DpiScale); + MergeFontAwesome(faSolid, faRegular, faBrands); + } + + ImGuiNative.ImFontConfig_destroy(cfg); + } io.Fonts.AddFontDefault(); io.Fonts.Build(); // Build font atlas diff --git a/FModel/Framework/ViewModel.cs b/FModel/Framework/ViewModel.cs index dbaf1c03..6c4eecab 100644 --- a/FModel/Framework/ViewModel.cs +++ b/FModel/Framework/ViewModel.cs @@ -19,12 +19,12 @@ public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErro if (string.IsNullOrEmpty(propertyName)) return Error; - return _validationErrors.ContainsKey(propertyName) ? string.Join(Environment.NewLine, _validationErrors[propertyName]) : string.Empty; + return _validationErrors.TryGetValue(propertyName, out IList validationError) ? string.Join(Environment.NewLine, validationError) : string.Empty; } } [JsonIgnore] public string Error => string.Join(Environment.NewLine, GetAllErrors()); - [JsonIgnore] public bool HasErrors => _validationErrors.Any(); + [JsonIgnore] public virtual bool HasErrors => _validationErrors.Count != 0; public IEnumerable GetErrors(string propertyName) { @@ -49,8 +49,7 @@ public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErro public void ClearValidationErrors(string propertyName) { - if (_validationErrors.ContainsKey(propertyName)) - _validationErrors.Remove(propertyName); + _validationErrors.Remove(propertyName); } public event PropertyChangedEventHandler PropertyChanged; @@ -72,4 +71,4 @@ public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErro RaisePropertyChanged(propertyName); return true; } -} \ No newline at end of file +} diff --git a/FModel/Helper.cs b/FModel/Helper.cs index 05f1530b..62d7c6e0 100644 --- a/FModel/Helper.cs +++ b/FModel/Helper.cs @@ -39,7 +39,7 @@ public static class Helper else { var w = GetOpenedWindow(windowName); - if (windowName == "Search For Packages") w.WindowState = WindowState.Normal; + if (w.WindowState == WindowState.Minimized) w.WindowState = WindowState.Normal; w.Focus(); } } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index 46d2ddb5..7f9cb529 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FModel" + xmlns:viewModels="clr-namespace:FModel.ViewModels" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" xmlns:inputs="clr-namespace:FModel.Views.Resources.Controls.Inputs" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" @@ -24,6 +25,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +