feat(resources): Replace pack:// URIs with avares:// AssetLoader [P2-013] (#80)

* feat(resources): Replace pack:// URIs with avares:// AssetLoader [P2-013]

- Typefaces.cs: Replace pack:// Uri field with avares:// and use AssetLoader.Open
- UCreator.cs: Replace Application.GetResourceStream for T_Placeholder_Item_Image.png
- BaseIcon.cs: Replace Application.GetResourceStream for T-Icon-Pets/Quests-64.png
- BaseTandem.cs: Replace Application.GetResourceStream for npcleftside.png
- Snooper.cs: Replace Application.GetResourceStream for engine.png; remove unused System.Windows.Forms using
- FModel.csproj: Change 6 <Resource> entries to <AvaloniaResource> so avares:// can locate them

Replace using System.Windows with using Avalonia.Platform in all affected files.

Closes #22

* fixup! feat(resources): Replace pack:// URIs with avares:// AssetLoader [P2-013]

- Dispose AssetLoader streams with 'using var' in Typefaces, UCreator, BaseIcon
- Extract stream + intermediate SKBitmap locals in BaseTandem.DrawBackground so all
  three (stream, decoded, resized) are deterministically disposed (fixes M1 + S2)
- Migrate Texture.cs (missed call site): ProcessPixels used Application.GetResourceStream
  for all Snooper icon textures (materialicon, square/square_off, cube/cube_off,
  light/light_off, checker, pointlight, spotlight, skybox faces, UI timeline icons)
- Update FModel.csproj: change 25 additional <Resource> entries to <AvaloniaResource>
  covering all resources loaded by Texture.ProcessPixels via AssetLoader

* fixup! feat(resources): Replace pack:// URIs with avares:// AssetLoader [P2-013]

Tighten scope of bgStream/decoded/resized in BaseTandem.DrawBackground:
use brace-scoped using() so all three disposables are freed immediately
after DrawBitmap returns, rather than surviving the rest of the method.
This commit is contained in:
Rob Trame 2026-03-15 19:49:48 -06:00 committed by GitHub
parent 4c233f9947
commit 88d78a025c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 279 additions and 233 deletions

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Avalonia.Platform;
using CUE4Parse.GameTypes.FN.Enums;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Engine;
@ -30,9 +30,12 @@ public class BaseIcon : UCreator
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 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"))
{
@ -134,7 +137,8 @@ public class BaseIcon : UCreator
private void GetSeries(FPackageIndex s)
{
if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
if (!Utils.TryGetPackageIndexExport(s, out UObject export))
return;
GetSeries(export);
}
@ -147,10 +151,12 @@ public class BaseIcon : UCreator
private void GetSeries(FStructFallback s)
{
if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return;
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;
!seriesDefObj.TryGetValue(out UObject series, "Series"))
return;
GetSeries(series);
}
@ -193,7 +199,8 @@ public class BaseIcon : UCreator
private void GetRarity(EFortRarity r)
{
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return;
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export))
return;
if (export.GetByIndex<FStructFallback>((int) r) is { } data &&
data.TryGetValue(out FLinearColor color1, "Color1") &&
@ -241,7 +248,8 @@ public class BaseIcon : UCreator
var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.");
if (onlySeason) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, seasonIdx)));
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}");
@ -287,8 +295,15 @@ public class BaseIcon : UCreator
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);
{
using var stream = AssetLoader.Open(new Uri("avares://FModel/Resources/T-Icon-Pets-64.png"));
UserFacingFlags[flag] = SKBitmap.Decode(stream);
}
else
{
using var stream = AssetLoader.Open(new Uri("avares://FModel/Resources/T-Icon-Quests-64.png"));
UserFacingFlags[flag] = SKBitmap.Decode(stream);
}
}
else
{
@ -307,7 +322,8 @@ public class BaseIcon : UCreator
private void DrawUserFacingFlags(SKCanvas c)
{
if (UserFacingFlags == null) return;
if (UserFacingFlags == null)
return;
const int size = 25;
var x = Margin * (int) 2.5;

View File

@ -1,5 +1,5 @@
using System;
using System.Windows;
using Avalonia.Platform;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Objects.Core.i18N;
using CUE4Parse.UE4.Objects.UObject;
@ -71,7 +71,10 @@ public class BaseTandem : BaseIcon
private new void DrawBackground(SKCanvas c)
{
c.DrawBitmap(SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/npcleftside.png"))?.Stream).Resize(Width, Height), 0, 0, new SKPaint { IsAntialias = false, FilterQuality = SKFilterQuality.None, ImageFilter = SKImageFilter.CreateBlur(0, 25) });
using (var bgStream = AssetLoader.Open(new Uri("avares://FModel/Resources/npcleftside.png")))
using (var decoded = SKBitmap.Decode(bgStream))
using (var resized = decoded.Resize(Width, Height))
c.DrawBitmap(resized, 0, 0, new SKPaint { IsAntialias = false, FilterQuality = SKFilterQuality.None, ImageFilter = SKImageFilter.CreateBlur(0, 25) });
using var rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
_panelPaint.Color = SKColor.Parse("#002A8C");
@ -154,7 +157,8 @@ public class BaseTandem : BaseIcon
private void DrawName(SKCanvas c)
{
if (string.IsNullOrWhiteSpace(DisplayName)) return;
if (string.IsNullOrWhiteSpace(DisplayName))
return;
DisplayNamePaint.TextSize = UserSettings.Default.AssetLanguage switch
{
@ -180,7 +184,8 @@ public class BaseTandem : BaseIcon
private void DrawGeneralDescription(SKCanvas c)
{
if (string.IsNullOrWhiteSpace(_generalDescription)) return;
if (string.IsNullOrWhiteSpace(_generalDescription))
return;
DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
{
@ -204,7 +209,8 @@ public class BaseTandem : BaseIcon
private void DrawAdditionalDescription(SKCanvas c)
{
if (string.IsNullOrWhiteSpace(_additionalDescription)) return;
if (string.IsNullOrWhiteSpace(_additionalDescription))
return;
DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
{
@ -225,4 +231,4 @@ public class BaseTandem : BaseIcon
Utils.DrawMultilineText(c, _additionalDescription, Width, 0, SKTextAlign.Left,
new SKRect(97, 960, Width - 10, Height), DescriptionPaint, out _);
}
}
}

View File

@ -1,5 +1,5 @@
using System;
using System.Windows;
using Avalonia.Platform;
using CUE4Parse.UE4.Assets.Exports;
using FModel.Creator.Bases.FN;
using FModel.Framework;
@ -27,7 +27,8 @@ public abstract class UCreator
protected UCreator(UObject uObject, EIconStyle style)
{
DefaultPreview = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png"))?.Stream);
using var placeholderStream = AssetLoader.Open(new Uri("avares://FModel/Resources/T_Placeholder_Item_Image.png"));
DefaultPreview = SKBitmap.Decode(placeholderStream);
Background = new[] { SKColor.Parse("5BFD00"), SKColor.Parse("003700") };
Border = new[] { SKColor.Parse("1E8500"), SKColor.Parse("5BFD00") };
DisplayName = string.Empty;
@ -42,34 +43,44 @@ public abstract class UCreator
private const int _STARTER_TEXT_POSITION = 380, _NAME_TEXT_SIZE = 45, _BOTTOM_TEXT_SIZE = 15;
protected readonly SKPaint DisplayNamePaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
Typeface = Utils.Typefaces.DisplayName, TextSize = _NAME_TEXT_SIZE,
Color = SKColors.White, TextAlign = SKTextAlign.Center
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Typeface = Utils.Typefaces.DisplayName,
TextSize = _NAME_TEXT_SIZE,
Color = SKColors.White,
TextAlign = SKTextAlign.Center
};
protected readonly SKPaint DescriptionPaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
Typeface = Utils.Typefaces.Description, TextSize = 13,
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Typeface = Utils.Typefaces.Description,
TextSize = 13,
Color = SKColors.White
};
protected readonly SKPaint ImagePaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High
IsAntialias = true,
FilterQuality = SKFilterQuality.High
};
private readonly SKPaint _textBackgroundPaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = new SKColor(0, 0, 50, 75)
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Color = new SKColor(0, 0, 50, 75)
};
private readonly SKPaint _shortDescriptionPaint = new()
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Color = SKColors.White
};
public void DrawBackground(SKCanvas c)
{
// reverse doesn't affect basic rarities
if (Background[0] == Background[1]) Background[0] = Border[0];
if (Background[0] == Background[1])
Background[0] = Border[0];
Background[0].ToHsl(out _, out _, out var l1);
Background[1].ToHsl(out _, out _, out var l2);
var reverse = l1 > l2;
@ -78,7 +89,8 @@ public abstract class UCreator
c.DrawRect(new SKRect(0, 0, Width, Height),
new SKPaint
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Shader = SKShader.CreateLinearGradient(
new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Border, SKShaderTileMode.Clamp)
});
@ -91,41 +103,44 @@ public abstract class UCreator
switch (Style)
{
case EIconStyle.Flat:
{
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
new SKPaint
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4),
new[] { Background[reverse ? 0 : 1].WithAlpha(150), Border[0] }, SKShaderTileMode.Clamp)
});
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
var pathTop = new SKPath { FillType = SKPathFillType.EvenOdd };
pathTop.MoveTo(Margin, Margin);
pathTop.LineTo(Margin + Width / 17 * 10, Margin);
pathTop.LineTo(Margin, Margin + Height / 17);
pathTop.Close();
c.DrawPath(pathTop, new SKPaint
{
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Color = Background[1].WithAlpha(75)
});
break;
}
default:
{
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
new SKPaint
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
new SKPaint
{
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4),
new[] { Background[reverse ? 0 : 1].WithAlpha(150), Border[0] }, SKShaderTileMode.Clamp)
});
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description))
return;
var pathTop = new SKPath { FillType = SKPathFillType.EvenOdd };
pathTop.MoveTo(Margin, Margin);
pathTop.LineTo(Margin + Width / 17 * 10, Margin);
pathTop.LineTo(Margin, Margin + Height / 17);
pathTop.Close();
c.DrawPath(pathTop, new SKPaint
{
IsAntialias = true, FilterQuality = SKFilterQuality.High,
Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, Height / 2), Width / 5 * 4,
new[] { Background[reverse ? 0 : 1], Background[reverse ? 1 : 0] },
SKShaderTileMode.Clamp)
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Color = Background[1].WithAlpha(75)
});
break;
}
break;
}
default:
{
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
new SKPaint
{
IsAntialias = true,
FilterQuality = SKFilterQuality.High,
Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, Height / 2), Width / 5 * 4,
new[] { Background[reverse ? 0 : 1], Background[reverse ? 1 : 0] },
SKShaderTileMode.Clamp)
});
break;
}
}
}
}
@ -135,20 +150,21 @@ public abstract class UCreator
protected void DrawTextBackground(SKCanvas c)
{
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description))
return;
switch (Style)
{
case EIconStyle.Flat:
{
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
pathBottom.MoveTo(Margin, Height - Margin);
pathBottom.LineTo(Margin, Height - Margin - Height / 17 * 2.5f);
pathBottom.LineTo(Width - Margin, Height - Margin - Height / 17 * 4.5f);
pathBottom.LineTo(Width - Margin, Height - Margin);
pathBottom.Close();
c.DrawPath(pathBottom, _textBackgroundPaint);
break;
}
{
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
pathBottom.MoveTo(Margin, Height - Margin);
pathBottom.LineTo(Margin, Height - Margin - Height / 17 * 2.5f);
pathBottom.LineTo(Width - Margin, Height - Margin - Height / 17 * 4.5f);
pathBottom.LineTo(Width - Margin, Height - Margin);
pathBottom.Close();
c.DrawPath(pathBottom, _textBackgroundPaint);
break;
}
default:
c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, Height - Margin), _textBackgroundPaint);
break;
@ -157,7 +173,8 @@ public abstract class UCreator
protected void DrawDisplayName(SKCanvas c)
{
if (string.IsNullOrWhiteSpace(DisplayName)) return;
if (string.IsNullOrWhiteSpace(DisplayName))
return;
while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
{
@ -172,11 +189,11 @@ public abstract class UCreator
switch (Style)
{
case EIconStyle.Flat:
{
DisplayNamePaint.TextAlign = SKTextAlign.Right;
x = Width - Margin * 2;
break;
}
{
DisplayNamePaint.TextAlign = SKTextAlign.Right;
x = Width - Margin * 2;
break;
}
}
#if DEBUG
@ -191,7 +208,8 @@ public abstract class UCreator
protected void DrawDescription(SKCanvas c)
{
if (string.IsNullOrWhiteSpace(Description)) return;
if (string.IsNullOrWhiteSpace(Description))
return;
var maxLine = string.IsNullOrEmpty(DisplayName) ? 8 : 4;
var side = SKTextAlign.Center;
@ -208,7 +226,8 @@ public abstract class UCreator
protected void DrawToBottom(SKCanvas c, SKTextAlign side, string text)
{
if (string.IsNullOrEmpty(text)) return;
if (string.IsNullOrEmpty(text))
return;
_shortDescriptionPaint.TextAlign = side;
_shortDescriptionPaint.TextSize = Utils.Typefaces.Bottom == null ? 15 : 13;

View File

@ -1,6 +1,6 @@
using System;
using System.IO;
using System.Windows;
using Avalonia.Platform;
using CUE4Parse.UE4.Versions;
using FModel.Settings;
using FModel.ViewModels;
@ -10,7 +10,7 @@ namespace FModel.Creator;
public class Typefaces
{
private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new("pack://application:,,,/Resources/BurbankBigCondensed-Bold.ttf");
private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new("avares://FModel/Resources/BurbankBigCondensed-Bold.ttf");
private const string _EXT = ".ufont";
// FortniteGame
@ -65,134 +65,136 @@ public class Typefaces
_viewModel = viewModel;
var language = UserSettings.Default.AssetLanguage;
Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream);
using var fontStream = AssetLoader.Open(_BURBANK_BIG_CONDENSED_BOLD);
Default = SKTypeface.FromStream(fontStream);
switch (viewModel.Provider.ProjectName.ToUpperInvariant())
{
case "FORTNITEGAME":
{
DisplayName = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => string.Empty
} + _EXT);
{
DisplayName = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => string.Empty
} + _EXT);
Description = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_REGULAR,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_REGULAR,
ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
_ => _NOTO_SANS_REGULAR
} + _EXT);
Description = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_REGULAR,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_REGULAR,
ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
_ => _NOTO_SANS_REGULAR
} + _EXT);
Bottom = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => string.Empty,
ELanguage.Japanese => string.Empty,
ELanguage.Arabic => string.Empty,
ELanguage.TraditionalChinese => string.Empty,
ELanguage.Chinese => string.Empty,
_ => _BURBANK_SMALL_BOLD
} + _EXT, true);
Bottom = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => string.Empty,
ELanguage.Japanese => string.Empty,
ELanguage.Arabic => string.Empty,
ELanguage.TraditionalChinese => string.Empty,
ELanguage.Chinese => string.Empty,
_ => _BURBANK_SMALL_BOLD
} + _EXT, true);
BundleNumber = OnTheFly(_FORTNITE_BASE_PATH + _BURBANK_SMALL_BOLD + _EXT);
BundleNumber = OnTheFly(_FORTNITE_BASE_PATH + _BURBANK_SMALL_BOLD + _EXT);
Bundle = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BOLD
} + _EXT, true) ?? BundleNumber;
Bundle = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BOLD
} + _EXT, true) ?? BundleNumber;
TandemDisplayName = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_BIG_REGULAR_BLACK
} + _EXT);
TandemDisplayName = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_BIG_REGULAR_BLACK
} + _EXT);
TandemGenDescription = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BLACK
} + _EXT);
TandemGenDescription = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BLACK
} + _EXT);
TandemAddDescription = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BOLD
} + _EXT);
break;
}
TandemAddDescription = OnTheFly(_FORTNITE_BASE_PATH +
language switch
{
ELanguage.Korean => _ASIA_ERINM,
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
_ => _BURBANK_SMALL_BOLD
} + _EXT);
break;
}
case "MULTIVERSUS":
{
DisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
_ => _NORMS_PRO_EXTRABOLD_ITALIC
} + _EXT);
DisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
_ => _NORMS_PRO_EXTRABOLD_ITALIC
} + _EXT);
Description = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
_ => _NORMS_STD_CONDENSED_MEDIUM
} + _EXT);
Description = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
_ => _NORMS_STD_CONDENSED_MEDIUM
} + _EXT);
TandemDisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
_ => _NORMS_STD_CONDENSED_EXTRABOLD_ITALIC
} + _EXT);
TandemDisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
_ => _NORMS_STD_CONDENSED_EXTRABOLD_ITALIC
} + _EXT);
TandemGenDescription = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
_ => _NORMS_STD_CONDENSED_MEDIUM
} + _EXT);
break;
}
TandemGenDescription = OnTheFly(_PANDAGAME_BASE_PATH + language switch
{
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
_ => _NORMS_STD_CONDENSED_MEDIUM
} + _EXT);
break;
}
default:
{
DisplayName = Default;
Description = Default;
break;
}
{
DisplayName = Default;
Description = Default;
break;
}
}
}
public SKTypeface OnTheFly(string path, bool fallback = false)
{
if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return fallback ? null : Default;
if (!_viewModel.Provider.TrySaveAsset(path, out var data))
return fallback ? null : Default;
var m = new MemoryStream(data) { Position = _viewModel.Provider.Versions.Game >= EGame.GAME_UE5_6 ? 4 : 0 };
return SKTypeface.FromStream(m);
}

View File

@ -221,16 +221,16 @@
<Resource Include="Resources\fortnite.png" />
<Resource Include="Resources\fortnitebr.png" />
<Resource Include="Resources\empty_folder.png" />
<Resource Include="Resources\engine.png" />
<AvaloniaResource Include="Resources\engine.png" />
<Resource Include="Resources\gear.png" />
<Resource Include="Resources\localization.png" />
<Resource Include="Resources\materialicon.png" />
<Resource Include="Resources\square.png" />
<Resource Include="Resources\square_off.png" />
<Resource Include="Resources\cube.png" />
<Resource Include="Resources\cube_off.png" />
<Resource Include="Resources\light.png" />
<Resource Include="Resources\light_off.png" />
<AvaloniaResource Include="Resources\materialicon.png" />
<AvaloniaResource Include="Resources\square.png" />
<AvaloniaResource Include="Resources\square_off.png" />
<AvaloniaResource Include="Resources\cube.png" />
<AvaloniaResource Include="Resources\cube_off.png" />
<AvaloniaResource Include="Resources\light.png" />
<AvaloniaResource Include="Resources\light_off.png" />
<Resource Include="Resources\pc.png" />
<Resource Include="Resources\puzzle.png" />
<Resource Include="Resources\roguecompany.png" />
@ -256,41 +256,41 @@
<Resource Include="Resources\unix.png" />
<Resource Include="Resources\linux.png" />
<Resource Include="Resources\stateofdecay2.png" />
<Resource Include="Resources\T_Placeholder_Item_Image.png" />
<Resource Include="Resources\checker.png" />
<AvaloniaResource Include="Resources\T_Placeholder_Item_Image.png" />
<AvaloniaResource Include="Resources\checker.png" />
<Resource Include="Resources\T_ClipSize_Weapon_Stats.png" />
<Resource Include="Resources\T_DamagePerBullet_Weapon_Stats.png" />
<Resource Include="Resources\T_ReloadTime_Weapon_Stats.png" />
<Resource Include="Resources\T-Icon-Pets-64.png" />
<Resource Include="Resources\T-Icon-Quests-64.png" />
<AvaloniaResource Include="Resources\T-Icon-Pets-64.png" />
<AvaloniaResource Include="Resources\T-Icon-Quests-64.png" />
<Resource Include="Resources\Default.png" />
<Resource Include="Resources\NoBackground.png" />
<Resource Include="Resources\NoText.png" />
<Resource Include="Resources\Flat.png" />
<Resource Include="Resources\Cataba.png" />
<Resource Include="Resources\BurbankBigCondensed-Bold.ttf" />
<AvaloniaResource Include="Resources\BurbankBigCondensed-Bold.ttf" />
<AvaloniaResource Include="Resources\add_directory.png" />
<AvaloniaResource Include="Resources\delete.png" />
<AvaloniaResource Include="Resources\edit.png" />
<AvaloniaResource Include="Resources\go_to_directory.png" />
<Resource Include="Resources\npcleftside.png" />
<Resource Include="Resources\nx.png" />
<Resource Include="Resources\ny.png" />
<Resource Include="Resources\nz.png" />
<Resource Include="Resources\px.png" />
<Resource Include="Resources\py.png" />
<Resource Include="Resources\pz.png" />
<Resource Include="Resources\pointlight.png" />
<Resource Include="Resources\spotlight.png" />
<Resource Include="Resources\link_on.png" />
<Resource Include="Resources\link_off.png" />
<Resource Include="Resources\link_has.png" />
<Resource Include="Resources\tl_play.png" />
<Resource Include="Resources\tl_pause.png" />
<Resource Include="Resources\tl_rewind.png" />
<Resource Include="Resources\tl_forward.png" />
<Resource Include="Resources\tl_previous.png" />
<Resource Include="Resources\tl_next.png" />
<AvaloniaResource Include="Resources\npcleftside.png" />
<AvaloniaResource Include="Resources\nx.png" />
<AvaloniaResource Include="Resources\ny.png" />
<AvaloniaResource Include="Resources\nz.png" />
<AvaloniaResource Include="Resources\px.png" />
<AvaloniaResource Include="Resources\py.png" />
<AvaloniaResource Include="Resources\pz.png" />
<AvaloniaResource Include="Resources\pointlight.png" />
<AvaloniaResource Include="Resources\spotlight.png" />
<AvaloniaResource Include="Resources\link_on.png" />
<AvaloniaResource Include="Resources\link_off.png" />
<AvaloniaResource Include="Resources\link_has.png" />
<AvaloniaResource Include="Resources\tl_play.png" />
<AvaloniaResource Include="Resources\tl_pause.png" />
<AvaloniaResource Include="Resources\tl_rewind.png" />
<AvaloniaResource Include="Resources\tl_forward.png" />
<AvaloniaResource Include="Resources\tl_previous.png" />
<AvaloniaResource Include="Resources\tl_next.png" />
</ItemGroup>
<ItemGroup>

View File

@ -1,6 +1,6 @@
using System;
using System.Numerics;
using System.Windows;
using Avalonia.Platform;
using CUE4Parse_Conversion.Textures;
using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.Math;
@ -29,7 +29,7 @@ public class Texture : IDisposable
public int Width;
public int Height;
private const int DisabledChannel = (int)BlendingFactor.Zero;
private const int DisabledChannel = (int) BlendingFactor.Zero;
private readonly bool[] _values = [true, true, true, true];
private readonly string[] _labels = ["R", "G", "B", "A"];
public int[] SwizzleMask =
@ -164,8 +164,8 @@ public class Texture : IDisposable
private void ProcessPixels(string texture, TextureTarget target)
{
var info = Application.GetResourceStream(new Uri($"/FModel;component/Resources/{texture}.png", UriKind.Relative));
using var img = Image.Load<Rgba32>(info.Stream);
using var stream = AssetLoader.Open(new Uri($"avares://FModel/Resources/{texture}.png"));
using var img = Image.Load<Rgba32>(stream);
Width = img.Width;
Height = img.Height;
GL.TexImage2D(target, 0, PixelInternalFormat.Rgba8, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
@ -228,16 +228,18 @@ public class Texture : IDisposable
GL.DeleteTexture(_handle);
}
private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f);
private Vector3 _scrolling = new(0.0f, 0.0f, 1.0f);
public void ImGuiTextureInspector()
{
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
{
SnimGui.NoFramePaddingOnY(() =>
{
SnimGui.Layout("Type");ImGui.Text($" : ({Format}) {Name}");
SnimGui.Layout("Type");
ImGui.Text($" : ({Format}) {Name}");
SnimGui.TooltipCopy("(?) Click to Copy Path", Path);
SnimGui.Layout("Guid");ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
SnimGui.Layout("Guid");
ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
SnimGui.Layout("Size");
ImGui.Text($" : {Width}x{Height}");
@ -259,8 +261,10 @@ public class Texture : IDisposable
var io = ImGui.GetIO();
var canvasP0 = ImGui.GetCursorScreenPos();
var canvasSize = ImGui.GetContentRegionAvail();
if (canvasSize.X < 50.0f) canvasSize.X = 50.0f;
if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f;
if (canvasSize.X < 50.0f)
canvasSize.X = 50.0f;
if (canvasSize.Y < 50.0f)
canvasSize.Y = 50.0f;
var canvasP1 = canvasP0 + canvasSize;
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
var absoluteMiddle = canvasSize / 2.0f;

View File

@ -2,18 +2,17 @@ using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using CUE4Parse.UE4.Assets.Exports;
using FModel.Views.Snooper.Buffers;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Common.Input;
using OpenTK.Windowing.Desktop;
using Avalonia.Platform;
using Avalonia.Threading;
using OpenTK.Windowing.GraphicsLibraryFramework;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Application = System.Windows.Application;
namespace FModel.Views.Snooper;
@ -83,8 +82,8 @@ public class Snooper : GameWindow
private unsafe void LoadWindowIcon()
{
var info = Application.GetResourceStream(new Uri("/FModel;component/Resources/engine.png", UriKind.Relative));
using var img = SixLabors.ImageSharp.Image.Load<Rgba32>(info.Stream);
using var stream = AssetLoader.Open(new Uri("avares://FModel/Resources/engine.png"));
using var img = SixLabors.ImageSharp.Image.Load<Rgba32>(stream);
var memoryGroup = img.GetPixelMemoryGroup();
Memory<byte> array = new byte[memoryGroup.TotalLength * sizeof(Rgba32)];
var block = MemoryMarshal.Cast<byte, Rgba32>(array.Span);