diff --git a/FModel/Creator/Bases/BaseGCosmetic.cs b/FModel/Creator/Bases/BaseGCosmetic.cs index 18b4a211..f5eaa4bc 100644 --- a/FModel/Creator/Bases/BaseGCosmetic.cs +++ b/FModel/Creator/Bases/BaseGCosmetic.cs @@ -14,6 +14,9 @@ namespace FModel.Creator.Bases public SKBitmap IconImage; public SKColor[] RarityBackgroundColors; public SKColor[] RarityBorderColor; + public SKBitmap RarityBackgroundImage1; + public SKBitmap RarityBackgroundImage2; + public string RarityDisplayName; public string DisplayName; public string Description; public int Width = 512; // keep it 512 (or a multiple of 512) if you don't want blurry icons @@ -26,6 +29,9 @@ namespace FModel.Creator.Bases IconImage = FallbackImage; RarityBackgroundColors = new SKColor[2] { SKColor.Parse("FFFFFF"), SKColor.Parse("636363") }; RarityBorderColor = new SKColor[2] { SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF") }; + RarityBackgroundImage1 = null; + RarityBackgroundImage2 = null; + RarityDisplayName = ""; DisplayName = ""; Description = ""; Width = exportType switch @@ -43,7 +49,9 @@ namespace FModel.Creator.Bases public BaseGCosmetic(IUExport export, string exportType) : this(exportType) { // rarity - Rarity.GetInGameRarity(this, export.GetExport("Rarity")); + EnumProperty r = export.GetExport("Rarity"); + Rarity.GetInGameRarity(this, r); + this.RarityDisplayName = r != null ? r?.Value.String["EXRarity::".Length..] : "Common"; // image if (export.GetExport("IconTexture") is SoftObjectProperty previewImage) @@ -56,6 +64,33 @@ namespace FModel.Creator.Bases DisplayName = Text.GetTextPropertyBase(displayName); if (export.GetExport("Description") is TextProperty description) Description = Text.GetTextPropertyBase(description); + + RarityBackgroundImage1 = Utils.GetTexture("/Game/UI/Textures/assets/HUDAccentFillBox.HUDAccentFillBox"); + RarityBackgroundImage2 = Utils.GetTexture("/Game/UI/Textures/assets/store/ItemBGStatic_UIT.ItemBGStatic_UIT"); + } + + public void Draw(SKCanvas c) + { + if (this.RarityBackgroundImage1 != null) + c.DrawBitmap(this.RarityBackgroundImage1, new SKRect(0, 0, Width, Height), + new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true }); + + if (this.RarityBackgroundImage2 != null) + c.DrawBitmap(this.RarityBackgroundImage2, new SKRect(0, 0, Width, Height), + new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true, Color = SKColors.Transparent.WithAlpha(75) }); + + int x = this.Margin * (int)2.5; + int radi = 15; + c.DrawCircle(x + radi, x + radi, radi, new SKPaint + { + IsAntialias = true, + FilterQuality = SKFilterQuality.High, + Shader = SKShader.CreateRadialGradient( + new SKPoint(radi, radi), + (radi * 2) / 5 * 4, + this.RarityBackgroundColors, + SKShaderTileMode.Clamp) + }); } SKBitmap IBase.FallbackImage => FallbackImage; diff --git a/FModel/Creator/Bases/BaseIcon.cs b/FModel/Creator/Bases/BaseIcon.cs index ea84dcb3..e14ffa2a 100644 --- a/FModel/Creator/Bases/BaseIcon.cs +++ b/FModel/Creator/Bases/BaseIcon.cs @@ -129,16 +129,16 @@ namespace FModel.Creator.Bases else if (export.GetExport("cosmetic_item") is ObjectProperty cosmeticItem) // variants CosmeticSource = cosmeticItem.Value.Resource.ObjectName.String; - if (export.GetExport("AmmoData") is SoftObjectProperty ammoData) + if (Properties.Settings.Default.DrawStats && export.GetExport("AmmoData") is SoftObjectProperty ammoData) Statistics.GetAmmoData(this, ammoData); - if (export.GetExport("WeaponStatHandle") is StructProperty weaponStatHandle && + if (Properties.Settings.Default.DrawStats && export.GetExport("WeaponStatHandle") is StructProperty weaponStatHandle && (exportType.Equals("FortWeaponMeleeItemDefinition") || (export.GetExport("StatList") is SoftObjectProperty statList && !statList.Value.AssetPathName.String.StartsWith("/Game/UI/Tooltips/NoTooltipStats")))) { Statistics.GetWeaponStats(this, weaponStatHandle); } - if (export.GetExport("HeroGameplayDefinition") is ObjectProperty heroGameplayDefinition) + if (Properties.Settings.Default.DrawStats && export.GetExport("HeroGameplayDefinition") is ObjectProperty heroGameplayDefinition) Statistics.GetHeroStats(this, heroGameplayDefinition); /* Please do not add Schematics support because it takes way too much memory */ diff --git a/FModel/Creator/Bundles/Reward.cs b/FModel/Creator/Bundles/Reward.cs index 7f6e1360..ab3e2d22 100644 --- a/FModel/Creator/Bundles/Reward.cs +++ b/FModel/Creator/Bundles/Reward.cs @@ -34,13 +34,12 @@ namespace FModel.Creator.Bundles string[] parts = assetName.Split(':'); if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase)) { - PakPackage p = Utils.GetPropertyPakPackage("/Game/Banners/BannerIcons"); + PakPackage p = Utils.GetPropertyPakPackage($"/Game/Items/BannerIcons/{parts[1]}.{parts[1]}"); if (p.HasExport() && !p.Equals(default)) { - var c = p.GetExport(); - if (c != null && c.TryGetCaseInsensitiveValue(parts[1], out var s) && s is UObject banner) + if (p.GetExport() is UObject banner) { - RewardIcon = new BaseIcon(banner, "BannerIcons.uasset", false).IconImage.Resize(64, 64); + RewardIcon = new BaseIcon(banner, $"{parts[1]}.uasset", false).IconImage.Resize(64, 64); } } } diff --git a/FModel/Creator/Creator.cs b/FModel/Creator/Creator.cs index f635c744..21a659c3 100644 --- a/FModel/Creator/Creator.cs +++ b/FModel/Creator/Creator.cs @@ -312,9 +312,16 @@ namespace FModel.Creator using (var ret = new SKBitmap(icon.Width, icon.Height, SKColorType.Rgba8888, SKAlphaType.Premul)) using (var c = new SKCanvas(ret)) { - if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoBackground) + if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign == EIconDesign.Flat) { - Rarity.DrawRarity(c, icon); + icon.Draw(c); + } + else + { + if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoBackground) + { + Rarity.DrawRarity(c, icon); + } } LargeSmallImage.DrawPreviewImage(c, icon); diff --git a/FModel/Creator/Texts/GameplayTag.cs b/FModel/Creator/Texts/GameplayTag.cs index 729f1320..3f70c47f 100644 --- a/FModel/Creator/Texts/GameplayTag.cs +++ b/FModel/Creator/Texts/GameplayTag.cs @@ -36,7 +36,7 @@ namespace FModel.Creator.Texts if (p.HasExport() && !p.Equals(default)) { var d = p.GetExport(); - if (d != null && d.TryGetValue(setName, out var obj) && obj is UObject o) + if (d != null && d.TryGetCaseInsensitiveValue(setName, out var obj) && obj is UObject o) { if (o.TryGetValue("DisplayName", out var displayName) && displayName is TextProperty t) { diff --git a/FModel/Creator/Texts/Text.cs b/FModel/Creator/Texts/Text.cs index b12767ca..a017b5ee 100644 --- a/FModel/Creator/Texts/Text.cs +++ b/FModel/Creator/Texts/Text.cs @@ -237,8 +237,8 @@ namespace FModel.Creator.Texts { IsAntialias = true, FilterQuality = SKFilterQuality.High, - Typeface = side == ETextSide.Left ? TypeFaces.DisplayNameTypeface : TypeFaces.DefaultTypeface, - TextSize = 15, + Typeface = side == ETextSide.Left ? TypeFaces.BottomDefaultTypeface ?? TypeFaces.DisplayNameTypeface : TypeFaces.BottomDefaultTypeface ?? TypeFaces.DefaultTypeface, + TextSize = TypeFaces.BottomDefaultTypeface == null ? 15 : 13, Color = SKColors.White, TextAlign = side == ETextSide.Left ? SKTextAlign.Left : SKTextAlign.Right, }; diff --git a/FModel/Creator/Texts/Typefaces.cs b/FModel/Creator/Texts/Typefaces.cs index e80056e0..7dd674d1 100644 --- a/FModel/Creator/Texts/Typefaces.cs +++ b/FModel/Creator/Texts/Typefaces.cs @@ -60,6 +60,7 @@ namespace FModel.Creator.Texts public SKTypeface DefaultTypeface; // used as default font for all untranslated strings (item source, ...) public SKTypeface BundleDefaultTypeface; // used for the last folder string + public SKTypeface BottomDefaultTypeface; public SKTypeface DisplayNameTypeface; public SKTypeface DescriptionTypeface; public SKTypeface BundleDisplayNameTypeface; @@ -90,6 +91,17 @@ namespace FModel.Creator.Texts } else DisplayNameTypeface = DefaultTypeface; + string bottomPath = _FORTNITE_BASE_PATH + ( + Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? string.Empty : + Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? string.Empty : + Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? string.Empty : + Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? string.Empty : + Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? string.Empty : + _BURBANK_SMALL_BOLD); + t = Utils.GetPropertyArraySegmentByte(bottomPath); + if (t != null && t.Length == 3 && t[2].Array != null) + BottomDefaultTypeface = SKTypeface.FromStream(t[2].AsStream()); + string descriptionPath = _FORTNITE_BASE_PATH + ( Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _NOTO_SANS_KR_REGULAR : Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _NOTO_SANS_JP_BOLD : diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs index e750ec6b..ae7fadbd 100644 --- a/FModel/Creator/Utils.cs +++ b/FModel/Creator/Utils.cs @@ -25,7 +25,7 @@ namespace FModel.Creator { string path = Strings.FixPath(value); foreach (var fileReader in Globals.CachedPakFiles.Values) - if (fileReader.TryGetValue(path, out var entry)) + if (fileReader.TryGetCaseInsensiteveValue(path, out var entry)) { // kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯ string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length); @@ -38,7 +38,7 @@ namespace FModel.Creator { string path = Strings.FixPath(value); foreach (var fileReader in Globals.CachedPakFiles.Values) - if (fileReader.TryGetValue(path, out var entry)) + if (fileReader.TryGetCaseInsensiteveValue(path, out var entry)) { // kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯ string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length); diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj index a9c5fec7..20923824 100644 --- a/FModel/FModel.csproj +++ b/FModel/FModel.csproj @@ -131,13 +131,13 @@ - + - + diff --git a/FModel/PakReader/Pak/PakFileReader.cs b/FModel/PakReader/Pak/PakFileReader.cs index a153aec0..c7c21f55 100644 --- a/FModel/PakReader/Pak/PakFileReader.cs +++ b/FModel/PakReader/Pak/PakFileReader.cs @@ -480,6 +480,20 @@ namespace PakReader.Pak key = string.Empty; return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetCaseInsensiteveValue(string key, out FPakEntry value) + { + foreach (var r in Entries) + { + if (r.Key.Equals(key, StringComparison.CurrentCultureIgnoreCase)) + { + value = r.Value; + return true; + } + } + value = null; + return false; + } FPakEntry IReadOnlyDictionary.this[string key] => Entries[key]; diff --git a/FModel/PakReader/Parsers/Objects/EBulkDataFlags.cs b/FModel/PakReader/Parsers/Objects/EBulkDataFlags.cs index 488bd9c8..4d075664 100644 --- a/FModel/PakReader/Parsers/Objects/EBulkDataFlags.cs +++ b/FModel/PakReader/Parsers/Objects/EBulkDataFlags.cs @@ -5,37 +5,50 @@ */ public enum EBulkDataFlags : uint { - /** Empty flag set. */ - BULKDATA_None = 0, - /** If set, payload is stored at the end of the file and not inline */ - BULKDATA_PayloadAtEndOfFile = 1 << 0, - /** If set, payload should be [un]compressed using ZLIB during serialization. */ - BULKDATA_SerializeCompressedZLIB = 1 << 1, - /** Force usage of SerializeElement over bulk serialization. */ - BULKDATA_ForceSingleElementSerialization = 1 << 2, - /** Bulk data is only used once at runtime in the game. */ - BULKDATA_SingleUse = 1 << 3, - /** Bulk data won't be used and doesn't need to be loaded */ - BULKDATA_Unused = 1 << 5, - /** Forces the payload to be saved inline, regardless of its size */ - BULKDATA_ForceInlinePayload = 1 << 6, - /** Flag to check if either compression mode is specified */ - BULKDATA_SerializeCompressed = (BULKDATA_SerializeCompressedZLIB), - /** Forces the payload to be always streamed, regardless of its size */ - BULKDATA_ForceStreamPayload = 1 << 7, - /** If set, payload is stored in a .upack file alongside the uasset */ - BULKDATA_PayloadInSeperateFile = 1 << 8, - /** DEPRECATED: If set, payload is compressed using platform specific bit window */ - BULKDATA_SerializeCompressedBitWindow = 1 << 9, - /** There is a new default to inline unless you opt out */ - BULKDATA_Force_NOT_InlinePayload = 1 << 10, - /** This payload is optional and may not be on device */ - BULKDATA_OptionalPayload = 1 << 11, - /** This payload will be memory mapped, this requires alignment, no compression etc. */ - BULKDATA_MemoryMappedPayload = 1 << 12, - /** Bulk data size is 64 bits long */ - BULKDATA_Size64Bit = 1 << 13, - /** Duplicate non-optional payload in optional bulk data. */ - BULKDATA_DuplicateNonOptionalPayload = 1 << 14 - } + /** Empty flag set. */ + BULKDATA_None = 0, + /** If set, payload is stored at the end of the file and not inline. */ + BULKDATA_PayloadAtEndOfFile = 1 << 0, + /** If set, payload should be [un]compressed using ZLIB during serialization. */ + BULKDATA_SerializeCompressedZLIB = 1 << 1, + /** Force usage of SerializeElement over bulk serialization. */ + BULKDATA_ForceSingleElementSerialization = 1 << 2, + /** Bulk data is only used once at runtime in the game. */ + BULKDATA_SingleUse = 1 << 3, + /** Bulk data won't be used and doesn't need to be loaded. */ + BULKDATA_Unused = 1 << 5, + /** Forces the payload to be saved inline, regardless of its size. */ + BULKDATA_ForceInlinePayload = 1 << 6, + /** Flag to check if either compression mode is specified. */ + BULKDATA_SerializeCompressed = (BULKDATA_SerializeCompressedZLIB), + /** Forces the payload to be always streamed, regardless of its size. */ + BULKDATA_ForceStreamPayload = 1 << 7, + /** If set, payload is stored in a .upack file alongside the uasset. */ + BULKDATA_PayloadInSeperateFile = 1 << 8, + /** DEPRECATED: If set, payload is compressed using platform specific bit window. */ + BULKDATA_SerializeCompressedBitWindow = 1 << 9, + /** There is a new default to inline unless you opt out. */ + BULKDATA_Force_NOT_InlinePayload = 1 << 10, + /** This payload is optional and may not be on device. */ + BULKDATA_OptionalPayload = 1 << 11, + /** This payload will be memory mapped, this requires alignment, no compression etc. */ + BULKDATA_MemoryMappedPayload = 1 << 12, + /** Bulk data size is 64 bits long. */ + BULKDATA_Size64Bit = 1 << 13, + /** Duplicate non-optional payload in optional bulk data. */ + BULKDATA_DuplicateNonOptionalPayload = 1 << 14, + /** Indicates that an old ID is present in the data, at some point when the DDCs are flushed we can remove this. */ + BULKDATA_BadDataVersion = 1 << 15, + /** BulkData did not have it's offset changed during the cook and does not need the fix up at load time */ + BULKDATA_NoOffsetFixUp = 1 << 16, + + /* Runtime only flags below this point! Note that they take the high bits in reverse order! */ + + /** Assigned at runtime to indicate that the BulkData allocation is a memory mapped region of a file and not raw data. */ + BULKDATA_DataIsMemoryMapped = 1 << 30, + /** Assigned at runtime to indicate that the BulkData object has an async loading request in flight and will need to wait on it. */ + BULKDATA_HasAsyncReadPending = 1 << 29, + /** Assigned at runtime to indicate that the BulkData object should be considered for discard even if it cannot load from disk. */ + BULKDATA_AlwaysAllowDiscard = 1 << 28, + } } diff --git a/FModel/PakReader/Parsers/Objects/FByteBulkData.cs b/FModel/PakReader/Parsers/Objects/FByteBulkData.cs index 41b23c6d..f0cdaf74 100644 --- a/FModel/PakReader/Parsers/Objects/FByteBulkData.cs +++ b/FModel/PakReader/Parsers/Objects/FByteBulkData.cs @@ -36,7 +36,10 @@ namespace PakReader.Parsers.Objects else if ((BulkDataFlags & (uint)EBulkDataFlags.BULKDATA_PayloadInSeperateFile) != 0 && ubulk != null && BulkDataOffsetInFile + ubulkOffset >= 0) //.ubulk { - ubulk.Position = BulkDataOffsetInFile + ubulkOffset; + if ((BulkDataFlags & (uint)EBulkDataFlags.BULKDATA_NoOffsetFixUp) == 0) // UE4.26 flag + { + ubulk.Position = BulkDataOffsetInFile + ubulkOffset; + } Data = new byte[ElementCount]; ubulk.Read(Data, 0, (int)ElementCount); } diff --git a/FModel/PakReader/Parsers/PropertyTagData/ByteProperty.cs b/FModel/PakReader/Parsers/PropertyTagData/ByteProperty.cs index 8d12b6c3..2a2fe8b7 100644 --- a/FModel/PakReader/Parsers/PropertyTagData/ByteProperty.cs +++ b/FModel/PakReader/Parsers/PropertyTagData/ByteProperty.cs @@ -3,20 +3,20 @@ using System; namespace PakReader.Parsers.PropertyTagData { - public sealed class ByteProperty : BaseProperty + public sealed class ByteProperty : BaseProperty { internal ByteProperty(PackageReader reader, FPropertyTag tag, ReadType readType) { Position = reader.Position; Value = readType switch { - ReadType.NORMAL => tag.EnumName.IsNone ? reader.ReadByte() : (byte)reader.ReadFName().Index, + ReadType.NORMAL => tag.EnumName.IsNone ? reader.ReadByte().ToString() : reader.ReadFName().String, ReadType.MAP => (byte)reader.ReadUInt32(), ReadType.ARRAY => reader.ReadByte(), _ => throw new ArgumentOutOfRangeException(nameof(readType)), }; } - public byte GetValue() => Value; + public object GetValue() => Value; } } diff --git a/FModel/Properties/Resources.Designer.cs b/FModel/Properties/Resources.Designer.cs index fc67ef7b..96458d26 100644 --- a/FModel/Properties/Resources.Designer.cs +++ b/FModel/Properties/Resources.Designer.cs @@ -792,6 +792,15 @@ namespace FModel.Properties { } } + /// + /// Recherche une chaîne localisée semblable à Show statistics. + /// + public static string DrawStats { + get { + return ResourceManager.GetString("DrawStats", resourceCulture); + } + } + /// /// Recherche une chaîne localisée semblable à Duration. /// diff --git a/FModel/Properties/Resources.fr-FR.resx b/FModel/Properties/Resources.fr-FR.resx index b0002484..285f316f 100644 --- a/FModel/Properties/Resources.fr-FR.resx +++ b/FModel/Properties/Resources.fr-FR.resx @@ -828,4 +828,7 @@ C'est maintenant le logiciel gratuit le plus utilisé pour leak sur Fortnite. Ignorer cette version + + Afficher les statistiques + \ No newline at end of file diff --git a/FModel/Properties/Resources.resx b/FModel/Properties/Resources.resx index ecd19d6b..d4325fd1 100644 --- a/FModel/Properties/Resources.resx +++ b/FModel/Properties/Resources.resx @@ -1082,4 +1082,7 @@ It's now the most used free software to leak on Fortnite. Skip this Version + + Show statistics + \ No newline at end of file diff --git a/FModel/Properties/Settings.Designer.cs b/FModel/Properties/Settings.Designer.cs index 4f81b53e..4c15137d 100644 --- a/FModel/Properties/Settings.Designer.cs +++ b/FModel/Properties/Settings.Designer.cs @@ -538,5 +538,17 @@ namespace FModel.Properties { this["LauncherExpiration"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool DrawStats { + get { + return ((bool)(this["DrawStats"])); + } + set { + this["DrawStats"] = value; + } + } } } diff --git a/FModel/Properties/Settings.settings b/FModel/Properties/Settings.settings index 0a1694f5..450a32a1 100644 --- a/FModel/Properties/Settings.settings +++ b/FModel/Properties/Settings.settings @@ -131,5 +131,8 @@ 0 + + True + \ No newline at end of file diff --git a/FModel/Windows/Settings/IconCreator.xaml b/FModel/Windows/Settings/IconCreator.xaml index 4cd83e6b..687a656c 100644 --- a/FModel/Windows/Settings/IconCreator.xaml +++ b/FModel/Windows/Settings/IconCreator.xaml @@ -78,6 +78,7 @@ + +