diff --git a/CUE4Parse b/CUE4Parse index 7772c6cc..68655624 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit 7772c6ccf0f6f195876d20d4b5d49fe533fc564e +Subproject commit 6865562475cb0661843438c4a20bd8748ba9c5e2 diff --git a/FModel/App.xaml b/FModel/App.xaml index f35316c1..fd1ca58e 100644 --- a/FModel/App.xaml +++ b/FModel/App.xaml @@ -1,4 +1,4 @@ - + + + + + + + + #206BD4 diff --git a/FModel/Constants.cs b/FModel/Constants.cs index bccfa2aa..af4c4a51 100644 --- a/FModel/Constants.cs +++ b/FModel/Constants.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Numerics; @@ -13,6 +13,7 @@ public static class Constants public static readonly string APP_VERSION = FileVersionInfo.GetVersionInfo(APP_PATH).FileVersion; public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion?.SubstringAfter('+'); public static readonly string APP_SHORT_COMMIT_ID = APP_COMMIT_ID[..7]; + public static readonly DateTime APP_BUILD_DATE = File.GetLastWriteTime(APP_PATH); public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000"; public static readonly FGuid ZERO_GUID = new(0U); 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 a72f265f..0fe8c317 100644 --- a/FModel/Creator/CreatorPackage.cs +++ b/FModel/Creator/CreatorPackage.cs @@ -1,266 +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) - { - 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": +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; - } -} + 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/Enums.cs b/FModel/Enums.cs index f59efb9d..c2f21f6e 100644 --- a/FModel/Enums.cs +++ b/FModel/Enums.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using FModel.Extensions; namespace FModel; @@ -65,16 +66,6 @@ public enum ELoadingMode AllButPatched, } -// public enum EUpdateMode -// { -// [Description("Stable")] -// Stable, -// [Description("Beta")] -// Beta, -// [Description("QA Testing")] -// Qa -// } - public enum ECompressedAudio { [Description("Play the decompressed data")] @@ -113,7 +104,56 @@ public enum EBulkType Properties = 1 << 1, Textures = 1 << 2, Meshes = 1 << 3, - Skeletons = 1 << 4, - Animations = 1 << 5, - Audio = 1 << 6 + Animations = 1 << 4, + Audio = 1 << 5, + Code = 1 << 6, +} + +public enum EAssetCategory : uint +{ + All = AssetCategoryExtensions.CategoryBase + (0 << 16), + Blueprints = AssetCategoryExtensions.CategoryBase + (1 << 16), + BlueprintGeneratedClass = Blueprints + 1, + WidgetBlueprintGeneratedClass = Blueprints + 2, + AnimBlueprintGeneratedClass = Blueprints + 3, + RigVMBlueprintGeneratedClass = Blueprints + 4, + UserDefinedEnum = Blueprints + 5, + UserDefinedStruct = Blueprints + 6, + //Metadata + Blueprint = Blueprints + 8, + CookedMetaData = Blueprints + 9, + Mesh = AssetCategoryExtensions.CategoryBase + (2 << 16), + StaticMesh = Mesh + 1, + SkeletalMesh = Mesh + 2, + CustomizableObject = Mesh + 3, + NaniteDisplacedMesh = Mesh + 4, + Texture = AssetCategoryExtensions.CategoryBase + (3 << 16), + Materials = AssetCategoryExtensions.CategoryBase + (4 << 16), + Material = Materials + 1, + MaterialEditorData = Materials + 2, + MaterialFunction = Materials + 3, + MaterialFunctionEditorData = Materials + 4, + MaterialParameterCollection = Materials + 5, + Animation = AssetCategoryExtensions.CategoryBase + (5 << 16), + Skeleton = Animation + 1, + Rig = Animation + 2, + Level = AssetCategoryExtensions.CategoryBase + (6 << 16), + World = Level + 1, + BuildData = Level + 2, + LevelSequence = Level + 3, + Foliage = Level + 4, + Data = AssetCategoryExtensions.CategoryBase + (7 << 16), + ItemDefinitionBase = Data + 1, + CurveBase = Data + 2, + PhysicsAsset = Data + 3, + ObjectRedirector = Data + 4, + PhysicalMaterial = Data + 5, + ByteCode = Data + 6, + Media = AssetCategoryExtensions.CategoryBase + (8 << 16), + Audio = Media + 1, + Video = Media + 2, + Font = Media + 3, + SoundBank = Media + 4, + AudioEvent = Media + 5, + Particle = AssetCategoryExtensions.CategoryBase + (9 << 16), } diff --git a/FModel/Extensions/AssetCategoryExtensions.cs b/FModel/Extensions/AssetCategoryExtensions.cs new file mode 100644 index 00000000..bc8be498 --- /dev/null +++ b/FModel/Extensions/AssetCategoryExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace FModel.Extensions; + +public static class AssetCategoryExtensions +{ + public const uint CategoryBase = 0x00010000; + + public static EAssetCategory GetBaseCategory(this EAssetCategory category) + { + return (EAssetCategory) ((uint) category & 0xFFFF0000); + } + + public static bool IsOfCategory(this EAssetCategory item, EAssetCategory category) + { + return item.GetBaseCategory() == category.GetBaseCategory(); + } + + public static bool IsBaseCategory(this EAssetCategory category) + { + return category == category.GetBaseCategory(); + } + + public static IEnumerable GetBaseCategories() + { + return Enum.GetValues().Where(c => c.IsBaseCategory()); + } +} 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/FModel.csproj b/FModel/FModel.csproj index 81a199a3..5327f7f5 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -166,8 +166,9 @@ - + + 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 43e53edd..28ed5caa 100644 --- a/FModel/Framework/FakeCUE4Parse.cs +++ b/FModel/Framework/FakeCUE4Parse.cs @@ -1,26 +1,22 @@ -using System; +using System; using CUE4Parse.Compression; using CUE4Parse.FileProvider.Objects; +using CUE4Parse.UE4.Assets.Objects; using CUE4Parse.UE4.Readers; namespace FModel.Framework; -public class FakeGameFile : GameFile +public class FakeGameFile(string path) : GameFile(path, 0) { - public FakeGameFile(string path) : base(path, 0) - { - - } - public override bool IsEncrypted => false; public override CompressionMethod CompressionMethod => CompressionMethod.None; - public override byte[] Read() + public override byte[] Read(FByteBulkDataHeader? header = null) { throw new NotImplementedException(); } - public override FArchive CreateReader() + public override FArchive CreateReader(FByteBulkDataHeader? header = null) { throw new NotImplementedException(); } 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/Helper.cs b/FModel/Helper.cs index c7a43c50..58d51545 100644 --- a/FModel/Helper.cs +++ b/FModel/Helper.cs @@ -38,7 +38,7 @@ public static class Helper else { var w = GetOpenedWindow(windowName); - if (windowName == "Search View") w.WindowState = WindowState.Normal; + if (windowName == "Search For Packages") w.WindowState = WindowState.Normal; w.Focus(); } } diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index f70fc5af..8dc4c0b0 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:FModel" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" + xmlns:inputs="clr-namespace:FModel.Views.Resources.Controls.Inputs" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:settings="clr-namespace:FModel.Settings" xmlns:services="clr-namespace:FModel.Services" @@ -10,8 +11,18 @@ xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI" xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI" WindowStartupLocation="CenterScreen" Closing="OnClosing" Loaded="OnLoaded" PreviewKeyDown="OnWindowKeyDown" - Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.85'}" - Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.75'}"> + Height="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.95'}" + Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.90'}"> + + + + + + + + + + - - - - - - - - + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + @@ -191,8 +223,8 @@ - + Padding="0" Background="Transparent"> + @@ -257,7 +289,8 @@ - + @@ -315,94 +348,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -437,32 +388,11 @@ Header="{Binding SelectedItem.AssetsList.Assets.Count, FallbackValue=0, ElementName=AssetsFolderName}" HeaderStringFormat="{}{0} Packages"> - - - - - - - - - - - - - - - - - + + @@ -471,217 +401,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -697,15 +425,15 @@ - + - + - + - + - + @@ -720,14 +448,130 @@ Background="{DynamicResource {x:Static adonisUi:Brushes.Layer0BackgroundBrush}}" /> + Padding="0" Background="Transparent"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -740,7 +584,7 @@