From 14e05da2e0ee6ee5024991714f8ea67466c7c3fa Mon Sep 17 00:00:00 2001 From: Marlon Date: Tue, 27 Jan 2026 14:17:43 +0100 Subject: [PATCH] fixed line endings to lf to match editorconfig --- FModel/Creator/Bases/FN/BaseIcon.cs | 640 ++-- FModel/Creator/Bases/FN/Reward.cs | 2 +- FModel/Creator/Bases/MV/BaseFighter.cs | 2 +- FModel/Creator/Bases/MV/BasePerkGroup.cs | 2 +- FModel/Creator/Bases/MV/BaseQuest.cs | 2 +- FModel/Creator/CreatorPackage.cs | 534 ++-- FModel/Extensions/AssetCategoryExtensions.cs | 2 +- FModel/Extensions/AvalonExtensions.cs | 2 +- FModel/Extensions/CUE4ParseExtensions.cs | 2 +- FModel/Extensions/ClipboardExtensions.cs | 2 +- FModel/Extensions/CollectionExtensions.cs | 2 +- FModel/Extensions/StreamExtensions.cs | 2 +- FModel/Framework/AsyncQueue.cs | 2 +- FModel/Framework/Command.cs | 2 +- FModel/Framework/CustomSKShaper.cs | 2 +- FModel/Framework/FStatus.cs | 2 +- FModel/Framework/FakeCUE4Parse.cs | 2 +- FModel/Framework/FullyObservableCollection.cs | 2 +- FModel/Framework/Hotkey.cs | 2 +- FModel/Framework/JsonNetSerializer.cs | 2 +- FModel/Framework/NavigationList.cs | 2 +- FModel/Framework/RangeObservableCollection.cs | 2 +- FModel/Framework/SerilogEnricher.cs | 2 +- FModel/Framework/ViewModel.cs | 2 +- FModel/Framework/ViewModelCommand.cs | 2 +- FModel/Properties/Resources.Designer.cs | 2 +- FModel/Services/ApplicationService.cs | 2 +- FModel/Services/DiscordService.cs | 2 +- FModel/Settings/CustomDirectory.cs | 2 +- FModel/Settings/EndpointSettings.cs | 218 +- FModel/Settings/VersioningSettings.cs | 2 +- FModel/ViewModels/AboutViewModel.cs | 2 +- FModel/ViewModels/AesManagerViewModel.cs | 2 +- .../ApiEndpoints/AbstractApiProvider.cs | 2 +- .../ApiEndpoints/DynamicApiEndpoint.cs | 2 +- .../ApiEndpoints/FModelApiEndpoint.cs | 2 +- .../ApiEndpoints/FortniteApiEndpoint.cs | 2 +- .../FortniteCentralApiEndpoint.cs | 2 +- .../ApiEndpoints/Models/AesResponse.cs | 2 +- .../ApiEndpoints/Models/EpicResponse.cs | 2 +- .../ApiEndpoints/Models/GitHubResponse.cs | 2 +- .../ApiEndpoints/Models/MappingsResponse.cs | 2 +- .../ApiEndpoints/Models/PlaylistResponse.cs | 2 +- FModel/ViewModels/CUE4ParseViewModel.cs | 2742 ++++++++--------- .../Commands/AddEditDirectoryCommand.cs | 2 +- FModel/ViewModels/Commands/AddTabCommand.cs | 2 +- FModel/ViewModels/Commands/AudioCommand.cs | 2 +- .../Commands/DeleteDirectoryCommand.cs | 2 +- FModel/ViewModels/Commands/GoToCommand.cs | 2 +- FModel/ViewModels/Commands/ImageCommand.cs | 2 +- FModel/ViewModels/Commands/RemindMeCommand.cs | 2 +- .../ViewModels/CustomDirectoriesViewModel.cs | 2 +- FModel/Views/About.xaml.cs | 2 +- FModel/Views/AesManager.xaml.cs | 2 +- FModel/Views/AudioPlayer.xaml.cs | 2 +- FModel/Views/BackupManager.xaml.cs | 2 +- FModel/Views/CustomDir.xaml.cs | 2 +- FModel/Views/DirectorySelector.xaml.cs | 2 +- FModel/Views/ImageMerger.xaml.cs | 2 +- .../Controls/Aed/BraceFoldingStrategy.cs | 2 +- .../Controls/Aed/GamePathElementGenerator.cs | 2 +- .../Controls/Aed/HexColorElementGenerator.cs | 2 +- .../Controls/Aed/HexColorVisualLineText.cs | 2 +- .../Controls/Aup/CustomCodecFactory.cs | 2 +- .../Views/Resources/Controls/Aup/ISource.cs | 2 +- .../Resources/Controls/Aup/NVorbisSource.cs | 2 +- .../Resources/Controls/Aup/SourceEventArgs.cs | 2 +- .../Aup/SourcePropertyChangedEventArgs.cs | 2 +- .../Controls/Aup/SpectrumAnalyzer.cs | 2 +- .../Controls/Aup/SpectrumProvider.cs | 2 +- .../Views/Resources/Controls/Aup/Timeclock.cs | 2 +- .../Views/Resources/Controls/Aup/Timeline.cs | 2 +- .../Resources/Controls/CommitControl.xaml.cs | 2 +- .../Controls/CommitDownloaderControl.xaml.cs | 2 +- .../Resources/Controls/EndpointEditor.xaml.cs | 2 +- .../Resources/Controls/FilterableComboBox.cs | 2 +- .../Views/Resources/Controls/HotkeyTextBox.cs | 2 +- .../Resources/Controls/ImagePopout.xaml.cs | 2 +- .../Controls/Inputs/SearchTextBox.xaml.cs | 2 +- .../Resources/Controls/ListBoxItemBehavior.cs | 2 +- .../Resources/Controls/Mgn/EFrameType.cs | 2 +- .../Views/Resources/Controls/Mgn/Magnifier.cs | 2 +- .../Controls/Mgn/MagnifierAdorner.cs | 2 +- .../Controls/Mgn/MagnifierManager.cs | 2 +- .../Controls/OnTagDataTemplateSelector.cs | 2 +- .../Controls/Rtb/CustomRichTextBox.cs | 2 +- .../Controls/Rtb/CustomScrollViewer.cs | 2 +- .../TiledExplorer/FileButton2.xaml.cs | 2 +- .../TiledExplorer/FolderButton2.xaml.cs | 2 +- .../TiledExplorer/FolderButton3.xaml.cs | 2 +- .../Controls/TiledExplorer/SmoothScroll.cs | 2 +- .../Controls/TreeViewItemBehavior.cs | 2 +- .../Controls/TypeDataTemplateSelector.cs | 2 +- .../Converters/BoolToRenderModeConverter.cs | 2 +- .../Converters/BoolToToggleConverter.cs | 2 +- ...rderThicknessToStrokeThicknessConverter.cs | 2 +- .../CaseInsensitiveStringEqualsConverter.cs | 2 +- .../Converters/CommitMessageConverter.cs | 2 +- .../Converters/DateTimeToDateConverter.cs | 2 +- .../Converters/EndpointToTypeConverter.cs | 2 +- .../Converters/EnumToStringConverter.cs | 2 +- .../FolderToSeparatorTagConverter.cs | 2 +- .../IsNullToBoolReversedConverter.cs | 2 +- .../Converters/MultiParameterConverter.cs | 2 +- .../Resources/Converters/RatioConverter.cs | 2 +- .../Converters/RatioToGridLengthConverter.cs | 2 +- .../Converters/SizeToStringConverter.cs | 2 +- .../Converters/StringToGameConverter.cs | 2 +- .../Converters/TrimRightToLeftConverter.cs | 2 +- FModel/Views/Snooper/Animations/Bone.cs | 2 +- FModel/Views/Snooper/Animations/Sequence.cs | 2 +- .../Views/Snooper/Animations/TimeTracker.cs | 2 +- FModel/Views/Snooper/Buffers/BufferObject.cs | 2 +- .../Snooper/Buffers/FramebufferObject.cs | 2 +- .../Views/Snooper/Buffers/PickingTexture.cs | 2 +- .../Snooper/Buffers/RenderbufferObject.cs | 2 +- .../Snooper/Buffers/VertexArrayObject.cs | 2 +- FModel/Views/Snooper/Camera.cs | 2 +- FModel/Views/Snooper/Lights/PointLight.cs | 2 +- FModel/Views/Snooper/Lights/SpotLight.cs | 2 +- FModel/Views/Snooper/Models/Attachment.cs | 2 +- FModel/Views/Snooper/Models/EAttribute.cs | 2 +- FModel/Views/Snooper/Models/Grid.cs | 2 +- .../Views/Snooper/Models/IRenderableModel.cs | 2 +- FModel/Views/Snooper/Models/Skybox.cs | 2 +- FModel/Views/Snooper/Models/SplineModel.cs | 2 +- FModel/Views/Snooper/Models/StaticModel.cs | 2 +- FModel/Views/Snooper/Shading/TextureHelper.cs | 2 +- FModel/Views/UpdateView.xaml.cs | 2 +- 129 files changed, 2192 insertions(+), 2192 deletions(-) diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs index 00c1e675..985b100a 100644 --- a/FModel/Creator/Bases/FN/BaseIcon.cs +++ b/FModel/Creator/Bases/FN/BaseIcon.cs @@ -1,320 +1,320 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using CUE4Parse.GameTypes.FN.Enums; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Engine; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Assets.Objects; -using CUE4Parse.UE4.Objects.Core.i18N; -using CUE4Parse.UE4.Objects.Core.Math; -using CUE4Parse.UE4.Objects.GameplayTags; -using CUE4Parse.UE4.Objects.UObject; -using CUE4Parse_Conversion.Textures; -using FModel.Settings; -using SkiaSharp; - -namespace FModel.Creator.Bases.FN; - -public class BaseIcon : UCreator -{ - public SKBitmap SeriesBackground { get; protected set; } - protected string ShortDescription { get; set; } - protected string CosmeticSource { get; set; } - protected Dictionary UserFacingFlags { get; set; } - - public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { } - - public void ParseForReward(bool isUsingDisplayAsset) - { - // rarity - if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series); - else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer); - else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon - - if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList")) - { - GetSeries(dataList); - Preview = Utils.GetBitmap(dataList); - } - - // preview - if (Preview is null) - { - if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview)) - Preview = preview; - else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition")) - Preview = Utils.GetBitmap(itemDefinition); - else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon")) - Preview = Utils.GetBitmap(largePreview); - else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s)) - Preview = Utils.GetBitmap(s); - else if (Object.TryGetValue(out FPackageIndex otherPreview, "SmallPreviewImage", "ToastIcon", "access_item")) - 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")) - Preview = Utils.GetBitmap(res); - } - - // text - if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle")) - DisplayName = displayName.Text; - if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription")) - Description = description.Text; - else if (Object.TryGetValue(out FText[] descriptions, "Description")) - Description = string.Join('\n', descriptions.Select(x => x.Text)); - if (Object.TryGetValue(out FText shortDescription, "ShortDescription", "UIDisplaySubName")) - ShortDescription = shortDescription.Text; - else if (Object.ExportType.Equals("AthenaItemWrapDefinition", StringComparison.OrdinalIgnoreCase)) - ShortDescription = Utils.GetLocalizedResource("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap"); - - // Only works on non-cataba designs - if (Object.TryGetValue(out FStructFallback eventArrowColor, "EventArrowColor") && - eventArrowColor.TryGetValue(out FLinearColor specifiedArrowColor, "SpecifiedColor") && - Object.TryGetValue(out FStructFallback eventArrowShadowColor, "EventArrowShadowColor") && - eventArrowShadowColor.TryGetValue(out FLinearColor specifiedShadowColor, "SpecifiedColor")) - { - Background = new[] { SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex) }; - Border = new[] { SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex) }; - } - - Description = Utils.RemoveHtmlTags(Description); - } - - public override void ParseForInfo() - { - ParseForReward(UserSettings.Default.CosmeticDisplayAsset); - - if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList")) - CheckGameplayTags(dataList); - if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags")) - CheckGameplayTags(gameplayTags); - if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item")) - CosmeticSource = cosmeticItem.Name; - } - - protected void Draw(SKCanvas c) - { - switch (Style) - { - case EIconStyle.NoBackground: - DrawPreview(c); - break; - case EIconStyle.NoText: - DrawBackground(c); - DrawPreview(c); - DrawUserFacingFlags(c); - break; - default: - DrawBackground(c); - DrawPreview(c); - DrawTextBackground(c); - DrawDisplayName(c); - DrawDescription(c); - DrawToBottom(c, SKTextAlign.Right, CosmeticSource); - if (Description != ShortDescription) - DrawToBottom(c, SKTextAlign.Left, ShortDescription); - DrawUserFacingFlags(c); - break; - } - } - - public override SKBitmap[] Draw() - { - var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul); - using var c = new SKCanvas(ret); - - Draw(c); - - return new[] { ret }; - } - - private void GetSeries(FPackageIndex s) - { - if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return; - - GetSeries(export); - } - - private void GetSeries(FInstancedStruct[] s) - { - if (s.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl) - GetSeries(dl.NonConstStruct?.Get("Series")); - } - - private void GetSeries(FStructFallback s) - { - if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return; - if (components.FirstOrDefault(c => c.Name.Contains("Series")) is not { } seriesDef || - !seriesDef.TryLoad(out var seriesDefObj) || seriesDefObj is null || - !seriesDefObj.TryGetValue(out UObject series, "Series")) return; - - GetSeries(series); - } - - protected void GetSeries(UObject uObject) - { - if (uObject is UTexture2D texture2D) - { - SeriesBackground = texture2D.Decode().ToSkBitmap(); - return; - } - - if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture")) - { - SeriesBackground = Utils.GetBitmap(backgroundTexture); - } - - if (uObject.TryGetValue(out FStructFallback colors, "Colors") && - colors.TryGetValue(out FLinearColor color1, "Color1") && - colors.TryGetValue(out FLinearColor color2, "Color2") && - colors.TryGetValue(out FLinearColor color3, "Color3")) - { - Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) }; - Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) }; - } - - if (uObject.Name.Equals("PlatformSeries") && - uObject.TryGetValue(out FSoftObjectPath itemCardMaterial, "ItemCardMaterial") && - Utils.TryLoadObject(itemCardMaterial.AssetPathName.Text, out UMaterialInstanceConstant material)) - { - foreach (var vectorParameter in material.VectorParameterValues) - { - if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground")) - continue; - - Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex); - } - } - } - - private void GetRarity(EFortRarity r) - { - if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return; - - if (export.GetByIndex((int) r) is { } data && - data.TryGetValue(out FLinearColor color1, "Color1") && - data.TryGetValue(out FLinearColor color2, "Color2") && - data.TryGetValue(out FLinearColor color3, "Color3")) - { - Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) }; - Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) }; - } - } - - protected string GetCosmeticSet(string setName) - { - if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets)) - return string.Empty; - - if (!cosmeticSets.TryGetDataTableRow(setName, StringComparison.OrdinalIgnoreCase, out var uObject)) - return string.Empty; - - var name = string.Empty; - if (uObject.TryGetValue(out FText displayName, "DisplayName")) - name = displayName.Text; - - var format = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_SetMembership", "\nPart of the {0} set."); - return Utils.RemoveHtmlTags(string.Format(format, name)); - } - - protected (string, string, bool) GetInternalSID(string number) - { - if (!Utils.TryLoadObject("FortniteGame/Plugins/GameFeatures/BattlePassBase/Content/DataTables/Athena_SeasonTitles.Athena_SeasonTitles", out UDataTable seasonTitles) || - !seasonTitles.TryGetDataTableRow(number, StringComparison.InvariantCulture, out var row) || - !row.TryGetValue(out FText chapterText, "DisplayChapterText") || - !row.TryGetValue(out FText seasonText, "DisplaySeasonText") || - !row.TryGetValue(out FName displayType, "DisplayType")) - return (string.Empty, string.Empty, true); - - var onlySeason = displayType.Text.EndsWith("::OnlySeason") || (chapterText.Text == seasonText.Text && !int.TryParse(seasonText.Text, out _)); - return (chapterText.Text, seasonText.Text, onlySeason); - } - - protected string GetCosmeticSeason(string seasonNumber) - { - var s = seasonNumber["Cosmetics.Filter.Season.".Length..]; - (string chapterIdx, string seasonIdx, bool onlySeason) = GetInternalSID(s); - - var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}"); - var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}."); - if (onlySeason) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, seasonIdx))); - - var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}"); - var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}"); - var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx)); - return Utils.RemoveHtmlTags(string.Format(introduced, d)); - } - - protected void CheckGameplayTags(FInstancedStruct[] dataList) - { - if (dataList.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FGameplayTagContainer _, "Tags") ?? false) is { NonConstStruct: not null } tags) - { - CheckGameplayTags(tags.NonConstStruct.Get("Tags")); - } - } - - protected virtual void CheckGameplayTags(FGameplayTagContainer gameplayTags) - { - if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source)) - CosmeticSource = source.Text["Cosmetics.Source.".Length..]; - else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action)) - CosmeticSource = action.Text["Athena.ItemAction.".Length..]; - - if (gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set)) - Description += GetCosmeticSet(set.Text); - if (gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season)) - Description += GetCosmeticSeason(season.Text); - - GetUserFacingFlags(gameplayTags.GetAllGameplayTags( - "Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender.")); - } - - protected void GetUserFacingFlags(IList userFacingFlags) - { - if (userFacingFlags.Count < 1 || !Utils.TryLoadObject("FortniteGame/Content/Items/ItemCategories.ItemCategories", out UObject itemCategories)) - return; - - if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories")) - return; - - UserFacingFlags = new Dictionary(userFacingFlags.Count); - foreach (var flag in userFacingFlags) - { - if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase)) - { - if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase)) - UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream); - else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream); - } - else - { - foreach (var category in tertiaryCategories) - { - if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) && - category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") && - brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture)) - { - UserFacingFlags[flag] = Utils.GetBitmap(texture); - } - } - } - } - } - - private void DrawUserFacingFlags(SKCanvas c) - { - if (UserFacingFlags == null) return; - - const int size = 25; - var x = Margin * (int) 2.5; - foreach (var flag in UserFacingFlags.Values.Where(flag => flag != null)) - { - c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint); - x += size; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using CUE4Parse.GameTypes.FN.Enums; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Engine; +using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.Texture; +using CUE4Parse.UE4.Assets.Objects; +using CUE4Parse.UE4.Objects.Core.i18N; +using CUE4Parse.UE4.Objects.Core.Math; +using CUE4Parse.UE4.Objects.GameplayTags; +using CUE4Parse.UE4.Objects.UObject; +using CUE4Parse_Conversion.Textures; +using FModel.Settings; +using SkiaSharp; + +namespace FModel.Creator.Bases.FN; + +public class BaseIcon : UCreator +{ + public SKBitmap SeriesBackground { get; protected set; } + protected string ShortDescription { get; set; } + protected string CosmeticSource { get; set; } + protected Dictionary UserFacingFlags { get; set; } + + public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { } + + public void ParseForReward(bool isUsingDisplayAsset) + { + // rarity + if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series); + else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer); + else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon + + if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList")) + { + GetSeries(dataList); + Preview = Utils.GetBitmap(dataList); + } + + // preview + if (Preview is null) + { + if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview)) + Preview = preview; + else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition")) + Preview = Utils.GetBitmap(itemDefinition); + else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon")) + Preview = Utils.GetBitmap(largePreview); + else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s)) + Preview = Utils.GetBitmap(s); + else if (Object.TryGetValue(out FPackageIndex otherPreview, "SmallPreviewImage", "ToastIcon", "access_item")) + 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")) + Preview = Utils.GetBitmap(res); + } + + // text + if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle")) + DisplayName = displayName.Text; + if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "SetDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription")) + Description = description.Text; + else if (Object.TryGetValue(out FText[] descriptions, "Description")) + Description = string.Join('\n', descriptions.Select(x => x.Text)); + if (Object.TryGetValue(out FText shortDescription, "ShortDescription", "UIDisplaySubName")) + ShortDescription = shortDescription.Text; + else if (Object.ExportType.Equals("AthenaItemWrapDefinition", StringComparison.OrdinalIgnoreCase)) + ShortDescription = Utils.GetLocalizedResource("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap"); + + // Only works on non-cataba designs + if (Object.TryGetValue(out FStructFallback eventArrowColor, "EventArrowColor") && + eventArrowColor.TryGetValue(out FLinearColor specifiedArrowColor, "SpecifiedColor") && + Object.TryGetValue(out FStructFallback eventArrowShadowColor, "EventArrowShadowColor") && + eventArrowShadowColor.TryGetValue(out FLinearColor specifiedShadowColor, "SpecifiedColor")) + { + Background = new[] { SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex) }; + Border = new[] { SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex) }; + } + + Description = Utils.RemoveHtmlTags(Description); + } + + public override void ParseForInfo() + { + ParseForReward(UserSettings.Default.CosmeticDisplayAsset); + + if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList")) + CheckGameplayTags(dataList); + if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags")) + CheckGameplayTags(gameplayTags); + if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item")) + CosmeticSource = cosmeticItem.Name; + } + + protected void Draw(SKCanvas c) + { + switch (Style) + { + case EIconStyle.NoBackground: + DrawPreview(c); + break; + case EIconStyle.NoText: + DrawBackground(c); + DrawPreview(c); + DrawUserFacingFlags(c); + break; + default: + DrawBackground(c); + DrawPreview(c); + DrawTextBackground(c); + DrawDisplayName(c); + DrawDescription(c); + DrawToBottom(c, SKTextAlign.Right, CosmeticSource); + if (Description != ShortDescription) + DrawToBottom(c, SKTextAlign.Left, ShortDescription); + DrawUserFacingFlags(c); + break; + } + } + + public override SKBitmap[] Draw() + { + var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul); + using var c = new SKCanvas(ret); + + Draw(c); + + return new[] { ret }; + } + + private void GetSeries(FPackageIndex s) + { + if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return; + + GetSeries(export); + } + + private void GetSeries(FInstancedStruct[] s) + { + if (s.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl) + GetSeries(dl.NonConstStruct?.Get("Series")); + } + + private void GetSeries(FStructFallback s) + { + if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return; + if (components.FirstOrDefault(c => c.Name.Contains("Series")) is not { } seriesDef || + !seriesDef.TryLoad(out var seriesDefObj) || seriesDefObj is null || + !seriesDefObj.TryGetValue(out UObject series, "Series")) return; + + GetSeries(series); + } + + protected void GetSeries(UObject uObject) + { + if (uObject is UTexture2D texture2D) + { + SeriesBackground = texture2D.Decode().ToSkBitmap(); + return; + } + + if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture")) + { + SeriesBackground = Utils.GetBitmap(backgroundTexture); + } + + if (uObject.TryGetValue(out FStructFallback colors, "Colors") && + colors.TryGetValue(out FLinearColor color1, "Color1") && + colors.TryGetValue(out FLinearColor color2, "Color2") && + colors.TryGetValue(out FLinearColor color3, "Color3")) + { + Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) }; + Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) }; + } + + if (uObject.Name.Equals("PlatformSeries") && + uObject.TryGetValue(out FSoftObjectPath itemCardMaterial, "ItemCardMaterial") && + Utils.TryLoadObject(itemCardMaterial.AssetPathName.Text, out UMaterialInstanceConstant material)) + { + foreach (var vectorParameter in material.VectorParameterValues) + { + if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground")) + continue; + + Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex); + } + } + } + + private void GetRarity(EFortRarity r) + { + if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return; + + if (export.GetByIndex((int) r) is { } data && + data.TryGetValue(out FLinearColor color1, "Color1") && + data.TryGetValue(out FLinearColor color2, "Color2") && + data.TryGetValue(out FLinearColor color3, "Color3")) + { + Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) }; + Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) }; + } + } + + protected string GetCosmeticSet(string setName) + { + if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets)) + return string.Empty; + + if (!cosmeticSets.TryGetDataTableRow(setName, StringComparison.OrdinalIgnoreCase, out var uObject)) + return string.Empty; + + var name = string.Empty; + if (uObject.TryGetValue(out FText displayName, "DisplayName")) + name = displayName.Text; + + var format = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_SetMembership", "\nPart of the {0} set."); + return Utils.RemoveHtmlTags(string.Format(format, name)); + } + + protected (string, string, bool) GetInternalSID(string number) + { + if (!Utils.TryLoadObject("FortniteGame/Plugins/GameFeatures/BattlePassBase/Content/DataTables/Athena_SeasonTitles.Athena_SeasonTitles", out UDataTable seasonTitles) || + !seasonTitles.TryGetDataTableRow(number, StringComparison.InvariantCulture, out var row) || + !row.TryGetValue(out FText chapterText, "DisplayChapterText") || + !row.TryGetValue(out FText seasonText, "DisplaySeasonText") || + !row.TryGetValue(out FName displayType, "DisplayType")) + return (string.Empty, string.Empty, true); + + var onlySeason = displayType.Text.EndsWith("::OnlySeason") || (chapterText.Text == seasonText.Text && !int.TryParse(seasonText.Text, out _)); + return (chapterText.Text, seasonText.Text, onlySeason); + } + + protected string GetCosmeticSeason(string seasonNumber) + { + var s = seasonNumber["Cosmetics.Filter.Season.".Length..]; + (string chapterIdx, string seasonIdx, bool onlySeason) = GetInternalSID(s); + + var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}"); + var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}."); + if (onlySeason) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, seasonIdx))); + + var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}"); + var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}"); + var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx)); + return Utils.RemoveHtmlTags(string.Format(introduced, d)); + } + + protected void CheckGameplayTags(FInstancedStruct[] dataList) + { + if (dataList.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FGameplayTagContainer _, "Tags") ?? false) is { NonConstStruct: not null } tags) + { + CheckGameplayTags(tags.NonConstStruct.Get("Tags")); + } + } + + protected virtual void CheckGameplayTags(FGameplayTagContainer gameplayTags) + { + if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source)) + CosmeticSource = source.Text["Cosmetics.Source.".Length..]; + else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action)) + CosmeticSource = action.Text["Athena.ItemAction.".Length..]; + + if (gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set)) + Description += GetCosmeticSet(set.Text); + if (gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season)) + Description += GetCosmeticSeason(season.Text); + + GetUserFacingFlags(gameplayTags.GetAllGameplayTags( + "Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender.")); + } + + protected void GetUserFacingFlags(IList userFacingFlags) + { + if (userFacingFlags.Count < 1 || !Utils.TryLoadObject("FortniteGame/Content/Items/ItemCategories.ItemCategories", out UObject itemCategories)) + return; + + if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories")) + return; + + UserFacingFlags = new Dictionary(userFacingFlags.Count); + foreach (var flag in userFacingFlags) + { + if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase)) + { + if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase)) + UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream); + else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream); + } + else + { + foreach (var category in tertiaryCategories) + { + if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) && + category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") && + brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture)) + { + UserFacingFlags[flag] = Utils.GetBitmap(texture); + } + } + } + } + } + + private void DrawUserFacingFlags(SKCanvas c) + { + if (UserFacingFlags == null) return; + + const int size = 25; + var x = Margin * (int) 2.5; + foreach (var flag in UserFacingFlags.Values.Where(flag => flag != null)) + { + c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint); + x += size; + } + } +} diff --git a/FModel/Creator/Bases/FN/Reward.cs b/FModel/Creator/Bases/FN/Reward.cs index 4c7d0659..99e7f0f5 100644 --- a/FModel/Creator/Bases/FN/Reward.cs +++ b/FModel/Creator/Bases/FN/Reward.cs @@ -1,4 +1,4 @@ -using System; +using System; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.UObject; using FModel.Framework; diff --git a/FModel/Creator/Bases/MV/BaseFighter.cs b/FModel/Creator/Bases/MV/BaseFighter.cs index 4d8128a3..8dd0ca3b 100644 --- a/FModel/Creator/Bases/MV/BaseFighter.cs +++ b/FModel/Creator/Bases/MV/BaseFighter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Collections.Generic; using System.ComponentModel; diff --git a/FModel/Creator/Bases/MV/BasePerkGroup.cs b/FModel/Creator/Bases/MV/BasePerkGroup.cs index e580c4cb..b27e1851 100644 --- a/FModel/Creator/Bases/MV/BasePerkGroup.cs +++ b/FModel/Creator/Bases/MV/BasePerkGroup.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.UObject; diff --git a/FModel/Creator/Bases/MV/BaseQuest.cs b/FModel/Creator/Bases/MV/BaseQuest.cs index 71d24624..a5f08c22 100644 --- a/FModel/Creator/Bases/MV/BaseQuest.cs +++ b/FModel/Creator/Bases/MV/BaseQuest.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Objects.UObject; diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs index b54d6f2f..0fe8c317 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -1,267 +1,267 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using CUE4Parse.UE4.Assets.Exports; -using FModel.Creator.Bases; -using FModel.Creator.Bases.FN; -using FModel.Creator.Bases.MV; - -namespace FModel.Creator; - -public class CreatorPackage : IDisposable -{ - private string _pkgName; - private string _exportType; - private Lazy _object; - private EIconStyle _style; - - public CreatorPackage(string packageName, string exportType, Lazy uObject, EIconStyle style) - { - _pkgName = packageName; - _exportType = exportType; - _object = uObject; - _style = style; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public UCreator ConstructCreator() - { - TryConstructCreator(out var creator); - return creator; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator) - { - // TODO: convert to a type based system - switch (_exportType) - { - // Fortnite - case "AthenaConsumableEmoteItemDefinition": - case "AthenaSkyDiveContrailItemDefinition": - case "AthenaLoadingScreenItemDefinition": - case "AthenaVictoryPoseItemDefinition": - case "AthenaPetCarrierItemDefinition": - case "AthenaMusicPackItemDefinition": - case "AthenaBattleBusItemDefinition": - case "AthenaCharacterItemDefinition": - case "AthenaMapMarkerItemDefinition": - case "AthenaBackpackItemDefinition": - case "CosmeticShoesItemDefinition": - case "CosmeticCompanionItemDefinition": - case "CosmeticCompanionReactFXItemDefinition": - case "AthenaPickaxeItemDefinition": - case "AthenaGadgetItemDefinition": - case "AthenaGliderItemDefinition": - case "AthenaHatItemDefinition": - case "AthenaSprayItemDefinition": - case "AthenaDanceItemDefinition": - case "AthenaEmojiItemDefinition": - case "AthenaItemWrapDefinition": - case "AthenaToyItemDefinition": - case "FortHeroType": - case "FortTokenType": - case "FortAbilityKit": - case "FortWorkerType": - case "RewardGraphToken": - case "JunoKnowledgeBundle": - case "FortBannerTokenType": - case "FortVariantTokenType": - case "FortDecoItemDefinition": - case "FortStatItemDefinition": - case "FortAmmoItemDefinition": - case "FortEmoteItemDefinition": - case "FortBadgeItemDefinition": - case "SparksMicItemDefinition": - case "FortAwardItemDefinition": - case "FortStackItemDefinition": - case "FortWorldItemDefinition": - case "SparksAuraItemDefinition": - case "SparksDrumItemDefinition": - case "SparksBassItemDefinition": - case "FortGadgetItemDefinition": - case "AthenaCharmItemDefinition": - case "FortPlaysetItemDefinition": - case "FortGiftBoxItemDefinition": - case "FortOutpostItemDefinition": - case "FortVehicleItemDefinition": - case "FortMissionItemDefinition": - case "FortAccountItemDefinition": - case "SparksGuitarItemDefinition": - case "FortCardPackItemDefinition": - case "FortDefenderItemDefinition": - case "FortCurrencyItemDefinition": - case "FortResourceItemDefinition": - case "FortBackpackItemDefinition": - case "FortEventQuestMapDataAsset": - case "FortBuildingItemDefinition": - case "FortItemCacheItemDefinition": - case "FortWeaponModItemDefinition": - case "FortCodeTokenItemDefinition": - case "FortSchematicItemDefinition": - case "FortAlterableItemDefinition": - case "SparksKeyboardItemDefinition": - case "FortWorldMultiItemDefinition": - case "FortAlterationItemDefinition": - case "FortExpeditionItemDefinition": - case "FortIngredientItemDefinition": - case "FortConsumableItemDefinition": - case "StWFortAccoladeItemDefinition": - case "FortAccountBuffItemDefinition": - case "FortFOBCoreDecoItemDefinition": - case "FortPlayerPerksItemDefinition": - case "FortPlaysetPropItemDefinition": - case "FortPrerollDataItemDefinition": - case "JunoRecipeBundleItemDefinition": - case "FortHomebaseNodeItemDefinition": - case "FortNeverPersistItemDefinition": - case "FortPlayerAugmentItemDefinition": - case "FortSmartBuildingItemDefinition": - case "FortGiftBoxUnlockItemDefinition": - case "FortCreativeGadgetItemDefinition": - case "FortWeaponModItemDefinitionOptic": - case "RadioContentSourceItemDefinition": - case "FortPlaysetGrenadeItemDefinition": - case "JunoWeaponCreatureItemDefinition": - case "FortEventDependentItemDefinition": - case "FortPersonalVehicleItemDefinition": - case "FortGameplayModifierItemDefinition": - case "FortHardcoreModifierItemDefinition": - case "FortWeaponModItemDefinitionMagazine": - case "FortConsumableAccountItemDefinition": - case "FortConversionControlItemDefinition": - case "FortAccountBuffCreditItemDefinition": - case "JunoBuildInstructionsItemDefinition": - case "FortCharacterCosmeticItemDefinition": - case "JunoBuildingSetAccountItemDefinition": - case "FortEventCurrencyItemDefinitionRedir": - case "FortPersistentResourceItemDefinition": - case "FortWeaponMeleeOffhandItemDefinition": - case "FortHomebaseBannerIconItemDefinition": - case "FortVehicleCosmeticsVariantTokenType": - case "JunoBuildingPropAccountItemDefinition": - case "FortCampaignHeroLoadoutItemDefinition": - case "FortConditionalResourceItemDefinition": - case "FortChallengeBundleScheduleDefinition": - case "FortDailyRewardScheduleTokenDefinition": - case "FortVehicleCosmeticsItemDefinition_Body": - case "FortVehicleCosmeticsItemDefinition_Skin": - case "FortVehicleCosmeticsItemDefinition_Wheel": - case "FortCreativeRealEstatePlotItemDefinition": - case "FortDeployableBaseCloudSaveItemDefinition": - case "FortVehicleCosmeticsItemDefinition_Booster": - case "AthenaDanceItemDefinition_AdHocSquadsJoin_C": - case "FortVehicleCosmeticsItemDefinition_DriftSmoke": - case "FortVehicleCosmeticsItemDefinition_EngineAudio": - creator = _style switch - { - EIconStyle.Cataba => new BaseCommunity(_object.Value, _style, "Cataba"), - _ => new BaseIcon(_object.Value, _style) - }; - return true; - case "JunoAthenaCharacterItemOverrideDefinition": - case "JunoAthenaDanceItemOverrideDefinition": - creator = new BaseJuno(_object.Value, _style); - return true; - case "FortTandemCharacterData": - creator = new BaseTandem(_object.Value, _style); - return true; - case "FortTrapItemDefinition": - case "FortSpyTechItemDefinition": - case "FortAccoladeItemDefinition": - case "FortContextTrapItemDefinition": - case "FortWeaponMeleeItemDefinition": - case "FortWeaponRangedItemDefinition": - case "FortCreativeWeaponMeleeItemDefinition": - case "FortWeaponMeleeDualWieldItemDefinition": - case "FortCreativeWeaponRangedItemDefinition": - case "Daybreak_LevelExitVehicle_PartItemDefinition_C": - creator = new BaseIconStats(_object.Value, _style); - return true; - case "FortItemSeriesDefinition": - creator = new BaseSeries(_object.Value, _style); - return true; - case "MaterialInstanceConstant" - when _pkgName.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || - _pkgName.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || - _pkgName.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase): - creator = new BaseMaterialInstance(_object.Value, _style); - return true; - case "AthenaItemShopOfferDisplayData": - creator = new BaseOfferDisplayData(_object.Value, _style); - return true; - case "FortMtxOfferData": - creator = new BaseMtxOffer(_object.Value, _style); - return true; - case "FortPlaylistAthena": - creator = new BasePlaylist(_object.Value, _style); - return true; - case "FortFeatItemDefinition": - case "FortQuestItemDefinition": - case "FortQuestItemDefinition_Athena": - case "FortQuestItemDefinition_Campaign": - case "AthenaDailyQuestDefinition": - case "FortUrgentQuestItemDefinition": - creator = new Bases.FN.BaseQuest(_object.Value, _style); - return true; - case "FortCompendiumItemDefinition": - case "FortCompendiumBundleDefinition": - case "FortChallengeBundleItemDefinition": - creator = new BaseBundle(_object.Value, _style); - return true; - // case "AthenaSeasonItemDefinition": - // creator = new BaseSeason(_object, _style); - // return true; - case "FortItemAccessTokenType": - creator = new BaseItemAccessToken(_object.Value, _style); - return true; - case "FortCreativeOption": - case "PlaylistUserOptionEnum": - case "PlaylistUserOptionBool": - case "PlaylistUserOptionString": - case "PlaylistUserOptionIntEnum": - case "PlaylistUserOptionIntRange": - case "PlaylistUserOptionColorEnum": - case "PlaylistUserOptionFloatEnum": - case "PlaylistUserOptionFloatRange": - case "PlaylistUserTintedIconIntEnum": - case "PlaylistUserOptionPrimaryAsset": - case "PlaylistUserOptionCollisionProfileEnum": - creator = new BaseUserControl(_object.Value, _style); - return true; - // PandaGame - case "CharacterData": - creator = new BaseFighter(_object.Value, _style); - return true; - case "PerkGroup": - creator = new BasePerkGroup(_object.Value, _style); - return true; - case "StatTrackingBundleData": - case "HydraSyncedDataAsset": - case "AnnouncerPackData": - case "CharacterGiftData": - case "ProfileIconData": - case "RingOutVfxData": - case "BannerData": - case "EmoteData": - case "TauntData": - case "SkinData": - case "PerkData": - creator = new BasePandaIcon(_object.Value, _style); - return true; - case "QuestData": - creator = new Bases.MV.BaseQuest(_object.Value, _style); - return true; - default: - creator = null; - return false; - } - } - - public override string ToString() => $"{_exportType} | {_style}"; - - public void Dispose() - { - _object = null; - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using CUE4Parse.UE4.Assets.Exports; +using FModel.Creator.Bases; +using FModel.Creator.Bases.FN; +using FModel.Creator.Bases.MV; + +namespace FModel.Creator; + +public class CreatorPackage : IDisposable +{ + private string _pkgName; + private string _exportType; + private Lazy _object; + private EIconStyle _style; + + public CreatorPackage(string packageName, string exportType, Lazy uObject, EIconStyle style) + { + _pkgName = packageName; + _exportType = exportType; + _object = uObject; + _style = style; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UCreator ConstructCreator() + { + TryConstructCreator(out var creator); + return creator; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryConstructCreator([MaybeNullWhen(false)] out UCreator creator) + { + // TODO: convert to a type based system + switch (_exportType) + { + // Fortnite + case "AthenaConsumableEmoteItemDefinition": + case "AthenaSkyDiveContrailItemDefinition": + case "AthenaLoadingScreenItemDefinition": + case "AthenaVictoryPoseItemDefinition": + case "AthenaPetCarrierItemDefinition": + case "AthenaMusicPackItemDefinition": + case "AthenaBattleBusItemDefinition": + case "AthenaCharacterItemDefinition": + case "AthenaMapMarkerItemDefinition": + case "AthenaBackpackItemDefinition": + case "CosmeticShoesItemDefinition": + case "CosmeticCompanionItemDefinition": + case "CosmeticCompanionReactFXItemDefinition": + case "AthenaPickaxeItemDefinition": + case "AthenaGadgetItemDefinition": + case "AthenaGliderItemDefinition": + case "AthenaHatItemDefinition": + case "AthenaSprayItemDefinition": + case "AthenaDanceItemDefinition": + case "AthenaEmojiItemDefinition": + case "AthenaItemWrapDefinition": + case "AthenaToyItemDefinition": + case "FortHeroType": + case "FortTokenType": + case "FortAbilityKit": + case "FortWorkerType": + case "RewardGraphToken": + case "JunoKnowledgeBundle": + case "FortBannerTokenType": + case "FortVariantTokenType": + case "FortDecoItemDefinition": + case "FortStatItemDefinition": + case "FortAmmoItemDefinition": + case "FortEmoteItemDefinition": + case "FortBadgeItemDefinition": + case "SparksMicItemDefinition": + case "FortAwardItemDefinition": + case "FortStackItemDefinition": + case "FortWorldItemDefinition": + case "SparksAuraItemDefinition": + case "SparksDrumItemDefinition": + case "SparksBassItemDefinition": + case "FortGadgetItemDefinition": + case "AthenaCharmItemDefinition": + case "FortPlaysetItemDefinition": + case "FortGiftBoxItemDefinition": + case "FortOutpostItemDefinition": + case "FortVehicleItemDefinition": + case "FortMissionItemDefinition": + case "FortAccountItemDefinition": + case "SparksGuitarItemDefinition": + case "FortCardPackItemDefinition": + case "FortDefenderItemDefinition": + case "FortCurrencyItemDefinition": + case "FortResourceItemDefinition": + case "FortBackpackItemDefinition": + case "FortEventQuestMapDataAsset": + case "FortBuildingItemDefinition": + case "FortItemCacheItemDefinition": + case "FortWeaponModItemDefinition": + case "FortCodeTokenItemDefinition": + case "FortSchematicItemDefinition": + case "FortAlterableItemDefinition": + case "SparksKeyboardItemDefinition": + case "FortWorldMultiItemDefinition": + case "FortAlterationItemDefinition": + case "FortExpeditionItemDefinition": + case "FortIngredientItemDefinition": + case "FortConsumableItemDefinition": + case "StWFortAccoladeItemDefinition": + case "FortAccountBuffItemDefinition": + case "FortFOBCoreDecoItemDefinition": + case "FortPlayerPerksItemDefinition": + case "FortPlaysetPropItemDefinition": + case "FortPrerollDataItemDefinition": + case "JunoRecipeBundleItemDefinition": + case "FortHomebaseNodeItemDefinition": + case "FortNeverPersistItemDefinition": + case "FortPlayerAugmentItemDefinition": + case "FortSmartBuildingItemDefinition": + case "FortGiftBoxUnlockItemDefinition": + case "FortCreativeGadgetItemDefinition": + case "FortWeaponModItemDefinitionOptic": + case "RadioContentSourceItemDefinition": + case "FortPlaysetGrenadeItemDefinition": + case "JunoWeaponCreatureItemDefinition": + case "FortEventDependentItemDefinition": + case "FortPersonalVehicleItemDefinition": + case "FortGameplayModifierItemDefinition": + case "FortHardcoreModifierItemDefinition": + case "FortWeaponModItemDefinitionMagazine": + case "FortConsumableAccountItemDefinition": + case "FortConversionControlItemDefinition": + case "FortAccountBuffCreditItemDefinition": + case "JunoBuildInstructionsItemDefinition": + case "FortCharacterCosmeticItemDefinition": + case "JunoBuildingSetAccountItemDefinition": + case "FortEventCurrencyItemDefinitionRedir": + case "FortPersistentResourceItemDefinition": + case "FortWeaponMeleeOffhandItemDefinition": + case "FortHomebaseBannerIconItemDefinition": + case "FortVehicleCosmeticsVariantTokenType": + case "JunoBuildingPropAccountItemDefinition": + case "FortCampaignHeroLoadoutItemDefinition": + case "FortConditionalResourceItemDefinition": + case "FortChallengeBundleScheduleDefinition": + case "FortDailyRewardScheduleTokenDefinition": + case "FortVehicleCosmeticsItemDefinition_Body": + case "FortVehicleCosmeticsItemDefinition_Skin": + case "FortVehicleCosmeticsItemDefinition_Wheel": + case "FortCreativeRealEstatePlotItemDefinition": + case "FortDeployableBaseCloudSaveItemDefinition": + case "FortVehicleCosmeticsItemDefinition_Booster": + case "AthenaDanceItemDefinition_AdHocSquadsJoin_C": + case "FortVehicleCosmeticsItemDefinition_DriftSmoke": + case "FortVehicleCosmeticsItemDefinition_EngineAudio": + creator = _style switch + { + EIconStyle.Cataba => new BaseCommunity(_object.Value, _style, "Cataba"), + _ => new BaseIcon(_object.Value, _style) + }; + return true; + case "JunoAthenaCharacterItemOverrideDefinition": + case "JunoAthenaDanceItemOverrideDefinition": + creator = new BaseJuno(_object.Value, _style); + return true; + case "FortTandemCharacterData": + creator = new BaseTandem(_object.Value, _style); + return true; + case "FortTrapItemDefinition": + case "FortSpyTechItemDefinition": + case "FortAccoladeItemDefinition": + case "FortContextTrapItemDefinition": + case "FortWeaponMeleeItemDefinition": + case "FortWeaponRangedItemDefinition": + case "FortCreativeWeaponMeleeItemDefinition": + case "FortWeaponMeleeDualWieldItemDefinition": + case "FortCreativeWeaponRangedItemDefinition": + case "Daybreak_LevelExitVehicle_PartItemDefinition_C": + creator = new BaseIconStats(_object.Value, _style); + return true; + case "FortItemSeriesDefinition": + creator = new BaseSeries(_object.Value, _style); + return true; + case "MaterialInstanceConstant" + when _pkgName.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || + _pkgName.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || + _pkgName.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase): + creator = new BaseMaterialInstance(_object.Value, _style); + return true; + case "AthenaItemShopOfferDisplayData": + creator = new BaseOfferDisplayData(_object.Value, _style); + return true; + case "FortMtxOfferData": + creator = new BaseMtxOffer(_object.Value, _style); + return true; + case "FortPlaylistAthena": + creator = new BasePlaylist(_object.Value, _style); + return true; + case "FortFeatItemDefinition": + case "FortQuestItemDefinition": + case "FortQuestItemDefinition_Athena": + case "FortQuestItemDefinition_Campaign": + case "AthenaDailyQuestDefinition": + case "FortUrgentQuestItemDefinition": + creator = new Bases.FN.BaseQuest(_object.Value, _style); + return true; + case "FortCompendiumItemDefinition": + case "FortCompendiumBundleDefinition": + case "FortChallengeBundleItemDefinition": + creator = new BaseBundle(_object.Value, _style); + return true; + // case "AthenaSeasonItemDefinition": + // creator = new BaseSeason(_object, _style); + // return true; + case "FortItemAccessTokenType": + creator = new BaseItemAccessToken(_object.Value, _style); + return true; + case "FortCreativeOption": + case "PlaylistUserOptionEnum": + case "PlaylistUserOptionBool": + case "PlaylistUserOptionString": + case "PlaylistUserOptionIntEnum": + case "PlaylistUserOptionIntRange": + case "PlaylistUserOptionColorEnum": + case "PlaylistUserOptionFloatEnum": + case "PlaylistUserOptionFloatRange": + case "PlaylistUserTintedIconIntEnum": + case "PlaylistUserOptionPrimaryAsset": + case "PlaylistUserOptionCollisionProfileEnum": + creator = new BaseUserControl(_object.Value, _style); + return true; + // PandaGame + case "CharacterData": + creator = new BaseFighter(_object.Value, _style); + return true; + case "PerkGroup": + creator = new BasePerkGroup(_object.Value, _style); + return true; + case "StatTrackingBundleData": + case "HydraSyncedDataAsset": + case "AnnouncerPackData": + case "CharacterGiftData": + case "ProfileIconData": + case "RingOutVfxData": + case "BannerData": + case "EmoteData": + case "TauntData": + case "SkinData": + case "PerkData": + creator = new BasePandaIcon(_object.Value, _style); + return true; + case "QuestData": + creator = new Bases.MV.BaseQuest(_object.Value, _style); + return true; + default: + creator = null; + return false; + } + } + + public override string ToString() => $"{_exportType} | {_style}"; + + public void Dispose() + { + _object = null; + } +} diff --git a/FModel/Extensions/AssetCategoryExtensions.cs b/FModel/Extensions/AssetCategoryExtensions.cs index 561db386..bc8be498 100644 --- a/FModel/Extensions/AssetCategoryExtensions.cs +++ b/FModel/Extensions/AssetCategoryExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; diff --git a/FModel/Extensions/AvalonExtensions.cs b/FModel/Extensions/AvalonExtensions.cs index 86627fcb..b356e106 100644 --- a/FModel/Extensions/AvalonExtensions.cs +++ b/FModel/Extensions/AvalonExtensions.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using System.Xml; using ICSharpCode.AvalonEdit.Highlighting; diff --git a/FModel/Extensions/CUE4ParseExtensions.cs b/FModel/Extensions/CUE4ParseExtensions.cs index 463a668e..bc2e3365 100644 --- a/FModel/Extensions/CUE4ParseExtensions.cs +++ b/FModel/Extensions/CUE4ParseExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using CUE4Parse.FileProvider; using CUE4Parse.FileProvider.Objects; using CUE4Parse.UE4.Assets; diff --git a/FModel/Extensions/ClipboardExtensions.cs b/FModel/Extensions/ClipboardExtensions.cs index 5ba68b0a..571e9156 100644 --- a/FModel/Extensions/ClipboardExtensions.cs +++ b/FModel/Extensions/ClipboardExtensions.cs @@ -1,4 +1,4 @@ -using SkiaSharp; +using SkiaSharp; using System; using System.Drawing; using System.Drawing.Imaging; diff --git a/FModel/Extensions/CollectionExtensions.cs b/FModel/Extensions/CollectionExtensions.cs index 3964f2a0..7ae7bf1c 100644 --- a/FModel/Extensions/CollectionExtensions.cs +++ b/FModel/Extensions/CollectionExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; diff --git a/FModel/Extensions/StreamExtensions.cs b/FModel/Extensions/StreamExtensions.cs index 21517936..781dde9b 100644 --- a/FModel/Extensions/StreamExtensions.cs +++ b/FModel/Extensions/StreamExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Runtime.CompilerServices; diff --git a/FModel/Framework/AsyncQueue.cs b/FModel/Framework/AsyncQueue.cs index 78de380b..f4f405ae 100644 --- a/FModel/Framework/AsyncQueue.cs +++ b/FModel/Framework/AsyncQueue.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks.Dataflow; diff --git a/FModel/Framework/Command.cs b/FModel/Framework/Command.cs index ed4b1d61..c102e1f8 100644 --- a/FModel/Framework/Command.cs +++ b/FModel/Framework/Command.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Input; namespace FModel.Framework; diff --git a/FModel/Framework/CustomSKShaper.cs b/FModel/Framework/CustomSKShaper.cs index c7a0756d..b2439ae1 100644 --- a/FModel/Framework/CustomSKShaper.cs +++ b/FModel/Framework/CustomSKShaper.cs @@ -1,4 +1,4 @@ -using System; +using System; using HarfBuzzSharp; using SkiaSharp; using SkiaSharp.HarfBuzz; diff --git a/FModel/Framework/FStatus.cs b/FModel/Framework/FStatus.cs index a702dccb..796160ad 100644 --- a/FModel/Framework/FStatus.cs +++ b/FModel/Framework/FStatus.cs @@ -1,4 +1,4 @@ -namespace FModel.Framework; +namespace FModel.Framework; public class FStatus : ViewModel { diff --git a/FModel/Framework/FakeCUE4Parse.cs b/FModel/Framework/FakeCUE4Parse.cs index 13a88aad..28ed5caa 100644 --- a/FModel/Framework/FakeCUE4Parse.cs +++ b/FModel/Framework/FakeCUE4Parse.cs @@ -1,4 +1,4 @@ -using System; +using System; using CUE4Parse.Compression; using CUE4Parse.FileProvider.Objects; using CUE4Parse.UE4.Assets.Objects; diff --git a/FModel/Framework/FullyObservableCollection.cs b/FModel/Framework/FullyObservableCollection.cs index abb00e09..890113e6 100644 --- a/FModel/Framework/FullyObservableCollection.cs +++ b/FModel/Framework/FullyObservableCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; diff --git a/FModel/Framework/Hotkey.cs b/FModel/Framework/Hotkey.cs index 11445f9c..cc984965 100644 --- a/FModel/Framework/Hotkey.cs +++ b/FModel/Framework/Hotkey.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using System.Windows.Input; namespace FModel.Framework; diff --git a/FModel/Framework/JsonNetSerializer.cs b/FModel/Framework/JsonNetSerializer.cs index c4bab9c3..8af7a025 100644 --- a/FModel/Framework/JsonNetSerializer.cs +++ b/FModel/Framework/JsonNetSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using RestSharp; diff --git a/FModel/Framework/NavigationList.cs b/FModel/Framework/NavigationList.cs index 77a45467..1952a72d 100644 --- a/FModel/Framework/NavigationList.cs +++ b/FModel/Framework/NavigationList.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace FModel.Framework; diff --git a/FModel/Framework/RangeObservableCollection.cs b/FModel/Framework/RangeObservableCollection.cs index 1372b522..cf0b5261 100644 --- a/FModel/Framework/RangeObservableCollection.cs +++ b/FModel/Framework/RangeObservableCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; diff --git a/FModel/Framework/SerilogEnricher.cs b/FModel/Framework/SerilogEnricher.cs index 2aadc28c..3567d5d0 100644 --- a/FModel/Framework/SerilogEnricher.cs +++ b/FModel/Framework/SerilogEnricher.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Reflection; using Serilog.Core; diff --git a/FModel/Framework/ViewModel.cs b/FModel/Framework/ViewModel.cs index 1efeaa8c..dbaf1c03 100644 --- a/FModel/Framework/ViewModel.cs +++ b/FModel/Framework/ViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; diff --git a/FModel/Framework/ViewModelCommand.cs b/FModel/Framework/ViewModelCommand.cs index 66aed244..06051f0e 100644 --- a/FModel/Framework/ViewModelCommand.cs +++ b/FModel/Framework/ViewModelCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace FModel.Framework; diff --git a/FModel/Properties/Resources.Designer.cs b/FModel/Properties/Resources.Designer.cs index 41a3eeaa..fce78540 100644 --- a/FModel/Properties/Resources.Designer.cs +++ b/FModel/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // Ce code a été généré par un outil. // Version du runtime :4.0.30319.42000 diff --git a/FModel/Services/ApplicationService.cs b/FModel/Services/ApplicationService.cs index 2d0f66c0..4f9c6673 100644 --- a/FModel/Services/ApplicationService.cs +++ b/FModel/Services/ApplicationService.cs @@ -1,4 +1,4 @@ -using FModel.ViewModels; +using FModel.ViewModels; namespace FModel.Services { diff --git a/FModel/Services/DiscordService.cs b/FModel/Services/DiscordService.cs index 22eabbbf..e2374a78 100644 --- a/FModel/Services/DiscordService.cs +++ b/FModel/Services/DiscordService.cs @@ -1,4 +1,4 @@ -using System; +using System; using DiscordRPC; using FModel.Extensions; using FModel.Settings; diff --git a/FModel/Settings/CustomDirectory.cs b/FModel/Settings/CustomDirectory.cs index f368a7c4..998e40ab 100644 --- a/FModel/Settings/CustomDirectory.cs +++ b/FModel/Settings/CustomDirectory.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using FModel.Framework; namespace FModel.Settings; diff --git a/FModel/Settings/EndpointSettings.cs b/FModel/Settings/EndpointSettings.cs index 5b70926f..bf0ef245 100644 --- a/FModel/Settings/EndpointSettings.cs +++ b/FModel/Settings/EndpointSettings.cs @@ -1,109 +1,109 @@ -using System.Linq; -using FModel.Framework; -using FModel.ViewModels.ApiEndpoints; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace FModel.Settings; - -public class EndpointSettings : ViewModel -{ - public static EndpointSettings[] Default(string gameName) - { - return gameName switch - { - "Fortnite" or "Fortnite [LIVE]" => [ - new("https://uedb.dev/svc/api/v1/fortnite/aes", "$.['mainKey','dynamicKeys']"), - new("https://uedb.dev/svc/api/v1/fortnite/mappings", "$.mappings.ZStandard") - ], - "VALORANT" or "VALORANT [LIVE]" => [ - new("https://uedb.dev/svc/api/v1/valorant/aes", "$.['mainKey','dynamicKeys']"), - new("https://uedb.dev/svc/api/v1/valorant/mappings", "$.mappings.ZStandard") - ], - _ => [new(), new()], - }; - } - - private string _url; - public string Url - { - get => _url; - set => SetProperty(ref _url, value); - } - - private string _path; - public string Path - { - get => _path; - set => SetProperty(ref _path, value); - } - - private bool _overwrite; - public bool Overwrite - { - get => _overwrite; - set => SetProperty(ref _overwrite, value); - } - - private string _filePath; - public string FilePath - { - get => _filePath; - set => SetProperty(ref _filePath, value); - } - - private bool _isValid; - public bool IsValid - { - get => _isValid; - set - { - SetProperty(ref _isValid, value); - RaisePropertyChanged(nameof(Label)); - } - } - - [JsonIgnore] - public string Label => IsValid ? - "Your endpoint configuration is valid! Please, avoid any unnecessary modifications!" : - "Your endpoint configuration DOES NOT seem to be valid yet! Please, test it out!"; - - public EndpointSettings() {} - public EndpointSettings(string url, string path) - { - Url = url; - Path = path; - IsValid = !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(path); // be careful with this - } - - public void TryValidate(DynamicApiEndpoint endpoint, EEndpointType type, out JToken response) - { - response = null; - if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(Path)) - { - IsValid = false; - } - else switch (type) - { - case EEndpointType.Aes: - { - var r = endpoint.GetAesKeys(default, Url, Path); - response = JToken.FromObject(r); - IsValid = r.IsValid; - break; - } - case EEndpointType.Mapping: - { - var r = endpoint.GetMappings(default, Url, Path); - response = JToken.FromObject(r); - IsValid = r.Any(x => x.IsValid); - break; - } - default: - { - IsValid = false; - break; - } - } - } -} +using System.Linq; +using FModel.Framework; +using FModel.ViewModels.ApiEndpoints; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace FModel.Settings; + +public class EndpointSettings : ViewModel +{ + public static EndpointSettings[] Default(string gameName) + { + return gameName switch + { + "Fortnite" or "Fortnite [LIVE]" => [ + new("https://uedb.dev/svc/api/v1/fortnite/aes", "$.['mainKey','dynamicKeys']"), + new("https://uedb.dev/svc/api/v1/fortnite/mappings", "$.mappings.ZStandard") + ], + "VALORANT" or "VALORANT [LIVE]" => [ + new("https://uedb.dev/svc/api/v1/valorant/aes", "$.['mainKey','dynamicKeys']"), + new("https://uedb.dev/svc/api/v1/valorant/mappings", "$.mappings.ZStandard") + ], + _ => [new(), new()], + }; + } + + private string _url; + public string Url + { + get => _url; + set => SetProperty(ref _url, value); + } + + private string _path; + public string Path + { + get => _path; + set => SetProperty(ref _path, value); + } + + private bool _overwrite; + public bool Overwrite + { + get => _overwrite; + set => SetProperty(ref _overwrite, value); + } + + private string _filePath; + public string FilePath + { + get => _filePath; + set => SetProperty(ref _filePath, value); + } + + private bool _isValid; + public bool IsValid + { + get => _isValid; + set + { + SetProperty(ref _isValid, value); + RaisePropertyChanged(nameof(Label)); + } + } + + [JsonIgnore] + public string Label => IsValid ? + "Your endpoint configuration is valid! Please, avoid any unnecessary modifications!" : + "Your endpoint configuration DOES NOT seem to be valid yet! Please, test it out!"; + + public EndpointSettings() {} + public EndpointSettings(string url, string path) + { + Url = url; + Path = path; + IsValid = !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(path); // be careful with this + } + + public void TryValidate(DynamicApiEndpoint endpoint, EEndpointType type, out JToken response) + { + response = null; + if (string.IsNullOrEmpty(Url) || string.IsNullOrEmpty(Path)) + { + IsValid = false; + } + else switch (type) + { + case EEndpointType.Aes: + { + var r = endpoint.GetAesKeys(default, Url, Path); + response = JToken.FromObject(r); + IsValid = r.IsValid; + break; + } + case EEndpointType.Mapping: + { + var r = endpoint.GetMappings(default, Url, Path); + response = JToken.FromObject(r); + IsValid = r.Any(x => x.IsValid); + break; + } + default: + { + IsValid = false; + break; + } + } + } +} diff --git a/FModel/Settings/VersioningSettings.cs b/FModel/Settings/VersioningSettings.cs index e6ecec91..0e5f27ca 100644 --- a/FModel/Settings/VersioningSettings.cs +++ b/FModel/Settings/VersioningSettings.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using CUE4Parse.UE4.Objects.Core.Serialization; using FModel.Framework; diff --git a/FModel/ViewModels/AboutViewModel.cs b/FModel/ViewModels/AboutViewModel.cs index 6c0478d3..223cc0a6 100644 --- a/FModel/ViewModels/AboutViewModel.cs +++ b/FModel/ViewModels/AboutViewModel.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using System.Threading.Tasks; using FModel.Framework; using FModel.Services; diff --git a/FModel/ViewModels/AesManagerViewModel.cs b/FModel/ViewModels/AesManagerViewModel.cs index c581d3cd..b887f9f3 100644 --- a/FModel/ViewModels/AesManagerViewModel.cs +++ b/FModel/ViewModels/AesManagerViewModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; diff --git a/FModel/ViewModels/ApiEndpoints/AbstractApiProvider.cs b/FModel/ViewModels/ApiEndpoints/AbstractApiProvider.cs index 400cafe7..2d4e04b9 100644 --- a/FModel/ViewModels/ApiEndpoints/AbstractApiProvider.cs +++ b/FModel/ViewModels/ApiEndpoints/AbstractApiProvider.cs @@ -1,4 +1,4 @@ -using RestSharp; +using RestSharp; using RestSharp.Interceptors; namespace FModel.ViewModels.ApiEndpoints; diff --git a/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs index 3e2fa9fb..6f6f4e94 100644 --- a/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/DynamicApiEndpoint.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading; using System.Threading.Tasks; using CUE4Parse.Utils; diff --git a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs index 47b7bd55..c12679bd 100644 --- a/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FModelApiEndpoint.cs @@ -1,4 +1,4 @@ -using System; +using System; using AdonisUI.Controls; using System.Collections.Generic; using System.Threading; diff --git a/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs index ec0576f7..34028ba8 100644 --- a/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FortniteApiEndpoint.cs @@ -1,4 +1,4 @@ -using System; +using System; using FModel.ViewModels.ApiEndpoints.Models; using RestSharp; using System.Threading.Tasks; diff --git a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs index 47daac49..0bb587bb 100644 --- a/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs +++ b/FModel/ViewModels/ApiEndpoints/FortniteCentralApiEndpoint.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using FModel.Framework; diff --git a/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs index 449033c1..b420cff3 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/AesResponse.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using J = Newtonsoft.Json.JsonPropertyAttribute; using I = Newtonsoft.Json.JsonIgnoreAttribute; diff --git a/FModel/ViewModels/ApiEndpoints/Models/EpicResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/EpicResponse.cs index 3ff3b55d..38633299 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/EpicResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/EpicResponse.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using J = Newtonsoft.Json.JsonPropertyAttribute; diff --git a/FModel/ViewModels/ApiEndpoints/Models/GitHubResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/GitHubResponse.cs index 69b09320..31f49683 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/GitHubResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/GitHubResponse.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Windows; using AdonisUI.Controls; diff --git a/FModel/ViewModels/ApiEndpoints/Models/MappingsResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/MappingsResponse.cs index 4f9ee734..8fddce7a 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/MappingsResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/MappingsResponse.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using J = Newtonsoft.Json.JsonPropertyAttribute; using I = Newtonsoft.Json.JsonIgnoreAttribute; diff --git a/FModel/ViewModels/ApiEndpoints/Models/PlaylistResponse.cs b/FModel/ViewModels/ApiEndpoints/Models/PlaylistResponse.cs index e65d794b..e54be000 100644 --- a/FModel/ViewModels/ApiEndpoints/Models/PlaylistResponse.cs +++ b/FModel/ViewModels/ApiEndpoints/Models/PlaylistResponse.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using J = Newtonsoft.Json.JsonPropertyAttribute; using I = Newtonsoft.Json.JsonIgnoreAttribute; diff --git a/FModel/ViewModels/CUE4ParseViewModel.cs b/FModel/ViewModels/CUE4ParseViewModel.cs index dc7fac8f..45e0dad5 100644 --- a/FModel/ViewModels/CUE4ParseViewModel.cs +++ b/FModel/ViewModels/CUE4ParseViewModel.cs @@ -1,1371 +1,1371 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; - -using AdonisUI.Controls; - -using CUE4Parse; -using CUE4Parse.Compression; -using CUE4Parse.Encryption.Aes; -using CUE4Parse.FileProvider; -using CUE4Parse.FileProvider.Objects; -using CUE4Parse.FileProvider.Vfs; -using CUE4Parse.GameTypes.Aion2.Objects; -using CUE4Parse.GameTypes.AshEchoes.FileProvider; -using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise; -using CUE4Parse.GameTypes.KRD.Assets.Exports; -using CUE4Parse.MappingsProvider; -using CUE4Parse.UE4.AssetRegistry; -using CUE4Parse.UE4.Assets; -using CUE4Parse.UE4.Assets.Exports; -using CUE4Parse.UE4.Assets.Exports.Animation; -using CUE4Parse.UE4.Assets.Exports.CriWare; -using CUE4Parse.UE4.Assets.Exports.Fmod; -using CUE4Parse.UE4.Assets.Exports.Material; -using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; -using CUE4Parse.UE4.Assets.Exports.Sound; -using CUE4Parse.UE4.Assets.Exports.StaticMesh; -using CUE4Parse.UE4.Assets.Exports.Texture; -using CUE4Parse.UE4.Assets.Exports.Verse; -using CUE4Parse.UE4.Assets.Exports.Wwise; -using CUE4Parse.UE4.BinaryConfig; -using CUE4Parse.UE4.CriWare; -using CUE4Parse.UE4.CriWare.Readers; -using CUE4Parse.UE4.FMod; -using CUE4Parse.UE4.IO; -using CUE4Parse.UE4.Localization; -using CUE4Parse.UE4.Objects.Core.Serialization; -using CUE4Parse.UE4.Objects.Engine; -using CUE4Parse.UE4.Objects.UObject; -using CUE4Parse.UE4.Objects.UObject.Editor; -using CUE4Parse.UE4.Oodle.Objects; -using CUE4Parse.UE4.Readers; -using CUE4Parse.UE4.Shaders; -using CUE4Parse.UE4.Versions; -using CUE4Parse.UE4.Wwise; -using CUE4Parse.Utils; - -using CUE4Parse_Conversion; -using CUE4Parse_Conversion.Sounds; - -using EpicManifestParser; -using EpicManifestParser.UE; -using EpicManifestParser.ZlibngDotNetDecompressor; - -using FModel.Creator; -using FModel.Extensions; -using FModel.Framework; -using FModel.Services; -using FModel.Settings; -using FModel.Views; -using FModel.Views.Resources.Controls; -using FModel.Views.Snooper; - -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; - -using OpenTK.Windowing.Common; -using OpenTK.Windowing.Desktop; - -using Serilog; - -using SkiaSharp; - -using Svg.Skia; - -using UE4Config.Parsing; - -using Application = System.Windows.Application; -using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid; - -namespace FModel.ViewModels; - -public class CUE4ParseViewModel : ViewModel -{ - private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView; - private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView; - private readonly Regex _fnLiveRegex = new(@"^FortniteGame[/\\]Content[/\\]Paks[/\\]", - RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - - private bool _modelIsOverwritingMaterial; - public bool ModelIsOverwritingMaterial - { - get => _modelIsOverwritingMaterial; - set => SetProperty(ref _modelIsOverwritingMaterial, value); - } - - private bool _modelIsWaitingAnimation; - public bool ModelIsWaitingAnimation - { - get => _modelIsWaitingAnimation; - set => SetProperty(ref _modelIsWaitingAnimation, value); - } - - public bool IsSnooperOpen => _snooper is { Exists: true, IsVisible: true }; - private Snooper _snooper; - public Snooper SnooperViewer - { - get - { - if (_snooper != null) return _snooper; - - return Application.Current.Dispatcher.Invoke(delegate - { - var scale = ImGuiController.GetDpiScale(); - var htz = Snooper.GetMaxRefreshFrequency(); - return _snooper = new Snooper( - new GameWindowSettings { UpdateFrequency = htz }, - new NativeWindowSettings - { - ClientSize = new OpenTK.Mathematics.Vector2i( - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .75 * scale), - Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85 * scale)), - NumberOfSamples = Constants.SAMPLES_COUNT, - WindowBorder = WindowBorder.Resizable, - Flags = ContextFlags.ForwardCompatible, - Profile = ContextProfile.Core, - Vsync = VSyncMode.Adaptive, - APIVersion = new Version(4, 6), - StartVisible = false, - StartFocused = false, - Title = "3D Viewer" - }); - }); - } - } - - public AbstractVfsFileProvider Provider { get; } - public GameDirectoryViewModel GameDirectory { get; } - public AssetsFolderViewModel AssetsFolder { get; } - public SearchViewModel SearchVm { get; } - public SearchViewModel RefVm { get; } - public TabControlViewModel TabControl { get; } - public ConfigIni IoStoreOnDemand { get; } - private Lazy _wwiseProviderLazy; - public WwiseProvider WwiseProvider => _wwiseProviderLazy.Value; - private Lazy _fmodProviderLazy; - public FModProvider FmodProvider => _fmodProviderLazy?.Value; - private Lazy _criWareProviderLazy; - public CriWareProvider CriWareProvider => _criWareProviderLazy?.Value; - public ConcurrentBag UnknownExtensions = []; - - public CUE4ParseViewModel() - { - var currentDir = UserSettings.Default.CurrentDir; - var gameDirectory = currentDir.GameDirectory; - var versionContainer = new VersionContainer( - game: currentDir.UeVersion, platform: currentDir.TexturePlatform, - 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", versionContainer, pathComparer); - break; - } - case Constants._VAL_LIVE_TRIGGER: - { - Provider = new StreamedFileProvider("ValorantLive", versionContainer, pathComparer); - break; - } - default: - { - var project = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\"); - Provider = project switch - { - "StateOfDecay2" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), - [ - new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"), - new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks") - ], SearchOption.AllDirectories, versionContainer, pathComparer), - "eFootball" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), - [ - new(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\KONAMI\\eFootball\\ST\\Download") - ], SearchOption.AllDirectories, versionContainer, pathComparer), - _ when versionContainer.Game is EGame.GAME_AshEchoes => new AEDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer), - _ when versionContainer.Game is EGame.GAME_BlackStigma => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, StringComparer.Ordinal), - _ => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer) - }; - - break; - } - } - - Provider.ReadScriptData = UserSettings.Default.ReadScriptData; - Provider.ReadShaderMaps = UserSettings.Default.ReadShaderMaps; - Provider.ReadNaniteData = true; - - GameDirectory = new GameDirectoryViewModel(); - AssetsFolder = new AssetsFolderViewModel(); - SearchVm = new SearchViewModel(); - RefVm = new SearchViewModel(); - TabControl = new TabControlViewModel(); - IoStoreOnDemand = new ConfigIni(nameof(IoStoreOnDemand)); - } - - public async Task Initialize() - { - await _apiEndpointView.EpicApi.VerifyAuth(CancellationToken.None); - await _threadWorkerView.Begin(cancellationToken => - { - Provider.OnDemandOptions = new IoStoreOnDemandOptions - { - ChunkHostUri = new Uri("https://download.epicgames.com/", UriKind.Absolute), - ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")), - Authorization = new AuthenticationHeaderValue("Bearer", UserSettings.Default.LastAuthResponse.AccessToken), - Timeout = TimeSpan.FromSeconds(30) - }; - - switch (Provider) - { - case StreamedFileProvider p: - switch (p.LiveGame) - { - case "FortniteLive": - { - var manifestInfo = _apiEndpointView.EpicApi.GetManifest(cancellationToken); - if (manifestInfo is null) - { - throw new FileLoadException("Could not load latest Fortnite manifest, you may have to switch to your local installation."); - } - - var cacheDir = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")).FullName; - var manifestOptions = new ManifestParseOptions - { - ChunkCacheDirectory = cacheDir, - ManifestCacheDirectory = cacheDir, - ChunkBaseUrl = "http://download.epicgames.com/Builds/Fortnite/CloudDir/", - Decompressor = ManifestZlibngDotNetDecompressor.Decompress, - DecompressorState = ZlibHelper.Instance, - CacheChunksAsIs = false - }; - - var startTs = Stopwatch.GetTimestamp(); - FBuildPatchAppManifest manifest; - - try - { - (manifest, _) = manifestInfo.DownloadAndParseAsync(manifestOptions, - cancellationToken: cancellationToken, - elementManifestPredicate: static x => x.Uri.Host == "download.epicgames.com" - ).GetAwaiter().GetResult(); - } - catch (HttpRequestException ex) - { - Log.Error("Failed to download manifest ({ManifestUri})", ex.Data["ManifestUri"]?.ToString() ?? ""); - throw; - } - - if (manifest.TryFindFile("Cloud/IoStoreOnDemand.ini", out var ioStoreOnDemandFile)) - { - IoStoreOnDemand.Read(new StreamReader(ioStoreOnDemandFile.GetStream())); - } - - Parallel.ForEach(manifest.Files.Where(x => _fnLiveRegex.IsMatch(x.FileName)), fileManifest => - { - p.RegisterVfs(fileManifest.FileName, [fileManifest.GetStream()], - it => new FRandomAccessStreamArchive(it, manifest.FindFile(it)!.GetStream(), p.Versions)); - }); - - var elapsedTime = Stopwatch.GetElapsedTime(startTs); - FLogger.Append(ELog.Information, () => - FLogger.Text($"Fortnite [LIVE] has been loaded successfully in {elapsedTime.TotalMilliseconds:F1}ms", Constants.WHITE, true)); - break; - } - case "ValorantLive": - { - var manifest = _apiEndpointView.ValorantApi.GetManifest(cancellationToken); - if (manifest == null) - { - throw new Exception("Could not load latest Valorant manifest, you may have to switch to your local installation."); - } - - Parallel.ForEach(manifest.Paks, pak => - { - p.RegisterVfs(pak.GetFullName(), [pak.GetStream(manifest)]); - }); - - FLogger.Append(ELog.Information, () => - FLogger.Text($"Valorant '{manifest.Header.GameVersion}' has been loaded successfully", Constants.WHITE, true)); - break; - } - } - - break; - case DefaultFileProvider: - { - var ioStoreOnDemandPath = Path.Combine(UserSettings.Default.GameDirectory, "..\\..\\..\\Cloud\\IoStoreOnDemand.ini"); - if (File.Exists(ioStoreOnDemandPath)) - { - using var s = new StreamReader(ioStoreOnDemandPath); - IoStoreOnDemand.Read(s); - } - break; - } - } - - Provider.Initialize(); - _wwiseProviderLazy = new Lazy(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory, UserSettings.Default.WwiseMaxBnkPrefetch)); - _fmodProviderLazy = new Lazy(() => new FModProvider(Provider, UserSettings.Default.GameDirectory)); - _criWareProviderLazy = new Lazy(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory)); - Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}"); - }); - } - - /// - /// load virtual files system from GameDirectory - /// - /// - public void LoadVfs(IEnumerable> aesKeys) - { - Provider.SubmitKeys(aesKeys); - Provider.PostMount(); - - var aesMax = Provider.RequiredKeys.Count + Provider.Keys.Count; - var archiveMax = Provider.UnloadedVfs.Count + Provider.MountedVfs.Count; - Log.Information($"Project: {Provider.ProjectName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax} | Files: x{Provider.Files.Count}"); - } - - public void ClearProvider() - { - if (Provider == null) return; - - AssetsFolder.Folders.Clear(); - SearchVm.SearchResults.Clear(); - Helper.CloseWindow("Search For Packages"); - Provider.UnloadNonStreamedVfs(); - GC.Collect(); - } - - public async Task RefreshAes() - { - // game directory dependent, we don't have the provider game name yet since we don't have aes keys - // except when this comes from the AES Manager - if (!UserSettings.IsEndpointValid(EEndpointType.Aes, out var endpoint)) - return; - - await _threadWorkerView.Begin(cancellationToken => - { - // deprecated values - if (endpoint.Url == "https://fortnitecentral.genxgames.gg/api/v1/aes") endpoint.Url = "https://uedb.dev/svc/api/v1/fortnite/aes"; - - var aes = _apiEndpointView.DynamicApi.GetAesKeys(cancellationToken, endpoint.Url, endpoint.Path); - if (aes is not { IsValid: true }) return; - - UserSettings.Default.CurrentDir.AesKeys = aes; - }); - } - - public async Task InitInformation() - { - await _threadWorkerView.Begin(cancellationToken => - { - var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.ProjectName); - if (info == null) return; - - FLogger.Append(ELog.None, () => - { - for (var i = 0; i < info.Messages.Length; i++) - { - FLogger.Text(info.Messages[i], info.Colors[i], bool.Parse(info.NewLines[i])); - } - }); - }); - } - - public Task InitMappings(bool force = false) - { - if (!UserSettings.IsEndpointValid(EEndpointType.Mapping, out var endpoint)) - { - Provider.MappingsContainer = null; - return Task.CompletedTask; - } - - return Task.Run(() => - { - var l = ELog.Information; - if (endpoint.Overwrite && File.Exists(endpoint.FilePath)) - { - Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(endpoint.FilePath); - } - else if (endpoint.IsValid) - { - // deprecated values - if (endpoint.Path == "$.[?(@.meta.compressionMethod=='Oodle')].['url','fileName']") endpoint.Path = "$.[0].['url','fileName']"; - if (endpoint.Url == "https://fortnitecentral.genxgames.gg/api/v1/mappings") - { - endpoint.Url = "https://uedb.dev/svc/api/v1/fortnite/mappings"; - endpoint.Path = "$.mappings.ZStandard"; - } - - var mappingsFolder = Path.Combine(UserSettings.Default.OutputDirectory, ".data"); - var mappings = _apiEndpointView.DynamicApi.GetMappings(CancellationToken.None, endpoint.Url, endpoint.Path); - if (mappings is { Length: > 0 }) - { - foreach (var mapping in mappings) - { - if (!mapping.IsValid) continue; - - var mappingPath = Path.Combine(mappingsFolder, mapping.FileName); - if (force || !File.Exists(mappingPath) || new FileInfo(mappingPath).Length == 0) - { - _apiEndpointView.DownloadFile(mapping.Url, mappingPath); - } - - Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(mappingPath); - break; - } - } - - if (Provider.MappingsContainer == null) - { - var latestUsmaps = new DirectoryInfo(mappingsFolder).GetFiles("*_oo.usmap"); - if (latestUsmaps.Length <= 0) return; - - var latestUsmapInfo = latestUsmaps.OrderBy(f => f.LastWriteTime).Last(); - Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(latestUsmapInfo.FullName); - l = ELog.Warning; - } - } - - if (Provider.MappingsContainer is FileUsmapTypeMappingsProvider m) - { - Log.Information($"Mappings pulled from '{m.FileName}'"); - FLogger.Append(l, () => FLogger.Text($"Mappings pulled from '{m.FileName}'", Constants.WHITE, true)); - } - }); - } - - public Task VerifyConsoleVariables() - { - if (Provider.Versions["StripAdditiveRefPose"]) - { - FLogger.Append(ELog.Warning, () => - FLogger.Text("Additive animations have their reference pose stripped, which will lead to inaccurate preview and export", Constants.WHITE, true)); - } - - if (Provider.Versions.Game is EGame.GAME_UE4_LATEST or EGame.GAME_UE5_LATEST && !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) // ignore fortnite globally - { - FLogger.Append(ELog.Warning, () => - FLogger.Text($"Experimental UE version selected, likely unsuitable for '{Provider.GameDisplayName ?? Provider.ProjectName}'", Constants.WHITE, true)); - } - - return Task.CompletedTask; - } - - public Task VerifyOnDemandArchives() - { - // only local fortnite - if (Provider is not DefaultFileProvider || !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) - return Task.CompletedTask; - - // scuffed but working - var persistentDownloadDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FortniteGame/Saved/PersistentDownloadDir"); - var iasFileInfo = new FileInfo(Path.Combine(persistentDownloadDir, "ias", "ias.cache.0")); - if (!iasFileInfo.Exists || iasFileInfo.Length == 0) - return Task.CompletedTask; - - return Task.Run(async () => - { - var inst = new List(); - IoStoreOnDemand.FindPropertyInstructions("Endpoint", "TocPath", inst); - if (inst.Count <= 0) return; - - var ioStoreOnDemandPath = Path.Combine(UserSettings.Default.GameDirectory, "..\\..\\..\\Cloud", inst[0].Value.SubstringAfterLast("/").SubstringBefore("\"")); - if (!File.Exists(ioStoreOnDemandPath)) return; - - await Provider.RegisterVfsAsync(new IoChunkToc(ioStoreOnDemandPath)); - var onDemandCount = await Provider.MountAsync(); - FLogger.Append(ELog.Information, () => - FLogger.Text($"{onDemandCount} on-demand archive{(onDemandCount > 1 ? "s" : "")} streamed via epicgames.com", Constants.WHITE, true)); - }); - } - - public int LocalizedResourcesCount { get; set; } - public bool LocalResourcesDone { get; set; } - public bool HotfixedResourcesDone { get; set; } - - public async Task LoadLocalizedResources() - { - var snapshot = LocalizedResourcesCount; - await Task.WhenAll(LoadGameLocalizedResources(), LoadHotfixedLocalizedResources()).ConfigureAwait(false); - - LocalizedResourcesCount = Provider.Internationalization.Count; - if (snapshot != LocalizedResourcesCount) - { - FLogger.Append(ELog.Information, () => - FLogger.Text($"{LocalizedResourcesCount} localized resources loaded for '{UserSettings.Default.AssetLanguage.GetDescription()}'", Constants.WHITE, true)); - Utils.Typefaces = new Typefaces(this); - } - } - - private Task LoadGameLocalizedResources() - { - if (LocalResourcesDone) return Task.CompletedTask; - return Task.Run(() => - { - LocalResourcesDone = Provider.TryChangeCulture(Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); - }); - } - - private Task LoadHotfixedLocalizedResources() - { - if (!Provider.ProjectName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask; - return Task.Run(() => - { - var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(CancellationToken.None, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); - if (hotfixes == null) return; - - Provider.Internationalization.Override(hotfixes); - HotfixedResourcesDone = true; - }); - } - - private int _virtualPathCount { get; set; } - public Task LoadVirtualPaths() - { - if (_virtualPathCount > 0) return Task.CompletedTask; - return Task.Run(() => - { - _virtualPathCount = Provider.LoadVirtualPaths(UserSettings.Default.CurrentDir.UeVersion.GetVersion()); - if (_virtualPathCount > 0) - { - FLogger.Append(ELog.Information, () => - FLogger.Text($"{_virtualPathCount} virtual paths loaded", Constants.WHITE, true)); - } - else - { - FLogger.Append(ELog.Warning, () => - FLogger.Text("Could not load virtual paths, plugin manifest may not exist", Constants.WHITE, true)); - } - }); - } - - public void ExtractSelected(CancellationToken cancellationToken, IEnumerable assetItems) - { - foreach (var entry in assetItems) - { - Thread.Yield(); - cancellationToken.ThrowIfCancellationRequested(); - Extract(cancellationToken, entry, TabControl.HasNoTabs); - } - } - - private void BulkFolder(CancellationToken cancellationToken, TreeItem folder, Action action) - { - foreach (var entry in folder.AssetsList.Assets) - { - Thread.Yield(); - cancellationToken.ThrowIfCancellationRequested(); - try - { - action(entry.Asset); - } - catch - { - // ignore - } - } - - foreach (var f in folder.Folders) BulkFolder(cancellationToken, f, action); - } - - public void ExportFolder(CancellationToken cancellationToken, TreeItem folder) - { - Parallel.ForEach(folder.AssetsList.Assets, entry => - { - cancellationToken.ThrowIfCancellationRequested(); - ExportData(entry.Asset, false); - }); - - foreach (var f in folder.Folders) ExportFolder(cancellationToken, f); - } - - public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs)); - - public void SaveFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); - - public void TextureFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto)); - - public void ModelFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto)); - - public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto)); - - public void AudioFolder(CancellationToken cancellationToken, TreeItem folder) - => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto)); - - public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None) - { - ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; - Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", entry.Path); - - if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(entry); - else TabControl.SelectedTab.SoftReset(entry); - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(entry.Extension); - - var updateUi = !HasFlag(bulk, EBulkType.Auto); - var saveProperties = HasFlag(bulk, EBulkType.Properties); - var saveTextures = HasFlag(bulk, EBulkType.Textures); - var saveAudio = HasFlag(bulk, EBulkType.Audio); - switch (entry.Extension) - { - case "uasset": - case "umap": - { - var result = Provider.GetLoadPackageResult(entry); - TabControl.SelectedTab.TitleExtra = result.TabTitleExtra; - - if (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 = result.InclusiveStart; i < result.ExclusiveEnd; i++) - { - if (CheckExport(cancellationToken, result.Package, i, bulk)) - break; - } - - break; - } - case "ini" when entry.Name.Contains("BinaryConfig"): - { - var ar = entry.CreateReader(); - var configCache = new FConfigCacheIni(ar); - - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json"); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(configCache, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "dat" when Provider.ProjectName.Equals("Aion2", StringComparison.OrdinalIgnoreCase): - { - ProcessAion2DatFile(entry, updateUi, saveProperties); - break; - } - case "upluginmanifest": - case "code-workspace": - case "projectstore": - case "uefnproject": - case "uproject": - case "manifest": - case "uplugin": - case "archive": - case "dnearchive": // Banishers: Ghosts of New Eden - case "gitignore": - case "LICENSE": - case "playstats": // Dispatch - case "template": - case "stUMeta": // LIS: Double Exposure - case "vmodule": - case "glslfx": - case "cptake": - case "uparam": // Steel Hunters - case "spi1d": - case "verse": - case "html": - case "json5": - case "json": - case "uref": - case "cube": - case "usda": - case "ocio": - case "data" when Provider.ProjectName is "OakGame": - case "ini": - case "txt": - case "log": - case "lsd": // Days Gone - case "bat": - case "dat": - case "cfg": - case "ddr": - case "ide": - case "ipl": - case "zon": - case "xml": - case "css": - case "csv": - case "pem": - case "tps": - case "tgc": // State of Decay 2 - case "cpp": - case "apx": - case "udn": - case "doc": - case "lua": - case "vdf": - case "yml": - case "js": - case "po": - case "md": - case "h": - // Uncharted Waters Origin - case "crn": - case "uwt": - case "wvh": - case "bf": - case "bl": - case "bm": - case "br": - { - var data = Provider.SaveAsset(entry); - using var stream = new MemoryStream(data) { Position = 0 }; - using var reader = new StreamReader(stream); - - TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); - - break; - } - case "locmeta": - { - var archive = entry.CreateReader(); - var metadata = new FTextLocalizationMetaDataResource(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "locres": - { - var archive = entry.CreateReader(); - var locres = new FTextLocalizationResource(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "bin" when entry.Name.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase): - { - var archive = entry.CreateReader(); - var registry = new FAssetRegistryState(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "bin" when entry.Name.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase): - { - var archive = entry.CreateReader(); - var registry = new FGlobalShaderCache(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "bank": - { - var archive = entry.CreateReader(); - if (!FModProvider.TryLoadBank(archive, entry.NameWithoutExtension, out var fmodReader)) - { - Log.Error($"Failed to load FMOD bank {entry.Path}"); - break; - } - - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(fmodReader, Formatting.Indented, converters: [new FmodSoundBankConverter(), new StringEnumConverter()]), saveProperties, updateUi); - - var extractedSounds = FmodProvider.ExtractBankSounds(fmodReader); - var directory = Path.GetDirectoryName(entry.Path) ?? "/FMOD/Desktop/"; - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); - } - - break; - } - case "bnk": - case "pck": - { - var archive = entry.CreateReader(); - var wwise = new WwiseReader(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi); - - var medias = WwiseProvider.ExtractBankSounds(wwise); - foreach (var media in medias) - { - SaveAndPlaySound(media.OutputPath, media.Extension, media.Data, saveAudio); - } - - break; - } - case "awb": - { - var archive = entry.CreateReader(); - var awbReader = new AwbReader(archive); - - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(awbReader, Formatting.Indented), saveProperties, updateUi); - - var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/"; - var extractedSounds = CriWareProvider.ExtractCriWareSounds(awbReader, archive.Name); - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); - } - - break; - } - case "acb": - { - var archive = entry.CreateReader(); - var acbReader = new AcbReader(archive); - - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(acbReader, Formatting.Indented), saveProperties, updateUi); - - var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/"; - var extractedSounds = CriWareProvider.ExtractCriWareSounds(acbReader, archive.Name); - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); - } - - break; - } - case "xvag": - case "flac": - case "at9": - case "wem": - case "wav": - case "WAV": - case "ogg": - // todo: CSCore.MediaFoundation.MediaFoundationException The byte stream type of the given URL is unsupported. case "aif": - { - var data = Provider.SaveAsset(entry); - SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data, saveAudio); - - break; - } - case "udic": - { - var archive = entry.CreateReader(); - var header = new FOodleDictionaryArchive(archive).Header; - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "png": - case "jpg": - case "bmp": - { - var data = Provider.SaveAsset(entry); - using var stream = new MemoryStream(data) { Position = 0 }; - TabControl.SelectedTab.AddImage(entry.NameWithoutExtension, false, SKBitmap.Decode(stream), saveTextures, updateUi); - - break; - } - case "svg": - { - var data = Provider.SaveAsset(entry); - using var stream = new MemoryStream(data) { Position = 0 }; - var svg = new SKSvg(); - svg.Load(stream); - - int size = 512; - var bitmap = new SKBitmap(size, size); - using var canvas = new SKCanvas(bitmap); - canvas.Clear(SKColors.Transparent); - - if (svg.Picture == null) - break; - - var bounds = svg.Picture.CullRect; - float scale = Math.Min(size / bounds.Width, size / bounds.Height); - canvas.Scale(scale); - canvas.Translate(-bounds.Left, -bounds.Top); - canvas.DrawPicture(svg.Picture); - - TabControl.SelectedTab.AddImage(entry.NameWithoutExtension, false, bitmap, saveTextures, updateUi); - - break; - } - case "ufont": - case "otf": - case "ttf": - FLogger.Append(ELog.Warning, () => - FLogger.Text($"Export '{entry.Name}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true)); - break; - case "ushaderbytecode": - case "ushadercode": - { - var archive = entry.CreateReader(); - var ar = new FShaderCodeArchive(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "upipelinecache": - { - var archive = entry.CreateReader(); - var ar = new FPipelineCacheFile(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "stinfo": - { - var archive = entry.CreateReader(); - var ar = new FShaderTypeHashes(archive); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); - - break; - } - case "res": // just skip - case "luac": // compiled lua - case "bytes": // wuthering waves - break; - default: - { - Log.Warning($"The package '{entry.Name}' is of an unknown type."); - if (!UnknownExtensions.Contains(entry.Extension)) - { - UnknownExtensions.Add(entry.Extension); - FLogger.Append(ELog.Warning, () => - FLogger.Text($"There are some packages with an unknown type {entry.Extension}. Check Log file for a full list.", Constants.WHITE, true)); - } - break; - } - } - - void ProcessAion2DatFile(GameFile entry, bool updateUi, bool saveProperties) - { - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json"); - if (entry.NameWithoutExtension.EndsWith("_MapEvent")) - { - var data = Provider.SaveAsset(entry); - FAion2DatFileArchive.DecryptData(data); - using var stream = new MemoryStream(data) { Position = 0 }; - using var reader = new StreamReader(stream); - - TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); - } - else if (entry.NameWithoutExtension.Equals("L10NString")) - { - var l10nData = new FAion2L10NFile(entry); - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(l10nData, Formatting.Indented), saveProperties, updateUi); - } - else - { - FAion2DataFile datfile = entry.NameWithoutExtension switch - { - "MapDataHierarchy" => new FAion2MapHierarchyFile(entry), - "MapData" => new FAion2MapDataFile(entry, Provider), - _ when entry.Directory.EndsWith("Data/WorldMap", StringComparison.OrdinalIgnoreCase) => new FAion2MapDataFile(entry, Provider), - _ => new FAion2DataTableFile(entry, Provider) - }; - - TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(datfile, Formatting.Indented), saveProperties, updateUi); - } - } - } - - public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType) - { - Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath); - - var entry = Provider[fullPath]; - TabControl.AddTab(entry, parentExportType); - TabControl.SelectedTab.ScrollTrigger = objectName; - - var result = Provider.GetLoadPackageResult(entry, objectName); - - 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, result.Package, i)) - break; - } - } - - private bool CheckExport(CancellationToken cancellationToken, IPackage pkg, int index, EBulkType bulk = EBulkType.None) // return true once you want to stop searching for exports - { - var isNone = bulk == EBulkType.None; - var updateUi = !HasFlag(bulk, EBulkType.Auto); - var saveTextures = HasFlag(bulk, EBulkType.Textures); - var saveAudio = HasFlag(bulk, EBulkType.Audio); - - var pointer = new FPackageIndex(pkg, index + 1).ResolvedObject; - if (pointer?.Object is null) return false; - - var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); - switch (dummy) - { - case UVerseDigest when isNone && pointer.Object.Value is UVerseDigest verseDigest: - { - if (!TabControl.CanAddTabs) return false; - - TabControl.AddTab($"{verseDigest.ProjectName}.verse"); - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("verse"); - TabControl.SelectedTab.SetDocumentText(verseDigest.ReadableCode, false, false); - return true; - } - case UTexture when (isNone || saveTextures) && pointer.Object.Value is UTexture texture: - { - TabControl.SelectedTab.AddImage(texture, saveTextures, updateUi); - return false; - } - case USvgAsset when (isNone || saveTextures) && pointer.Object.Value is USvgAsset svgasset: - { - const int size = 512; - var data = svgasset.GetOrDefault("SvgData"); - var sourceFile = svgasset.GetOrDefault("SourceFile"); - using var stream = new MemoryStream(data) { Position = 0 }; - - var svg = new SKSvg(); - svg.Load(stream); - - if (svg.Picture == null) - return false; - - var b = svg.Picture.CullRect; - float s = Math.Min(size / b.Width, size / b.Height); - - var bitmap = new SKBitmap(size, size); - using var canvas = new SKCanvas(bitmap); - using var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium }; - - canvas.Scale(s); - canvas.Translate(-b.Left, -b.Top); - canvas.DrawPicture(svg.Picture, paint); - - if (saveTextures) - { - var fileName = sourceFile.SubstringAfterLast('/'); - var path = Path.Combine(UserSettings.Default.TextureDirectory, - UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Entry.Directory : "", fileName!).Replace('\\', '/'); - - Directory.CreateDirectory(path.SubstringBeforeLast('/')); - - using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); - fs.Write(data, 0, data.Length); - if (File.Exists(path)) - { - Log.Information("{FileName} successfully saved", fileName); - if (updateUi) - { - FLogger.Append(ELog.Information, () => - { - FLogger.Text("Successfully saved ", Constants.WHITE); - FLogger.Link(fileName, path, true); - }); - } - } - else - { - Log.Error("{FileName} could not be saved", fileName); - if (updateUi) - FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{fileName}'", Constants.WHITE, true)); - } - } - - TabControl.SelectedTab.AddImage(sourceFile.SubstringAfterLast('/'), false, bitmap, false, updateUi); - return false; - } - // The Dark Pictures Anthology: House of Ashes - case UExternalSource when (isNone || saveAudio) && pointer.Object.Value is UExternalSource externalSource: - { - var audioName = Path.GetFileNameWithoutExtension(externalSource.ExternalSourcePath); - SaveAndPlaySound(audioName, "wem", externalSource.Data?.WemFile ?? [], saveAudio); - return false; - } - case UAkAudioEvent when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEvent audioEvent: - { - var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent); - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data, saveAudio); - } - return false; - } - case UFMODEvent when (isNone || saveAudio) && pointer.Object.Value is UFMODEvent fmodEvent: - { - var extractedSounds = FmodProvider.ExtractEventSounds(fmodEvent); - var directory = Path.GetDirectoryName(fmodEvent.Owner?.Name) ?? "/FMOD/Desktop/"; - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); - } - return false; - } - case UFMODBank when (isNone || saveAudio) && pointer.Object.Value is UFMODBank fmodBank: - { - var extractedSounds = FmodProvider.ExtractBankSounds(fmodBank); - var directory = Path.GetDirectoryName(fmodBank.Owner?.Name) ?? "/FMOD/Desktop/"; - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); - } - return false; - } - case USoundAtomCueSheet or UAtomCueSheet or USoundAtomCue or UAtomWaveBank when (isNone || saveAudio) && pointer.Object.Value is UObject atomObject: - { - var extractedSounds = atomObject switch - { - USoundAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet), - UAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet), - USoundAtomCue cue => CriWareProvider.ExtractCriWareSounds(cue), - UAtomWaveBank awb => CriWareProvider.ExtractCriWareSounds(awb), - _ => [] - }; - - var directory = Path.GetDirectoryName(atomObject.Owner?.Name) ?? "/Criware/"; - directory = Path.GetDirectoryName(atomObject.Owner.Provider.FixPath(directory)); - foreach (var sound in extractedSounds) - { - SaveAndPlaySound(Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio); - } - return false; - } - case UAkMediaAssetData when isNone || saveAudio: - case USoundWave when isNone || saveAudio: - { - var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed; - pointer.Object.Value.Decode(shouldDecompress, out var audioFormat, out var data); - var hasAf = !string.IsNullOrEmpty(audioFormat); - if (data == null || !hasAf) - { - if (hasAf) FLogger.Append(ELog.Warning, () => FLogger.Text($"Unsupported audio format '{audioFormat}'", Constants.WHITE, true)); - return false; - } - - SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data, saveAudio); - return false; - } - case UWorld when isNone && UserSettings.Default.PreviewWorlds: - case UBlueprintGeneratedClass when isNone && UserSettings.Default.PreviewWorlds && TabControl.SelectedTab.ParentExportType switch - { - "JunoBuildInstructionsItemDefinition" => true, - "JunoBuildingSetAccountItemDefinition" => true, - "JunoBuildingPropAccountItemDefinition" => true, - _ => false - }: - case UPaperSprite when isNone && UserSettings.Default.PreviewMaterials: - case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes: - case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: - case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh: - case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial && - !(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && - (pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || - pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || - pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))): - { - if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object)) - SnooperViewer.Run(); - return true; - } - case UMaterialInstance when isNone && ModelIsOverwritingMaterial && pointer.Object.Value is UMaterialInstance m: - { - SnooperViewer.Renderer.Swap(m); - SnooperViewer.Run(); - return true; - } - case UAnimSequenceBase when isNone && UserSettings.Default.PreviewAnimations || ModelIsWaitingAnimation: - { - // animate all animations using their specified skeleton or when we explicitly asked for a loaded model to be animated (ignoring whether we wanted to preview animations) - SnooperViewer.Renderer.Animate(pointer.Object.Value); - SnooperViewer.Run(); - return true; - } - case UStaticMesh when HasFlag(bulk, EBulkType.Meshes): - case USkeletalMesh when HasFlag(bulk, EBulkType.Meshes): - case USkeleton when UserSettings.Default.SaveSkeletonAsMesh && HasFlag(bulk, EBulkType.Meshes): - // case UMaterialInstance when HasFlag(bulk, EBulkType.Materials): // read the fucking json - case UAnimSequenceBase when HasFlag(bulk, EBulkType.Animations): - { - SaveExport(pointer.Object.Value, updateUi); - return true; - } - default: - { - if (!isNone && !saveTextures) return false; - - using var cPackage = new CreatorPackage(pkg.Name, dummy.ExportType, pointer.Object, UserSettings.Default.CosmeticStyle); - if (!cPackage.TryConstructCreator(out var creator)) - return false; - - creator.ParseForInfo(); - TabControl.SelectedTab.AddImage(pointer.Object.Value.Name, false, creator.Draw(), saveTextures, updateUi); - return true; - - } - } - } - - public void ShowMetadata(GameFile entry) - { - ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; - - var package = Provider.LoadPackage(entry); - - 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); - } - - public void FindReferences(GameFile entry) - { - var refs = Provider.ScanForPackageRefs(entry); - Application.Current.Dispatcher.Invoke(delegate - { - var refView = Helper.GetWindow("Search For Packages", () => new SearchView().Show()); - refView.ChangeCollection(ESearchViewTab.RefView, refs, entry); - refView.FocusTab(ESearchViewTab.RefView); - }); - } - - - public void Decompile(GameFile entry) - { - ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; - - if (TabControl.CanAddTabs) TabControl.AddTab(entry); - else TabControl.SelectedTab.SoftReset(entry); - - TabControl.SelectedTab.TitleExtra = "Decompiled"; - TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("cpp"); - - UClassCookedMetaData cookedMetaData = null; - try - { - var editorPkg = Provider.LoadPackage(entry.Path.Replace(".uasset", ".o.uasset")); - cookedMetaData = editorPkg.GetExport("CookedClassMetaData"); - } - catch - { - // ignored - } - - var cppList = new List(); - var pkg = Provider.LoadPackage(entry); - for (var i = 0; i < pkg.ExportMapLength; i++) - { - var pointer = new FPackageIndex(pkg, i + 1).ResolvedObject; - if (pointer?.Object is null && pointer.Class?.Object?.Value is null) - continue; - - var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); - if (dummy is not UClass || pointer.Object.Value is not UClass blueprint) - continue; - - cppList.Add(blueprint.DecompileBlueprintToPseudo(cookedMetaData)); - } - - var cpp = cppList.Count > 1 ? string.Join("\n\n", cppList) : cppList.FirstOrDefault() ?? string.Empty; - if (entry.Path.Contains("_Verse.uasset")) - { - cpp = Regex.Replace(cpp, "__verse_0x[a-fA-F0-9]{8}_", ""); // UnmangleCasedName - } - cpp = Regex.Replace(cpp, @"CallFunc_([A-Za-z0-9_]+)_ReturnValue", "$1"); - - - TabControl.SelectedTab.SetDocumentText(cpp, false, false); - } - - private void SaveAndPlaySound(string fullPath, string ext, byte[] data, bool isBulk) - { - if (fullPath.StartsWith('/')) fullPath = fullPath[1..]; - var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory, - UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}"; - - if (isBulk) - { - Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/')); - using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write); - using var writer = new BinaryWriter(stream); - writer.Write(data); - writer.Flush(); - return; - } - - // TODO - // since we are currently in a thread, the audio player's lifetime (memory-wise) will keep the current thread up and running until fmodel itself closes - // the solution would be to kill the current thread at this line and then open the audio player without "Application.Current.Dispatcher.Invoke" - // but the ThreadWorkerViewModel is an idiot and doesn't understand we want to kill the current thread inside the current thread and continue the code - Application.Current.Dispatcher.Invoke(delegate - { - var audioPlayer = Helper.GetWindow("Audio Player", () => new AudioPlayer().Show()); - audioPlayer.Load(data, savedAudioPath); - }); - } - - private void SaveExport(UObject export, bool updateUi = true) - { - var toSave = new Exporter(export, UserSettings.Default.ExportOptions); - var toSaveDirectory = new DirectoryInfo(UserSettings.Default.ModelDirectory); - if (toSave.TryWriteToDir(toSaveDirectory, out var label, out var savedFilePath)) - { - Log.Information("Successfully saved {FilePath}", savedFilePath); - if (updateUi) - { - FLogger.Append(ELog.Information, () => - { - FLogger.Text("Successfully saved ", Constants.WHITE); - FLogger.Link(label, savedFilePath, true); - }); - } - } - else - { - Log.Error("{FileName} could not be saved", export.Name); - FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{export.Name}'", Constants.WHITE, true)); - } - } - - private readonly object _rawData = new (); - public void ExportData(GameFile entry, bool updateUi = true) - { - if (Provider.TrySavePackage(entry, out var assets)) - { - string path = UserSettings.Default.RawDataDirectory; - Parallel.ForEach(assets, kvp => - { - lock (_rawData) - { - path = Path.Combine(UserSettings.Default.RawDataDirectory, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/'); - Directory.CreateDirectory(path.SubstringBeforeLast('/')); - File.WriteAllBytes(path, kvp.Value); - } - }); - - Log.Information("{FileName} successfully exported", entry.Name); - if (updateUi) - { - FLogger.Append(ELog.Information, () => - { - FLogger.Text("Successfully exported ", Constants.WHITE); - FLogger.Link(entry.Name, path, true); - }); - } - } - else - { - Log.Error("{FileName} could not be exported", entry.Name); - if (updateUi) - FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{entry.Name}'", Constants.WHITE, true)); - } - } - - private static bool HasFlag(EBulkType a, EBulkType b) - { - return (a & b) == b; - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; + +using AdonisUI.Controls; + +using CUE4Parse; +using CUE4Parse.Compression; +using CUE4Parse.Encryption.Aes; +using CUE4Parse.FileProvider; +using CUE4Parse.FileProvider.Objects; +using CUE4Parse.FileProvider.Vfs; +using CUE4Parse.GameTypes.Aion2.Objects; +using CUE4Parse.GameTypes.AshEchoes.FileProvider; +using CUE4Parse.GameTypes.SMG.UE4.Assets.Exports.Wwise; +using CUE4Parse.GameTypes.KRD.Assets.Exports; +using CUE4Parse.MappingsProvider; +using CUE4Parse.UE4.AssetRegistry; +using CUE4Parse.UE4.Assets; +using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports.Animation; +using CUE4Parse.UE4.Assets.Exports.CriWare; +using CUE4Parse.UE4.Assets.Exports.Fmod; +using CUE4Parse.UE4.Assets.Exports.Material; +using CUE4Parse.UE4.Assets.Exports.SkeletalMesh; +using CUE4Parse.UE4.Assets.Exports.Sound; +using CUE4Parse.UE4.Assets.Exports.StaticMesh; +using CUE4Parse.UE4.Assets.Exports.Texture; +using CUE4Parse.UE4.Assets.Exports.Verse; +using CUE4Parse.UE4.Assets.Exports.Wwise; +using CUE4Parse.UE4.BinaryConfig; +using CUE4Parse.UE4.CriWare; +using CUE4Parse.UE4.CriWare.Readers; +using CUE4Parse.UE4.FMod; +using CUE4Parse.UE4.IO; +using CUE4Parse.UE4.Localization; +using CUE4Parse.UE4.Objects.Core.Serialization; +using CUE4Parse.UE4.Objects.Engine; +using CUE4Parse.UE4.Objects.UObject; +using CUE4Parse.UE4.Objects.UObject.Editor; +using CUE4Parse.UE4.Oodle.Objects; +using CUE4Parse.UE4.Readers; +using CUE4Parse.UE4.Shaders; +using CUE4Parse.UE4.Versions; +using CUE4Parse.UE4.Wwise; +using CUE4Parse.Utils; + +using CUE4Parse_Conversion; +using CUE4Parse_Conversion.Sounds; + +using EpicManifestParser; +using EpicManifestParser.UE; +using EpicManifestParser.ZlibngDotNetDecompressor; + +using FModel.Creator; +using FModel.Extensions; +using FModel.Framework; +using FModel.Services; +using FModel.Settings; +using FModel.Views; +using FModel.Views.Resources.Controls; +using FModel.Views.Snooper; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +using OpenTK.Windowing.Common; +using OpenTK.Windowing.Desktop; + +using Serilog; + +using SkiaSharp; + +using Svg.Skia; + +using UE4Config.Parsing; + +using Application = System.Windows.Application; +using FGuid = CUE4Parse.UE4.Objects.Core.Misc.FGuid; + +namespace FModel.ViewModels; + +public class CUE4ParseViewModel : ViewModel +{ + private ThreadWorkerViewModel _threadWorkerView => ApplicationService.ThreadWorkerView; + private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView; + private readonly Regex _fnLiveRegex = new(@"^FortniteGame[/\\]Content[/\\]Paks[/\\]", + RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + private bool _modelIsOverwritingMaterial; + public bool ModelIsOverwritingMaterial + { + get => _modelIsOverwritingMaterial; + set => SetProperty(ref _modelIsOverwritingMaterial, value); + } + + private bool _modelIsWaitingAnimation; + public bool ModelIsWaitingAnimation + { + get => _modelIsWaitingAnimation; + set => SetProperty(ref _modelIsWaitingAnimation, value); + } + + public bool IsSnooperOpen => _snooper is { Exists: true, IsVisible: true }; + private Snooper _snooper; + public Snooper SnooperViewer + { + get + { + if (_snooper != null) return _snooper; + + return Application.Current.Dispatcher.Invoke(delegate + { + var scale = ImGuiController.GetDpiScale(); + var htz = Snooper.GetMaxRefreshFrequency(); + return _snooper = new Snooper( + new GameWindowSettings { UpdateFrequency = htz }, + new NativeWindowSettings + { + ClientSize = new OpenTK.Mathematics.Vector2i( + Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenWidth * .75 * scale), + Convert.ToInt32(SystemParameters.MaximizedPrimaryScreenHeight * .85 * scale)), + NumberOfSamples = Constants.SAMPLES_COUNT, + WindowBorder = WindowBorder.Resizable, + Flags = ContextFlags.ForwardCompatible, + Profile = ContextProfile.Core, + Vsync = VSyncMode.Adaptive, + APIVersion = new Version(4, 6), + StartVisible = false, + StartFocused = false, + Title = "3D Viewer" + }); + }); + } + } + + public AbstractVfsFileProvider Provider { get; } + public GameDirectoryViewModel GameDirectory { get; } + public AssetsFolderViewModel AssetsFolder { get; } + public SearchViewModel SearchVm { get; } + public SearchViewModel RefVm { get; } + public TabControlViewModel TabControl { get; } + public ConfigIni IoStoreOnDemand { get; } + private Lazy _wwiseProviderLazy; + public WwiseProvider WwiseProvider => _wwiseProviderLazy.Value; + private Lazy _fmodProviderLazy; + public FModProvider FmodProvider => _fmodProviderLazy?.Value; + private Lazy _criWareProviderLazy; + public CriWareProvider CriWareProvider => _criWareProviderLazy?.Value; + public ConcurrentBag UnknownExtensions = []; + + public CUE4ParseViewModel() + { + var currentDir = UserSettings.Default.CurrentDir; + var gameDirectory = currentDir.GameDirectory; + var versionContainer = new VersionContainer( + game: currentDir.UeVersion, platform: currentDir.TexturePlatform, + 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", versionContainer, pathComparer); + break; + } + case Constants._VAL_LIVE_TRIGGER: + { + Provider = new StreamedFileProvider("ValorantLive", versionContainer, pathComparer); + break; + } + default: + { + var project = gameDirectory.SubstringBeforeLast(gameDirectory.Contains("eFootball") ? "\\pak" : "\\Content").SubstringAfterLast("\\"); + Provider = project switch + { + "StateOfDecay2" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), + [ + new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\Paks"), + new(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\StateOfDecay2\\Saved\\DisabledPaks") + ], SearchOption.AllDirectories, versionContainer, pathComparer), + "eFootball" => new DefaultFileProvider(new DirectoryInfo(gameDirectory), + [ + new(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\KONAMI\\eFootball\\ST\\Download") + ], SearchOption.AllDirectories, versionContainer, pathComparer), + _ when versionContainer.Game is EGame.GAME_AshEchoes => new AEDefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer), + _ when versionContainer.Game is EGame.GAME_BlackStigma => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, StringComparer.Ordinal), + _ => new DefaultFileProvider(gameDirectory, SearchOption.AllDirectories, versionContainer, pathComparer) + }; + + break; + } + } + + Provider.ReadScriptData = UserSettings.Default.ReadScriptData; + Provider.ReadShaderMaps = UserSettings.Default.ReadShaderMaps; + Provider.ReadNaniteData = true; + + GameDirectory = new GameDirectoryViewModel(); + AssetsFolder = new AssetsFolderViewModel(); + SearchVm = new SearchViewModel(); + RefVm = new SearchViewModel(); + TabControl = new TabControlViewModel(); + IoStoreOnDemand = new ConfigIni(nameof(IoStoreOnDemand)); + } + + public async Task Initialize() + { + await _apiEndpointView.EpicApi.VerifyAuth(CancellationToken.None); + await _threadWorkerView.Begin(cancellationToken => + { + Provider.OnDemandOptions = new IoStoreOnDemandOptions + { + ChunkHostUri = new Uri("https://download.epicgames.com/", UriKind.Absolute), + ChunkCacheDirectory = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")), + Authorization = new AuthenticationHeaderValue("Bearer", UserSettings.Default.LastAuthResponse.AccessToken), + Timeout = TimeSpan.FromSeconds(30) + }; + + switch (Provider) + { + case StreamedFileProvider p: + switch (p.LiveGame) + { + case "FortniteLive": + { + var manifestInfo = _apiEndpointView.EpicApi.GetManifest(cancellationToken); + if (manifestInfo is null) + { + throw new FileLoadException("Could not load latest Fortnite manifest, you may have to switch to your local installation."); + } + + var cacheDir = Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data")).FullName; + var manifestOptions = new ManifestParseOptions + { + ChunkCacheDirectory = cacheDir, + ManifestCacheDirectory = cacheDir, + ChunkBaseUrl = "http://download.epicgames.com/Builds/Fortnite/CloudDir/", + Decompressor = ManifestZlibngDotNetDecompressor.Decompress, + DecompressorState = ZlibHelper.Instance, + CacheChunksAsIs = false + }; + + var startTs = Stopwatch.GetTimestamp(); + FBuildPatchAppManifest manifest; + + try + { + (manifest, _) = manifestInfo.DownloadAndParseAsync(manifestOptions, + cancellationToken: cancellationToken, + elementManifestPredicate: static x => x.Uri.Host == "download.epicgames.com" + ).GetAwaiter().GetResult(); + } + catch (HttpRequestException ex) + { + Log.Error("Failed to download manifest ({ManifestUri})", ex.Data["ManifestUri"]?.ToString() ?? ""); + throw; + } + + if (manifest.TryFindFile("Cloud/IoStoreOnDemand.ini", out var ioStoreOnDemandFile)) + { + IoStoreOnDemand.Read(new StreamReader(ioStoreOnDemandFile.GetStream())); + } + + Parallel.ForEach(manifest.Files.Where(x => _fnLiveRegex.IsMatch(x.FileName)), fileManifest => + { + p.RegisterVfs(fileManifest.FileName, [fileManifest.GetStream()], + it => new FRandomAccessStreamArchive(it, manifest.FindFile(it)!.GetStream(), p.Versions)); + }); + + var elapsedTime = Stopwatch.GetElapsedTime(startTs); + FLogger.Append(ELog.Information, () => + FLogger.Text($"Fortnite [LIVE] has been loaded successfully in {elapsedTime.TotalMilliseconds:F1}ms", Constants.WHITE, true)); + break; + } + case "ValorantLive": + { + var manifest = _apiEndpointView.ValorantApi.GetManifest(cancellationToken); + if (manifest == null) + { + throw new Exception("Could not load latest Valorant manifest, you may have to switch to your local installation."); + } + + Parallel.ForEach(manifest.Paks, pak => + { + p.RegisterVfs(pak.GetFullName(), [pak.GetStream(manifest)]); + }); + + FLogger.Append(ELog.Information, () => + FLogger.Text($"Valorant '{manifest.Header.GameVersion}' has been loaded successfully", Constants.WHITE, true)); + break; + } + } + + break; + case DefaultFileProvider: + { + var ioStoreOnDemandPath = Path.Combine(UserSettings.Default.GameDirectory, "..\\..\\..\\Cloud\\IoStoreOnDemand.ini"); + if (File.Exists(ioStoreOnDemandPath)) + { + using var s = new StreamReader(ioStoreOnDemandPath); + IoStoreOnDemand.Read(s); + } + break; + } + } + + Provider.Initialize(); + _wwiseProviderLazy = new Lazy(() => new WwiseProvider(Provider, UserSettings.Default.GameDirectory, UserSettings.Default.WwiseMaxBnkPrefetch)); + _fmodProviderLazy = new Lazy(() => new FModProvider(Provider, UserSettings.Default.GameDirectory)); + _criWareProviderLazy = new Lazy(() => new CriWareProvider(Provider, UserSettings.Default.GameDirectory)); + Log.Information($"{Provider.Versions.Game} ({Provider.Versions.Platform}) | Archives: x{Provider.UnloadedVfs.Count} | AES: x{Provider.RequiredKeys.Count} | Loose Files: x{Provider.Files.Count}"); + }); + } + + /// + /// load virtual files system from GameDirectory + /// + /// + public void LoadVfs(IEnumerable> aesKeys) + { + Provider.SubmitKeys(aesKeys); + Provider.PostMount(); + + var aesMax = Provider.RequiredKeys.Count + Provider.Keys.Count; + var archiveMax = Provider.UnloadedVfs.Count + Provider.MountedVfs.Count; + Log.Information($"Project: {Provider.ProjectName} | Mounted: {Provider.MountedVfs.Count}/{archiveMax} | AES: {Provider.Keys.Count}/{aesMax} | Files: x{Provider.Files.Count}"); + } + + public void ClearProvider() + { + if (Provider == null) return; + + AssetsFolder.Folders.Clear(); + SearchVm.SearchResults.Clear(); + Helper.CloseWindow("Search For Packages"); + Provider.UnloadNonStreamedVfs(); + GC.Collect(); + } + + public async Task RefreshAes() + { + // game directory dependent, we don't have the provider game name yet since we don't have aes keys + // except when this comes from the AES Manager + if (!UserSettings.IsEndpointValid(EEndpointType.Aes, out var endpoint)) + return; + + await _threadWorkerView.Begin(cancellationToken => + { + // deprecated values + if (endpoint.Url == "https://fortnitecentral.genxgames.gg/api/v1/aes") endpoint.Url = "https://uedb.dev/svc/api/v1/fortnite/aes"; + + var aes = _apiEndpointView.DynamicApi.GetAesKeys(cancellationToken, endpoint.Url, endpoint.Path); + if (aes is not { IsValid: true }) return; + + UserSettings.Default.CurrentDir.AesKeys = aes; + }); + } + + public async Task InitInformation() + { + await _threadWorkerView.Begin(cancellationToken => + { + var info = _apiEndpointView.FModelApi.GetNews(cancellationToken, Provider.ProjectName); + if (info == null) return; + + FLogger.Append(ELog.None, () => + { + for (var i = 0; i < info.Messages.Length; i++) + { + FLogger.Text(info.Messages[i], info.Colors[i], bool.Parse(info.NewLines[i])); + } + }); + }); + } + + public Task InitMappings(bool force = false) + { + if (!UserSettings.IsEndpointValid(EEndpointType.Mapping, out var endpoint)) + { + Provider.MappingsContainer = null; + return Task.CompletedTask; + } + + return Task.Run(() => + { + var l = ELog.Information; + if (endpoint.Overwrite && File.Exists(endpoint.FilePath)) + { + Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(endpoint.FilePath); + } + else if (endpoint.IsValid) + { + // deprecated values + if (endpoint.Path == "$.[?(@.meta.compressionMethod=='Oodle')].['url','fileName']") endpoint.Path = "$.[0].['url','fileName']"; + if (endpoint.Url == "https://fortnitecentral.genxgames.gg/api/v1/mappings") + { + endpoint.Url = "https://uedb.dev/svc/api/v1/fortnite/mappings"; + endpoint.Path = "$.mappings.ZStandard"; + } + + var mappingsFolder = Path.Combine(UserSettings.Default.OutputDirectory, ".data"); + var mappings = _apiEndpointView.DynamicApi.GetMappings(CancellationToken.None, endpoint.Url, endpoint.Path); + if (mappings is { Length: > 0 }) + { + foreach (var mapping in mappings) + { + if (!mapping.IsValid) continue; + + var mappingPath = Path.Combine(mappingsFolder, mapping.FileName); + if (force || !File.Exists(mappingPath) || new FileInfo(mappingPath).Length == 0) + { + _apiEndpointView.DownloadFile(mapping.Url, mappingPath); + } + + Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(mappingPath); + break; + } + } + + if (Provider.MappingsContainer == null) + { + var latestUsmaps = new DirectoryInfo(mappingsFolder).GetFiles("*_oo.usmap"); + if (latestUsmaps.Length <= 0) return; + + var latestUsmapInfo = latestUsmaps.OrderBy(f => f.LastWriteTime).Last(); + Provider.MappingsContainer = new FileUsmapTypeMappingsProvider(latestUsmapInfo.FullName); + l = ELog.Warning; + } + } + + if (Provider.MappingsContainer is FileUsmapTypeMappingsProvider m) + { + Log.Information($"Mappings pulled from '{m.FileName}'"); + FLogger.Append(l, () => FLogger.Text($"Mappings pulled from '{m.FileName}'", Constants.WHITE, true)); + } + }); + } + + public Task VerifyConsoleVariables() + { + if (Provider.Versions["StripAdditiveRefPose"]) + { + FLogger.Append(ELog.Warning, () => + FLogger.Text("Additive animations have their reference pose stripped, which will lead to inaccurate preview and export", Constants.WHITE, true)); + } + + if (Provider.Versions.Game is EGame.GAME_UE4_LATEST or EGame.GAME_UE5_LATEST && !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) // ignore fortnite globally + { + FLogger.Append(ELog.Warning, () => + FLogger.Text($"Experimental UE version selected, likely unsuitable for '{Provider.GameDisplayName ?? Provider.ProjectName}'", Constants.WHITE, true)); + } + + return Task.CompletedTask; + } + + public Task VerifyOnDemandArchives() + { + // only local fortnite + if (Provider is not DefaultFileProvider || !Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase)) + return Task.CompletedTask; + + // scuffed but working + var persistentDownloadDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FortniteGame/Saved/PersistentDownloadDir"); + var iasFileInfo = new FileInfo(Path.Combine(persistentDownloadDir, "ias", "ias.cache.0")); + if (!iasFileInfo.Exists || iasFileInfo.Length == 0) + return Task.CompletedTask; + + return Task.Run(async () => + { + var inst = new List(); + IoStoreOnDemand.FindPropertyInstructions("Endpoint", "TocPath", inst); + if (inst.Count <= 0) return; + + var ioStoreOnDemandPath = Path.Combine(UserSettings.Default.GameDirectory, "..\\..\\..\\Cloud", inst[0].Value.SubstringAfterLast("/").SubstringBefore("\"")); + if (!File.Exists(ioStoreOnDemandPath)) return; + + await Provider.RegisterVfsAsync(new IoChunkToc(ioStoreOnDemandPath)); + var onDemandCount = await Provider.MountAsync(); + FLogger.Append(ELog.Information, () => + FLogger.Text($"{onDemandCount} on-demand archive{(onDemandCount > 1 ? "s" : "")} streamed via epicgames.com", Constants.WHITE, true)); + }); + } + + public int LocalizedResourcesCount { get; set; } + public bool LocalResourcesDone { get; set; } + public bool HotfixedResourcesDone { get; set; } + + public async Task LoadLocalizedResources() + { + var snapshot = LocalizedResourcesCount; + await Task.WhenAll(LoadGameLocalizedResources(), LoadHotfixedLocalizedResources()).ConfigureAwait(false); + + LocalizedResourcesCount = Provider.Internationalization.Count; + if (snapshot != LocalizedResourcesCount) + { + FLogger.Append(ELog.Information, () => + FLogger.Text($"{LocalizedResourcesCount} localized resources loaded for '{UserSettings.Default.AssetLanguage.GetDescription()}'", Constants.WHITE, true)); + Utils.Typefaces = new Typefaces(this); + } + } + + private Task LoadGameLocalizedResources() + { + if (LocalResourcesDone) return Task.CompletedTask; + return Task.Run(() => + { + LocalResourcesDone = Provider.TryChangeCulture(Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); + }); + } + + private Task LoadHotfixedLocalizedResources() + { + if (!Provider.ProjectName.Equals("fortnitegame", StringComparison.OrdinalIgnoreCase) || HotfixedResourcesDone) return Task.CompletedTask; + return Task.Run(() => + { + var hotfixes = ApplicationService.ApiEndpointView.CentralApi.GetHotfixes(CancellationToken.None, Provider.GetLanguageCode(UserSettings.Default.AssetLanguage)); + if (hotfixes == null) return; + + Provider.Internationalization.Override(hotfixes); + HotfixedResourcesDone = true; + }); + } + + private int _virtualPathCount { get; set; } + public Task LoadVirtualPaths() + { + if (_virtualPathCount > 0) return Task.CompletedTask; + return Task.Run(() => + { + _virtualPathCount = Provider.LoadVirtualPaths(UserSettings.Default.CurrentDir.UeVersion.GetVersion()); + if (_virtualPathCount > 0) + { + FLogger.Append(ELog.Information, () => + FLogger.Text($"{_virtualPathCount} virtual paths loaded", Constants.WHITE, true)); + } + else + { + FLogger.Append(ELog.Warning, () => + FLogger.Text("Could not load virtual paths, plugin manifest may not exist", Constants.WHITE, true)); + } + }); + } + + public void ExtractSelected(CancellationToken cancellationToken, IEnumerable assetItems) + { + foreach (var entry in assetItems) + { + Thread.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + Extract(cancellationToken, entry, TabControl.HasNoTabs); + } + } + + private void BulkFolder(CancellationToken cancellationToken, TreeItem folder, Action action) + { + foreach (var entry in folder.AssetsList.Assets) + { + Thread.Yield(); + cancellationToken.ThrowIfCancellationRequested(); + try + { + action(entry.Asset); + } + catch + { + // ignore + } + } + + foreach (var f in folder.Folders) BulkFolder(cancellationToken, f, action); + } + + public void ExportFolder(CancellationToken cancellationToken, TreeItem folder) + { + Parallel.ForEach(folder.AssetsList.Assets, entry => + { + cancellationToken.ThrowIfCancellationRequested(); + ExportData(entry.Asset, false); + }); + + foreach (var f in folder.Folders) ExportFolder(cancellationToken, f); + } + + public void ExtractFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs)); + + public void SaveFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Properties | EBulkType.Auto)); + + public void TextureFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Textures | EBulkType.Auto)); + + public void ModelFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Meshes | EBulkType.Auto)); + + public void AnimationFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Animations | EBulkType.Auto)); + + public void AudioFolder(CancellationToken cancellationToken, TreeItem folder) + => BulkFolder(cancellationToken, folder, asset => Extract(cancellationToken, asset, TabControl.HasNoTabs, EBulkType.Audio | EBulkType.Auto)); + + public void Extract(CancellationToken cancellationToken, GameFile entry, bool addNewTab = false, EBulkType bulk = EBulkType.None) + { + ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; + Log.Information("User DOUBLE-CLICKED to extract '{FullPath}'", entry.Path); + + if (addNewTab && TabControl.CanAddTabs) TabControl.AddTab(entry); + else TabControl.SelectedTab.SoftReset(entry); + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector(entry.Extension); + + var updateUi = !HasFlag(bulk, EBulkType.Auto); + var saveProperties = HasFlag(bulk, EBulkType.Properties); + var saveTextures = HasFlag(bulk, EBulkType.Textures); + var saveAudio = HasFlag(bulk, EBulkType.Audio); + switch (entry.Extension) + { + case "uasset": + case "umap": + { + var result = Provider.GetLoadPackageResult(entry); + TabControl.SelectedTab.TitleExtra = result.TabTitleExtra; + + if (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 = result.InclusiveStart; i < result.ExclusiveEnd; i++) + { + if (CheckExport(cancellationToken, result.Package, i, bulk)) + break; + } + + break; + } + case "ini" when entry.Name.Contains("BinaryConfig"): + { + var ar = entry.CreateReader(); + var configCache = new FConfigCacheIni(ar); + + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json"); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(configCache, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "dat" when Provider.ProjectName.Equals("Aion2", StringComparison.OrdinalIgnoreCase): + { + ProcessAion2DatFile(entry, updateUi, saveProperties); + break; + } + case "upluginmanifest": + case "code-workspace": + case "projectstore": + case "uefnproject": + case "uproject": + case "manifest": + case "uplugin": + case "archive": + case "dnearchive": // Banishers: Ghosts of New Eden + case "gitignore": + case "LICENSE": + case "playstats": // Dispatch + case "template": + case "stUMeta": // LIS: Double Exposure + case "vmodule": + case "glslfx": + case "cptake": + case "uparam": // Steel Hunters + case "spi1d": + case "verse": + case "html": + case "json5": + case "json": + case "uref": + case "cube": + case "usda": + case "ocio": + case "data" when Provider.ProjectName is "OakGame": + case "ini": + case "txt": + case "log": + case "lsd": // Days Gone + case "bat": + case "dat": + case "cfg": + case "ddr": + case "ide": + case "ipl": + case "zon": + case "xml": + case "css": + case "csv": + case "pem": + case "tps": + case "tgc": // State of Decay 2 + case "cpp": + case "apx": + case "udn": + case "doc": + case "lua": + case "vdf": + case "yml": + case "js": + case "po": + case "md": + case "h": + // Uncharted Waters Origin + case "crn": + case "uwt": + case "wvh": + case "bf": + case "bl": + case "bm": + case "br": + { + var data = Provider.SaveAsset(entry); + using var stream = new MemoryStream(data) { Position = 0 }; + using var reader = new StreamReader(stream); + + TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); + + break; + } + case "locmeta": + { + var archive = entry.CreateReader(); + var metadata = new FTextLocalizationMetaDataResource(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(metadata, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "locres": + { + var archive = entry.CreateReader(); + var locres = new FTextLocalizationResource(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(locres, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "bin" when entry.Name.Contains("AssetRegistry", StringComparison.OrdinalIgnoreCase): + { + var archive = entry.CreateReader(); + var registry = new FAssetRegistryState(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "bin" when entry.Name.Contains("GlobalShaderCache", StringComparison.OrdinalIgnoreCase): + { + var archive = entry.CreateReader(); + var registry = new FGlobalShaderCache(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(registry, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "bank": + { + var archive = entry.CreateReader(); + if (!FModProvider.TryLoadBank(archive, entry.NameWithoutExtension, out var fmodReader)) + { + Log.Error($"Failed to load FMOD bank {entry.Path}"); + break; + } + + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(fmodReader, Formatting.Indented, converters: [new FmodSoundBankConverter(), new StringEnumConverter()]), saveProperties, updateUi); + + var extractedSounds = FmodProvider.ExtractBankSounds(fmodReader); + var directory = Path.GetDirectoryName(entry.Path) ?? "/FMOD/Desktop/"; + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); + } + + break; + } + case "bnk": + case "pck": + { + var archive = entry.CreateReader(); + var wwise = new WwiseReader(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(wwise, Formatting.Indented), saveProperties, updateUi); + + var medias = WwiseProvider.ExtractBankSounds(wwise); + foreach (var media in medias) + { + SaveAndPlaySound(media.OutputPath, media.Extension, media.Data, saveAudio); + } + + break; + } + case "awb": + { + var archive = entry.CreateReader(); + var awbReader = new AwbReader(archive); + + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(awbReader, Formatting.Indented), saveProperties, updateUi); + + var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/"; + var extractedSounds = CriWareProvider.ExtractCriWareSounds(awbReader, archive.Name); + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); + } + + break; + } + case "acb": + { + var archive = entry.CreateReader(); + var acbReader = new AcbReader(archive); + + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(acbReader, Formatting.Indented), saveProperties, updateUi); + + var directory = Path.GetDirectoryName(archive.Name) ?? "/Criware/"; + var extractedSounds = CriWareProvider.ExtractCriWareSounds(acbReader, archive.Name); + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); + } + + break; + } + case "xvag": + case "flac": + case "at9": + case "wem": + case "wav": + case "WAV": + case "ogg": + // todo: CSCore.MediaFoundation.MediaFoundationException The byte stream type of the given URL is unsupported. case "aif": + { + var data = Provider.SaveAsset(entry); + SaveAndPlaySound(entry.PathWithoutExtension, entry.Extension, data, saveAudio); + + break; + } + case "udic": + { + var archive = entry.CreateReader(); + var header = new FOodleDictionaryArchive(archive).Header; + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(header, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "png": + case "jpg": + case "bmp": + { + var data = Provider.SaveAsset(entry); + using var stream = new MemoryStream(data) { Position = 0 }; + TabControl.SelectedTab.AddImage(entry.NameWithoutExtension, false, SKBitmap.Decode(stream), saveTextures, updateUi); + + break; + } + case "svg": + { + var data = Provider.SaveAsset(entry); + using var stream = new MemoryStream(data) { Position = 0 }; + var svg = new SKSvg(); + svg.Load(stream); + + int size = 512; + var bitmap = new SKBitmap(size, size); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Transparent); + + if (svg.Picture == null) + break; + + var bounds = svg.Picture.CullRect; + float scale = Math.Min(size / bounds.Width, size / bounds.Height); + canvas.Scale(scale); + canvas.Translate(-bounds.Left, -bounds.Top); + canvas.DrawPicture(svg.Picture); + + TabControl.SelectedTab.AddImage(entry.NameWithoutExtension, false, bitmap, saveTextures, updateUi); + + break; + } + case "ufont": + case "otf": + case "ttf": + FLogger.Append(ELog.Warning, () => + FLogger.Text($"Export '{entry.Name}' raw data and change its extension if you want it to be an installable font file", Constants.WHITE, true)); + break; + case "ushaderbytecode": + case "ushadercode": + { + var archive = entry.CreateReader(); + var ar = new FShaderCodeArchive(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "upipelinecache": + { + var archive = entry.CreateReader(); + var ar = new FPipelineCacheFile(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "stinfo": + { + var archive = entry.CreateReader(); + var ar = new FShaderTypeHashes(archive); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(ar, Formatting.Indented), saveProperties, updateUi); + + break; + } + case "res": // just skip + case "luac": // compiled lua + case "bytes": // wuthering waves + break; + default: + { + Log.Warning($"The package '{entry.Name}' is of an unknown type."); + if (!UnknownExtensions.Contains(entry.Extension)) + { + UnknownExtensions.Add(entry.Extension); + FLogger.Append(ELog.Warning, () => + FLogger.Text($"There are some packages with an unknown type {entry.Extension}. Check Log file for a full list.", Constants.WHITE, true)); + } + break; + } + } + + void ProcessAion2DatFile(GameFile entry, bool updateUi, bool saveProperties) + { + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("json"); + if (entry.NameWithoutExtension.EndsWith("_MapEvent")) + { + var data = Provider.SaveAsset(entry); + FAion2DatFileArchive.DecryptData(data); + using var stream = new MemoryStream(data) { Position = 0 }; + using var reader = new StreamReader(stream); + + TabControl.SelectedTab.SetDocumentText(reader.ReadToEnd(), saveProperties, updateUi); + } + else if (entry.NameWithoutExtension.Equals("L10NString")) + { + var l10nData = new FAion2L10NFile(entry); + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(l10nData, Formatting.Indented), saveProperties, updateUi); + } + else + { + FAion2DataFile datfile = entry.NameWithoutExtension switch + { + "MapDataHierarchy" => new FAion2MapHierarchyFile(entry), + "MapData" => new FAion2MapDataFile(entry, Provider), + _ when entry.Directory.EndsWith("Data/WorldMap", StringComparison.OrdinalIgnoreCase) => new FAion2MapDataFile(entry, Provider), + _ => new FAion2DataTableFile(entry, Provider) + }; + + TabControl.SelectedTab.SetDocumentText(JsonConvert.SerializeObject(datfile, Formatting.Indented), saveProperties, updateUi); + } + } + } + + public void ExtractAndScroll(CancellationToken cancellationToken, string fullPath, string objectName, string parentExportType) + { + Log.Information("User CTRL-CLICKED to extract '{FullPath}'", fullPath); + + var entry = Provider[fullPath]; + TabControl.AddTab(entry, parentExportType); + TabControl.SelectedTab.ScrollTrigger = objectName; + + var result = Provider.GetLoadPackageResult(entry, objectName); + + 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, result.Package, i)) + break; + } + } + + private bool CheckExport(CancellationToken cancellationToken, IPackage pkg, int index, EBulkType bulk = EBulkType.None) // return true once you want to stop searching for exports + { + var isNone = bulk == EBulkType.None; + var updateUi = !HasFlag(bulk, EBulkType.Auto); + var saveTextures = HasFlag(bulk, EBulkType.Textures); + var saveAudio = HasFlag(bulk, EBulkType.Audio); + + var pointer = new FPackageIndex(pkg, index + 1).ResolvedObject; + if (pointer?.Object is null) return false; + + var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); + switch (dummy) + { + case UVerseDigest when isNone && pointer.Object.Value is UVerseDigest verseDigest: + { + if (!TabControl.CanAddTabs) return false; + + TabControl.AddTab($"{verseDigest.ProjectName}.verse"); + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("verse"); + TabControl.SelectedTab.SetDocumentText(verseDigest.ReadableCode, false, false); + return true; + } + case UTexture when (isNone || saveTextures) && pointer.Object.Value is UTexture texture: + { + TabControl.SelectedTab.AddImage(texture, saveTextures, updateUi); + return false; + } + case USvgAsset when (isNone || saveTextures) && pointer.Object.Value is USvgAsset svgasset: + { + const int size = 512; + var data = svgasset.GetOrDefault("SvgData"); + var sourceFile = svgasset.GetOrDefault("SourceFile"); + using var stream = new MemoryStream(data) { Position = 0 }; + + var svg = new SKSvg(); + svg.Load(stream); + + if (svg.Picture == null) + return false; + + var b = svg.Picture.CullRect; + float s = Math.Min(size / b.Width, size / b.Height); + + var bitmap = new SKBitmap(size, size); + using var canvas = new SKCanvas(bitmap); + using var paint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.Medium }; + + canvas.Scale(s); + canvas.Translate(-b.Left, -b.Top); + canvas.DrawPicture(svg.Picture, paint); + + if (saveTextures) + { + var fileName = sourceFile.SubstringAfterLast('/'); + var path = Path.Combine(UserSettings.Default.TextureDirectory, + UserSettings.Default.KeepDirectoryStructure ? TabControl.SelectedTab.Entry.Directory : "", fileName!).Replace('\\', '/'); + + Directory.CreateDirectory(path.SubstringBeforeLast('/')); + + using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); + fs.Write(data, 0, data.Length); + if (File.Exists(path)) + { + Log.Information("{FileName} successfully saved", fileName); + if (updateUi) + { + FLogger.Append(ELog.Information, () => + { + FLogger.Text("Successfully saved ", Constants.WHITE); + FLogger.Link(fileName, path, true); + }); + } + } + else + { + Log.Error("{FileName} could not be saved", fileName); + if (updateUi) + FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{fileName}'", Constants.WHITE, true)); + } + } + + TabControl.SelectedTab.AddImage(sourceFile.SubstringAfterLast('/'), false, bitmap, false, updateUi); + return false; + } + // The Dark Pictures Anthology: House of Ashes + case UExternalSource when (isNone || saveAudio) && pointer.Object.Value is UExternalSource externalSource: + { + var audioName = Path.GetFileNameWithoutExtension(externalSource.ExternalSourcePath); + SaveAndPlaySound(audioName, "wem", externalSource.Data?.WemFile ?? [], saveAudio); + return false; + } + case UAkAudioEvent when (isNone || saveAudio) && pointer.Object.Value is UAkAudioEvent audioEvent: + { + var extractedSounds = WwiseProvider.ExtractAudioEventSounds(audioEvent); + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(sound.OutputPath, sound.Extension, sound.Data, saveAudio); + } + return false; + } + case UFMODEvent when (isNone || saveAudio) && pointer.Object.Value is UFMODEvent fmodEvent: + { + var extractedSounds = FmodProvider.ExtractEventSounds(fmodEvent); + var directory = Path.GetDirectoryName(fmodEvent.Owner?.Name) ?? "/FMOD/Desktop/"; + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); + } + return false; + } + case UFMODBank when (isNone || saveAudio) && pointer.Object.Value is UFMODBank fmodBank: + { + var extractedSounds = FmodProvider.ExtractBankSounds(fmodBank); + var directory = Path.GetDirectoryName(fmodBank.Owner?.Name) ?? "/FMOD/Desktop/"; + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name), sound.Extension, sound.Data, saveAudio); + } + return false; + } + case USoundAtomCueSheet or UAtomCueSheet or USoundAtomCue or UAtomWaveBank when (isNone || saveAudio) && pointer.Object.Value is UObject atomObject: + { + var extractedSounds = atomObject switch + { + USoundAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet), + UAtomCueSheet cueSheet => CriWareProvider.ExtractCriWareSounds(cueSheet), + USoundAtomCue cue => CriWareProvider.ExtractCriWareSounds(cue), + UAtomWaveBank awb => CriWareProvider.ExtractCriWareSounds(awb), + _ => [] + }; + + var directory = Path.GetDirectoryName(atomObject.Owner?.Name) ?? "/Criware/"; + directory = Path.GetDirectoryName(atomObject.Owner.Provider.FixPath(directory)); + foreach (var sound in extractedSounds) + { + SaveAndPlaySound(Path.Combine(directory, sound.Name).Replace("\\", "/"), sound.Extension, sound.Data, saveAudio); + } + return false; + } + case UAkMediaAssetData when isNone || saveAudio: + case USoundWave when isNone || saveAudio: + { + var shouldDecompress = UserSettings.Default.CompressedAudioMode == ECompressedAudio.PlayDecompressed; + pointer.Object.Value.Decode(shouldDecompress, out var audioFormat, out var data); + var hasAf = !string.IsNullOrEmpty(audioFormat); + if (data == null || !hasAf) + { + if (hasAf) FLogger.Append(ELog.Warning, () => FLogger.Text($"Unsupported audio format '{audioFormat}'", Constants.WHITE, true)); + return false; + } + + SaveAndPlaySound(TabControl.SelectedTab.Entry.PathWithoutExtension.Replace('\\', '/'), audioFormat, data, saveAudio); + return false; + } + case UWorld when isNone && UserSettings.Default.PreviewWorlds: + case UBlueprintGeneratedClass when isNone && UserSettings.Default.PreviewWorlds && TabControl.SelectedTab.ParentExportType switch + { + "JunoBuildInstructionsItemDefinition" => true, + "JunoBuildingSetAccountItemDefinition" => true, + "JunoBuildingPropAccountItemDefinition" => true, + _ => false + }: + case UPaperSprite when isNone && UserSettings.Default.PreviewMaterials: + case UStaticMesh when isNone && UserSettings.Default.PreviewStaticMeshes: + case USkeletalMesh when isNone && UserSettings.Default.PreviewSkeletalMeshes: + case USkeleton when isNone && UserSettings.Default.SaveSkeletonAsMesh: + case UMaterialInstance when isNone && UserSettings.Default.PreviewMaterials && !ModelIsOverwritingMaterial && + !(Provider.ProjectName.Equals("FortniteGame", StringComparison.OrdinalIgnoreCase) && + (pkg.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) || + pkg.Name.Contains("/RenderSwitch_Materials/", StringComparison.OrdinalIgnoreCase) || + pkg.Name.Contains("/MI_BPTile/", StringComparison.OrdinalIgnoreCase))): + { + if (SnooperViewer.TryLoadExport(cancellationToken, dummy, pointer.Object)) + SnooperViewer.Run(); + return true; + } + case UMaterialInstance when isNone && ModelIsOverwritingMaterial && pointer.Object.Value is UMaterialInstance m: + { + SnooperViewer.Renderer.Swap(m); + SnooperViewer.Run(); + return true; + } + case UAnimSequenceBase when isNone && UserSettings.Default.PreviewAnimations || ModelIsWaitingAnimation: + { + // animate all animations using their specified skeleton or when we explicitly asked for a loaded model to be animated (ignoring whether we wanted to preview animations) + SnooperViewer.Renderer.Animate(pointer.Object.Value); + SnooperViewer.Run(); + return true; + } + case UStaticMesh when HasFlag(bulk, EBulkType.Meshes): + case USkeletalMesh when HasFlag(bulk, EBulkType.Meshes): + case USkeleton when UserSettings.Default.SaveSkeletonAsMesh && HasFlag(bulk, EBulkType.Meshes): + // case UMaterialInstance when HasFlag(bulk, EBulkType.Materials): // read the fucking json + case UAnimSequenceBase when HasFlag(bulk, EBulkType.Animations): + { + SaveExport(pointer.Object.Value, updateUi); + return true; + } + default: + { + if (!isNone && !saveTextures) return false; + + using var cPackage = new CreatorPackage(pkg.Name, dummy.ExportType, pointer.Object, UserSettings.Default.CosmeticStyle); + if (!cPackage.TryConstructCreator(out var creator)) + return false; + + creator.ParseForInfo(); + TabControl.SelectedTab.AddImage(pointer.Object.Value.Name, false, creator.Draw(), saveTextures, updateUi); + return true; + + } + } + } + + public void ShowMetadata(GameFile entry) + { + ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; + + var package = Provider.LoadPackage(entry); + + 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); + } + + public void FindReferences(GameFile entry) + { + var refs = Provider.ScanForPackageRefs(entry); + Application.Current.Dispatcher.Invoke(delegate + { + var refView = Helper.GetWindow("Search For Packages", () => new SearchView().Show()); + refView.ChangeCollection(ESearchViewTab.RefView, refs, entry); + refView.FocusTab(ESearchViewTab.RefView); + }); + } + + + public void Decompile(GameFile entry) + { + ApplicationService.ApplicationView.IsAssetsExplorerVisible = false; + + if (TabControl.CanAddTabs) TabControl.AddTab(entry); + else TabControl.SelectedTab.SoftReset(entry); + + TabControl.SelectedTab.TitleExtra = "Decompiled"; + TabControl.SelectedTab.Highlighter = AvalonExtensions.HighlighterSelector("cpp"); + + UClassCookedMetaData cookedMetaData = null; + try + { + var editorPkg = Provider.LoadPackage(entry.Path.Replace(".uasset", ".o.uasset")); + cookedMetaData = editorPkg.GetExport("CookedClassMetaData"); + } + catch + { + // ignored + } + + var cppList = new List(); + var pkg = Provider.LoadPackage(entry); + for (var i = 0; i < pkg.ExportMapLength; i++) + { + var pointer = new FPackageIndex(pkg, i + 1).ResolvedObject; + if (pointer?.Object is null && pointer.Class?.Object?.Value is null) + continue; + + var dummy = ((AbstractUePackage) pkg).ConstructObject(pointer.Class?.Object?.Value as UStruct, pkg); + if (dummy is not UClass || pointer.Object.Value is not UClass blueprint) + continue; + + cppList.Add(blueprint.DecompileBlueprintToPseudo(cookedMetaData)); + } + + var cpp = cppList.Count > 1 ? string.Join("\n\n", cppList) : cppList.FirstOrDefault() ?? string.Empty; + if (entry.Path.Contains("_Verse.uasset")) + { + cpp = Regex.Replace(cpp, "__verse_0x[a-fA-F0-9]{8}_", ""); // UnmangleCasedName + } + cpp = Regex.Replace(cpp, @"CallFunc_([A-Za-z0-9_]+)_ReturnValue", "$1"); + + + TabControl.SelectedTab.SetDocumentText(cpp, false, false); + } + + private void SaveAndPlaySound(string fullPath, string ext, byte[] data, bool isBulk) + { + if (fullPath.StartsWith('/')) fullPath = fullPath[1..]; + var savedAudioPath = Path.Combine(UserSettings.Default.AudioDirectory, + UserSettings.Default.KeepDirectoryStructure ? fullPath : fullPath.SubstringAfterLast('/')).Replace('\\', '/') + $".{ext.ToLowerInvariant()}"; + + if (isBulk) + { + Directory.CreateDirectory(savedAudioPath.SubstringBeforeLast('/')); + using var stream = new FileStream(savedAudioPath, FileMode.Create, FileAccess.Write); + using var writer = new BinaryWriter(stream); + writer.Write(data); + writer.Flush(); + return; + } + + // TODO + // since we are currently in a thread, the audio player's lifetime (memory-wise) will keep the current thread up and running until fmodel itself closes + // the solution would be to kill the current thread at this line and then open the audio player without "Application.Current.Dispatcher.Invoke" + // but the ThreadWorkerViewModel is an idiot and doesn't understand we want to kill the current thread inside the current thread and continue the code + Application.Current.Dispatcher.Invoke(delegate + { + var audioPlayer = Helper.GetWindow("Audio Player", () => new AudioPlayer().Show()); + audioPlayer.Load(data, savedAudioPath); + }); + } + + private void SaveExport(UObject export, bool updateUi = true) + { + var toSave = new Exporter(export, UserSettings.Default.ExportOptions); + var toSaveDirectory = new DirectoryInfo(UserSettings.Default.ModelDirectory); + if (toSave.TryWriteToDir(toSaveDirectory, out var label, out var savedFilePath)) + { + Log.Information("Successfully saved {FilePath}", savedFilePath); + if (updateUi) + { + FLogger.Append(ELog.Information, () => + { + FLogger.Text("Successfully saved ", Constants.WHITE); + FLogger.Link(label, savedFilePath, true); + }); + } + } + else + { + Log.Error("{FileName} could not be saved", export.Name); + FLogger.Append(ELog.Error, () => FLogger.Text($"Could not save '{export.Name}'", Constants.WHITE, true)); + } + } + + private readonly object _rawData = new (); + public void ExportData(GameFile entry, bool updateUi = true) + { + if (Provider.TrySavePackage(entry, out var assets)) + { + string path = UserSettings.Default.RawDataDirectory; + Parallel.ForEach(assets, kvp => + { + lock (_rawData) + { + path = Path.Combine(UserSettings.Default.RawDataDirectory, UserSettings.Default.KeepDirectoryStructure ? kvp.Key : kvp.Key.SubstringAfterLast('/')).Replace('\\', '/'); + Directory.CreateDirectory(path.SubstringBeforeLast('/')); + File.WriteAllBytes(path, kvp.Value); + } + }); + + Log.Information("{FileName} successfully exported", entry.Name); + if (updateUi) + { + FLogger.Append(ELog.Information, () => + { + FLogger.Text("Successfully exported ", Constants.WHITE); + FLogger.Link(entry.Name, path, true); + }); + } + } + else + { + Log.Error("{FileName} could not be exported", entry.Name); + if (updateUi) + FLogger.Append(ELog.Error, () => FLogger.Text($"Could not export '{entry.Name}'", Constants.WHITE, true)); + } + } + + private static bool HasFlag(EBulkType a, EBulkType b) + { + return (a & b) == b; + } +} diff --git a/FModel/ViewModels/Commands/AddEditDirectoryCommand.cs b/FModel/ViewModels/Commands/AddEditDirectoryCommand.cs index 4ed647d4..ea17267a 100644 --- a/FModel/ViewModels/Commands/AddEditDirectoryCommand.cs +++ b/FModel/ViewModels/Commands/AddEditDirectoryCommand.cs @@ -1,4 +1,4 @@ -using AdonisUI.Controls; +using AdonisUI.Controls; using FModel.Framework; using FModel.Settings; using FModel.Views; diff --git a/FModel/ViewModels/Commands/AddTabCommand.cs b/FModel/ViewModels/Commands/AddTabCommand.cs index 6f0822fd..28d3422f 100644 --- a/FModel/ViewModels/Commands/AddTabCommand.cs +++ b/FModel/ViewModels/Commands/AddTabCommand.cs @@ -1,4 +1,4 @@ -using FModel.Framework; +using FModel.Framework; namespace FModel.ViewModels.Commands; diff --git a/FModel/ViewModels/Commands/AudioCommand.cs b/FModel/ViewModels/Commands/AudioCommand.cs index 5ac55b4f..64f6da4b 100644 --- a/FModel/ViewModels/Commands/AudioCommand.cs +++ b/FModel/ViewModels/Commands/AudioCommand.cs @@ -1,4 +1,4 @@ -using FModel.Framework; +using FModel.Framework; namespace FModel.ViewModels.Commands; diff --git a/FModel/ViewModels/Commands/DeleteDirectoryCommand.cs b/FModel/ViewModels/Commands/DeleteDirectoryCommand.cs index 1c3b1f99..841b8428 100644 --- a/FModel/ViewModels/Commands/DeleteDirectoryCommand.cs +++ b/FModel/ViewModels/Commands/DeleteDirectoryCommand.cs @@ -1,4 +1,4 @@ -using FModel.Framework; +using FModel.Framework; using FModel.Settings; namespace FModel.ViewModels.Commands; diff --git a/FModel/ViewModels/Commands/GoToCommand.cs b/FModel/ViewModels/Commands/GoToCommand.cs index 412e7f27..226d5026 100644 --- a/FModel/ViewModels/Commands/GoToCommand.cs +++ b/FModel/ViewModels/Commands/GoToCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; using FModel.Framework; using FModel.Services; diff --git a/FModel/ViewModels/Commands/ImageCommand.cs b/FModel/ViewModels/Commands/ImageCommand.cs index 0201c989..df4b7735 100644 --- a/FModel/ViewModels/Commands/ImageCommand.cs +++ b/FModel/ViewModels/Commands/ImageCommand.cs @@ -1,4 +1,4 @@ -using AdonisUI.Controls; +using AdonisUI.Controls; using FModel.Extensions; using FModel.Framework; using FModel.Views.Resources.Controls; diff --git a/FModel/ViewModels/Commands/RemindMeCommand.cs b/FModel/ViewModels/Commands/RemindMeCommand.cs index 21464761..27c22dcf 100644 --- a/FModel/ViewModels/Commands/RemindMeCommand.cs +++ b/FModel/ViewModels/Commands/RemindMeCommand.cs @@ -1,4 +1,4 @@ -using System; +using System; using FModel.Framework; using FModel.Settings; diff --git a/FModel/ViewModels/CustomDirectoriesViewModel.cs b/FModel/ViewModels/CustomDirectoriesViewModel.cs index d5394edf..ac38f058 100644 --- a/FModel/ViewModels/CustomDirectoriesViewModel.cs +++ b/FModel/ViewModels/CustomDirectoriesViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; diff --git a/FModel/Views/About.xaml.cs b/FModel/Views/About.xaml.cs index d23328e6..a27a535e 100644 --- a/FModel/Views/About.xaml.cs +++ b/FModel/Views/About.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using FModel.ViewModels; namespace FModel.Views; diff --git a/FModel/Views/AesManager.xaml.cs b/FModel/Views/AesManager.xaml.cs index aa0c59ba..6a384481 100644 --- a/FModel/Views/AesManager.xaml.cs +++ b/FModel/Views/AesManager.xaml.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using System.Windows; using FModel.Services; using FModel.ViewModels; diff --git a/FModel/Views/AudioPlayer.xaml.cs b/FModel/Views/AudioPlayer.xaml.cs index 6367e35c..332b6101 100644 --- a/FModel/Views/AudioPlayer.xaml.cs +++ b/FModel/Views/AudioPlayer.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Linq; using System.Windows; diff --git a/FModel/Views/BackupManager.xaml.cs b/FModel/Views/BackupManager.xaml.cs index 59b07e6b..08205951 100644 --- a/FModel/Views/BackupManager.xaml.cs +++ b/FModel/Views/BackupManager.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using FModel.ViewModels; namespace FModel.Views; diff --git a/FModel/Views/CustomDir.xaml.cs b/FModel/Views/CustomDir.xaml.cs index 4bba4d08..db88b088 100644 --- a/FModel/Views/CustomDir.xaml.cs +++ b/FModel/Views/CustomDir.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using FModel.Settings; namespace FModel.Views; diff --git a/FModel/Views/DirectorySelector.xaml.cs b/FModel/Views/DirectorySelector.xaml.cs index cef51ef3..70d16e1a 100644 --- a/FModel/Views/DirectorySelector.xaml.cs +++ b/FModel/Views/DirectorySelector.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using FModel.ViewModels; diff --git a/FModel/Views/ImageMerger.xaml.cs b/FModel/Views/ImageMerger.xaml.cs index 6190973d..6a3a4189 100644 --- a/FModel/Views/ImageMerger.xaml.cs +++ b/FModel/Views/ImageMerger.xaml.cs @@ -1,4 +1,4 @@ -using AdonisUI.Controls; +using AdonisUI.Controls; using FModel.Extensions; using FModel.Settings; using FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs b/FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs index 66e6655a..7d3b6da2 100644 --- a/FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs +++ b/FModel/Views/Resources/Controls/Aed/BraceFoldingStrategy.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using ICSharpCode.AvalonEdit; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Folding; diff --git a/FModel/Views/Resources/Controls/Aed/GamePathElementGenerator.cs b/FModel/Views/Resources/Controls/Aed/GamePathElementGenerator.cs index dbf83c0a..babf9a55 100644 --- a/FModel/Views/Resources/Controls/Aed/GamePathElementGenerator.cs +++ b/FModel/Views/Resources/Controls/Aed/GamePathElementGenerator.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using FModel.Extensions; using ICSharpCode.AvalonEdit.Rendering; diff --git a/FModel/Views/Resources/Controls/Aed/HexColorElementGenerator.cs b/FModel/Views/Resources/Controls/Aed/HexColorElementGenerator.cs index c2b5a293..8e9a0a4c 100644 --- a/FModel/Views/Resources/Controls/Aed/HexColorElementGenerator.cs +++ b/FModel/Views/Resources/Controls/Aed/HexColorElementGenerator.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using ICSharpCode.AvalonEdit.Rendering; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/Aed/HexColorVisualLineText.cs b/FModel/Views/Resources/Controls/Aed/HexColorVisualLineText.cs index b275add4..c6a996f0 100644 --- a/FModel/Views/Resources/Controls/Aed/HexColorVisualLineText.cs +++ b/FModel/Views/Resources/Controls/Aed/HexColorVisualLineText.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Media; using System.Windows.Media.TextFormatting; using ICSharpCode.AvalonEdit.Rendering; diff --git a/FModel/Views/Resources/Controls/Aup/CustomCodecFactory.cs b/FModel/Views/Resources/Controls/Aup/CustomCodecFactory.cs index 6e2bc7e1..fa19f9a0 100644 --- a/FModel/Views/Resources/Controls/Aup/CustomCodecFactory.cs +++ b/FModel/Views/Resources/Controls/Aup/CustomCodecFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using CSCore; diff --git a/FModel/Views/Resources/Controls/Aup/ISource.cs b/FModel/Views/Resources/Controls/Aup/ISource.cs index e8ee30f9..2df5fad3 100644 --- a/FModel/Views/Resources/Controls/Aup/ISource.cs +++ b/FModel/Views/Resources/Controls/Aup/ISource.cs @@ -1,4 +1,4 @@ -using System; +using System; using FModel.ViewModels; namespace FModel.Views.Resources.Controls.Aup; diff --git a/FModel/Views/Resources/Controls/Aup/NVorbisSource.cs b/FModel/Views/Resources/Controls/Aup/NVorbisSource.cs index 7753bf5c..8650025e 100644 --- a/FModel/Views/Resources/Controls/Aup/NVorbisSource.cs +++ b/FModel/Views/Resources/Controls/Aup/NVorbisSource.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using CSCore; using NVorbis; diff --git a/FModel/Views/Resources/Controls/Aup/SourceEventArgs.cs b/FModel/Views/Resources/Controls/Aup/SourceEventArgs.cs index aeca57f0..102546ea 100644 --- a/FModel/Views/Resources/Controls/Aup/SourceEventArgs.cs +++ b/FModel/Views/Resources/Controls/Aup/SourceEventArgs.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace FModel.Views.Resources.Controls.Aup; diff --git a/FModel/Views/Resources/Controls/Aup/SourcePropertyChangedEventArgs.cs b/FModel/Views/Resources/Controls/Aup/SourcePropertyChangedEventArgs.cs index c5e23c4b..bf3e9ade 100644 --- a/FModel/Views/Resources/Controls/Aup/SourcePropertyChangedEventArgs.cs +++ b/FModel/Views/Resources/Controls/Aup/SourcePropertyChangedEventArgs.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace FModel.Views.Resources.Controls.Aup; diff --git a/FModel/Views/Resources/Controls/Aup/SpectrumAnalyzer.cs b/FModel/Views/Resources/Controls/Aup/SpectrumAnalyzer.cs index 1fd7a869..b1f5a6ce 100644 --- a/FModel/Views/Resources/Controls/Aup/SpectrumAnalyzer.cs +++ b/FModel/Views/Resources/Controls/Aup/SpectrumAnalyzer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; diff --git a/FModel/Views/Resources/Controls/Aup/SpectrumProvider.cs b/FModel/Views/Resources/Controls/Aup/SpectrumProvider.cs index 28bcc248..9f7ab8de 100644 --- a/FModel/Views/Resources/Controls/Aup/SpectrumProvider.cs +++ b/FModel/Views/Resources/Controls/Aup/SpectrumProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using CSCore.DSP; diff --git a/FModel/Views/Resources/Controls/Aup/Timeclock.cs b/FModel/Views/Resources/Controls/Aup/Timeclock.cs index 66d2483d..eab86f96 100644 --- a/FModel/Views/Resources/Controls/Aup/Timeclock.cs +++ b/FModel/Views/Resources/Controls/Aup/Timeclock.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; diff --git a/FModel/Views/Resources/Controls/Aup/Timeline.cs b/FModel/Views/Resources/Controls/Aup/Timeline.cs index d4953cb2..07500fe0 100644 --- a/FModel/Views/Resources/Controls/Aup/Timeline.cs +++ b/FModel/Views/Resources/Controls/Aup/Timeline.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; diff --git a/FModel/Views/Resources/Controls/CommitControl.xaml.cs b/FModel/Views/Resources/Controls/CommitControl.xaml.cs index 198c05ae..0ee501d2 100644 --- a/FModel/Views/Resources/Controls/CommitControl.xaml.cs +++ b/FModel/Views/Resources/Controls/CommitControl.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Controls; +using System.Windows.Controls; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/CommitDownloaderControl.xaml.cs b/FModel/Views/Resources/Controls/CommitDownloaderControl.xaml.cs index b779543a..ac9c788a 100644 --- a/FModel/Views/Resources/Controls/CommitDownloaderControl.xaml.cs +++ b/FModel/Views/Resources/Controls/CommitDownloaderControl.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using FModel.ViewModels.ApiEndpoints.Models; diff --git a/FModel/Views/Resources/Controls/EndpointEditor.xaml.cs b/FModel/Views/Resources/Controls/EndpointEditor.xaml.cs index 3070242b..7f530c99 100644 --- a/FModel/Views/Resources/Controls/EndpointEditor.xaml.cs +++ b/FModel/Views/Resources/Controls/EndpointEditor.xaml.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Windows; using System.Windows.Controls; using FModel.Extensions; diff --git a/FModel/Views/Resources/Controls/FilterableComboBox.cs b/FModel/Views/Resources/Controls/FilterableComboBox.cs index 4679311a..35bb3d64 100644 --- a/FModel/Views/Resources/Controls/FilterableComboBox.cs +++ b/FModel/Views/Resources/Controls/FilterableComboBox.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Windows; diff --git a/FModel/Views/Resources/Controls/HotkeyTextBox.cs b/FModel/Views/Resources/Controls/HotkeyTextBox.cs index c15cf12a..384e3941 100644 --- a/FModel/Views/Resources/Controls/HotkeyTextBox.cs +++ b/FModel/Views/Resources/Controls/HotkeyTextBox.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using System.Windows.Input; using FModel.Framework; diff --git a/FModel/Views/Resources/Controls/ImagePopout.xaml.cs b/FModel/Views/Resources/Controls/ImagePopout.xaml.cs index 4ff4f967..32287b88 100644 --- a/FModel/Views/Resources/Controls/ImagePopout.xaml.cs +++ b/FModel/Views/Resources/Controls/ImagePopout.xaml.cs @@ -1,4 +1,4 @@ -namespace FModel.Views.Resources.Controls; +namespace FModel.Views.Resources.Controls; public partial class ImagePopout { diff --git a/FModel/Views/Resources/Controls/Inputs/SearchTextBox.xaml.cs b/FModel/Views/Resources/Controls/Inputs/SearchTextBox.xaml.cs index 111c8d79..dca16683 100644 --- a/FModel/Views/Resources/Controls/Inputs/SearchTextBox.xaml.cs +++ b/FModel/Views/Resources/Controls/Inputs/SearchTextBox.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace FModel.Views.Resources.Controls.Inputs; diff --git a/FModel/Views/Resources/Controls/ListBoxItemBehavior.cs b/FModel/Views/Resources/Controls/ListBoxItemBehavior.cs index 68a2e5d1..d44b11eb 100644 --- a/FModel/Views/Resources/Controls/ListBoxItemBehavior.cs +++ b/FModel/Views/Resources/Controls/ListBoxItemBehavior.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/Mgn/EFrameType.cs b/FModel/Views/Resources/Controls/Mgn/EFrameType.cs index 6620b8de..16b171a6 100644 --- a/FModel/Views/Resources/Controls/Mgn/EFrameType.cs +++ b/FModel/Views/Resources/Controls/Mgn/EFrameType.cs @@ -1,4 +1,4 @@ -namespace FModel.Views.Resources.Controls; +namespace FModel.Views.Resources.Controls; public enum EFrameType { diff --git a/FModel/Views/Resources/Controls/Mgn/Magnifier.cs b/FModel/Views/Resources/Controls/Mgn/Magnifier.cs index d5d29d41..b58ce24b 100644 --- a/FModel/Views/Resources/Controls/Mgn/Magnifier.cs +++ b/FModel/Views/Resources/Controls/Mgn/Magnifier.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using System.Windows.Media; diff --git a/FModel/Views/Resources/Controls/Mgn/MagnifierAdorner.cs b/FModel/Views/Resources/Controls/Mgn/MagnifierAdorner.cs index 0c752404..04d0bd68 100644 --- a/FModel/Views/Resources/Controls/Mgn/MagnifierAdorner.cs +++ b/FModel/Views/Resources/Controls/Mgn/MagnifierAdorner.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; diff --git a/FModel/Views/Resources/Controls/Mgn/MagnifierManager.cs b/FModel/Views/Resources/Controls/Mgn/MagnifierManager.cs index c18ce92a..ec0c9b14 100644 --- a/FModel/Views/Resources/Controls/Mgn/MagnifierManager.cs +++ b/FModel/Views/Resources/Controls/Mgn/MagnifierManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows; using System.Windows.Documents; using System.Windows.Input; diff --git a/FModel/Views/Resources/Controls/OnTagDataTemplateSelector.cs b/FModel/Views/Resources/Controls/OnTagDataTemplateSelector.cs index e106125a..922ed92e 100644 --- a/FModel/Views/Resources/Controls/OnTagDataTemplateSelector.cs +++ b/FModel/Views/Resources/Controls/OnTagDataTemplateSelector.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs index e9de6696..c480a4bf 100644 --- a/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs +++ b/FModel/Views/Resources/Controls/Rtb/CustomRichTextBox.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Windows; using System.Windows.Controls; diff --git a/FModel/Views/Resources/Controls/Rtb/CustomScrollViewer.cs b/FModel/Views/Resources/Controls/Rtb/CustomScrollViewer.cs index e8acc848..71275b10 100644 --- a/FModel/Views/Resources/Controls/Rtb/CustomScrollViewer.cs +++ b/FModel/Views/Resources/Controls/Rtb/CustomScrollViewer.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/TiledExplorer/FileButton2.xaml.cs b/FModel/Views/Resources/Controls/TiledExplorer/FileButton2.xaml.cs index 3111ccf0..ed250212 100644 --- a/FModel/Views/Resources/Controls/TiledExplorer/FileButton2.xaml.cs +++ b/FModel/Views/Resources/Controls/TiledExplorer/FileButton2.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Controls; +using System.Windows.Controls; namespace FModel.Views.Resources.Controls.TiledExplorer; diff --git a/FModel/Views/Resources/Controls/TiledExplorer/FolderButton2.xaml.cs b/FModel/Views/Resources/Controls/TiledExplorer/FolderButton2.xaml.cs index dd0b6d85..e0443a44 100644 --- a/FModel/Views/Resources/Controls/TiledExplorer/FolderButton2.xaml.cs +++ b/FModel/Views/Resources/Controls/TiledExplorer/FolderButton2.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Controls; +using System.Windows.Controls; namespace FModel.Views.Resources.Controls.TiledExplorer; diff --git a/FModel/Views/Resources/Controls/TiledExplorer/FolderButton3.xaml.cs b/FModel/Views/Resources/Controls/TiledExplorer/FolderButton3.xaml.cs index 6ae8a906..d8a1d73f 100644 --- a/FModel/Views/Resources/Controls/TiledExplorer/FolderButton3.xaml.cs +++ b/FModel/Views/Resources/Controls/TiledExplorer/FolderButton3.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows.Controls; +using System.Windows.Controls; namespace FModel.Views.Resources.Controls.TiledExplorer; diff --git a/FModel/Views/Resources/Controls/TiledExplorer/SmoothScroll.cs b/FModel/Views/Resources/Controls/TiledExplorer/SmoothScroll.cs index be395739..cdd07d05 100644 --- a/FModel/Views/Resources/Controls/TiledExplorer/SmoothScroll.cs +++ b/FModel/Views/Resources/Controls/TiledExplorer/SmoothScroll.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; diff --git a/FModel/Views/Resources/Controls/TreeViewItemBehavior.cs b/FModel/Views/Resources/Controls/TreeViewItemBehavior.cs index e4cd12a7..d92c4d39 100644 --- a/FModel/Views/Resources/Controls/TreeViewItemBehavior.cs +++ b/FModel/Views/Resources/Controls/TreeViewItemBehavior.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; namespace FModel.Views.Resources.Controls; diff --git a/FModel/Views/Resources/Controls/TypeDataTemplateSelector.cs b/FModel/Views/Resources/Controls/TypeDataTemplateSelector.cs index 41ad8ec7..f1a4eed5 100644 --- a/FModel/Views/Resources/Controls/TypeDataTemplateSelector.cs +++ b/FModel/Views/Resources/Controls/TypeDataTemplateSelector.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using FModel.ViewModels; diff --git a/FModel/Views/Resources/Converters/BoolToRenderModeConverter.cs b/FModel/Views/Resources/Converters/BoolToRenderModeConverter.cs index 22ccbc55..31e2184b 100644 --- a/FModel/Views/Resources/Converters/BoolToRenderModeConverter.cs +++ b/FModel/Views/Resources/Converters/BoolToRenderModeConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; using System.Windows.Media; diff --git a/FModel/Views/Resources/Converters/BoolToToggleConverter.cs b/FModel/Views/Resources/Converters/BoolToToggleConverter.cs index 535cc4c0..76c1d0a3 100644 --- a/FModel/Views/Resources/Converters/BoolToToggleConverter.cs +++ b/FModel/Views/Resources/Converters/BoolToToggleConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/BorderThicknessToStrokeThicknessConverter.cs b/FModel/Views/Resources/Converters/BorderThicknessToStrokeThicknessConverter.cs index d246a9d6..710ebb28 100644 --- a/FModel/Views/Resources/Converters/BorderThicknessToStrokeThicknessConverter.cs +++ b/FModel/Views/Resources/Converters/BorderThicknessToStrokeThicknessConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/CaseInsensitiveStringEqualsConverter.cs b/FModel/Views/Resources/Converters/CaseInsensitiveStringEqualsConverter.cs index 60419fee..5439b935 100644 --- a/FModel/Views/Resources/Converters/CaseInsensitiveStringEqualsConverter.cs +++ b/FModel/Views/Resources/Converters/CaseInsensitiveStringEqualsConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/CommitMessageConverter.cs b/FModel/Views/Resources/Converters/CommitMessageConverter.cs index 22b32fd3..53ecaea5 100644 --- a/FModel/Views/Resources/Converters/CommitMessageConverter.cs +++ b/FModel/Views/Resources/Converters/CommitMessageConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/DateTimeToDateConverter.cs b/FModel/Views/Resources/Converters/DateTimeToDateConverter.cs index 245f0155..fe19ffd0 100644 --- a/FModel/Views/Resources/Converters/DateTimeToDateConverter.cs +++ b/FModel/Views/Resources/Converters/DateTimeToDateConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs b/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs index e92708ae..78eae1c8 100644 --- a/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs +++ b/FModel/Views/Resources/Converters/EndpointToTypeConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/EnumToStringConverter.cs b/FModel/Views/Resources/Converters/EnumToStringConverter.cs index 034918ab..d317f4e6 100644 --- a/FModel/Views/Resources/Converters/EnumToStringConverter.cs +++ b/FModel/Views/Resources/Converters/EnumToStringConverter.cs @@ -1,4 +1,4 @@ -using FModel.Extensions; +using FModel.Extensions; using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/FolderToSeparatorTagConverter.cs b/FModel/Views/Resources/Converters/FolderToSeparatorTagConverter.cs index ee0c275e..66670cf7 100644 --- a/FModel/Views/Resources/Converters/FolderToSeparatorTagConverter.cs +++ b/FModel/Views/Resources/Converters/FolderToSeparatorTagConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/IsNullToBoolReversedConverter.cs b/FModel/Views/Resources/Converters/IsNullToBoolReversedConverter.cs index 9bd7bc4f..ab839069 100644 --- a/FModel/Views/Resources/Converters/IsNullToBoolReversedConverter.cs +++ b/FModel/Views/Resources/Converters/IsNullToBoolReversedConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/MultiParameterConverter.cs b/FModel/Views/Resources/Converters/MultiParameterConverter.cs index 1b185fce..8f987d6b 100644 --- a/FModel/Views/Resources/Converters/MultiParameterConverter.cs +++ b/FModel/Views/Resources/Converters/MultiParameterConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/RatioConverter.cs b/FModel/Views/Resources/Converters/RatioConverter.cs index 21778629..22816e3a 100644 --- a/FModel/Views/Resources/Converters/RatioConverter.cs +++ b/FModel/Views/Resources/Converters/RatioConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; using System.Windows.Markup; diff --git a/FModel/Views/Resources/Converters/RatioToGridLengthConverter.cs b/FModel/Views/Resources/Converters/RatioToGridLengthConverter.cs index 9d2eaae1..b9f69c8b 100644 --- a/FModel/Views/Resources/Converters/RatioToGridLengthConverter.cs +++ b/FModel/Views/Resources/Converters/RatioToGridLengthConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/SizeToStringConverter.cs b/FModel/Views/Resources/Converters/SizeToStringConverter.cs index 751933f7..a256e092 100644 --- a/FModel/Views/Resources/Converters/SizeToStringConverter.cs +++ b/FModel/Views/Resources/Converters/SizeToStringConverter.cs @@ -1,4 +1,4 @@ -using FModel.Extensions; +using FModel.Extensions; using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Resources/Converters/StringToGameConverter.cs b/FModel/Views/Resources/Converters/StringToGameConverter.cs index 8573f28a..70181335 100644 --- a/FModel/Views/Resources/Converters/StringToGameConverter.cs +++ b/FModel/Views/Resources/Converters/StringToGameConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; using FModel.Extensions; diff --git a/FModel/Views/Resources/Converters/TrimRightToLeftConverter.cs b/FModel/Views/Resources/Converters/TrimRightToLeftConverter.cs index 314a0f34..faf0b4db 100644 --- a/FModel/Views/Resources/Converters/TrimRightToLeftConverter.cs +++ b/FModel/Views/Resources/Converters/TrimRightToLeftConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Windows.Data; diff --git a/FModel/Views/Snooper/Animations/Bone.cs b/FModel/Views/Snooper/Animations/Bone.cs index e82be630..cb0d4dec 100644 --- a/FModel/Views/Snooper/Animations/Bone.cs +++ b/FModel/Views/Snooper/Animations/Bone.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace FModel.Views.Snooper.Animations; diff --git a/FModel/Views/Snooper/Animations/Sequence.cs b/FModel/Views/Snooper/Animations/Sequence.cs index d6457e24..61956a93 100644 --- a/FModel/Views/Snooper/Animations/Sequence.cs +++ b/FModel/Views/Snooper/Animations/Sequence.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using CUE4Parse_Conversion.Animations.PSA; using CUE4Parse.Utils; using ImGuiNET; diff --git a/FModel/Views/Snooper/Animations/TimeTracker.cs b/FModel/Views/Snooper/Animations/TimeTracker.cs index 28cc9538..5775c08f 100644 --- a/FModel/Views/Snooper/Animations/TimeTracker.cs +++ b/FModel/Views/Snooper/Animations/TimeTracker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; using FModel.Views.Snooper.Shading; diff --git a/FModel/Views/Snooper/Buffers/BufferObject.cs b/FModel/Views/Snooper/Buffers/BufferObject.cs index dbda6ee5..f92fa167 100644 --- a/FModel/Views/Snooper/Buffers/BufferObject.cs +++ b/FModel/Views/Snooper/Buffers/BufferObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using OpenTK.Graphics.OpenGL4; namespace FModel.Views.Snooper.Buffers; diff --git a/FModel/Views/Snooper/Buffers/FramebufferObject.cs b/FModel/Views/Snooper/Buffers/FramebufferObject.cs index 2a0f2645..d509d2c0 100644 --- a/FModel/Views/Snooper/Buffers/FramebufferObject.cs +++ b/FModel/Views/Snooper/Buffers/FramebufferObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using FModel.Views.Snooper.Shading; using OpenTK.Graphics.OpenGL4; diff --git a/FModel/Views/Snooper/Buffers/PickingTexture.cs b/FModel/Views/Snooper/Buffers/PickingTexture.cs index fe45cb7c..c7b1fbe4 100644 --- a/FModel/Views/Snooper/Buffers/PickingTexture.cs +++ b/FModel/Views/Snooper/Buffers/PickingTexture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using CUE4Parse.UE4.Objects.Core.Misc; using OpenTK.Graphics.OpenGL4; diff --git a/FModel/Views/Snooper/Buffers/RenderbufferObject.cs b/FModel/Views/Snooper/Buffers/RenderbufferObject.cs index 047461c4..2338341f 100644 --- a/FModel/Views/Snooper/Buffers/RenderbufferObject.cs +++ b/FModel/Views/Snooper/Buffers/RenderbufferObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using OpenTK.Graphics.OpenGL4; namespace FModel.Views.Snooper.Buffers; diff --git a/FModel/Views/Snooper/Buffers/VertexArrayObject.cs b/FModel/Views/Snooper/Buffers/VertexArrayObject.cs index 6bf24c12..59a0aa9e 100644 --- a/FModel/Views/Snooper/Buffers/VertexArrayObject.cs +++ b/FModel/Views/Snooper/Buffers/VertexArrayObject.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using OpenTK.Graphics.OpenGL4; diff --git a/FModel/Views/Snooper/Camera.cs b/FModel/Views/Snooper/Camera.cs index 9e4312c2..426fe357 100644 --- a/FModel/Views/Snooper/Camera.cs +++ b/FModel/Views/Snooper/Camera.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using CUE4Parse.UE4.Objects.Core.Math; using FModel.Settings; diff --git a/FModel/Views/Snooper/Lights/PointLight.cs b/FModel/Views/Snooper/Lights/PointLight.cs index 55fcda20..c1d71d88 100644 --- a/FModel/Views/Snooper/Lights/PointLight.cs +++ b/FModel/Views/Snooper/Lights/PointLight.cs @@ -1,4 +1,4 @@ -using System; +using System; using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Misc; using FModel.Views.Snooper.Shading; diff --git a/FModel/Views/Snooper/Lights/SpotLight.cs b/FModel/Views/Snooper/Lights/SpotLight.cs index 3800d888..267dde9c 100644 --- a/FModel/Views/Snooper/Lights/SpotLight.cs +++ b/FModel/Views/Snooper/Lights/SpotLight.cs @@ -1,4 +1,4 @@ -using CUE4Parse.UE4.Assets.Exports; +using CUE4Parse.UE4.Assets.Exports; using CUE4Parse.UE4.Objects.Core.Misc; using FModel.Views.Snooper.Shading; using ImGuiNET; diff --git a/FModel/Views/Snooper/Models/Attachment.cs b/FModel/Views/Snooper/Models/Attachment.cs index 6e955e4a..f1f53aa0 100644 --- a/FModel/Views/Snooper/Models/Attachment.cs +++ b/FModel/Views/Snooper/Models/Attachment.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Numerics; using CUE4Parse.UE4.Objects.Core.Math; diff --git a/FModel/Views/Snooper/Models/EAttribute.cs b/FModel/Views/Snooper/Models/EAttribute.cs index 4528a968..06badbb2 100644 --- a/FModel/Views/Snooper/Models/EAttribute.cs +++ b/FModel/Views/Snooper/Models/EAttribute.cs @@ -1,4 +1,4 @@ -namespace FModel.Views.Snooper.Models; +namespace FModel.Views.Snooper.Models; public enum EAttribute { diff --git a/FModel/Views/Snooper/Models/Grid.cs b/FModel/Views/Snooper/Models/Grid.cs index 8d6b6814..d6ee539a 100644 --- a/FModel/Views/Snooper/Models/Grid.cs +++ b/FModel/Views/Snooper/Models/Grid.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using FModel.Views.Snooper.Buffers; using FModel.Views.Snooper.Shading; diff --git a/FModel/Views/Snooper/Models/IRenderableModel.cs b/FModel/Views/Snooper/Models/IRenderableModel.cs index a863ec98..17fde580 100644 --- a/FModel/Views/Snooper/Models/IRenderableModel.cs +++ b/FModel/Views/Snooper/Models/IRenderableModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; using FModel.Views.Snooper.Buffers; diff --git a/FModel/Views/Snooper/Models/Skybox.cs b/FModel/Views/Snooper/Models/Skybox.cs index af04e3b5..d025bbc8 100644 --- a/FModel/Views/Snooper/Models/Skybox.cs +++ b/FModel/Views/Snooper/Models/Skybox.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using FModel.Views.Snooper.Buffers; using FModel.Views.Snooper.Shading; diff --git a/FModel/Views/Snooper/Models/SplineModel.cs b/FModel/Views/Snooper/Models/SplineModel.cs index 3850a4ce..1012707e 100644 --- a/FModel/Views/Snooper/Models/SplineModel.cs +++ b/FModel/Views/Snooper/Models/SplineModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using CUE4Parse_Conversion.Meshes.PSK; diff --git a/FModel/Views/Snooper/Models/StaticModel.cs b/FModel/Views/Snooper/Models/StaticModel.cs index e0464f42..4abb4c63 100644 --- a/FModel/Views/Snooper/Models/StaticModel.cs +++ b/FModel/Views/Snooper/Models/StaticModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using CUE4Parse_Conversion.Meshes.PSK; using CUE4Parse.UE4.Assets.Exports.Material; diff --git a/FModel/Views/Snooper/Shading/TextureHelper.cs b/FModel/Views/Snooper/Shading/TextureHelper.cs index 7c7f170b..01831229 100644 --- a/FModel/Views/Snooper/Shading/TextureHelper.cs +++ b/FModel/Views/Snooper/Shading/TextureHelper.cs @@ -1,4 +1,4 @@ -using OpenTK.Graphics.OpenGL4; +using OpenTK.Graphics.OpenGL4; namespace FModel.Views.Snooper.Shading; diff --git a/FModel/Views/UpdateView.xaml.cs b/FModel/Views/UpdateView.xaml.cs index df433aff..f18eec5e 100644 --- a/FModel/Views/UpdateView.xaml.cs +++ b/FModel/Views/UpdateView.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using FModel.ViewModels; using FModel.Views.Resources.Controls;