diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 6c02694b..bc2d0c20 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -21,16 +21,17 @@ jobs:
- name: Fetch Submodules Recursively
run: git submodule update --init --recursive
- - name: .NET 6 Setup
- uses: actions/setup-dotnet@v1
+ - name: .NET 7 Setup
+ uses: actions/setup-dotnet@v2
with:
- dotnet-version: 6.0.x
+ dotnet-version: '7.0.x'
+ include-prerelease: true
- name: .NET Restore
run: dotnet restore FModel
- name: .NET Publish
- run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net6.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }}
+ run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net7.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }}
- name: ZIP File
uses: papeloto/action-zip@v1
diff --git a/FModel/App.xaml.cs b/FModel/App.xaml.cs
index 07845592..e991c0ad 100644
--- a/FModel/App.xaml.cs
+++ b/FModel/App.xaml.cs
@@ -16,146 +16,150 @@ using MessageBox = AdonisUI.Controls.MessageBox;
using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
using MessageBoxResult = AdonisUI.Controls.MessageBoxResult;
-namespace FModel
+namespace FModel;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App
{
- ///
- /// Interaction logic for App.xaml
- ///
- public partial class App
+ [DllImport("kernel32.dll")]
+ private static extern bool AttachConsole(int dwProcessId);
+
+ [DllImport("winbrand.dll", CharSet = CharSet.Unicode)]
+ [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
+ static extern string BrandingFormatString(string format);
+
+ protected override void OnStartup(StartupEventArgs e)
{
- [DllImport("kernel32.dll")]
- private static extern bool AttachConsole(int dwProcessId);
-
- [DllImport("winbrand.dll", CharSet = CharSet.Unicode)]
- [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
- static extern string BrandingFormatString(string format);
-
- protected override void OnStartup(StartupEventArgs e)
- {
#if DEBUG
- AttachConsole(-1);
+ AttachConsole(-1);
#endif
- base.OnStartup(e);
+ base.OnStartup(e);
- try
- {
- UserSettings.Default = JsonConvert.DeserializeObject(
- File.ReadAllText(UserSettings.FilePath), JsonNetSerializer.SerializerSettings);
- }
- catch
- {
- UserSettings.Default = new UserSettings();
- }
-
- var createMe = false;
- if (!Directory.Exists(UserSettings.Default.OutputDirectory))
- {
- UserSettings.Default.OutputDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Output");
- }
- if (!Directory.Exists(UserSettings.Default.RawDataDirectory))
- {
- createMe = true;
- UserSettings.Default.RawDataDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
- }
- if (!Directory.Exists(UserSettings.Default.PropertiesDirectory))
- {
- createMe = true;
- UserSettings.Default.PropertiesDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
- }
- if (!Directory.Exists(UserSettings.Default.TextureDirectory))
- {
- createMe = true;
- UserSettings.Default.TextureDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
- }
- if (!Directory.Exists(UserSettings.Default.AudioDirectory))
- {
- createMe = true;
- UserSettings.Default.AudioDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
- }
- if (!Directory.Exists(UserSettings.Default.ModelDirectory))
- {
- createMe = true;
- UserSettings.Default.ModelDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
- }
-
- Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FModel"));
- Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Backups"));
- if (createMe) Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Exports"));
- Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Logs"));
- Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"));
-
- Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).WriteTo.File(
- Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.txt"),
- outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [FModel] [{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger();
-
- Log.Information("Version {Version}", Constants.APP_VERSION);
- Log.Information("{OS}", GetOperatingSystemProductName());
- Log.Information("{RuntimeVer}", RuntimeInformation.FrameworkDescription);
- Log.Information("Culture {SysLang}", Thread.CurrentThread.CurrentUICulture);
- }
-
- private void AppExit(object sender, ExitEventArgs e)
+ try
{
- Log.Information("––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––");
- Log.CloseAndFlush();
- UserSettings.Save();
- Environment.Exit(0);
+ UserSettings.Default = JsonConvert.DeserializeObject(
+ File.ReadAllText(UserSettings.FilePath), JsonNetSerializer.SerializerSettings);
}
-
- private void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
+ catch
{
- Log.Error("{Exception}", e.Exception);
-
- var messageBox = new MessageBoxModel
- {
- Text = $"An unhandled exception occurred: {e.Exception.Message}",
- Caption = "Fatal Error",
- Icon = MessageBoxImage.Error,
- Buttons = new[]
- {
- MessageBoxButtons.Custom("Reset Settings", EErrorKind.ResetSettings),
- MessageBoxButtons.Custom("Restart", EErrorKind.Restart),
- MessageBoxButtons.Custom("OK", EErrorKind.Ignore)
- },
- IsSoundEnabled = false
- };
-
- MessageBox.Show(messageBox);
- if (messageBox.Result == MessageBoxResult.Custom && (EErrorKind) messageBox.ButtonPressed.Id != EErrorKind.Ignore)
- {
- if ((EErrorKind) messageBox.ButtonPressed.Id == EErrorKind.ResetSettings)
- UserSettings.Default = new UserSettings();
-
- ApplicationService.ApplicationView.Restart();
- }
-
- e.Handled = true;
+ UserSettings.Default = new UserSettings();
}
- private string GetOperatingSystemProductName()
+ var createMe = false;
+ if (!Directory.Exists(UserSettings.Default.OutputDirectory))
{
- var productName = string.Empty;
- try
- {
- productName = BrandingFormatString("%WINDOWS_LONG%");
- }
- catch
- {
- // ignored
- }
-
- if (string.IsNullOrEmpty(productName))
- productName = Environment.OSVersion.VersionString;
-
- return $"{productName} ({(Environment.Is64BitOperatingSystem ? "64" : "32")}-bit)";
+ UserSettings.Default.OutputDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Output");
}
- public static string GetRegistryValue(string path, string name = null, RegistryHive root = RegistryHive.CurrentUser)
+ if (!Directory.Exists(UserSettings.Default.RawDataDirectory))
{
- using var rk = RegistryKey.OpenBaseKey(root, RegistryView.Default).OpenSubKey(path);
- if (rk != null)
- return rk.GetValue(name, null) as string;
- return string.Empty;
+ createMe = true;
+ UserSettings.Default.RawDataDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
}
+
+ if (!Directory.Exists(UserSettings.Default.PropertiesDirectory))
+ {
+ createMe = true;
+ UserSettings.Default.PropertiesDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
+ }
+
+ if (!Directory.Exists(UserSettings.Default.TextureDirectory))
+ {
+ createMe = true;
+ UserSettings.Default.TextureDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
+ }
+
+ if (!Directory.Exists(UserSettings.Default.AudioDirectory))
+ {
+ createMe = true;
+ UserSettings.Default.AudioDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
+ }
+
+ if (!Directory.Exists(UserSettings.Default.ModelDirectory))
+ {
+ createMe = true;
+ UserSettings.Default.ModelDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
+ }
+
+ Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FModel"));
+ Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Backups"));
+ if (createMe) Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Exports"));
+ Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Logs"));
+ Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"));
+
+ Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).WriteTo.File(
+ Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.txt"),
+ outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [FModel] [{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger();
+
+ Log.Information("Version {Version}", Constants.APP_VERSION);
+ Log.Information("{OS}", GetOperatingSystemProductName());
+ Log.Information("{RuntimeVer}", RuntimeInformation.FrameworkDescription);
+ Log.Information("Culture {SysLang}", Thread.CurrentThread.CurrentUICulture);
}
-}
+
+ private void AppExit(object sender, ExitEventArgs e)
+ {
+ Log.Information("––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––");
+ Log.CloseAndFlush();
+ UserSettings.Save();
+ Environment.Exit(0);
+ }
+
+ private void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
+ {
+ Log.Error("{Exception}", e.Exception);
+
+ var messageBox = new MessageBoxModel
+ {
+ Text = $"An unhandled exception occurred: {e.Exception.Message}",
+ Caption = "Fatal Error",
+ Icon = MessageBoxImage.Error,
+ Buttons = new[]
+ {
+ MessageBoxButtons.Custom("Reset Settings", EErrorKind.ResetSettings),
+ MessageBoxButtons.Custom("Restart", EErrorKind.Restart),
+ MessageBoxButtons.Custom("OK", EErrorKind.Ignore)
+ },
+ IsSoundEnabled = false
+ };
+
+ MessageBox.Show(messageBox);
+ if (messageBox.Result == MessageBoxResult.Custom && (EErrorKind) messageBox.ButtonPressed.Id != EErrorKind.Ignore)
+ {
+ if ((EErrorKind) messageBox.ButtonPressed.Id == EErrorKind.ResetSettings)
+ UserSettings.Default = new UserSettings();
+
+ ApplicationService.ApplicationView.Restart();
+ }
+
+ e.Handled = true;
+ }
+
+ private string GetOperatingSystemProductName()
+ {
+ var productName = string.Empty;
+ try
+ {
+ productName = BrandingFormatString("%WINDOWS_LONG%");
+ }
+ catch
+ {
+ // ignored
+ }
+
+ if (string.IsNullOrEmpty(productName))
+ productName = Environment.OSVersion.VersionString;
+
+ return $"{productName} ({(Environment.Is64BitOperatingSystem ? "64" : "32")}-bit)";
+ }
+
+ public static string GetRegistryValue(string path, string name = null, RegistryHive root = RegistryHive.CurrentUser)
+ {
+ using var rk = RegistryKey.OpenBaseKey(root, RegistryView.Default).OpenSubKey(path);
+ if (rk != null)
+ return rk.GetValue(name, null) as string;
+ return string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/FModel/Constants.cs b/FModel/Constants.cs
index 2c00ab02..6cb078ac 100644
--- a/FModel/Constants.cs
+++ b/FModel/Constants.cs
@@ -1,27 +1,26 @@
using System.Reflection;
using CUE4Parse.UE4.Objects.Core.Misc;
-namespace FModel
+namespace FModel;
+
+public static class Constants
{
- public static class Constants
- {
- public static readonly string APP_VERSION = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
- public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000";
- public static readonly FGuid ZERO_GUID = new(0U);
+ public static readonly string APP_VERSION = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
+ public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000";
+ public static readonly FGuid ZERO_GUID = new(0U);
- public const string WHITE = "#DAE5F2";
- public const string RED = "#E06C75";
- public const string GREEN = "#98C379";
- public const string YELLOW = "#E5C07B";
- public const string BLUE = "#528BCC";
-
- public const string ISSUE_LINK = "https://github.com/iAmAsval/FModel/issues/new/choose";
- public const string DONATE_LINK = "https://fmodel.app/donate";
- public const string DISCORD_LINK = "https://fmodel.app/discord";
+ public const string WHITE = "#DAE5F2";
+ public const string RED = "#E06C75";
+ public const string GREEN = "#98C379";
+ public const string YELLOW = "#E5C07B";
+ public const string BLUE = "#528BCC";
- public const string _FN_LIVE_TRIGGER = "fortnite-live.manifest";
- public const string _VAL_LIVE_TRIGGER = "valorant-live.manifest";
+ public const string ISSUE_LINK = "https://github.com/iAmAsval/FModel/issues/new/choose";
+ public const string DONATE_LINK = "https://fmodel.app/donate";
+ public const string DISCORD_LINK = "https://fmodel.app/discord";
- public const string _NO_PRESET_TRIGGER = "Hand Made";
- }
+ public const string _FN_LIVE_TRIGGER = "fortnite-live.manifest";
+ public const string _VAL_LIVE_TRIGGER = "valorant-live.manifest";
+
+ public const string _NO_PRESET_TRIGGER = "Hand Made";
}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/BB/BaseBreakersIcon.cs b/FModel/Creator/Bases/BB/BaseBreakersIcon.cs
index 21f6bc8a..448e52c9 100644
--- a/FModel/Creator/Bases/BB/BaseBreakersIcon.cs
+++ b/FModel/Creator/Bases/BB/BaseBreakersIcon.cs
@@ -4,40 +4,39 @@ using CUE4Parse.UE4.Objects.UObject;
using FModel.Creator.Bases.FN;
using SkiaSharp;
-namespace FModel.Creator.Bases.BB
+namespace FModel.Creator.Bases.BB;
+
+public class BaseBreakersIcon : BaseIcon
{
- public class BaseBreakersIcon : BaseIcon
+ public BaseBreakersIcon(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public BaseBreakersIcon(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- SeriesBackground = Utils.GetBitmap("WorldExplorers/Content/UMG/Materials/t_TextGradient.t_TextGradient");
- Background = new[] {SKColor.Parse("D0D0D0"), SKColor.Parse("636363")};
- Border = new[] {SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF")};
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FSoftObjectPath iconTextureAssetData, "IconTextureAssetData", "UnlockPortraitGuideImage"))
- Preview = Utils.GetBitmap(iconTextureAssetData);
-
- if (Object.TryGetValue(out FText displayName, "DisplayName", "RegionDisplayName", "ZoneName"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText description, "Description", "RegionShortName", "ZoneDescription"))
- Description = description.Text;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawDescription(c);
-
- return new []{ret};
- }
+ SeriesBackground = Utils.GetBitmap("WorldExplorers/Content/UMG/Materials/t_TextGradient.t_TextGradient");
+ Background = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("636363") };
+ Border = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF") };
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FSoftObjectPath iconTextureAssetData, "IconTextureAssetData", "UnlockPortraitGuideImage"))
+ Preview = Utils.GetBitmap(iconTextureAssetData);
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName", "RegionDisplayName", "ZoneName"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText description, "Description", "RegionShortName", "ZoneDescription"))
+ Description = description.Text;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawDescription(c);
+
+ return new[] { ret };
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseBundle.cs b/FModel/Creator/Bases/FN/BaseBundle.cs
index 00ac3815..a8b4f782 100644
--- a/FModel/Creator/Bases/FN/BaseBundle.cs
+++ b/FModel/Creator/Bases/FN/BaseBundle.cs
@@ -8,134 +8,133 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
-{
- public class BaseBundle : UCreator
- {
- private IList _quests;
- private const int _headerHeight = 100;
+namespace FModel.Creator.Bases.FN;
- public BaseBundle(UObject uObject, EIconStyle style) : base(uObject, style)
+public class BaseBundle : UCreator
+{
+ private IList _quests;
+ private const int _headerHeight = 100;
+
+ public BaseBundle(UObject uObject, EIconStyle style) : base(uObject, style)
+ {
+ Width = 1024;
+ Height = _headerHeight;
+ Margin = 0;
+ }
+
+ public override void ParseForInfo()
+ {
+ _quests = new List();
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text.ToUpperInvariant();
+
+ if (Object.TryGetValue(out FStructFallback[] quests, "QuestInfos")) // prout :)
{
- Width = 1024;
- Height = _headerHeight;
- Margin = 0;
+ foreach (var quest in quests)
+ {
+ if (!quest.TryGetValue(out FSoftObjectPath questDefinition, "QuestDefinition")) continue;
+
+ BaseQuest q;
+ var path = questDefinition.AssetPathName.Text;
+ do
+ {
+ if (!Utils.TryLoadObject(path, out UObject uObject)) break;
+
+ q = new BaseQuest(uObject, Style);
+ q.ParseForInfo();
+ _quests.Add(q);
+ path = path.SubstringBeforeWithLast('/') + q.NextQuestName + "." + q.NextQuestName;
+ } while (!string.IsNullOrEmpty(q.NextQuestName));
+ }
}
- public override void ParseForInfo()
+ if (Object.TryGetValue(out FStructFallback[] completionRewards, "BundleCompletionRewards"))
{
- _quests = new List();
-
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text.ToUpperInvariant();
-
- if (Object.TryGetValue(out FStructFallback[] quests, "QuestInfos")) // prout :)
+ foreach (var completionReward in completionRewards)
{
- foreach (var quest in quests)
+ if (!completionReward.TryGetValue(out int completionCount, "CompletionCount") ||
+ !completionReward.TryGetValue(out FStructFallback[] rewards, "Rewards")) continue;
+
+ foreach (var reward in rewards)
{
- if (!quest.TryGetValue(out FSoftObjectPath questDefinition, "QuestDefinition")) continue;
+ if (!reward.TryGetValue(out int quantity, "Quantity") ||
+ !reward.TryGetValue(out string templateId, "TemplateId") ||
+ !reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition")) continue;
- BaseQuest q;
- var path = questDefinition.AssetPathName.Text;
- do
+ if (!itemDefinition.AssetPathName.IsNone &&
+ !itemDefinition.AssetPathName.Text.Contains("/Items/Tokens/") &&
+ !itemDefinition.AssetPathName.Text.Contains("/Items/Quests"))
{
- if (!Utils.TryLoadObject(path, out UObject uObject)) break;
-
- q = new BaseQuest(uObject, Style);
- q.ParseForInfo();
- _quests.Add(q);
- path = path.SubstringBeforeWithLast('/') + q.NextQuestName + "." + q.NextQuestName;
- } while (!string.IsNullOrEmpty(q.NextQuestName));
- }
- }
-
- if (Object.TryGetValue(out FStructFallback[] completionRewards, "BundleCompletionRewards"))
- {
- foreach (var completionReward in completionRewards)
- {
- if (!completionReward.TryGetValue(out int completionCount, "CompletionCount") ||
- !completionReward.TryGetValue(out FStructFallback[] rewards, "Rewards")) continue;
-
- foreach (var reward in rewards)
+ _quests.Add(new BaseQuest(completionCount, itemDefinition, Style));
+ }
+ else if (!string.IsNullOrWhiteSpace(templateId))
{
- if (!reward.TryGetValue(out int quantity, "Quantity") ||
- !reward.TryGetValue(out string templateId, "TemplateId") ||
- !reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition")) continue;
-
- if (!itemDefinition.AssetPathName.IsNone &&
- !itemDefinition.AssetPathName.Text.Contains("/Items/Tokens/") &&
- !itemDefinition.AssetPathName.Text.Contains("/Items/Quests"))
- {
- _quests.Add(new BaseQuest(completionCount, itemDefinition, Style));
- }
- else if (!string.IsNullOrWhiteSpace(templateId))
- {
- _quests.Add(new BaseQuest(completionCount, quantity, templateId, Style));
- }
+ _quests.Add(new BaseQuest(completionCount, quantity, templateId, Style));
}
}
}
-
- Height += 256 * _quests.Count;
}
- public override SKBitmap[] Draw()
+ Height += 256 * _quests.Count;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawHeader(c);
+ DrawDisplayName(c);
+ DrawQuests(c);
+
+ return new[] { ret };
+ }
+
+ private readonly SKPaint _headerPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.Bundle, TextSize = 50,
+ TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
+ };
+
+ private void DrawHeader(SKCanvas c)
+ {
+ c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
+
+ var background = _quests.Count > 0 ? _quests[0].Background : Background;
+ _headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
+ new[] { background[0].WithAlpha(50), background[1].WithAlpha(50) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
+
+ _headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
+ new[] { SKColors.Black.WithAlpha(25), background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
+ }
+
+ private new void DrawDisplayName(SKCanvas c)
+ {
+ if (string.IsNullOrEmpty(DisplayName)) return;
+
+ _headerPaint.Shader = null;
+ _headerPaint.Color = SKColors.White;
+ while (_headerPaint.MeasureText(DisplayName) > Width)
{
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawHeader(c);
- DrawDisplayName(c);
- DrawQuests(c);
-
- return new []{ret};
+ _headerPaint.TextSize -= 1;
}
- private readonly SKPaint _headerPaint = new()
+ var shaper = new CustomSKShaper(_headerPaint.Typeface);
+ var shapedText = shaper.Shape(DisplayName, _headerPaint);
+ c.DrawShapedText(shaper, DisplayName, (Width - shapedText.Points[^1].X) / 2, _headerHeight / 2 + _headerPaint.TextSize / 2 - 10, _headerPaint);
+ }
+
+ private void DrawQuests(SKCanvas c)
+ {
+ var y = _headerHeight;
+ foreach (var quest in _quests)
{
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.Bundle, TextSize = 50,
- TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
- };
-
- private void DrawHeader(SKCanvas c)
- {
- c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
-
- var background = _quests.Count > 0 ? _quests[0].Background : Background;
- _headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
- new[] {background[0].WithAlpha(50), background[1].WithAlpha(50)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
-
- _headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
- new[] {SKColors.Black.WithAlpha(25), background[1].WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
- }
-
- private new void DrawDisplayName(SKCanvas c)
- {
- if (string.IsNullOrEmpty(DisplayName)) return;
-
- _headerPaint.Shader = null;
- _headerPaint.Color = SKColors.White;
- while (_headerPaint.MeasureText(DisplayName) > Width)
- {
- _headerPaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(_headerPaint.Typeface);
- var shapedText = shaper.Shape(DisplayName, _headerPaint);
- c.DrawShapedText(shaper, DisplayName, (Width - shapedText.Points[^1].X) / 2, _headerHeight / 2 + _headerPaint.TextSize / 2 - 10, _headerPaint);
- }
-
- private void DrawQuests(SKCanvas c)
- {
- var y = _headerHeight;
- foreach (var quest in _quests)
- {
- quest.DrawQuest(c, y);
- y += quest.Height;
- }
+ quest.DrawQuest(c, y);
+ y += quest.Height;
}
}
-}
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseCommunity.cs b/FModel/Creator/Bases/FN/BaseCommunity.cs
index 280dc298..8daf66a4 100644
--- a/FModel/Creator/Bases/FN/BaseCommunity.cs
+++ b/FModel/Creator/Bases/FN/BaseCommunity.cs
@@ -11,231 +11,145 @@ using FModel.ViewModels.ApiEndpoints.Models;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseCommunity : BaseIcon
{
- public class BaseCommunity : BaseIcon
+ private readonly CommunityDesign _design;
+ private string _rarityName;
+ private string _source;
+ private string _season;
+ private bool _lowerDrawn;
+
+ public BaseCommunity(UObject uObject, EIconStyle style, string designName) : base(uObject, style)
{
- private readonly CommunityDesign _design;
- private string _rarityName;
- private string _source;
- private string _season;
- private bool _lowerDrawn;
+ Margin = 0;
+ _lowerDrawn = false;
+ _design = ApplicationService.ApiEndpointView.FModelApi.GetDesign(designName);
+ }
- public BaseCommunity(UObject uObject, EIconStyle style, string designName) : base(uObject, style)
+ public override void ParseForInfo()
+ {
+ ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
+
+ if (Object.TryGetValue(out FPackageIndex series, "Series") && Utils.TryGetPackageIndexExport(series, out UObject export))
+ _rarityName = export.Name;
+ else
+ _rarityName = Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription();
+
+ if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
+ CheckGameplayTags(gameplayTags);
+ if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
+ CosmeticSource = cosmeticItem.Name.ToUpper();
+
+ DisplayName = DisplayName.ToUpper();
+ Description = Description.ToUpper();
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ if (_design == null)
{
- Margin = 0;
- _lowerDrawn = false;
- _design = ApplicationService.ApiEndpointView.FModelApi.GetDesign(designName);
+ base.Draw(c);
+ }
+ else
+ {
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawDescription(c);
+ if (_design.DrawSeason && _design.Fonts.TryGetValue("Season", out var font))
+ DrawToBottom(c, font, _season);
+ if (_design.DrawSource && _design.Fonts.TryGetValue("Source", out font))
+ DrawToBottom(c, font, _source);
+ DrawUserFacingFlags(c, _design.GameplayTags.DrawCustomOnly);
}
- public override void ParseForInfo()
+ return new[] { ret };
+ }
+
+ private void CheckGameplayTags(FGameplayTagContainer gameplayTags)
+ {
+ if (_design == null) return;
+ if (_design.DrawSource)
{
- ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
-
- if (Object.TryGetValue(out FPackageIndex series, "Series") && Utils.TryGetPackageIndexExport(series, out UObject export))
- _rarityName = export.Name;
- else
- _rarityName = Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription();
-
- if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
- CheckGameplayTags(gameplayTags);
- if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
- CosmeticSource = cosmeticItem.Name.ToUpper();
-
- DisplayName = DisplayName.ToUpper();
- Description = Description.ToUpper();
+ if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
+ _source = source.Text["Cosmetics.Source.".Length..].ToUpper();
+ else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
+ _source = action.Text["Athena.ItemAction.".Length..].ToUpper();
}
- public override SKBitmap[] Draw()
+ if (_design.DrawSet && gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
+ Description += GetCosmeticSet(set.Text, _design.DrawSetShort);
+ if (_design.DrawSeason && gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
+ _season = GetCosmeticSeason(season.Text, _design.DrawSeasonShort);
+
+ var triggers = _design.GameplayTags.DrawCustomOnly ? new[] { "Cosmetics.UserFacingFlags." } : new[] { "Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender." };
+ GetUserFacingFlags(gameplayTags.GetAllGameplayTags(triggers));
+ }
+
+ private string GetCosmeticSet(string setName, bool bShort)
+ {
+ return bShort ? setName["Cosmetics.Set.".Length..] : base.GetCosmeticSet(setName);
+ }
+
+ private string GetCosmeticSeason(string seasonNumber, bool bShort)
+ {
+ if (!bShort) return base.GetCosmeticSeason(seasonNumber);
+ var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
+ var number = int.Parse(s);
+
+ switch (number)
{
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- if (_design == null)
- {
- base.Draw(c);
- }
- else
- {
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawDescription(c);
- if (_design.DrawSeason && _design.Fonts.TryGetValue("Season", out var font))
- DrawToBottom(c, font, _season);
- if (_design.DrawSource && _design.Fonts.TryGetValue("Source", out font))
- DrawToBottom(c, font, _source);
- DrawUserFacingFlags(c, _design.GameplayTags.DrawCustomOnly);
- }
-
- return new []{ret};
+ case 10:
+ s = "X";
+ break;
+ case > 18:
+ number += 2;
+ s = number.ToString();
+ break;
}
- private void CheckGameplayTags(FGameplayTagContainer gameplayTags)
+ return $"C{number / 10 + 1} S{s[^1..]}";
+ }
+
+ private new void DrawBackground(SKCanvas c)
+ {
+ if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
{
- if (_design == null) return;
- if (_design.DrawSource)
- {
- if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
- _source = source.Text["Cosmetics.Source.".Length..].ToUpper();
- else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
- _source = action.Text["Athena.ItemAction.".Length..].ToUpper();
- }
-
- if (_design.DrawSet && gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
- Description += GetCosmeticSet(set.Text, _design.DrawSetShort);
- if (_design.DrawSeason && gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
- _season = GetCosmeticSeason(season.Text, _design.DrawSeasonShort);
-
- var triggers = _design.GameplayTags.DrawCustomOnly ? new[] {"Cosmetics.UserFacingFlags."} : new[] {"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."};
- GetUserFacingFlags(gameplayTags.GetAllGameplayTags(triggers));
+ c.DrawBitmap(rarity.Background, 0, 0, ImagePaint);
+ c.DrawBitmap(rarity.Upper, 0, 0, ImagePaint);
}
-
- private string GetCosmeticSet(string setName, bool bShort)
+ else
{
- return bShort ? setName["Cosmetics.Set.".Length..] : base.GetCosmeticSet(setName);
+ base.DrawBackground(c);
}
+ }
- private string GetCosmeticSeason(string seasonNumber, bool bShort)
+ private new void DrawTextBackground(SKCanvas c)
+ {
+ if (!_lowerDrawn && string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
+
+ _lowerDrawn = true;
+ if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
{
- if (!bShort) return base.GetCosmeticSeason(seasonNumber);
- var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
- var number = int.Parse(s);
-
- switch (number)
- {
- case 10:
- s = "X";
- break;
- case > 18:
- number += 2;
- s = number.ToString();
- break;
- }
-
- return $"C{number / 10 + 1} S{s[^1..]}";
+ c.DrawBitmap(rarity.Lower, 0, 0, ImagePaint);
}
-
- private new void DrawBackground(SKCanvas c)
+ else
{
- if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
- {
- c.DrawBitmap(rarity.Background, 0, 0, ImagePaint);
- c.DrawBitmap(rarity.Upper, 0, 0, ImagePaint);
- }
- else
- {
- base.DrawBackground(c);
- }
+ base.DrawTextBackground(c);
}
+ }
- private new void DrawTextBackground(SKCanvas c)
+ private new void DrawDisplayName(SKCanvas c)
+ {
+ if (string.IsNullOrEmpty(DisplayName)) return;
+ if (_design.Fonts.TryGetValue(nameof(DisplayName), out var font))
{
- if (!_lowerDrawn && string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
-
- _lowerDrawn = true;
- if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
- {
- c.DrawBitmap(rarity.Lower, 0, 0, ImagePaint);
- }
- else
- {
- base.DrawTextBackground(c);
- }
- }
-
- private new void DrawDisplayName(SKCanvas c)
- {
- if (string.IsNullOrEmpty(DisplayName)) return;
- if (_design.Fonts.TryGetValue(nameof(DisplayName), out var font))
- {
- DisplayNamePaint.TextSize = font.FontSize;
- DisplayNamePaint.TextScaleX = font.FontScale;
- DisplayNamePaint.Color = font.FontColor;
- DisplayNamePaint.TextSkewX = font.SkewValue;
- DisplayNamePaint.TextAlign = font.Alignment;
- if (font.ShadowValue > 0)
- DisplayNamePaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
- if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
- font.Typeface.TryGetValue(ELanguage.English, out path))
- DisplayNamePaint.Typeface = Utils.Typefaces.OnTheFly(path);
-
- while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
- {
- DisplayNamePaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
- var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
- var x = font.Alignment switch
- {
- SKTextAlign.Center => (Width - shapedText.Points[^1].X) / 2f,
- _ => font.X
- };
-
- c.DrawShapedText(shaper, DisplayName, x, font.Y, DisplayNamePaint);
- }
- else
- {
- base.DrawDisplayName(c);
- }
- }
-
- private new void DrawDescription(SKCanvas c)
- {
- if (string.IsNullOrEmpty(Description)) return;
- if (_design.Fonts.TryGetValue(nameof(Description), out var font))
- {
- DescriptionPaint.TextSize = font.FontSize;
- DescriptionPaint.TextScaleX = font.FontScale;
- DescriptionPaint.Color = font.FontColor;
- DescriptionPaint.TextSkewX = font.SkewValue;
- DescriptionPaint.TextAlign = font.Alignment;
- if (font.ShadowValue > 0)
- DescriptionPaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
- if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
- font.Typeface.TryGetValue(ELanguage.English, out path))
- DescriptionPaint.Typeface = Utils.Typefaces.OnTheFly(path);
-
- while (DescriptionPaint.MeasureText(Description) > Width - Margin * 2)
- {
- DescriptionPaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
- var shapedText = shaper.Shape(Description, DescriptionPaint);
- var x = font.Alignment switch
- {
- SKTextAlign.Center => (Width - shapedText.Points[^1].X) / 2f,
- _ => font.X
- };
-
- if (font.MaxLineCount < 2)
- {
- c.DrawShapedText(shaper, Description, x, font.Y, DescriptionPaint);
- }
- else
- {
- Utils.DrawMultilineText(c, Description, Width - Margin * 2, Margin, DescriptionPaint.TextAlign,
- new SKRect(Margin, font.Y, Width - Margin, Height), DescriptionPaint, out _);
- }
- }
- else
- {
- base.DrawDescription(c);
- }
- }
-
- private void DrawToBottom(SKCanvas c, FontDesign font, string text)
- {
- if (string.IsNullOrEmpty(text)) return;
- if (!_lowerDrawn)
- {
- _lowerDrawn = true;
- DrawTextBackground(c);
- }
-
DisplayNamePaint.TextSize = font.FontSize;
DisplayNamePaint.TextScaleX = font.FontScale;
DisplayNamePaint.Color = font.FontColor;
@@ -247,30 +161,115 @@ namespace FModel.Creator.Bases.FN
font.Typeface.TryGetValue(ELanguage.English, out path))
DisplayNamePaint.Typeface = Utils.Typefaces.OnTheFly(path);
+ while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
+ {
+ DisplayNamePaint.TextSize -= 1;
+ }
+
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
- var shapedText = shaper.Shape(text, DisplayNamePaint);
+ var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
var x = font.Alignment switch
{
SKTextAlign.Center => (Width - shapedText.Points[^1].X) / 2f,
- SKTextAlign.Right => font.X - DisplayNamePaint.MeasureText(text),
_ => font.X
};
- c.DrawShapedText(shaper, text, x, font.Y, DisplayNamePaint);
+ c.DrawShapedText(shaper, DisplayName, x, font.Y, DisplayNamePaint);
}
-
- private void DrawUserFacingFlags(SKCanvas c, bool customOnly)
+ else
{
- if (UserFacingFlags == null || UserFacingFlags.Count < 1) return;
- if (customOnly)
+ base.DrawDisplayName(c);
+ }
+ }
+
+ private new void DrawDescription(SKCanvas c)
+ {
+ if (string.IsNullOrEmpty(Description)) return;
+ if (_design.Fonts.TryGetValue(nameof(Description), out var font))
+ {
+ DescriptionPaint.TextSize = font.FontSize;
+ DescriptionPaint.TextScaleX = font.FontScale;
+ DescriptionPaint.Color = font.FontColor;
+ DescriptionPaint.TextSkewX = font.SkewValue;
+ DescriptionPaint.TextAlign = font.Alignment;
+ if (font.ShadowValue > 0)
+ DescriptionPaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
+ if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
+ font.Typeface.TryGetValue(ELanguage.English, out path))
+ DescriptionPaint.Typeface = Utils.Typefaces.OnTheFly(path);
+
+ while (DescriptionPaint.MeasureText(Description) > Width - Margin * 2)
{
- c.DrawBitmap(_design.GameplayTags.Custom, 0, 0, ImagePaint);
+ DescriptionPaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
+ var shapedText = shaper.Shape(Description, DescriptionPaint);
+ var x = font.Alignment switch
+ {
+ SKTextAlign.Center => (Width - shapedText.Points[^1].X) / 2f,
+ _ => font.X
+ };
+
+ if (font.MaxLineCount < 2)
+ {
+ c.DrawShapedText(shaper, Description, x, font.Y, DescriptionPaint);
}
else
{
- // add size to api
- // draw
+ Utils.DrawMultilineText(c, Description, Width - Margin * 2, Margin, DescriptionPaint.TextAlign,
+ new SKRect(Margin, font.Y, Width - Margin, Height), DescriptionPaint, out _);
}
}
+ else
+ {
+ base.DrawDescription(c);
+ }
}
-}
+
+ private void DrawToBottom(SKCanvas c, FontDesign font, string text)
+ {
+ if (string.IsNullOrEmpty(text)) return;
+ if (!_lowerDrawn)
+ {
+ _lowerDrawn = true;
+ DrawTextBackground(c);
+ }
+
+ DisplayNamePaint.TextSize = font.FontSize;
+ DisplayNamePaint.TextScaleX = font.FontScale;
+ DisplayNamePaint.Color = font.FontColor;
+ DisplayNamePaint.TextSkewX = font.SkewValue;
+ DisplayNamePaint.TextAlign = font.Alignment;
+ if (font.ShadowValue > 0)
+ DisplayNamePaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
+ if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
+ font.Typeface.TryGetValue(ELanguage.English, out path))
+ DisplayNamePaint.Typeface = Utils.Typefaces.OnTheFly(path);
+
+ var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
+ var shapedText = shaper.Shape(text, DisplayNamePaint);
+ var x = font.Alignment switch
+ {
+ SKTextAlign.Center => (Width - shapedText.Points[^1].X) / 2f,
+ SKTextAlign.Right => font.X - DisplayNamePaint.MeasureText(text),
+ _ => font.X
+ };
+
+ c.DrawShapedText(shaper, text, x, font.Y, DisplayNamePaint);
+ }
+
+ private void DrawUserFacingFlags(SKCanvas c, bool customOnly)
+ {
+ if (UserFacingFlags == null || UserFacingFlags.Count < 1) return;
+ if (customOnly)
+ {
+ c.DrawBitmap(_design.GameplayTags.Custom, 0, 0, ImagePaint);
+ }
+ else
+ {
+ // add size to api
+ // draw
+ }
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseIcon.cs b/FModel/Creator/Bases/FN/BaseIcon.cs
index 931b7ee3..dd00cb57 100644
--- a/FModel/Creator/Bases/FN/BaseIcon.cs
+++ b/FModel/Creator/Bases/FN/BaseIcon.cs
@@ -16,272 +16,269 @@ using CUE4Parse_Fortnite.Enums;
using FModel.Settings;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseIcon : UCreator
{
- 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 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 GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
+
+ // preview
+ 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", "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", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText description, "Description", "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) };
}
- public void ParseForReward(bool isUsingDisplayAsset)
+ Description = Utils.RemoveHtmlTags(Description);
+ }
+
+ public override void ParseForInfo()
+ {
+ ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
+
+ 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)
{
- // rarity
- if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series);
- else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
+ 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;
+ }
+ }
- // preview
- 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", "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);
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
- // text
- if (Object.TryGetValue(out FText displayName, "DisplayName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText description, "Description", "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");
+ Draw(c);
- // 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"))
+ return new[] { ret };
+ }
+
+ private void GetSeries(FPackageIndex s)
+ {
+ if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
+
+ GetSeries(export);
+ }
+
+ protected void GetSeries(UObject uObject)
+ {
+ if (uObject is UTexture2D texture2D)
+ {
+ SeriesBackground = texture2D.Decode();
+ 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)
{
- Background = new[] {SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex)};
- Border = new[] {SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex)};
- }
+ if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground"))
+ continue;
- Description = Utils.RemoveHtmlTags(Description);
- }
-
- public override void ParseForInfo()
- {
- ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
-
- 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;
+ Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
}
}
+ }
- public override SKBitmap[] Draw()
+ 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"))
{
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
+ Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
+ Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
+ }
+ }
- Draw(c);
+ protected string GetCosmeticSet(string setName)
+ {
+ if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets))
+ return string.Empty;
- return new []{ret};
+ 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_NotRich", "\nPart of the {0} set.");
+ return string.Format(format, name);
+ }
+
+ protected string GetCosmeticSeason(string seasonNumber)
+ {
+ var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
+ var number = int.Parse(s);
+
+ switch (number)
+ {
+ case 10:
+ s = "X";
+ break;
+ case > 18:
+ number += 2;
+ s = number.ToString();
+ break;
}
- private void GetSeries(FPackageIndex s)
+ var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
+ var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}>.");
+ if (number <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s)));
+
+ var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
+ var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
+ var d = string.Format(chapterFormat, string.Format(chapter, number / 10 + 1), string.Format(season, s[^1..]));
+ return Utils.RemoveHtmlTags(string.Format(introduced, d));
+ }
+
+ private 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 (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
-
- GetSeries(export);
- }
-
- protected void GetSeries(UObject uObject)
- {
- if (uObject is UTexture2D texture2D)
+ if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
{
- SeriesBackground = texture2D.Decode();
- return;
+ 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);
}
-
- if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture"))
+ else
{
- 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)
+ foreach (var category in tertiaryCategories)
{
- 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_NotRich", "\nPart of the {0} set.");
- return string.Format(format, name);
- }
-
- protected string GetCosmeticSeason(string seasonNumber)
- {
- var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
- var number = int.Parse(s);
-
- switch (number)
- {
- case 10:
- s = "X";
- break;
- case > 18:
- number += 2;
- s = number.ToString();
- break;
- }
-
- var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
- var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in {0}>.");
- if (number <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s)));
-
- var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
- var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
- var d = string.Format(chapterFormat, string.Format(chapter, number / 10 + 1), string.Format(season, s[^1..]));
- return Utils.RemoveHtmlTags(string.Format(introduced, d));
- }
-
- private 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))
{
- 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);
- }
+ UserFacingFlags[flag] = Utils.GetBitmap(texture);
}
}
}
}
+ }
- private void DrawUserFacingFlags(SKCanvas c)
+ 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))
{
- if (UserFacingFlags == null) return;
-
- const int size = 25;
- var x = Margin * (int) 2.5;
- foreach (var flag in UserFacingFlags.Values)
- {
- if (flag == null) continue;
-
- c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint);
- x += size;
- }
+ c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint);
+ x += size;
}
}
-}
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseIconStats.cs b/FModel/Creator/Bases/FN/BaseIconStats.cs
index c68e8dc3..a23e02fc 100644
--- a/FModel/Creator/Bases/FN/BaseIconStats.cs
+++ b/FModel/Creator/Bases/FN/BaseIconStats.cs
@@ -13,280 +13,279 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseIconStats : BaseIcon
{
- public class BaseIconStats : BaseIcon
+ private readonly IList _statistics;
+ private const int _headerHeight = 128;
+ private bool _screenLayer;
+
+ public BaseIconStats(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private readonly IList _statistics;
- private const int _headerHeight = 128;
- private bool _screenLayer;
-
- public BaseIconStats(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- Width = 1024;
- Height = _headerHeight;
- Margin = 0;
- _statistics = new List();
- _screenLayer = uObject.ExportType.Equals("FortAccoladeItemDefinition", StringComparison.OrdinalIgnoreCase);
- DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
- }
-
- public override void ParseForInfo()
- {
- base.ParseForInfo();
- DisplayName = DisplayName.ToUpperInvariant();
-
- if (Object.TryGetValue(out FName accoladeType, "AccoladeType") &&
- accoladeType.Text.Equals("EFortAccoladeType::Medal", StringComparison.OrdinalIgnoreCase))
- {
- _screenLayer = false;
- }
-
- if (Object.TryGetValue(out FGameplayTagContainer poiLocations, "POILocations") &&
- Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData.QuestIndicatorData", out UObject uObject) &&
- uObject.TryGetValue(out FStructFallback[] challengeMapPoiData, "ChallengeMapPoiData"))
- {
- foreach (var location in poiLocations)
- {
- var locationName = "Unknown";
- foreach (var poi in challengeMapPoiData)
- {
- if (!poi.TryGetValue(out FStructFallback locationTag, "LocationTag") || !locationTag.TryGetValue(out FName tagName, "TagName") ||
- !tagName.Text.Equals(location.Text, StringComparison.OrdinalIgnoreCase) || !poi.TryGetValue(out FText text, "Text")) continue;
-
- locationName = text.Text;
- break;
- }
-
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "B0C091D7409B1657423C5F97E9CF4C77", "LOCATION NAME"), locationName.ToUpper()));
- }
- }
-
- if (Object.TryGetValue(out FStructFallback maxStackSize, "MaxStackSize"))
- {
- if (maxStackSize.TryGetValue(out float v, "Value") && v > 0)
- {
- _statistics.Add(new IconStat("Max Stack", v , 15));
- }
- else if (TryGetCurveTableStat(maxStackSize, out var s))
- {
- _statistics.Add(new IconStat("Max Stack", s , 15));
- }
- }
-
- if (Object.TryGetValue(out FStructFallback xpRewardAmount, "XpRewardAmount") && TryGetCurveTableStat(xpRewardAmount, out var x))
- {
- _statistics.Add(new IconStat("XP Amount", x));
- }
-
- if (Object.TryGetValue(out FStructFallback weaponStatHandle, "WeaponStatHandle") &&
- weaponStatHandle.TryGetValue(out FName weaponRowName, "RowName") &&
- weaponStatHandle.TryGetValue(out UDataTable dataTable, "DataTable") &&
- dataTable.TryGetDataTableRow(weaponRowName.Text, StringComparison.OrdinalIgnoreCase, out var weaponRowValue))
- {
- if (weaponRowValue.TryGetValue(out int bpc, "BulletsPerCartridge"))
- {
- var multiplier = bpc != 0f ? bpc : 1;
- if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f)
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 200));
- }
-
- if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"))
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 200));
- }
- }
-
- if (weaponRowValue.TryGetValue(out int clipSize, "ClipSize") && clipSize != 0)
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), clipSize, 50));
- }
-
- if (weaponRowValue.TryGetValue(out float firingRate, "FiringRate") && firingRate != 0f)
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), firingRate, 15));
- }
-
- if (weaponRowValue.TryGetValue(out float armTime, "ArmTime") && armTime != 0f)
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "3BFEB8BD41A677CC5F45B9A90D6EAD6F", "Arming Delay"), armTime, 125));
- }
-
- if (weaponRowValue.TryGetValue(out float reloadTime, "ReloadTime") && reloadTime != 0f)
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time"), reloadTime, 15));
- }
-
- if ((Object.ExportType.Equals("FortContextTrapItemDefinition", StringComparison.OrdinalIgnoreCase) ||
- Object.ExportType.Equals("FortTrapItemDefinition", StringComparison.OrdinalIgnoreCase)) &&
- weaponRowValue.TryGetValue(out UDataTable durabilityTable, "Durability") &&
- weaponRowValue.TryGetValue(out FName durabilityRowName, "DurabilityRowName") &&
- durabilityTable.TryGetDataTableRow(durabilityRowName.Text, StringComparison.OrdinalIgnoreCase, out var durability) &&
- durability.TryGetValue(out int duraByRarity, Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription()))
- {
- _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6FA2882140CB69DE32FD73A392F0585B", "Durability"), duraByRarity, 20));
- }
- }
-
- if (!string.IsNullOrEmpty(Description))
- Height += 40 + (int) _informationPaint.TextSize * Utils.SplitLines(Description, _informationPaint, Width - 20).Count;
- Height += 50 * _statistics.Count;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawHeader(c);
- DrawDisplayName(c);
- DrawStatistics(c);
-
- return new []{ret};
- }
-
- private bool TryGetCurveTableStat(FStructFallback property, out float statValue)
- {
- if (property.TryGetValue(out FStructFallback curve, "Curve") &&
- curve.TryGetValue(out FName rowName, "RowName") &&
- curve.TryGetValue(out UCurveTable curveTable, "CurveTable") &&
- curveTable.TryFindCurve(rowName, out var rowValue) &&
- rowValue is FSimpleCurve s && s.Keys.Length > 0)
- {
- statValue = s.Keys[0].Value;
- return true;
- }
-
- statValue = 0F;
- return false;
- }
-
- private readonly SKPaint _informationPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Color = SKColor.Parse("#262630"), TextSize = 16,
- Typeface = Utils.Typefaces.Description
- };
-
- private void DrawHeader(SKCanvas c)
- {
- c.DrawRect(new SKRect(0, 0, Width, Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
- new[] {Background[0].WithAlpha(180), Background[1].WithAlpha(220)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(_headerHeight, 0, Width, _headerHeight), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
- new[] {SKColor.Parse("#262630"), SKColor.Parse("#1f1f26")}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, _headerHeight, Width, Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
- new[] {SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
- using var rect = new SKPath {FillType = SKPathFillType.EvenOdd};
- rect.MoveTo(0, 0);
- rect.LineTo(_headerHeight + _headerHeight / 3, 0);
- rect.LineTo(_headerHeight, _headerHeight);
- rect.LineTo(0, _headerHeight);
- rect.Close();
- c.DrawPath(rect, _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(_headerHeight / 2, _headerHeight / 2), new SKPoint(_headerHeight / 2 + 100, _headerHeight / 2),
- new[] {SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawPath(rect, _informationPaint);
-
- _informationPaint.Shader = null;
-
- ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
- c.DrawBitmap((Preview ?? DefaultPreview).Resize(_headerHeight), 0, 0, ImagePaint);
- }
-
- private new void DrawDisplayName(SKCanvas c)
- {
- if (string.IsNullOrEmpty(DisplayName)) return;
-
- _informationPaint.TextSize = 50;
- _informationPaint.Color = SKColors.White;
- _informationPaint.Typeface = Utils.Typefaces.Bundle;
- while (_informationPaint.MeasureText(DisplayName) > Width - _headerHeight * 2)
- {
- _informationPaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(_informationPaint.Typeface);
- shaper.Shape(DisplayName, _informationPaint);
- c.DrawShapedText(shaper, DisplayName, _headerHeight + _headerHeight / 3 + 10, _headerHeight / 2 + _informationPaint.TextSize / 3, _informationPaint);
- }
-
- private void DrawStatistics(SKCanvas c)
- {
- var outY = _headerHeight + 25f;
- if (!string.IsNullOrEmpty(Description))
- {
- _informationPaint.TextSize = 16;
- _informationPaint.Color = SKColors.White.WithAlpha(175);
- _informationPaint.Typeface = Utils.Typefaces.Description;
- Utils.DrawMultilineText(c, Description, Width - 40, 0, SKTextAlign.Center,
- new SKRect(20, outY, Width - 20, Height), _informationPaint, out outY);
- outY += 25;
- }
-
- foreach (var stat in _statistics)
- {
- stat.Draw(c, Border[0].WithAlpha(100), Width, _headerHeight, ref outY);
- outY += 50;
- }
- }
+ Width = 1024;
+ Height = _headerHeight;
+ Margin = 0;
+ _statistics = new List();
+ _screenLayer = uObject.ExportType.Equals("FortAccoladeItemDefinition", StringComparison.OrdinalIgnoreCase);
+ DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
}
- public class IconStat
+ public override void ParseForInfo()
{
- private readonly string _statName;
- private readonly object _value;
- private readonly float _maxValue;
+ base.ParseForInfo();
+ DisplayName = DisplayName.ToUpperInvariant();
- public IconStat(string statName, object value, float maxValue = 0)
+ if (Object.TryGetValue(out FName accoladeType, "AccoladeType") &&
+ accoladeType.Text.Equals("EFortAccoladeType::Medal", StringComparison.OrdinalIgnoreCase))
{
- _statName = statName.ToUpperInvariant();
- _value = value;
- _maxValue = maxValue;
+ _screenLayer = false;
}
- private readonly SKPaint _statPaint = new()
+ if (Object.TryGetValue(out FGameplayTagContainer poiLocations, "POILocations") &&
+ Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData.QuestIndicatorData", out UObject uObject) &&
+ uObject.TryGetValue(out FStructFallback[] challengeMapPoiData, "ChallengeMapPoiData"))
{
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- TextSize = 25, Typeface = Utils.Typefaces.DisplayName,
- Color = SKColors.White
- };
-
- public void Draw(SKCanvas c, SKColor sliderColor, int width, int height, ref float y)
- {
- while (_statPaint.MeasureText(_statName) > height * 2 - 40)
+ foreach (var location in poiLocations)
{
- _statPaint.TextSize -= 1;
+ var locationName = "Unknown";
+ foreach (var poi in challengeMapPoiData)
+ {
+ if (!poi.TryGetValue(out FStructFallback locationTag, "LocationTag") || !locationTag.TryGetValue(out FName tagName, "TagName") ||
+ !tagName.Text.Equals(location.Text, StringComparison.OrdinalIgnoreCase) || !poi.TryGetValue(out FText text, "Text")) continue;
+
+ locationName = text.Text;
+ break;
+ }
+
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "B0C091D7409B1657423C5F97E9CF4C77", "LOCATION NAME"), locationName.ToUpper()));
+ }
+ }
+
+ if (Object.TryGetValue(out FStructFallback maxStackSize, "MaxStackSize"))
+ {
+ if (maxStackSize.TryGetValue(out float v, "Value") && v > 0)
+ {
+ _statistics.Add(new IconStat("Max Stack", v, 15));
+ }
+ else if (TryGetCurveTableStat(maxStackSize, out var s))
+ {
+ _statistics.Add(new IconStat("Max Stack", s, 15));
+ }
+ }
+
+ if (Object.TryGetValue(out FStructFallback xpRewardAmount, "XpRewardAmount") && TryGetCurveTableStat(xpRewardAmount, out var x))
+ {
+ _statistics.Add(new IconStat("XP Amount", x));
+ }
+
+ if (Object.TryGetValue(out FStructFallback weaponStatHandle, "WeaponStatHandle") &&
+ weaponStatHandle.TryGetValue(out FName weaponRowName, "RowName") &&
+ weaponStatHandle.TryGetValue(out UDataTable dataTable, "DataTable") &&
+ dataTable.TryGetDataTableRow(weaponRowName.Text, StringComparison.OrdinalIgnoreCase, out var weaponRowValue))
+ {
+ if (weaponRowValue.TryGetValue(out int bpc, "BulletsPerCartridge"))
+ {
+ var multiplier = bpc != 0f ? bpc : 1;
+ if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f)
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 200));
+ }
+
+ if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"))
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 200));
+ }
}
- var shaper = new CustomSKShaper(_statPaint.Typeface);
- shaper.Shape(_statName, _statPaint);
- c.DrawShapedText(shaper, _statName, 50, y + 10, _statPaint);
+ if (weaponRowValue.TryGetValue(out int clipSize, "ClipSize") && clipSize != 0)
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), clipSize, 50));
+ }
- _statPaint.TextAlign = SKTextAlign.Right;
- _statPaint.Typeface = Utils.Typefaces.BundleNumber;
- _statPaint.Color = sliderColor;
- var sliderRight = width - 100 - _statPaint.MeasureText(_value.ToString());
- c.DrawRect(new SKRect(height * 2, y, Math.Min(width - height, sliderRight), y + 5), _statPaint);
+ if (weaponRowValue.TryGetValue(out float firingRate, "FiringRate") && firingRate != 0f)
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), firingRate, 15));
+ }
- _statPaint.Color = SKColors.White;
- c.DrawText(_value.ToString(), new SKPoint(width - 50, y + 10), _statPaint);
+ if (weaponRowValue.TryGetValue(out float armTime, "ArmTime") && armTime != 0f)
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "3BFEB8BD41A677CC5F45B9A90D6EAD6F", "Arming Delay"), armTime, 125));
+ }
- if (_maxValue < 1 || !float.TryParse(_value.ToString(), out var floatValue)) return;
- if (floatValue < 0)
- floatValue = 0;
- var sliderWidth = (sliderRight - height * 2) * (floatValue / _maxValue);
- c.DrawRect(new SKRect(height * 2, y, Math.Min(height * 2 + sliderWidth, sliderRight), y + 5), _statPaint);
+ if (weaponRowValue.TryGetValue(out float reloadTime, "ReloadTime") && reloadTime != 0f)
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time"), reloadTime, 15));
+ }
+
+ if ((Object.ExportType.Equals("FortContextTrapItemDefinition", StringComparison.OrdinalIgnoreCase) ||
+ Object.ExportType.Equals("FortTrapItemDefinition", StringComparison.OrdinalIgnoreCase)) &&
+ weaponRowValue.TryGetValue(out UDataTable durabilityTable, "Durability") &&
+ weaponRowValue.TryGetValue(out FName durabilityRowName, "DurabilityRowName") &&
+ durabilityTable.TryGetDataTableRow(durabilityRowName.Text, StringComparison.OrdinalIgnoreCase, out var durability) &&
+ durability.TryGetValue(out int duraByRarity, Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription()))
+ {
+ _statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6FA2882140CB69DE32FD73A392F0585B", "Durability"), duraByRarity, 20));
+ }
+ }
+
+ if (!string.IsNullOrEmpty(Description))
+ Height += 40 + (int) _informationPaint.TextSize * Utils.SplitLines(Description, _informationPaint, Width - 20).Count;
+ Height += 50 * _statistics.Count;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawHeader(c);
+ DrawDisplayName(c);
+ DrawStatistics(c);
+
+ return new[] { ret };
+ }
+
+ private bool TryGetCurveTableStat(FStructFallback property, out float statValue)
+ {
+ if (property.TryGetValue(out FStructFallback curve, "Curve") &&
+ curve.TryGetValue(out FName rowName, "RowName") &&
+ curve.TryGetValue(out UCurveTable curveTable, "CurveTable") &&
+ curveTable.TryFindCurve(rowName, out var rowValue) &&
+ rowValue is FSimpleCurve s && s.Keys.Length > 0)
+ {
+ statValue = s.Keys[0].Value;
+ return true;
+ }
+
+ statValue = 0F;
+ return false;
+ }
+
+ private readonly SKPaint _informationPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Color = SKColor.Parse("#262630"), TextSize = 16,
+ Typeface = Utils.Typefaces.Description
+ };
+
+ private void DrawHeader(SKCanvas c)
+ {
+ c.DrawRect(new SKRect(0, 0, Width, Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
+ new[] { Background[0].WithAlpha(180), Background[1].WithAlpha(220) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(_headerHeight, 0, Width, _headerHeight), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
+ new[] { SKColor.Parse("#262630"), SKColor.Parse("#1f1f26") }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, _headerHeight, Width, Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
+ new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
+ using var rect = new SKPath { FillType = SKPathFillType.EvenOdd };
+ rect.MoveTo(0, 0);
+ rect.LineTo(_headerHeight + _headerHeight / 3, 0);
+ rect.LineTo(_headerHeight, _headerHeight);
+ rect.LineTo(0, _headerHeight);
+ rect.Close();
+ c.DrawPath(rect, _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(_headerHeight / 2, _headerHeight / 2), new SKPoint(_headerHeight / 2 + 100, _headerHeight / 2),
+ new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawPath(rect, _informationPaint);
+
+ _informationPaint.Shader = null;
+
+ ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
+ c.DrawBitmap((Preview ?? DefaultPreview).Resize(_headerHeight), 0, 0, ImagePaint);
+ }
+
+ private new void DrawDisplayName(SKCanvas c)
+ {
+ if (string.IsNullOrEmpty(DisplayName)) return;
+
+ _informationPaint.TextSize = 50;
+ _informationPaint.Color = SKColors.White;
+ _informationPaint.Typeface = Utils.Typefaces.Bundle;
+ while (_informationPaint.MeasureText(DisplayName) > Width - _headerHeight * 2)
+ {
+ _informationPaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(_informationPaint.Typeface);
+ shaper.Shape(DisplayName, _informationPaint);
+ c.DrawShapedText(shaper, DisplayName, _headerHeight + _headerHeight / 3 + 10, _headerHeight / 2 + _informationPaint.TextSize / 3, _informationPaint);
+ }
+
+ private void DrawStatistics(SKCanvas c)
+ {
+ var outY = _headerHeight + 25f;
+ if (!string.IsNullOrEmpty(Description))
+ {
+ _informationPaint.TextSize = 16;
+ _informationPaint.Color = SKColors.White.WithAlpha(175);
+ _informationPaint.Typeface = Utils.Typefaces.Description;
+ Utils.DrawMultilineText(c, Description, Width - 40, 0, SKTextAlign.Center,
+ new SKRect(20, outY, Width - 20, Height), _informationPaint, out outY);
+ outY += 25;
+ }
+
+ foreach (var stat in _statistics)
+ {
+ stat.Draw(c, Border[0].WithAlpha(100), Width, _headerHeight, ref outY);
+ outY += 50;
}
}
}
+
+public class IconStat
+{
+ private readonly string _statName;
+ private readonly object _value;
+ private readonly float _maxValue;
+
+ public IconStat(string statName, object value, float maxValue = 0)
+ {
+ _statName = statName.ToUpperInvariant();
+ _value = value;
+ _maxValue = maxValue;
+ }
+
+ private readonly SKPaint _statPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ TextSize = 25, Typeface = Utils.Typefaces.DisplayName,
+ Color = SKColors.White
+ };
+
+ public void Draw(SKCanvas c, SKColor sliderColor, int width, int height, ref float y)
+ {
+ while (_statPaint.MeasureText(_statName) > height * 2 - 40)
+ {
+ _statPaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(_statPaint.Typeface);
+ shaper.Shape(_statName, _statPaint);
+ c.DrawShapedText(shaper, _statName, 50, y + 10, _statPaint);
+
+ _statPaint.TextAlign = SKTextAlign.Right;
+ _statPaint.Typeface = Utils.Typefaces.BundleNumber;
+ _statPaint.Color = sliderColor;
+ var sliderRight = width - 100 - _statPaint.MeasureText(_value.ToString());
+ c.DrawRect(new SKRect(height * 2, y, Math.Min(width - height, sliderRight), y + 5), _statPaint);
+
+ _statPaint.Color = SKColors.White;
+ c.DrawText(_value.ToString(), new SKPoint(width - 50, y + 10), _statPaint);
+
+ if (_maxValue < 1 || !float.TryParse(_value.ToString(), out var floatValue)) return;
+ if (floatValue < 0)
+ floatValue = 0;
+ var sliderWidth = (sliderRight - height * 2) * (floatValue / _maxValue);
+ c.DrawRect(new SKRect(height * 2, y, Math.Min(height * 2 + sliderWidth, sliderRight), y + 5), _statPaint);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseItemAccessToken.cs b/FModel/Creator/Bases/FN/BaseItemAccessToken.cs
index 49ac8119..fb621d6f 100644
--- a/FModel/Creator/Bases/FN/BaseItemAccessToken.cs
+++ b/FModel/Creator/Bases/FN/BaseItemAccessToken.cs
@@ -5,96 +5,95 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseItemAccessToken : UCreator
{
- public class BaseItemAccessToken : UCreator
+ private readonly SKBitmap _locked, _unlocked;
+ private string _unlockedDescription, _exportName;
+ private BaseIcon _icon;
+
+ public BaseItemAccessToken(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private readonly SKBitmap _locked, _unlocked;
- private string _unlockedDescription, _exportName;
- private BaseIcon _icon;
-
- public BaseItemAccessToken(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- _unlocked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Unlocked-128.T-Icon-Unlocked-128").Resize(24);
- _locked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Lock-128.T-Icon-Lock-128").Resize(24);
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FPackageIndex accessItem, "access_item") &&
- Utils.TryGetPackageIndexExport(accessItem, out UObject uObject))
- {
- _exportName = uObject.Name;
- _icon = new BaseIcon(uObject, EIconStyle.Default);
- _icon.ParseForReward(false);
- }
-
- if (Object.TryGetValue(out FText displayName, "DisplayName") && displayName.Text != "TBD")
- DisplayName = displayName.Text;
- else
- DisplayName = _icon?.DisplayName;
-
- Description = Object.TryGetValue(out FText description, "Description") ? description.Text : _icon?.Description;
- if (Object.TryGetValue(out FText unlockDescription, "UnlockDescription")) _unlockedDescription = unlockDescription.Text;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- switch (Style)
- {
- case EIconStyle.NoBackground:
- Preview = _icon.Preview;
- DrawPreview(c);
- break;
- case EIconStyle.NoText:
- Preview = _icon.Preview;
- _icon.DrawBackground(c);
- DrawPreview(c);
- break;
- default:
- _icon.DrawBackground(c);
- DrawInformation(c);
- DrawToBottom(c, SKTextAlign.Right, _exportName);
- break;
- }
-
- return new []{ret};
- }
-
- private void DrawInformation(SKCanvas c)
- {
- var size = 45;
- var left = Width / 2;
-
- while (DisplayNamePaint.MeasureText(DisplayName) > Width - _icon.Margin * 2)
- {
- DisplayNamePaint.TextSize = size -= 2;
- }
-
- var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
- var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
- c.DrawShapedText(shaper, DisplayName, left - shapedText.Points[^1].X / 2, _icon.Margin * 8 + size, DisplayNamePaint);
-
- float topBase = _icon.Margin + size * 2;
- if (!string.IsNullOrEmpty(_unlockedDescription))
- {
- c.DrawBitmap(_locked, new SKRect(50, topBase, 50 + _locked.Width, topBase + _locked.Height), ImagePaint);
- Utils.DrawMultilineText(c, _unlockedDescription, Width, _icon.Margin, SKTextAlign.Left,
- new SKRect(70 + _locked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
- }
-
- if (!string.IsNullOrEmpty(Description))
- {
- c.DrawBitmap(_unlocked, new SKRect(50, topBase, 50 + _unlocked.Width, topBase + _unlocked.Height), ImagePaint);
- Utils.DrawMultilineText(c, Description, Width, _icon.Margin, SKTextAlign.Left,
- new SKRect(70 + _unlocked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
- }
-
- var h = Width - _icon.Margin - topBase;
- c.DrawBitmap(_icon.Preview ?? _icon.DefaultPreview, new SKRect(left - h / 2, topBase, left + h / 2, Width - _icon.Margin), ImagePaint);
- }
+ _unlocked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Unlocked-128.T-Icon-Unlocked-128").Resize(24);
+ _locked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Lock-128.T-Icon-Lock-128").Resize(24);
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FPackageIndex accessItem, "access_item") &&
+ Utils.TryGetPackageIndexExport(accessItem, out UObject uObject))
+ {
+ _exportName = uObject.Name;
+ _icon = new BaseIcon(uObject, EIconStyle.Default);
+ _icon.ParseForReward(false);
+ }
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName") && displayName.Text != "TBD")
+ DisplayName = displayName.Text;
+ else
+ DisplayName = _icon?.DisplayName;
+
+ Description = Object.TryGetValue(out FText description, "Description") ? description.Text : _icon?.Description;
+ if (Object.TryGetValue(out FText unlockDescription, "UnlockDescription")) _unlockedDescription = unlockDescription.Text;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ switch (Style)
+ {
+ case EIconStyle.NoBackground:
+ Preview = _icon.Preview;
+ DrawPreview(c);
+ break;
+ case EIconStyle.NoText:
+ Preview = _icon.Preview;
+ _icon.DrawBackground(c);
+ DrawPreview(c);
+ break;
+ default:
+ _icon.DrawBackground(c);
+ DrawInformation(c);
+ DrawToBottom(c, SKTextAlign.Right, _exportName);
+ break;
+ }
+
+ return new[] { ret };
+ }
+
+ private void DrawInformation(SKCanvas c)
+ {
+ var size = 45;
+ var left = Width / 2;
+
+ while (DisplayNamePaint.MeasureText(DisplayName) > Width - _icon.Margin * 2)
+ {
+ DisplayNamePaint.TextSize = size -= 2;
+ }
+
+ var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
+ var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
+ c.DrawShapedText(shaper, DisplayName, left - shapedText.Points[^1].X / 2, _icon.Margin * 8 + size, DisplayNamePaint);
+
+ float topBase = _icon.Margin + size * 2;
+ if (!string.IsNullOrEmpty(_unlockedDescription))
+ {
+ c.DrawBitmap(_locked, new SKRect(50, topBase, 50 + _locked.Width, topBase + _locked.Height), ImagePaint);
+ Utils.DrawMultilineText(c, _unlockedDescription, Width, _icon.Margin, SKTextAlign.Left,
+ new SKRect(70 + _locked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
+ }
+
+ if (!string.IsNullOrEmpty(Description))
+ {
+ c.DrawBitmap(_unlocked, new SKRect(50, topBase, 50 + _unlocked.Width, topBase + _unlocked.Height), ImagePaint);
+ Utils.DrawMultilineText(c, Description, Width, _icon.Margin, SKTextAlign.Left,
+ new SKRect(70 + _unlocked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
+ }
+
+ var h = Width - _icon.Margin - topBase;
+ c.DrawBitmap(_icon.Preview ?? _icon.DefaultPreview, new SKRect(left - h / 2, topBase, left + h / 2, Width - _icon.Margin), ImagePaint);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseMaterialInstance.cs b/FModel/Creator/Bases/FN/BaseMaterialInstance.cs
index f8293614..55e06923 100644
--- a/FModel/Creator/Bases/FN/BaseMaterialInstance.cs
+++ b/FModel/Creator/Bases/FN/BaseMaterialInstance.cs
@@ -5,83 +5,82 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.UObject;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseMaterialInstance : BaseIcon
{
- public class BaseMaterialInstance : BaseIcon
+ public BaseMaterialInstance(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public BaseMaterialInstance(UObject uObject, EIconStyle style) : base(uObject, style)
+ Background = new[] { SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69") };
+ Border = new[] { SKColor.Parse("9092AB") };
+ }
+
+ public override void ParseForInfo()
+ {
+ if (Object is not UMaterialInstanceConstant material) return;
+
+ texture_finding:
+ foreach (var textureParameter in material.TextureParameterValues) // get texture from base material
{
- Background = new[] {SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69")};
- Border = new[] {SKColor.Parse("9092AB")};
- }
-
- public override void ParseForInfo()
- {
- if (Object is not UMaterialInstanceConstant material) return;
-
- texture_finding:
- foreach (var textureParameter in material.TextureParameterValues) // get texture from base material
+ if (!textureParameter.ParameterValue.TryLoad(out var texture) || Preview != null) continue;
+ switch (textureParameter.ParameterInfo.Name.Text)
{
- if (!textureParameter.ParameterValue.TryLoad(out var texture) || Preview != null) continue;
- switch (textureParameter.ParameterInfo.Name.Text)
- {
- case "SeriesTexture":
- GetSeries(texture);
- break;
- case "TextureA":
- case "TextureB":
- case "OfferImage":
- Preview = Utils.GetBitmap(texture);
- break;
- }
- }
-
- while (material.VectorParameterValues.Length == 0 || // try to get color from parent if not found here
- material.VectorParameterValues.All(x => x.ParameterInfo.Name.Text.Equals("FallOff_Color"))) // use parent if it only contains FallOff_Color
- {
- if (material.TryGetValue(out FPackageIndex parent, "Parent"))
- Utils.TryGetPackageIndexExport(parent, out material);
- else return;
-
- if (material == null) return;
- }
-
- if (Preview == null)
- goto texture_finding;
-
- foreach (var vectorParameter in material.VectorParameterValues)
- {
- if (vectorParameter.ParameterValue == null) continue;
- switch (vectorParameter.ParameterInfo.Name.Text)
- {
- case "Background_Color_A":
- Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
- Border[0] = Background[0];
- break;
- case "Background_Color_B": // Border color can be defaulted here in some case where Background_Color_A should be taken from parent but Background_Color_B from base
- Background[1] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
- break;
- }
+ case "SeriesTexture":
+ GetSeries(texture);
+ break;
+ case "TextureA":
+ case "TextureB":
+ case "OfferImage":
+ Preview = Utils.GetBitmap(texture);
+ break;
}
}
- public override SKBitmap[] Draw()
+ while (material.VectorParameterValues.Length == 0 || // try to get color from parent if not found here
+ material.VectorParameterValues.All(x => x.ParameterInfo.Name.Text.Equals("FallOff_Color"))) // use parent if it only contains FallOff_Color
{
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
+ if (material.TryGetValue(out FPackageIndex parent, "Parent"))
+ Utils.TryGetPackageIndexExport(parent, out material);
+ else return;
- switch (Style)
+ if (material == null) return;
+ }
+
+ if (Preview == null)
+ goto texture_finding;
+
+ foreach (var vectorParameter in material.VectorParameterValues)
+ {
+ if (vectorParameter.ParameterValue == null) continue;
+ switch (vectorParameter.ParameterInfo.Name.Text)
{
- case EIconStyle.NoBackground:
- DrawPreview(c);
+ case "Background_Color_A":
+ Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
+ Border[0] = Background[0];
break;
- default:
- DrawBackground(c);
- DrawPreview(c);
+ case "Background_Color_B": // Border color can be defaulted here in some case where Background_Color_A should be taken from parent but Background_Color_B from base
+ Background[1] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
break;
}
-
- return new []{ret};
}
}
-}
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ switch (Style)
+ {
+ case EIconStyle.NoBackground:
+ DrawPreview(c);
+ break;
+ default:
+ DrawBackground(c);
+ DrawPreview(c);
+ break;
+ }
+
+ return new[] { ret };
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseMtxOffer.cs b/FModel/Creator/Bases/FN/BaseMtxOffer.cs
index 89c4c861..68e64762 100644
--- a/FModel/Creator/Bases/FN/BaseMtxOffer.cs
+++ b/FModel/Creator/Bases/FN/BaseMtxOffer.cs
@@ -5,81 +5,80 @@ using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.UObject;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseMtxOffer : UCreator
{
- public class BaseMtxOffer : UCreator
+ public BaseMtxOffer(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public BaseMtxOffer(UObject uObject, EIconStyle style) : base(uObject, style)
+ Background = new[] { SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69") };
+ Border = new[] { SKColor.Parse("9092AB") };
+ }
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FStructFallback typeImage, "DetailsImage", "TileImage") &&
+ typeImage.TryGetValue(out FPackageIndex resource, "ResourceObject"))
{
- Background = new[] {SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69")};
- Border = new[] {SKColor.Parse("9092AB")};
+ Preview = Utils.GetBitmap(resource);
}
- public override void ParseForInfo()
+ if (Object.TryGetValue(out FStructFallback gradient, "Gradient") &&
+ gradient.TryGetValue(out FLinearColor start, "Start") &&
+ gradient.TryGetValue(out FLinearColor stop, "Stop"))
{
- if (Object.TryGetValue(out FStructFallback typeImage, "DetailsImage", "TileImage") &&
- typeImage.TryGetValue(out FPackageIndex resource, "ResourceObject"))
- {
- Preview = Utils.GetBitmap(resource);
- }
+ Background = new[] { SKColor.Parse(start.Hex), SKColor.Parse(stop.Hex) };
+ }
- if (Object.TryGetValue(out FStructFallback gradient, "Gradient") &&
- gradient.TryGetValue(out FLinearColor start, "Start") &&
- gradient.TryGetValue(out FLinearColor stop, "Stop"))
- {
- Background = new[] {SKColor.Parse(start.Hex), SKColor.Parse(stop.Hex)};
- }
+ if (Object.TryGetValue(out FLinearColor background, "Background"))
+ Border = new[] { SKColor.Parse(background.Hex) };
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText shortDescription, "ShortDescription"))
+ Description = shortDescription.Text;
- if (Object.TryGetValue(out FLinearColor background, "Background"))
- Border = new[] {SKColor.Parse(background.Hex)};
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText shortDescription, "ShortDescription"))
- Description = shortDescription.Text;
-
- if (Object.TryGetValue(out FStructFallback[] details, "DetailsAttributes"))
+ if (Object.TryGetValue(out FStructFallback[] details, "DetailsAttributes"))
+ {
+ foreach (var detail in details)
{
- foreach (var detail in details)
+ if (detail.TryGetValue(out FText detailName, "Name"))
{
- if (detail.TryGetValue(out FText detailName, "Name"))
- {
- Description += $"\n- {detailName.Text.TrimEnd()}";
- }
+ Description += $"\n- {detailName.Text.TrimEnd()}";
+ }
- if (detail.TryGetValue(out FText detailValue, "Value") && !string.IsNullOrEmpty(detailValue.Text))
- {
- Description += $" ({detailValue.Text})";
- }
+ if (detail.TryGetValue(out FText detailValue, "Value") && !string.IsNullOrEmpty(detailValue.Text))
+ {
+ Description += $" ({detailValue.Text})";
}
}
-
- Description = Utils.RemoveHtmlTags(Description);
}
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- switch (Style)
- {
- case EIconStyle.NoBackground:
- DrawPreview(c);
- break;
- case EIconStyle.NoText:
- DrawBackground(c);
- DrawPreview(c);
- break;
- default:
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawDescription(c);
- break;
- }
-
- return new []{ret};
- }
+ Description = Utils.RemoveHtmlTags(Description);
}
-}
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ switch (Style)
+ {
+ case EIconStyle.NoBackground:
+ DrawPreview(c);
+ break;
+ case EIconStyle.NoText:
+ DrawBackground(c);
+ DrawPreview(c);
+ break;
+ default:
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawDescription(c);
+ break;
+ }
+
+ return new[] { ret };
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseOfferDisplayData.cs b/FModel/Creator/Bases/FN/BaseOfferDisplayData.cs
index bf4458b1..b1a1808e 100644
--- a/FModel/Creator/Bases/FN/BaseOfferDisplayData.cs
+++ b/FModel/Creator/Bases/FN/BaseOfferDisplayData.cs
@@ -2,39 +2,38 @@ using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Material;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseOfferDisplayData : UCreator
{
- public class BaseOfferDisplayData : UCreator
+ private BaseMaterialInstance[] _offerImages;
+
+ public BaseOfferDisplayData(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private BaseMaterialInstance[] _offerImages;
+ }
- public BaseOfferDisplayData(UObject uObject, EIconStyle style) : base(uObject, style)
+ public override void ParseForInfo()
+ {
+ if (!Object.TryGetValue(out UMaterialInterface[] presentations, "Presentations"))
+ return;
+
+ _offerImages = new BaseMaterialInstance[presentations.Length];
+ for (var i = 0; i < _offerImages.Length; i++)
{
- }
-
- public override void ParseForInfo()
- {
- if (!Object.TryGetValue(out UMaterialInterface[] presentations, "Presentations"))
- return;
-
- _offerImages = new BaseMaterialInstance[presentations.Length];
- for (int i = 0; i < _offerImages.Length; i++)
- {
- var offerImage = new BaseMaterialInstance(presentations[i], Style);
- offerImage.ParseForInfo();
- _offerImages[i] = offerImage;
- }
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap[_offerImages.Length];
- for (int i = 0; i < ret.Length; i++)
- {
- ret[i] = _offerImages[i].Draw()[0];
- }
-
- return ret;
+ var offerImage = new BaseMaterialInstance(presentations[i], Style);
+ offerImage.ParseForInfo();
+ _offerImages[i] = offerImage;
}
}
-}
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap[_offerImages.Length];
+ for (var i = 0; i < ret.Length; i++)
+ {
+ ret[i] = _offerImages[i].Draw()[0];
+ }
+
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BasePlaylist.cs b/FModel/Creator/Bases/FN/BasePlaylist.cs
index 9637d453..fe7fd7c9 100644
--- a/FModel/Creator/Bases/FN/BasePlaylist.cs
+++ b/FModel/Creator/Bases/FN/BasePlaylist.cs
@@ -6,73 +6,72 @@ using FModel.Services;
using FModel.ViewModels;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BasePlaylist : UCreator
{
- public class BasePlaylist : UCreator
+ private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView;
+ private SKBitmap _missionIcon;
+
+ public BasePlaylist(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView;
- private SKBitmap _missionIcon;
-
- public BasePlaylist(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- Margin = 0;
- Width = 1024;
- Height = 512;
- Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Tiles/T_Athena_Tile_Matchmaking_Default.T_Athena_Tile_Matchmaking_Default");
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FText displayName, "UIDisplayName", "DisplayName"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText description, "UIDescription", "Description"))
- Description = description.Text;
- if (Object.TryGetValue(out UTexture2D missionIcon, "MissionIcon"))
- _missionIcon = Utils.GetBitmap(missionIcon).Resize(25);
-
- if (!Object.TryGetValue(out FName playlistName, "PlaylistName") || string.IsNullOrWhiteSpace(playlistName.Text))
- return;
-
- var playlist = _apiEndpointView.FortniteApi.GetPlaylist(playlistName.Text);
- if (!playlist.IsSuccess || !playlist.Data.Images.HasShowcase ||
- !_apiEndpointView.FortniteApi.TryGetBytes(playlist.Data.Images.Showcase, out var image))
- return;
-
- Preview = Utils.GetBitmap(image).ResizeWithRatio(1024, 512);
- Width = Preview.Width;
- Height = Preview.Height;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- switch (Style)
- {
- case EIconStyle.NoBackground:
- DrawPreview(c);
- break;
- case EIconStyle.NoText:
- DrawPreview(c);
- DrawMissionIcon(c);
- break;
- default:
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawDescription(c);
- DrawMissionIcon(c);
- break;
- }
-
- return new []{ret};
- }
-
- private void DrawMissionIcon(SKCanvas c)
- {
- if (_missionIcon == null) return;
- c.DrawBitmap(_missionIcon, new SKPoint(5, 5), ImagePaint);
- }
+ Margin = 0;
+ Width = 1024;
+ Height = 512;
+ Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Tiles/T_Athena_Tile_Matchmaking_Default.T_Athena_Tile_Matchmaking_Default");
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FText displayName, "UIDisplayName", "DisplayName"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText description, "UIDescription", "Description"))
+ Description = description.Text;
+ if (Object.TryGetValue(out UTexture2D missionIcon, "MissionIcon"))
+ _missionIcon = Utils.GetBitmap(missionIcon).Resize(25);
+
+ if (!Object.TryGetValue(out FName playlistName, "PlaylistName") || string.IsNullOrWhiteSpace(playlistName.Text))
+ return;
+
+ var playlist = _apiEndpointView.FortniteApi.GetPlaylist(playlistName.Text);
+ if (!playlist.IsSuccess || !playlist.Data.Images.HasShowcase ||
+ !_apiEndpointView.FortniteApi.TryGetBytes(playlist.Data.Images.Showcase, out var image))
+ return;
+
+ Preview = Utils.GetBitmap(image).ResizeWithRatio(1024, 512);
+ Width = Preview.Width;
+ Height = Preview.Height;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ switch (Style)
+ {
+ case EIconStyle.NoBackground:
+ DrawPreview(c);
+ break;
+ case EIconStyle.NoText:
+ DrawPreview(c);
+ DrawMissionIcon(c);
+ break;
+ default:
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawDescription(c);
+ DrawMissionIcon(c);
+ break;
+ }
+
+ return new[] { ret };
+ }
+
+ private void DrawMissionIcon(SKCanvas c)
+ {
+ if (_missionIcon == null) return;
+ c.DrawBitmap(_missionIcon, new SKPoint(5, 5), ImagePaint);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseQuest.cs b/FModel/Creator/Bases/FN/BaseQuest.cs
index 23c00422..0a35dc21 100644
--- a/FModel/Creator/Bases/FN/BaseQuest.cs
+++ b/FModel/Creator/Bases/FN/BaseQuest.cs
@@ -12,256 +12,255 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseQuest : BaseIcon
{
- public class BaseQuest : BaseIcon
+ private int _count;
+ private Reward _reward;
+ private readonly bool _screenLayer;
+ private readonly string[] _unauthorizedReward = { "Token", "ChallengeBundle", "GiftBox" };
+
+ public string NextQuestName { get; private set; }
+
+ public BaseQuest(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private int _count;
- private Reward _reward;
- private readonly bool _screenLayer;
- private readonly string[] _unauthorizedReward = {"Token", "ChallengeBundle", "GiftBox"};
-
- public string NextQuestName { get; private set; }
-
- public BaseQuest(UObject uObject, EIconStyle style) : base(uObject, style)
+ Margin = 0;
+ Width = 1024;
+ Height = 256;
+ DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
+ if (uObject != null)
{
- Margin = 0;
- Width = 1024;
- Height = 256;
- DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
- if (uObject != null)
- {
- _screenLayer = uObject.ExportType.Equals("FortFeatItemDefinition", StringComparison.OrdinalIgnoreCase);
- }
- }
-
- private BaseQuest(int completionCount, EIconStyle style) : this(null, style) // completion
- {
- var description = completionCount < 0 ?
- Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat_All", "Complete all {0} challenges> to earn the reward item") :
- Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat", "Complete any {0} challenges> to earn the reward item");
-
- DisplayName = ReformatString(description, completionCount.ToString(), completionCount < 0);
- }
-
- public BaseQuest(int completionCount, FSoftObjectPath itemDefinition, EIconStyle style) : this(completionCount, style) // completion
- {
- _reward = Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject) ? new Reward(uObject) : new Reward();
- }
-
- public BaseQuest(int completionCount, int quantity, string reward, EIconStyle style) : this(completionCount, style) // completion
- {
- _reward = new Reward(quantity, reward);
- }
-
- public override void ParseForInfo()
- {
- ParseForReward(false);
-
- if (Object.TryGetValue(out FStructFallback urgentQuestData, "UrgentQuestData"))
- {
- if (urgentQuestData.TryGetValue(out FText eventTitle, "EventTitle"))
- DisplayName = eventTitle.Text;
- if (urgentQuestData.TryGetValue(out FText eventDescription, "EventDescription"))
- Description = eventDescription.Text;
- if (urgentQuestData.TryGetValue(out FPackageIndex alertIcon, "AlertIcon", "BountyPriceImage"))
- Preview = Utils.GetBitmap(alertIcon);
- }
- else
- {
- Description = ShortDescription;
- if (Object.TryGetValue(out FText completionText, "CompletionText"))
- Description += "\n" + completionText.Text;
- if (Object.TryGetValue(out FSoftObjectPath tandemCharacterData, "TandemCharacterData") &&
- Utils.TryLoadObject(tandemCharacterData.AssetPathName.Text, out UObject uObject) &&
- uObject.TryGetValue(out FSoftObjectPath tandemIcon, "EntryListIcon", "ToastIcon") &&
- Utils.TryLoadObject(tandemIcon.AssetPathName.Text, out UObject iconObject))
- {
- Preview = iconObject switch
- {
- UTexture2D text => Utils.GetBitmap(text),
- UMaterialInstanceConstant mat => Utils.GetBitmap(mat),
- _ => Preview
- };
- }
- }
-
- if (Object.TryGetValue(out int objectiveCompletionCount, "ObjectiveCompletionCount"))
- _count = objectiveCompletionCount;
-
- if (Object.TryGetValue(out FStructFallback[] objectives, "Objectives") && objectives.Length > 0)
- {
- // actual description doesn't exist
- if (string.IsNullOrEmpty(Description) && objectives[0].TryGetValue(out FText description, "Description"))
- Description = description.Text;
-
- // ObjectiveCompletionCount doesn't exist
- if (_count == 0)
- {
- if (objectives[0].TryGetValue(out int count, "Count") && count > 1)
- _count = count;
- else
- _count = objectives.Length;
- }
- }
-
- if (Object.TryGetValue(out FStructFallback[] rewards, "Rewards"))
- {
- foreach (var reward in rewards)
- {
- if (!reward.TryGetValue(out FStructFallback itemPrimaryAssetId, "ItemPrimaryAssetId") ||
- !reward.TryGetValue(out int quantity, "Quantity")) continue;
-
- if (!itemPrimaryAssetId.TryGetValue(out FStructFallback primaryAssetType, "PrimaryAssetType") ||
- !itemPrimaryAssetId.TryGetValue(out FName primaryAssetName, "PrimaryAssetName") ||
- !primaryAssetType.TryGetValue(out FName name, "Name")) continue;
-
- if (name.Text.Equals("Quest", StringComparison.OrdinalIgnoreCase))
- {
- NextQuestName = primaryAssetName.Text;
- }
- else if (!_unauthorizedReward.Contains(name.Text))
- {
- _reward = new Reward(quantity, $"{name}:{primaryAssetName}");
- }
- }
- }
-
- if (_reward == null && Object.TryGetValue(out UDataTable rewardsTable, "RewardsTable"))
- {
- if (rewardsTable.TryGetDataTableRow("Default", StringComparison.InvariantCulture, out var row))
- {
- if (row.TryGetValue(out FName templateId, "TemplateId") &&
- row.TryGetValue(out int quantity, "Quantity"))
- {
- _reward = new Reward(quantity, templateId);
- }
- }
- }
-
- if (_reward == null && Object.TryGetValue(out FStructFallback[] hiddenRewards, "HiddenRewards"))
- {
- foreach (var hiddenReward in hiddenRewards)
- {
- if (!hiddenReward.TryGetValue(out FName templateId, "TemplateId") ||
- !hiddenReward.TryGetValue(out int quantity, "Quantity")) continue;
-
- _reward = new Reward(quantity, templateId);
- break;
- }
- }
-
- _reward ??= new Reward();
- }
-
- public void DrawQuest(SKCanvas c, int y)
- {
- DrawBackground(c, y);
- DrawPreview(c, y);
- DrawTexts(c, y);
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawQuest(c, 0);
-
- return new []{ret};
- }
-
- private string ReformatString(string s, string completionCount, bool isAll)
- {
- s = s.Replace("({0})", "{0}").Replace("{QuestNumber}", "{0}>");
- var index = s.IndexOf("{0}|plural(", StringComparison.OrdinalIgnoreCase);
- if (index > -1)
- {
- var p = s.Substring(index, s[index..].IndexOf(')') + 1);
- s = s.Replace(p, string.Empty);
- s = s.Insert(s.IndexOf(">", StringComparison.OrdinalIgnoreCase), p.SubstringAfter("(").SubstringAfter("=").SubstringBefore(","));
- }
-
- var upper = s.SubstringAfter(">").SubstringBefore(">");
- return string.Format(Utils.RemoveHtmlTags(s.Replace(upper, upper.ToUpper())), isAll ? string.Empty : completionCount).Replace(" ", " ");
- }
-
- private readonly SKPaint _informationPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Color = SKColor.Parse("#262630")
- };
-
- private void DrawBackground(SKCanvas c, int y)
- {
- c.DrawRect(new SKRect(Margin, y, Width, y + Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4,
- new[] {Background[0].WithAlpha(50), Background[1].WithAlpha(50)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(Height / 2, y, Width, y + Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, y + Height), new SKPoint(Width / 2, 75),
- new[] {SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, y + 75, Width, y + Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(Margin, y, Height / 2, y + Height), _informationPaint);
-
- _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Height / 2, y + Height / 2), new SKPoint(Height / 2 + 100, y + Height / 2),
- new[] {SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(Height / 2, y, Height / 2 + 100, y + Height), _informationPaint);
- }
-
- private void DrawPreview(SKCanvas c, int y)
- {
- ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
- c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, y, Height - Margin, y + Height), ImagePaint);
- }
-
- private void DrawTexts(SKCanvas c, int y)
- {
- _informationPaint.Shader = null;
-
- if (!string.IsNullOrWhiteSpace(DisplayName))
- {
- _informationPaint.TextSize = 40;
- _informationPaint.Color = SKColors.White;
- _informationPaint.Typeface = Utils.Typefaces.Bundle;
- while (_informationPaint.MeasureText(DisplayName) > Width - Height - 10)
- {
- _informationPaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(_informationPaint.Typeface);
- shaper.Shape(DisplayName, _informationPaint);
- c.DrawShapedText(shaper, DisplayName, Height, y + 50, _informationPaint);
- }
-
- var outY = y + 75f;
- if (!string.IsNullOrWhiteSpace(Description))
- {
- _informationPaint.TextSize = 16;
- _informationPaint.Color = SKColors.White.WithAlpha(175);
- _informationPaint.Typeface = Utils.Typefaces.Description;
- Utils.DrawMultilineText(c, Description, Width - Height, y, SKTextAlign.Left,
- new SKRect(Height, outY, Width - 10, y + Height), _informationPaint, out outY);
- }
-
- _informationPaint.Color = Border[0].WithAlpha(100);
- c.DrawRect(new SKRect(Height, outY, Width - 150, outY + 5), _informationPaint);
-
- if (_count > 0)
- {
- _informationPaint.TextSize = 25;
- _informationPaint.Color = SKColors.White;
- _informationPaint.Typeface = Utils.Typefaces.BundleNumber;
- c.DrawText("0 / ", new SKPoint(Width - 130, outY + 10), _informationPaint);
-
- _informationPaint.Color = Border[0];
- c.DrawText(_count.ToString(), new SKPoint(Width - 95, outY + 10), _informationPaint);
- }
-
- _reward.DrawQuest(c, new SKRect(Height, outY + 25, Width - 20, y + Height - 25));
+ _screenLayer = uObject.ExportType.Equals("FortFeatItemDefinition", StringComparison.OrdinalIgnoreCase);
}
}
-}
+
+ private BaseQuest(int completionCount, EIconStyle style) : this(null, style) // completion
+ {
+ var description = completionCount < 0 ?
+ Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat_All", "Complete all {0} challenges> to earn the reward item") :
+ Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat", "Complete any {0} challenges> to earn the reward item");
+
+ DisplayName = ReformatString(description, completionCount.ToString(), completionCount < 0);
+ }
+
+ public BaseQuest(int completionCount, FSoftObjectPath itemDefinition, EIconStyle style) : this(completionCount, style) // completion
+ {
+ _reward = Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject) ? new Reward(uObject) : new Reward();
+ }
+
+ public BaseQuest(int completionCount, int quantity, string reward, EIconStyle style) : this(completionCount, style) // completion
+ {
+ _reward = new Reward(quantity, reward);
+ }
+
+ public override void ParseForInfo()
+ {
+ ParseForReward(false);
+
+ if (Object.TryGetValue(out FStructFallback urgentQuestData, "UrgentQuestData"))
+ {
+ if (urgentQuestData.TryGetValue(out FText eventTitle, "EventTitle"))
+ DisplayName = eventTitle.Text;
+ if (urgentQuestData.TryGetValue(out FText eventDescription, "EventDescription"))
+ Description = eventDescription.Text;
+ if (urgentQuestData.TryGetValue(out FPackageIndex alertIcon, "AlertIcon", "BountyPriceImage"))
+ Preview = Utils.GetBitmap(alertIcon);
+ }
+ else
+ {
+ Description = ShortDescription;
+ if (Object.TryGetValue(out FText completionText, "CompletionText"))
+ Description += "\n" + completionText.Text;
+ if (Object.TryGetValue(out FSoftObjectPath tandemCharacterData, "TandemCharacterData") &&
+ Utils.TryLoadObject(tandemCharacterData.AssetPathName.Text, out UObject uObject) &&
+ uObject.TryGetValue(out FSoftObjectPath tandemIcon, "EntryListIcon", "ToastIcon") &&
+ Utils.TryLoadObject(tandemIcon.AssetPathName.Text, out UObject iconObject))
+ {
+ Preview = iconObject switch
+ {
+ UTexture2D text => Utils.GetBitmap(text),
+ UMaterialInstanceConstant mat => Utils.GetBitmap(mat),
+ _ => Preview
+ };
+ }
+ }
+
+ if (Object.TryGetValue(out int objectiveCompletionCount, "ObjectiveCompletionCount"))
+ _count = objectiveCompletionCount;
+
+ if (Object.TryGetValue(out FStructFallback[] objectives, "Objectives") && objectives.Length > 0)
+ {
+ // actual description doesn't exist
+ if (string.IsNullOrEmpty(Description) && objectives[0].TryGetValue(out FText description, "Description"))
+ Description = description.Text;
+
+ // ObjectiveCompletionCount doesn't exist
+ if (_count == 0)
+ {
+ if (objectives[0].TryGetValue(out int count, "Count") && count > 1)
+ _count = count;
+ else
+ _count = objectives.Length;
+ }
+ }
+
+ if (Object.TryGetValue(out FStructFallback[] rewards, "Rewards"))
+ {
+ foreach (var reward in rewards)
+ {
+ if (!reward.TryGetValue(out FStructFallback itemPrimaryAssetId, "ItemPrimaryAssetId") ||
+ !reward.TryGetValue(out int quantity, "Quantity")) continue;
+
+ if (!itemPrimaryAssetId.TryGetValue(out FStructFallback primaryAssetType, "PrimaryAssetType") ||
+ !itemPrimaryAssetId.TryGetValue(out FName primaryAssetName, "PrimaryAssetName") ||
+ !primaryAssetType.TryGetValue(out FName name, "Name")) continue;
+
+ if (name.Text.Equals("Quest", StringComparison.OrdinalIgnoreCase))
+ {
+ NextQuestName = primaryAssetName.Text;
+ }
+ else if (!_unauthorizedReward.Contains(name.Text))
+ {
+ _reward = new Reward(quantity, $"{name}:{primaryAssetName}");
+ }
+ }
+ }
+
+ if (_reward == null && Object.TryGetValue(out UDataTable rewardsTable, "RewardsTable"))
+ {
+ if (rewardsTable.TryGetDataTableRow("Default", StringComparison.InvariantCulture, out var row))
+ {
+ if (row.TryGetValue(out FName templateId, "TemplateId") &&
+ row.TryGetValue(out int quantity, "Quantity"))
+ {
+ _reward = new Reward(quantity, templateId);
+ }
+ }
+ }
+
+ if (_reward == null && Object.TryGetValue(out FStructFallback[] hiddenRewards, "HiddenRewards"))
+ {
+ foreach (var hiddenReward in hiddenRewards)
+ {
+ if (!hiddenReward.TryGetValue(out FName templateId, "TemplateId") ||
+ !hiddenReward.TryGetValue(out int quantity, "Quantity")) continue;
+
+ _reward = new Reward(quantity, templateId);
+ break;
+ }
+ }
+
+ _reward ??= new Reward();
+ }
+
+ public void DrawQuest(SKCanvas c, int y)
+ {
+ DrawBackground(c, y);
+ DrawPreview(c, y);
+ DrawTexts(c, y);
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawQuest(c, 0);
+
+ return new[] { ret };
+ }
+
+ private string ReformatString(string s, string completionCount, bool isAll)
+ {
+ s = s.Replace("({0})", "{0}").Replace("{QuestNumber}", "{0}>");
+ var index = s.IndexOf("{0}|plural(", StringComparison.OrdinalIgnoreCase);
+ if (index > -1)
+ {
+ var p = s.Substring(index, s[index..].IndexOf(')') + 1);
+ s = s.Replace(p, string.Empty);
+ s = s.Insert(s.IndexOf(">", StringComparison.OrdinalIgnoreCase), p.SubstringAfter("(").SubstringAfter("=").SubstringBefore(","));
+ }
+
+ var upper = s.SubstringAfter(">").SubstringBefore(">");
+ return string.Format(Utils.RemoveHtmlTags(s.Replace(upper, upper.ToUpper())), isAll ? string.Empty : completionCount).Replace(" ", " ");
+ }
+
+ private readonly SKPaint _informationPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Color = SKColor.Parse("#262630")
+ };
+
+ private void DrawBackground(SKCanvas c, int y)
+ {
+ c.DrawRect(new SKRect(Margin, y, Width, y + Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4,
+ new[] { Background[0].WithAlpha(50), Background[1].WithAlpha(50) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(Height / 2, y, Width, y + Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, y + Height), new SKPoint(Width / 2, 75),
+ new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, y + 75, Width, y + Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(Margin, y, Height / 2, y + Height), _informationPaint);
+
+ _informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Height / 2, y + Height / 2), new SKPoint(Height / 2 + 100, y + Height / 2),
+ new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(Height / 2, y, Height / 2 + 100, y + Height), _informationPaint);
+ }
+
+ private void DrawPreview(SKCanvas c, int y)
+ {
+ ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
+ c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, y, Height - Margin, y + Height), ImagePaint);
+ }
+
+ private void DrawTexts(SKCanvas c, int y)
+ {
+ _informationPaint.Shader = null;
+
+ if (!string.IsNullOrWhiteSpace(DisplayName))
+ {
+ _informationPaint.TextSize = 40;
+ _informationPaint.Color = SKColors.White;
+ _informationPaint.Typeface = Utils.Typefaces.Bundle;
+ while (_informationPaint.MeasureText(DisplayName) > Width - Height - 10)
+ {
+ _informationPaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(_informationPaint.Typeface);
+ shaper.Shape(DisplayName, _informationPaint);
+ c.DrawShapedText(shaper, DisplayName, Height, y + 50, _informationPaint);
+ }
+
+ var outY = y + 75f;
+ if (!string.IsNullOrWhiteSpace(Description))
+ {
+ _informationPaint.TextSize = 16;
+ _informationPaint.Color = SKColors.White.WithAlpha(175);
+ _informationPaint.Typeface = Utils.Typefaces.Description;
+ Utils.DrawMultilineText(c, Description, Width - Height, y, SKTextAlign.Left,
+ new SKRect(Height, outY, Width - 10, y + Height), _informationPaint, out outY);
+ }
+
+ _informationPaint.Color = Border[0].WithAlpha(100);
+ c.DrawRect(new SKRect(Height, outY, Width - 150, outY + 5), _informationPaint);
+
+ if (_count > 0)
+ {
+ _informationPaint.TextSize = 25;
+ _informationPaint.Color = SKColors.White;
+ _informationPaint.Typeface = Utils.Typefaces.BundleNumber;
+ c.DrawText("0 / ", new SKPoint(Width - 130, outY + 10), _informationPaint);
+
+ _informationPaint.Color = Border[0];
+ c.DrawText(_count.ToString(), new SKPoint(Width - 95, outY + 10), _informationPaint);
+ }
+
+ _reward.DrawQuest(c, new SKRect(Height, outY + 25, Width - 20, y + Height - 25));
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseSeason.cs b/FModel/Creator/Bases/FN/BaseSeason.cs
index a063f2c9..2d06deb3 100644
--- a/FModel/Creator/Bases/FN/BaseSeason.cs
+++ b/FModel/Creator/Bases/FN/BaseSeason.cs
@@ -8,158 +8,152 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class Page
{
- public class Page
+ public int LevelsNeededForUnlock;
+ public int RewardsNeededForUnlock;
+ public Reward[] RewardEntryList;
+}
+
+public class BaseSeason : UCreator
+{
+ private Reward _firstWinReward;
+ private Page[] _bookXpSchedule;
+ private const int _headerHeight = 150;
+ // keep the list because rewards are ordered by least to most important
+ // we only care about the most but we also have filters so we can't just take the last reward
+
+ public BaseSeason(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public int LevelsNeededForUnlock;
- public int RewardsNeededForUnlock;
- public Reward[] RewardEntryList;
+ Width = 1024;
+ Height = _headerHeight + 50;
+ Margin = 0;
}
- public class BaseSeason : UCreator
+ public override void ParseForInfo()
{
- private Reward _firstWinReward;
- private Page[] _bookXpSchedule;
- private const int _headerHeight = 150;
- // keep the list because rewards are ordered by least to most important
- // we only care about the most but we also have filters so we can't just take the last reward
+ _bookXpSchedule = Array.Empty();
- public BaseSeason(UObject uObject, EIconStyle style) : base(uObject, style)
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text.ToUpperInvariant();
+
+ if (Object.TryGetValue(out FStructFallback seasonFirstWinRewards, "SeasonFirstWinRewards") &&
+ seasonFirstWinRewards.TryGetValue(out FStructFallback[] rewards, "Rewards"))
{
- Width = 1024;
- Height = _headerHeight + 50;
- Margin = 0;
+ foreach (var reward in rewards)
+ {
+ if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
+ !Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
+
+ _firstWinReward = new Reward(uObject);
+ break;
+ }
}
- public override void ParseForInfo()
+ if (Object.TryGetValue(out FPackageIndex[] additionalSeasonData, "AdditionalSeasonData"))
{
- _bookXpSchedule = Array.Empty();
-
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text.ToUpperInvariant();
-
- if (Object.TryGetValue(out FStructFallback seasonFirstWinRewards, "SeasonFirstWinRewards") &&
- seasonFirstWinRewards.TryGetValue(out FStructFallback[] rewards, "Rewards"))
+ foreach (var data in additionalSeasonData)
{
- foreach (var reward in rewards)
+ if (!Utils.TryGetPackageIndexExport(data, out UObject packageIndex) ||
+ !packageIndex.TryGetValue(out FStructFallback[] pageList, "PageList")) continue;
+
+ var i = 0;
+ _bookXpSchedule = new Page[pageList.Length];
+ foreach (var page in pageList)
{
- if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
- !Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
+ if (!page.TryGetValue(out int levelsNeededForUnlock, "LevelsNeededForUnlock") ||
+ !page.TryGetValue(out int rewardsNeededForUnlock, "RewardsNeededForUnlock") ||
+ !page.TryGetValue(out FPackageIndex[] rewardEntryList, "RewardEntryList"))
+ continue;
- _firstWinReward = new Reward(uObject);
- break;
- }
- }
-
- if (Object.TryGetValue(out FPackageIndex[] additionalSeasonData, "AdditionalSeasonData"))
- {
- foreach (var data in additionalSeasonData)
- {
- if (!Utils.TryGetPackageIndexExport(data, out UObject packageIndex) ||
- !packageIndex.TryGetValue(out FStructFallback[] pageList, "PageList")) continue;
-
- var i = 0;
- _bookXpSchedule = new Page[pageList.Length];
- foreach (var page in pageList)
+ var p = new Page
{
- if (!page.TryGetValue(out int levelsNeededForUnlock, "LevelsNeededForUnlock") ||
- !page.TryGetValue(out int rewardsNeededForUnlock, "RewardsNeededForUnlock") ||
- !page.TryGetValue(out FPackageIndex[] rewardEntryList, "RewardEntryList"))
- continue;
+ LevelsNeededForUnlock = levelsNeededForUnlock,
+ RewardsNeededForUnlock = rewardsNeededForUnlock,
+ RewardEntryList = new Reward[rewardEntryList.Length]
+ };
- var p = new Page
- {
- LevelsNeededForUnlock = levelsNeededForUnlock,
- RewardsNeededForUnlock = rewardsNeededForUnlock,
- RewardEntryList = new Reward[rewardEntryList.Length]
- };
+ for (var j = 0; j < p.RewardEntryList.Length; j++)
+ {
+ if (!Utils.TryGetPackageIndexExport(rewardEntryList[j], out packageIndex) ||
+ !packageIndex.TryGetValue(out FStructFallback battlePassOffer, "BattlePassOffer") ||
+ !battlePassOffer.TryGetValue(out FStructFallback rewardItem, "RewardItem") ||
+ !rewardItem.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
+ !Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
- for (var j = 0; j < p.RewardEntryList.Length; j++)
- {
- if (!Utils.TryGetPackageIndexExport(rewardEntryList[j], out packageIndex) ||
- !packageIndex.TryGetValue(out FStructFallback battlePassOffer, "BattlePassOffer") ||
- !battlePassOffer.TryGetValue(out FStructFallback rewardItem, "RewardItem") ||
- !rewardItem.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
- !Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
-
- p.RewardEntryList[j] = new Reward(uObject);
- }
-
- _bookXpSchedule[i++] = p;
+ p.RewardEntryList[j] = new Reward(uObject);
}
- break;
+ _bookXpSchedule[i++] = p;
}
+
+ break;
}
-
- Height += 100 * _bookXpSchedule.Sum(x => x.RewardEntryList.Length) / _bookXpSchedule.Length;
}
- public override SKBitmap[] Draw()
+ Height += 100 * _bookXpSchedule.Sum(x => x.RewardEntryList.Length) / _bookXpSchedule.Length;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawHeader(c);
+ _firstWinReward?.DrawSeasonWin(c, _headerHeight);
+ DrawBookSchedule(c);
+
+ return new[] { ret };
+ }
+
+ private const int _DEFAULT_AREA_SIZE = 80;
+ private readonly SKPaint _headerPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.Bundle, TextSize = 50,
+ TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
+ };
+
+ public void DrawHeader(SKCanvas c)
+ {
+ c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
+
+ _headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
+ new[] { SKColors.SkyBlue.WithAlpha(50), SKColors.Blue.WithAlpha(50) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
+
+ _headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
+ new[] { SKColors.Black.WithAlpha(25), SKColors.Blue.WithAlpha(0) }, SKShaderTileMode.Clamp);
+ c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
+
+ _headerPaint.Shader = null;
+ _headerPaint.Color = SKColors.White;
+ while (_headerPaint.MeasureText(DisplayName) > Width)
{
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawHeader(c);
- _firstWinReward?.DrawSeasonWin(c, _headerHeight);
- DrawBookSchedule(c);
-
- return new []{ret};
+ _headerPaint.TextSize -= 1;
}
- private const int _DEFAULT_AREA_SIZE = 80;
- private readonly SKPaint _headerPaint = new()
+ var shaper = new CustomSKShaper(_headerPaint.Typeface);
+ var shapedText = shaper.Shape(DisplayName, _headerPaint);
+ c.DrawShapedText(shaper, DisplayName, (Width - shapedText.Points[^1].X) / 2, _headerHeight / 2 + _headerPaint.TextSize / 2 - 10, _headerPaint);
+ }
+
+ private void DrawBookSchedule(SKCanvas c)
+ {
+ var x = 20;
+ var y = _headerHeight + 50;
+ foreach (var page in _bookXpSchedule)
{
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.Bundle, TextSize = 50,
- TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
- };
- private readonly SKPaint _bookPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.BundleNumber,
- Color = SKColors.White, TextAlign = SKTextAlign.Center, TextSize = 15
- };
-
- public void DrawHeader(SKCanvas c)
- {
- c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
-
- _headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
- new[] {SKColors.SkyBlue.WithAlpha(50), SKColors.Blue.WithAlpha(50)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
-
- _headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
- new[] {SKColors.Black.WithAlpha(25), SKColors.Blue.WithAlpha(0)}, SKShaderTileMode.Clamp);
- c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
-
- _headerPaint.Shader = null;
- _headerPaint.Color = SKColors.White;
- while (_headerPaint.MeasureText(DisplayName) > Width)
+ foreach (var reward in page.RewardEntryList)
{
- _headerPaint.TextSize -= 1;
+ reward.DrawSeason(c, x, y, _DEFAULT_AREA_SIZE);
+ x += _DEFAULT_AREA_SIZE + 20;
}
- var shaper = new CustomSKShaper(_headerPaint.Typeface);
- var shapedText = shaper.Shape(DisplayName, _headerPaint);
- c.DrawShapedText(shaper, DisplayName, (Width - shapedText.Points[^1].X) / 2, _headerHeight / 2 + _headerPaint.TextSize / 2 - 10, _headerPaint);
- }
-
- private void DrawBookSchedule(SKCanvas c)
- {
- var x = 20;
- var y = _headerHeight + 50;
- foreach (var page in _bookXpSchedule)
- {
- foreach (var reward in page.RewardEntryList)
- {
- reward.DrawSeason(c, x, y, _DEFAULT_AREA_SIZE);
- x += _DEFAULT_AREA_SIZE + 20;
- }
- y += _DEFAULT_AREA_SIZE + 20;
- x = 20;
- }
+ y += _DEFAULT_AREA_SIZE + 20;
+ x = 20;
}
}
-}
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseSeries.cs b/FModel/Creator/Bases/FN/BaseSeries.cs
index 769b443a..dcc29897 100644
--- a/FModel/Creator/Bases/FN/BaseSeries.cs
+++ b/FModel/Creator/Bases/FN/BaseSeries.cs
@@ -1,27 +1,26 @@
using CUE4Parse.UE4.Assets.Exports;
using SkiaSharp;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseSeries : BaseIcon
{
- public class BaseSeries : BaseIcon
+ public BaseSeries(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public BaseSeries(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- }
-
- public override void ParseForInfo()
- {
- GetSeries(Object);
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
-
- return new []{ret};
- }
}
-}
+
+ public override void ParseForInfo()
+ {
+ GetSeries(Object);
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+
+ return new []{ret};
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseTandem.cs b/FModel/Creator/Bases/FN/BaseTandem.cs
index 350394dd..e9ab250c 100644
--- a/FModel/Creator/Bases/FN/BaseTandem.cs
+++ b/FModel/Creator/Bases/FN/BaseTandem.cs
@@ -9,221 +9,220 @@ using FModel.Settings;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseTandem : BaseIcon
{
- public class BaseTandem : BaseIcon
+ private string _generalDescription, _additionalDescription;
+
+ public BaseTandem(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private string _generalDescription, _additionalDescription;
-
- public BaseTandem(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- DefaultPreview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/FeaturedItems/Outfit/T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy.T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy");
- Margin = 0;
- Width = 690;
- Height = 1080;
- }
-
- public override void ParseForInfo()
- {
- base.ParseForInfo();
-
- string sidePanel = string.Empty, entryList = string.Empty;
-
- if (Object.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
- sidePanel = sidePanelIcon.AssetPathName.Text;
- if (Object.TryGetValue(out FSoftObjectPath entryListIcon, "EntryListIcon"))
- entryList = entryListIcon.AssetPathName.Text;
-
- // Overrides for generic "default" images Epic uses for Quest-only or unfinished NPCs
- if (sidePanel.Contains("Clown") && entryList.Contains("Clown"))
- Preview = null;
- else if (sidePanel.Contains("Bane") && !Object.Name.Contains("Sorana"))
- Preview = Utils.GetBitmap(entryList);
- else if (!string.IsNullOrWhiteSpace(sidePanel) && !sidePanel.Contains("Clown"))
- Preview = Utils.GetBitmap(sidePanel);
- else if ((string.IsNullOrWhiteSpace(sidePanel) || sidePanel.Contains("Clown")) && !string.IsNullOrWhiteSpace(entryList))
- Preview = Utils.GetBitmap(entryList);
-
- if (Object.TryGetValue(out FText genDesc, "GeneralDescription"))
- _generalDescription = genDesc.Text;
- if (Object.TryGetValue(out FText addDesc, "AdditionalDescription"))
- _additionalDescription = addDesc.Text;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
- DrawPreview(c);
- DrawHaze(c);
-
- // Korean is slightly smaller than other languages, so the font size is increased slightly
- DrawName(c);
- DrawGeneralDescription(c);
- DrawAdditionalDescription(c);
-
- return new []{ret};
- }
-
- private readonly SKPaint _panelPaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = SKColor.Parse("#0045C7") };
-
- 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 rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
- _panelPaint.Color = SKColor.Parse("#002A8C");
- rect1.MoveTo(29, 0);
- rect1.LineTo(62, Height);
- rect1.LineTo(Width, Height);
- rect1.LineTo(Width, 0);
- rect1.LineTo(29, 0);
- rect1.Close();
- c.DrawPath(rect1, _panelPaint);
-
- _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(29, 0), new SKPoint(Width, Height),
- new[] { SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
- c.DrawPath(rect1, _panelPaint);
-
- _panelPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(348, 196), 300, new[] { SKColor.Parse("#0049CE"), SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
- c.DrawPath(rect1, _panelPaint);
-
- using var rect2 = new SKPath { FillType = SKPathFillType.EvenOdd };
-
- rect2.MoveTo(10, 0);
- rect2.LineTo(30, 0);
- rect2.LineTo(63, Height);
- rect2.LineTo(56, Height);
- rect2.LineTo(10, 0);
- rect2.Close();
- c.DrawPath(rect2, _panelPaint);
-
- _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(10, 0), new SKPoint(62, Height),
- new[] { SKColor.Parse("#0045C7") }, SKShaderTileMode.Clamp);
- c.DrawPath(rect2, _panelPaint);
- }
-
- private new void DrawPreview(SKCanvas c)
- {
- var previewToUse = Preview ?? DefaultPreview;
-
- if (Preview == null)
- {
- previewToUse = DefaultPreview;
- ImagePaint.BlendMode = SKBlendMode.DstOut;
- ImagePaint.Color = SKColor.Parse("#00175F");
- }
-
- var x = -125;
-
- switch (previewToUse.Width)
- {
- case 512 when previewToUse.Height == 1024:
- previewToUse = previewToUse.ResizeWithRatio(500, 1000);
- x = 100;
- break;
- case 512 when previewToUse.Height == 512:
- case 128 when previewToUse.Height == 128:
- previewToUse = previewToUse.Resize(512);
- x = 125;
- break;
- default:
- previewToUse = previewToUse.Resize(1000, 1000);
- break;
- }
-
- c.DrawBitmap(previewToUse, x, 30, ImagePaint);
- }
-
- private void DrawHaze(SKCanvas c)
- {
- using var rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
- rect1.MoveTo(29, 0);
- rect1.LineTo(62, Height);
- rect1.LineTo(Width, Height);
- rect1.LineTo(Width, 0);
- rect1.LineTo(29, 0);
- rect1.Close();
-
- _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(343, 0), new SKPoint(343, Height),
- new[] { SKColors.Transparent, SKColor.Parse("#001E70FF"), SKColor.Parse("#001E70").WithAlpha(200), SKColor.Parse("#001E70").WithAlpha(245), SKColor.Parse("#001E70") }, new[] { 0, (float) .1, (float) .65, (float) .85, 1 }, SKShaderTileMode.Clamp);
- c.DrawPath(rect1, _panelPaint);
- }
-
- private void DrawName(SKCanvas c)
- {
- if (string.IsNullOrWhiteSpace(DisplayName)) return;
-
- DisplayNamePaint.TextSize = UserSettings.Default.AssetLanguage switch
- {
- ELanguage.Korean => 56,
- _ => 42
- };
-
- DisplayNamePaint.TextScaleX = (float) 1.1;
- DisplayNamePaint.Color = SKColors.White;
- DisplayNamePaint.TextSkewX = (float) -.25;
- DisplayNamePaint.TextAlign = SKTextAlign.Left;
-
- var typeface = Utils.Typefaces.TandemDisplayName;
- if (typeface == Utils.Typefaces.Default)
- {
- DisplayNamePaint.TextSize = 30;
- }
-
- DisplayNamePaint.Typeface = typeface;
- var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
- c.DrawShapedText(shaper, DisplayName.ToUpper(), 97, 900, DisplayNamePaint);
- }
-
- private void DrawGeneralDescription(SKCanvas c)
- {
- if (string.IsNullOrWhiteSpace(_generalDescription)) return;
-
- DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
- {
- ELanguage.Korean => 20,
- _ => 17
- };
-
- DescriptionPaint.Color = SKColor.Parse("#00FFFB");
- DescriptionPaint.TextAlign = SKTextAlign.Left;
-
- var typeface = Utils.Typefaces.TandemGenDescription;
- if (typeface == Utils.Typefaces.Default)
- {
- DescriptionPaint.TextSize = 21;
- }
-
- DescriptionPaint.Typeface = typeface;
- var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
- c.DrawShapedText(shaper, _generalDescription.ToUpper(), 97, 930, DescriptionPaint);
- }
-
- private void DrawAdditionalDescription(SKCanvas c)
- {
- if (string.IsNullOrWhiteSpace(_additionalDescription)) return;
-
- DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
- {
- ELanguage.Korean => 22,
- _ => 18
- };
-
- DescriptionPaint.Color = SKColor.Parse("#89D8FF");
- DescriptionPaint.TextAlign = SKTextAlign.Left;
-
- var typeface = Utils.Typefaces.TandemAddDescription;
- if (typeface == Utils.Typefaces.Default)
- {
- DescriptionPaint.TextSize = 20;
- }
-
- DescriptionPaint.Typeface = typeface;
- Utils.DrawMultilineText(c, _additionalDescription, Width, 0, SKTextAlign.Left,
- new SKRect(97, 960, Width - 10, Height), DescriptionPaint, out _);
- }
+ DefaultPreview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/FeaturedItems/Outfit/T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy.T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy");
+ Margin = 0;
+ Width = 690;
+ Height = 1080;
}
-}
+
+ public override void ParseForInfo()
+ {
+ base.ParseForInfo();
+
+ string sidePanel = string.Empty, entryList = string.Empty;
+
+ if (Object.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
+ sidePanel = sidePanelIcon.AssetPathName.Text;
+ if (Object.TryGetValue(out FSoftObjectPath entryListIcon, "EntryListIcon"))
+ entryList = entryListIcon.AssetPathName.Text;
+
+ // Overrides for generic "default" images Epic uses for Quest-only or unfinished NPCs
+ if (sidePanel.Contains("Clown") && entryList.Contains("Clown"))
+ Preview = null;
+ else if (sidePanel.Contains("Bane") && !Object.Name.Contains("Sorana"))
+ Preview = Utils.GetBitmap(entryList);
+ else if (!string.IsNullOrWhiteSpace(sidePanel) && !sidePanel.Contains("Clown"))
+ Preview = Utils.GetBitmap(sidePanel);
+ else if ((string.IsNullOrWhiteSpace(sidePanel) || sidePanel.Contains("Clown")) && !string.IsNullOrWhiteSpace(entryList))
+ Preview = Utils.GetBitmap(entryList);
+
+ if (Object.TryGetValue(out FText genDesc, "GeneralDescription"))
+ _generalDescription = genDesc.Text;
+ if (Object.TryGetValue(out FText addDesc, "AdditionalDescription"))
+ _additionalDescription = addDesc.Text;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawHaze(c);
+
+ // Korean is slightly smaller than other languages, so the font size is increased slightly
+ DrawName(c);
+ DrawGeneralDescription(c);
+ DrawAdditionalDescription(c);
+
+ return new[] { ret };
+ }
+
+ private readonly SKPaint _panelPaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = SKColor.Parse("#0045C7") };
+
+ 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 rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
+ _panelPaint.Color = SKColor.Parse("#002A8C");
+ rect1.MoveTo(29, 0);
+ rect1.LineTo(62, Height);
+ rect1.LineTo(Width, Height);
+ rect1.LineTo(Width, 0);
+ rect1.LineTo(29, 0);
+ rect1.Close();
+ c.DrawPath(rect1, _panelPaint);
+
+ _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(29, 0), new SKPoint(Width, Height),
+ new[] { SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
+ c.DrawPath(rect1, _panelPaint);
+
+ _panelPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(348, 196), 300, new[] { SKColor.Parse("#0049CE"), SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
+ c.DrawPath(rect1, _panelPaint);
+
+ using var rect2 = new SKPath { FillType = SKPathFillType.EvenOdd };
+
+ rect2.MoveTo(10, 0);
+ rect2.LineTo(30, 0);
+ rect2.LineTo(63, Height);
+ rect2.LineTo(56, Height);
+ rect2.LineTo(10, 0);
+ rect2.Close();
+ c.DrawPath(rect2, _panelPaint);
+
+ _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(10, 0), new SKPoint(62, Height),
+ new[] { SKColor.Parse("#0045C7") }, SKShaderTileMode.Clamp);
+ c.DrawPath(rect2, _panelPaint);
+ }
+
+ private new void DrawPreview(SKCanvas c)
+ {
+ var previewToUse = Preview ?? DefaultPreview;
+
+ if (Preview == null)
+ {
+ previewToUse = DefaultPreview;
+ ImagePaint.BlendMode = SKBlendMode.DstOut;
+ ImagePaint.Color = SKColor.Parse("#00175F");
+ }
+
+ var x = -125;
+
+ switch (previewToUse.Width)
+ {
+ case 512 when previewToUse.Height == 1024:
+ previewToUse = previewToUse.ResizeWithRatio(500, 1000);
+ x = 100;
+ break;
+ case 512 when previewToUse.Height == 512:
+ case 128 when previewToUse.Height == 128:
+ previewToUse = previewToUse.Resize(512);
+ x = 125;
+ break;
+ default:
+ previewToUse = previewToUse.Resize(1000, 1000);
+ break;
+ }
+
+ c.DrawBitmap(previewToUse, x, 30, ImagePaint);
+ }
+
+ private void DrawHaze(SKCanvas c)
+ {
+ using var rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
+ rect1.MoveTo(29, 0);
+ rect1.LineTo(62, Height);
+ rect1.LineTo(Width, Height);
+ rect1.LineTo(Width, 0);
+ rect1.LineTo(29, 0);
+ rect1.Close();
+
+ _panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(343, 0), new SKPoint(343, Height),
+ new[] { SKColors.Transparent, SKColor.Parse("#001E70FF"), SKColor.Parse("#001E70").WithAlpha(200), SKColor.Parse("#001E70").WithAlpha(245), SKColor.Parse("#001E70") }, new[] { 0, (float) .1, (float) .65, (float) .85, 1 }, SKShaderTileMode.Clamp);
+ c.DrawPath(rect1, _panelPaint);
+ }
+
+ private void DrawName(SKCanvas c)
+ {
+ if (string.IsNullOrWhiteSpace(DisplayName)) return;
+
+ DisplayNamePaint.TextSize = UserSettings.Default.AssetLanguage switch
+ {
+ ELanguage.Korean => 56,
+ _ => 42
+ };
+
+ DisplayNamePaint.TextScaleX = (float) 1.1;
+ DisplayNamePaint.Color = SKColors.White;
+ DisplayNamePaint.TextSkewX = (float) -.25;
+ DisplayNamePaint.TextAlign = SKTextAlign.Left;
+
+ var typeface = Utils.Typefaces.TandemDisplayName;
+ if (typeface == Utils.Typefaces.Default)
+ {
+ DisplayNamePaint.TextSize = 30;
+ }
+
+ DisplayNamePaint.Typeface = typeface;
+ var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
+ c.DrawShapedText(shaper, DisplayName.ToUpper(), 97, 900, DisplayNamePaint);
+ }
+
+ private void DrawGeneralDescription(SKCanvas c)
+ {
+ if (string.IsNullOrWhiteSpace(_generalDescription)) return;
+
+ DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
+ {
+ ELanguage.Korean => 20,
+ _ => 17
+ };
+
+ DescriptionPaint.Color = SKColor.Parse("#00FFFB");
+ DescriptionPaint.TextAlign = SKTextAlign.Left;
+
+ var typeface = Utils.Typefaces.TandemGenDescription;
+ if (typeface == Utils.Typefaces.Default)
+ {
+ DescriptionPaint.TextSize = 21;
+ }
+
+ DescriptionPaint.Typeface = typeface;
+ var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
+ c.DrawShapedText(shaper, _generalDescription.ToUpper(), 97, 930, DescriptionPaint);
+ }
+
+ private void DrawAdditionalDescription(SKCanvas c)
+ {
+ if (string.IsNullOrWhiteSpace(_additionalDescription)) return;
+
+ DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
+ {
+ ELanguage.Korean => 22,
+ _ => 18
+ };
+
+ DescriptionPaint.Color = SKColor.Parse("#89D8FF");
+ DescriptionPaint.TextAlign = SKTextAlign.Left;
+
+ var typeface = Utils.Typefaces.TandemAddDescription;
+ if (typeface == Utils.Typefaces.Default)
+ {
+ DescriptionPaint.TextSize = 20;
+ }
+
+ DescriptionPaint.Typeface = typeface;
+ Utils.DrawMultilineText(c, _additionalDescription, Width, 0, SKTextAlign.Left,
+ new SKRect(97, 960, Width - 10, Height), DescriptionPaint, out _);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/BaseUserControl.cs b/FModel/Creator/Bases/FN/BaseUserControl.cs
index 502ad8ca..39bbae5d 100644
--- a/FModel/Creator/Bases/FN/BaseUserControl.cs
+++ b/FModel/Creator/Bases/FN/BaseUserControl.cs
@@ -9,185 +9,184 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class BaseUserControl : UCreator
{
- public class BaseUserControl : UCreator
+ private List _optionValues = new();
+
+ private readonly SKPaint _displayNamePaint = new()
{
- private List _optionValues = new();
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.DisplayName, TextSize = 45,
+ Color = SKColors.White, TextAlign = SKTextAlign.Left
+ };
+ private readonly SKPaint _descriptionPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.DisplayName, TextSize = 25,
+ Color = SKColor.Parse("88DBFF"), TextAlign = SKTextAlign.Left
+ };
- private readonly SKPaint _displayNamePaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.DisplayName, TextSize = 45,
- Color = SKColors.White, TextAlign = SKTextAlign.Left
- };
- private readonly SKPaint _descriptionPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.DisplayName, TextSize = 25,
- Color = SKColor.Parse("88DBFF"), TextAlign = SKTextAlign.Left
- };
-
- public BaseUserControl(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- Width = 512;
- Height = 128;
- Margin = 32;
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FText optionDisplayName, "OptionDisplayName"))
- DisplayName = optionDisplayName.Text.ToUpperInvariant();
- if (Object.TryGetValue(out FText optionDescription, "OptionDescription"))
- {
- Description = optionDescription.Text;
-
- if (string.IsNullOrWhiteSpace(Description)) return;
- Height += (int) _descriptionPaint.TextSize * Utils.SplitLines(Description, _descriptionPaint, Width - Margin).Count;
- Height += (int) _descriptionPaint.TextSize;
- }
-
- if (Object.TryGetValue(out FStructFallback[] optionValues, "OptionValues"))
- {
- _optionValues = new List();
- foreach (var option in optionValues)
- {
- if (option.TryGetValue(out FText displayName, "DisplayName"))
- {
- var opt = new Options {Option = displayName.Text.ToUpperInvariant()};
- if (option.TryGetValue(out FLinearColor color, "Value"))
- opt.Color = SKColor.Parse(color.Hex).WithAlpha(150);
-
- _optionValues.Add(opt);
- }
- else if (option.TryGetValue(out FName primaryAssetName, "PrimaryAssetName"))
- {
- _optionValues.Add(new Options {Option = primaryAssetName.Text});
- }
- }
- }
-
- if (Object.TryGetValue(out FText optionOnText, "OptionOnText"))
- _optionValues.Add(new Options {Option = optionOnText.Text});
- if (Object.TryGetValue(out FText optionOffText, "OptionOffText"))
- _optionValues.Add(new Options {Option = optionOffText.Text});
-
- if (Object.TryGetValue(out int iMin, "Min", "DefaultValue") &&
- Object.TryGetValue(out int iMax, "Max"))
- {
- var increment = iMin;
- if (Object.TryGetValue(out int incrementValue, "IncrementValue"))
- increment = incrementValue;
-
- var format = "{0}";
- if (Object.TryGetValue(out FText unitName, "UnitName"))
- format = unitName.Text;
-
- for (var i = iMin; i <= iMax; i += increment)
- {
- _optionValues.Add(new Options {Option = string.Format(format, i)});
- }
- }
-
- if (Object.TryGetValue(out float fMin, "Min", "DefaultValue") &&
- Object.TryGetValue(out float fMax, "Max"))
- {
- var increment = fMin;
- if (Object.TryGetValue(out float incrementValue, "IncrementValue"))
- increment = incrementValue;
-
- var format = "{0}";
- if (Object.TryGetValue(out FText unitName, "UnitName"))
- format = unitName.Text;
-
- for (var i = fMin; i <= fMax; i += increment)
- {
- _optionValues.Add(new Options {Option = string.Format(format, i)});
- }
- }
-
- Height += Margin;
- Height += 35 * _optionValues.Count;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
- DrawInformation(c);
-
- return new []{ret};
- }
-
- private new void DrawBackground(SKCanvas c)
- {
- c.DrawRect(new SKRect(0, 0, Width, Height),
- new SKPaint
- {
- IsAntialias = true,
- FilterQuality = SKFilterQuality.High,
- Shader = SKShader.CreateLinearGradient(
- new SKPoint(Width / 2, Height),
- new SKPoint(Width, Height / 4),
- new[] {SKColor.Parse("01369C"), SKColor.Parse("1273C8")},
- SKShaderTileMode.Clamp)
- });
- }
-
- private void DrawInformation(SKCanvas c)
- {
- // display name
- while (_displayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
- {
- _displayNamePaint.TextSize -= 2;
- }
-
- var shaper = new CustomSKShaper(_displayNamePaint.Typeface);
- shaper.Shape(DisplayName, _displayNamePaint);
- c.DrawShapedText(shaper, DisplayName, Margin, Margin + _displayNamePaint.TextSize, _displayNamePaint);
-#if DEBUG
- c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Margin + _displayNamePaint.TextSize), new SKPaint {Color = SKColors.Blue, IsStroke = true});
-#endif
-
- // description
- float y = Margin;
- if (!string.IsNullOrEmpty(DisplayName)) y += _displayNamePaint.TextSize;
- if (!string.IsNullOrEmpty(Description)) y += _descriptionPaint.TextSize + Margin / 2F;
-
- Utils.DrawMultilineText(c, Description, Width, Margin, SKTextAlign.Left,
- new SKRect(Margin, y, Width - Margin, 256), _descriptionPaint, out var top);
-
- // options
- foreach (var option in _optionValues)
- {
- option.Draw(c, Margin, Width, ref top);
- }
- }
+ public BaseUserControl(UObject uObject, EIconStyle style) : base(uObject, style)
+ {
+ Width = 512;
+ Height = 128;
+ Margin = 32;
}
- public class Options
+ public override void ParseForInfo()
{
- private const int _SPACE = 5;
- private const int _HEIGHT = 30;
-
- private readonly SKPaint _optionPaint = new()
+ if (Object.TryGetValue(out FText optionDisplayName, "OptionDisplayName"))
+ DisplayName = optionDisplayName.Text.ToUpperInvariant();
+ if (Object.TryGetValue(out FText optionDescription, "OptionDescription"))
{
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.DisplayName, TextSize = 20,
- Color = SKColor.Parse("EEFFFF"), TextAlign = SKTextAlign.Left
- };
+ Description = optionDescription.Text;
- public string Option;
- public SKColor Color = SKColor.Parse("55C5FC").WithAlpha(150);
+ if (string.IsNullOrWhiteSpace(Description)) return;
+ Height += (int) _descriptionPaint.TextSize * Utils.SplitLines(Description, _descriptionPaint, Width - Margin).Count;
+ Height += (int) _descriptionPaint.TextSize;
+ }
- public void Draw(SKCanvas c, int margin, int width, ref float top)
+ if (Object.TryGetValue(out FStructFallback[] optionValues, "OptionValues"))
{
- c.DrawRect(new SKRect(margin, top, width - margin, top + _HEIGHT), new SKPaint {IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = Color});
- c.DrawText(Option, margin + _SPACE * 2, top + 20 * 1.1f, _optionPaint);
- top += _HEIGHT + _SPACE;
+ _optionValues = new List();
+ foreach (var option in optionValues)
+ {
+ if (option.TryGetValue(out FText displayName, "DisplayName"))
+ {
+ var opt = new Options { Option = displayName.Text.ToUpperInvariant() };
+ if (option.TryGetValue(out FLinearColor color, "Value"))
+ opt.Color = SKColor.Parse(color.Hex).WithAlpha(150);
+
+ _optionValues.Add(opt);
+ }
+ else if (option.TryGetValue(out FName primaryAssetName, "PrimaryAssetName"))
+ {
+ _optionValues.Add(new Options { Option = primaryAssetName.Text });
+ }
+ }
+ }
+
+ if (Object.TryGetValue(out FText optionOnText, "OptionOnText"))
+ _optionValues.Add(new Options { Option = optionOnText.Text });
+ if (Object.TryGetValue(out FText optionOffText, "OptionOffText"))
+ _optionValues.Add(new Options { Option = optionOffText.Text });
+
+ if (Object.TryGetValue(out int iMin, "Min", "DefaultValue") &&
+ Object.TryGetValue(out int iMax, "Max"))
+ {
+ var increment = iMin;
+ if (Object.TryGetValue(out int incrementValue, "IncrementValue"))
+ increment = incrementValue;
+
+ var format = "{0}";
+ if (Object.TryGetValue(out FText unitName, "UnitName"))
+ format = unitName.Text;
+
+ for (var i = iMin; i <= iMax; i += increment)
+ {
+ _optionValues.Add(new Options { Option = string.Format(format, i) });
+ }
+ }
+
+ if (Object.TryGetValue(out float fMin, "Min", "DefaultValue") &&
+ Object.TryGetValue(out float fMax, "Max"))
+ {
+ var increment = fMin;
+ if (Object.TryGetValue(out float incrementValue, "IncrementValue"))
+ increment = incrementValue;
+
+ var format = "{0}";
+ if (Object.TryGetValue(out FText unitName, "UnitName"))
+ format = unitName.Text;
+
+ for (var i = fMin; i <= fMax; i += increment)
+ {
+ _optionValues.Add(new Options { Option = string.Format(format, i) });
+ }
+ }
+
+ Height += Margin;
+ Height += 35 * _optionValues.Count;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+ DrawInformation(c);
+
+ return new[] { ret };
+ }
+
+ private new void DrawBackground(SKCanvas c)
+ {
+ c.DrawRect(new SKRect(0, 0, Width, Height),
+ new SKPaint
+ {
+ IsAntialias = true,
+ FilterQuality = SKFilterQuality.High,
+ Shader = SKShader.CreateLinearGradient(
+ new SKPoint(Width / 2, Height),
+ new SKPoint(Width, Height / 4),
+ new[] { SKColor.Parse("01369C"), SKColor.Parse("1273C8") },
+ SKShaderTileMode.Clamp)
+ });
+ }
+
+ private void DrawInformation(SKCanvas c)
+ {
+ // display name
+ while (_displayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
+ {
+ _displayNamePaint.TextSize -= 2;
+ }
+
+ var shaper = new CustomSKShaper(_displayNamePaint.Typeface);
+ shaper.Shape(DisplayName, _displayNamePaint);
+ c.DrawShapedText(shaper, DisplayName, Margin, Margin + _displayNamePaint.TextSize, _displayNamePaint);
+#if DEBUG
+ c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Margin + _displayNamePaint.TextSize), new SKPaint { Color = SKColors.Blue, IsStroke = true });
+#endif
+
+ // description
+ float y = Margin;
+ if (!string.IsNullOrEmpty(DisplayName)) y += _displayNamePaint.TextSize;
+ if (!string.IsNullOrEmpty(Description)) y += _descriptionPaint.TextSize + Margin / 2F;
+
+ Utils.DrawMultilineText(c, Description, Width, Margin, SKTextAlign.Left,
+ new SKRect(Margin, y, Width - Margin, 256), _descriptionPaint, out var top);
+
+ // options
+ foreach (var option in _optionValues)
+ {
+ option.Draw(c, Margin, Width, ref top);
}
}
}
+
+public class Options
+{
+ private const int _SPACE = 5;
+ private const int _HEIGHT = 30;
+
+ private readonly SKPaint _optionPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.DisplayName, TextSize = 20,
+ Color = SKColor.Parse("EEFFFF"), TextAlign = SKTextAlign.Left
+ };
+
+ public string Option;
+ public SKColor Color = SKColor.Parse("55C5FC").WithAlpha(150);
+
+ public void Draw(SKCanvas c, int margin, int width, ref float top)
+ {
+ c.DrawRect(new SKRect(margin, top, width - margin, top + _HEIGHT), new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = Color });
+ c.DrawText(Option, margin + _SPACE * 2, top + 20 * 1.1f, _optionPaint);
+ top += _HEIGHT + _SPACE;
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/FN/Reward.cs b/FModel/Creator/Bases/FN/Reward.cs
index 0b2b287d..908dd7d6 100644
--- a/FModel/Creator/Bases/FN/Reward.cs
+++ b/FModel/Creator/Bases/FN/Reward.cs
@@ -5,148 +5,147 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases.FN
+namespace FModel.Creator.Bases.FN;
+
+public class Reward
{
- public class Reward
+ private string _rewardQuantity;
+ private BaseIcon _theReward;
+
+ public bool HasReward() => _theReward != null;
+
+ public Reward()
{
- private string _rewardQuantity;
- private BaseIcon _theReward;
+ _rewardQuantity = "x0";
+ }
- public bool HasReward() => _theReward != null;
+ public Reward(int quantity, FName primaryAssetName) : this(quantity, primaryAssetName.Text)
+ {
+ }
- public Reward()
+ public Reward(int quantity, string assetName) : this()
+ {
+ _rewardQuantity = $"x{quantity:###,###,###}".Trim();
+
+ if (assetName.Contains(':'))
{
- _rewardQuantity = "x0";
- }
+ var parts = assetName.Split(':');
- public Reward(int quantity, FName primaryAssetName) : this(quantity, primaryAssetName.Text)
- {
- }
-
- public Reward(int quantity, string assetName) : this()
- {
- _rewardQuantity = $"x{quantity:###,###,###}".Trim();
-
- if (assetName.Contains(':'))
+ if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase))
{
- var parts = assetName.Split(':');
+ if (!Utils.TryLoadObject($"FortniteGame/Content/Items/BannerIcons/{parts[1]}.{parts[1]}", out UObject p))
+ return;
- if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase))
+ _theReward = new BaseIcon(p, EIconStyle.Default);
+ _theReward.ParseForReward(false);
+ _theReward.Border[0] = SKColors.White;
+ _rewardQuantity = _theReward.DisplayName;
+ }
+ else GetReward(parts[1]);
+ }
+ else GetReward(assetName);
+ }
+
+ public Reward(UObject uObject)
+ {
+ _theReward = new BaseIcon(uObject, EIconStyle.Default);
+ _theReward.ParseForReward(false);
+ _theReward.Border[0] = SKColors.White;
+ _rewardQuantity = _theReward.DisplayName;
+ }
+
+ private readonly SKPaint _rewardPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High
+ };
+
+ public void DrawQuest(SKCanvas c, SKRect rect)
+ {
+ _rewardPaint.TextSize = 50;
+ if (HasReward())
+ {
+ c.DrawBitmap(_theReward.Preview.Resize((int) rect.Height), new SKPoint(rect.Left, rect.Top), _rewardPaint);
+
+ _rewardPaint.Color = _theReward.Border[0];
+ _rewardPaint.Typeface = _rewardQuantity.StartsWith("x") ? Utils.Typefaces.BundleNumber : Utils.Typefaces.Bundle;
+ _rewardPaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, _theReward.Background[0].WithAlpha(150));
+ while (_rewardPaint.MeasureText(_rewardQuantity) > rect.Width)
+ {
+ _rewardPaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(_rewardPaint.Typeface);
+ shaper.Shape(_rewardQuantity, _rewardPaint);
+ c.DrawShapedText(shaper, _rewardQuantity, rect.Left + rect.Height + 25, rect.MidY + 20, _rewardPaint);
+ }
+ else
+ {
+ _rewardPaint.Color = SKColors.White;
+ _rewardPaint.Typeface = Utils.Typefaces.BundleNumber;
+ c.DrawText("No Reward", new SKPoint(rect.Left, rect.MidY + 20), _rewardPaint);
+ }
+ }
+
+ public void DrawSeasonWin(SKCanvas c, int size)
+ {
+ if (!HasReward()) return;
+ c.DrawBitmap(_theReward.Preview.Resize(size), new SKPoint(0, 0), _rewardPaint);
+ }
+
+ public void DrawSeason(SKCanvas c, int x, int y, int areaSize)
+ {
+ if (!HasReward()) return;
+
+ // area + icon
+ _rewardPaint.Color = SKColor.Parse("#0F5CAF");
+ c.DrawRect(new SKRect(x, y, x + areaSize, y + areaSize), _rewardPaint);
+ c.DrawBitmap(_theReward.Preview.Resize(areaSize), new SKPoint(x, y), _rewardPaint);
+
+ // rarity color
+ _rewardPaint.Color = _theReward.Background[0];
+ var pathBottom = new SKPath {FillType = SKPathFillType.EvenOdd};
+ pathBottom.MoveTo(x, y + areaSize);
+ pathBottom.LineTo(x, y + areaSize - areaSize / 25 * 2.5f);
+ pathBottom.LineTo(x + areaSize, y + areaSize - areaSize / 25 * 4.5f);
+ pathBottom.LineTo(x + areaSize, y + areaSize);
+ pathBottom.Close();
+ c.DrawPath(pathBottom, _rewardPaint);
+ }
+
+ private void GetReward(string trigger)
+ {
+ switch (trigger.ToLower())
+ {
+ case "athenabattlestar":
+ _theReward = new BaseIcon(null, EIconStyle.Default);
+ _theReward.Border[0] = SKColor.Parse("FFDB67");
+ _theReward.Background[0] = SKColor.Parse("8F4A20");
+ _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/Athena/UI/Frontend/Art/T_UI_BP_BattleStar_L.T_UI_BP_BattleStar_L");
+ break;
+ case "athenaseasonalxp":
+ _theReward = new BaseIcon(null, EIconStyle.Default);
+ _theReward.Border[0] = SKColor.Parse("E6FDB1");
+ _theReward.Background[0] = SKColor.Parse("51830F");
+ _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-XPUncommon-L.T-FNBR-XPUncommon-L");
+ break;
+ case "mtxgiveaway":
+ _theReward = new BaseIcon(null, EIconStyle.Default);
+ _theReward.Border[0] = SKColor.Parse("DCE6FF");
+ _theReward.Background[0] = SKColor.Parse("64A0AF");
+ _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-Items-MTX.T-Items-MTX");
+ break;
+ default:
+ {
+ var path = Utils.GetFullPath($"FortniteGame/Content/Athena/.*?/{trigger}.*"); // path has no objectname and its needed so we push the trigger again as the objectname
+ if (!string.IsNullOrWhiteSpace(path) && Utils.TryLoadObject(path.Replace("uasset", trigger), out UObject d))
{
- if (!Utils.TryLoadObject($"FortniteGame/Content/Items/BannerIcons/{parts[1]}.{parts[1]}", out UObject p))
- return;
-
- _theReward = new BaseIcon(p, EIconStyle.Default);
+ _theReward = new BaseIcon(d, EIconStyle.Default);
_theReward.ParseForReward(false);
_theReward.Border[0] = SKColors.White;
_rewardQuantity = _theReward.DisplayName;
}
- else GetReward(parts[1]);
- }
- else GetReward(assetName);
- }
- public Reward(UObject uObject)
- {
- _theReward = new BaseIcon(uObject, EIconStyle.Default);
- _theReward.ParseForReward(false);
- _theReward.Border[0] = SKColors.White;
- _rewardQuantity = _theReward.DisplayName;
- }
-
- private readonly SKPaint _rewardPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High
- };
-
- public void DrawQuest(SKCanvas c, SKRect rect)
- {
- _rewardPaint.TextSize = 50;
- if (HasReward())
- {
- c.DrawBitmap(_theReward.Preview.Resize((int) rect.Height), new SKPoint(rect.Left, rect.Top), _rewardPaint);
-
- _rewardPaint.Color = _theReward.Border[0];
- _rewardPaint.Typeface = _rewardQuantity.StartsWith("x") ? Utils.Typefaces.BundleNumber : Utils.Typefaces.Bundle;
- _rewardPaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, _theReward.Background[0].WithAlpha(150));
- while (_rewardPaint.MeasureText(_rewardQuantity) > rect.Width)
- {
- _rewardPaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(_rewardPaint.Typeface);
- shaper.Shape(_rewardQuantity, _rewardPaint);
- c.DrawShapedText(shaper, _rewardQuantity, rect.Left + rect.Height + 25, rect.MidY + 20, _rewardPaint);
- }
- else
- {
- _rewardPaint.Color = SKColors.White;
- _rewardPaint.Typeface = Utils.Typefaces.BundleNumber;
- c.DrawText("No Reward", new SKPoint(rect.Left, rect.MidY + 20), _rewardPaint);
- }
- }
-
- public void DrawSeasonWin(SKCanvas c, int size)
- {
- if (!HasReward()) return;
- c.DrawBitmap(_theReward.Preview.Resize(size), new SKPoint(0, 0), _rewardPaint);
- }
-
- public void DrawSeason(SKCanvas c, int x, int y, int areaSize)
- {
- if (!HasReward()) return;
-
- // area + icon
- _rewardPaint.Color = SKColor.Parse("#0F5CAF");
- c.DrawRect(new SKRect(x, y, x + areaSize, y + areaSize), _rewardPaint);
- c.DrawBitmap(_theReward.Preview.Resize(areaSize), new SKPoint(x, y), _rewardPaint);
-
- // rarity color
- _rewardPaint.Color = _theReward.Background[0];
- var pathBottom = new SKPath {FillType = SKPathFillType.EvenOdd};
- pathBottom.MoveTo(x, y + areaSize);
- pathBottom.LineTo(x, y + areaSize - areaSize / 25 * 2.5f);
- pathBottom.LineTo(x + areaSize, y + areaSize - areaSize / 25 * 4.5f);
- pathBottom.LineTo(x + areaSize, y + areaSize);
- pathBottom.Close();
- c.DrawPath(pathBottom, _rewardPaint);
- }
-
- private void GetReward(string trigger)
- {
- switch (trigger.ToLower())
- {
- case "athenabattlestar":
- _theReward = new BaseIcon(null, EIconStyle.Default);
- _theReward.Border[0] = SKColor.Parse("FFDB67");
- _theReward.Background[0] = SKColor.Parse("8F4A20");
- _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/Athena/UI/Frontend/Art/T_UI_BP_BattleStar_L.T_UI_BP_BattleStar_L");
- break;
- case "athenaseasonalxp":
- _theReward = new BaseIcon(null, EIconStyle.Default);
- _theReward.Border[0] = SKColor.Parse("E6FDB1");
- _theReward.Background[0] = SKColor.Parse("51830F");
- _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-XPUncommon-L.T-FNBR-XPUncommon-L");
- break;
- case "mtxgiveaway":
- _theReward = new BaseIcon(null, EIconStyle.Default);
- _theReward.Border[0] = SKColor.Parse("DCE6FF");
- _theReward.Background[0] = SKColor.Parse("64A0AF");
- _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-Items-MTX.T-Items-MTX");
- break;
- default:
- {
- var path = Utils.GetFullPath($"FortniteGame/Content/Athena/.*?/{trigger}.*"); // path has no objectname and its needed so we push the trigger again as the objectname
- if (!string.IsNullOrWhiteSpace(path) && Utils.TryLoadObject(path.Replace("uasset", trigger), out UObject d))
- {
- _theReward = new BaseIcon(d, EIconStyle.Default);
- _theReward.ParseForReward(false);
- _theReward.Border[0] = SKColors.White;
- _rewardQuantity = _theReward.DisplayName;
- }
-
- break;
- }
+ break;
}
}
}
diff --git a/FModel/Creator/Bases/SB/BaseDivision.cs b/FModel/Creator/Bases/SB/BaseDivision.cs
index 183c4163..1fcea63e 100644
--- a/FModel/Creator/Bases/SB/BaseDivision.cs
+++ b/FModel/Creator/Bases/SB/BaseDivision.cs
@@ -4,45 +4,44 @@ using CUE4Parse.UE4.Objects.Core.Math;
using CUE4Parse.UE4.Objects.UObject;
using SkiaSharp;
-namespace FModel.Creator.Bases.SB
+namespace FModel.Creator.Bases.SB;
+
+public class BaseDivision : UCreator
{
- public class BaseDivision : UCreator
+ public BaseDivision(UObject uObject, EIconStyle style) : base(uObject, style)
{
- public BaseDivision(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FPackageIndex icon, "Icon", "IconNoTier"))
- {
- Preview = Utils.GetBitmap(icon);
- }
-
- if (Object.TryGetValue(out FLinearColor lightColor, "UILightColor") &&
- Object.TryGetValue(out FLinearColor mediumColor, "UIMediumColor") &&
- Object.TryGetValue(out FLinearColor darkColor, "UIDarkColor") &&
- Object.TryGetValue(out FLinearColor cardColor, "UICardColor"))
- {
- Background = new[] {SKColor.Parse(lightColor.Hex), SKColor.Parse(cardColor.Hex)};
- Border = new[] {SKColor.Parse(mediumColor.Hex), SKColor.Parse(darkColor.Hex)};
- }
-
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
-
- return new []{ret};
- }
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FPackageIndex icon, "Icon", "IconNoTier"))
+ {
+ Preview = Utils.GetBitmap(icon);
+ }
+
+ if (Object.TryGetValue(out FLinearColor lightColor, "UILightColor") &&
+ Object.TryGetValue(out FLinearColor mediumColor, "UIMediumColor") &&
+ Object.TryGetValue(out FLinearColor darkColor, "UIDarkColor") &&
+ Object.TryGetValue(out FLinearColor cardColor, "UICardColor"))
+ {
+ Background = new[] { SKColor.Parse(lightColor.Hex), SKColor.Parse(cardColor.Hex) };
+ Border = new[] { SKColor.Parse(mediumColor.Hex), SKColor.Parse(darkColor.Hex) };
+ }
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+
+ return new[] { ret };
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/SB/BaseGameModeInfo.cs b/FModel/Creator/Bases/SB/BaseGameModeInfo.cs
index 39a7cea0..47709e9a 100644
--- a/FModel/Creator/Bases/SB/BaseGameModeInfo.cs
+++ b/FModel/Creator/Bases/SB/BaseGameModeInfo.cs
@@ -4,47 +4,46 @@ using CUE4Parse.UE4.Assets.Exports.Texture;
using CUE4Parse.UE4.Objects.Core.i18N;
using SkiaSharp;
-namespace FModel.Creator.Bases.SB
+namespace FModel.Creator.Bases.SB;
+
+public class BaseGameModeInfo : UCreator
{
- public class BaseGameModeInfo : UCreator
+ private SKBitmap _icon;
+
+ public BaseGameModeInfo(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private SKBitmap _icon;
-
- public BaseGameModeInfo(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- Width = 738;
- Height = 1024;
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText description, "Description"))
- Description = description.Text;
- if (Object.TryGetValue(out UMaterialInstanceConstant portrait, "Portrait"))
- Preview = Utils.GetBitmap(portrait);
- if (Object.TryGetValue(out UTexture2D icon, "Icon"))
- _icon = Utils.GetBitmap(icon).Resize(25);
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawIcon(c);
-
- return new []{ret};
- }
-
- private void DrawIcon(SKCanvas c)
- {
- if (_icon == null) return;
- c.DrawBitmap(_icon, new SKPoint(5, 5), ImagePaint);
- }
+ Width = 738;
+ Height = 1024;
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText description, "Description"))
+ Description = description.Text;
+ if (Object.TryGetValue(out UMaterialInstanceConstant portrait, "Portrait"))
+ Preview = Utils.GetBitmap(portrait);
+ if (Object.TryGetValue(out UTexture2D icon, "Icon"))
+ _icon = Utils.GetBitmap(icon).Resize(25);
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawIcon(c);
+
+ return new[] { ret };
+ }
+
+ private void DrawIcon(SKCanvas c)
+ {
+ if (_icon == null) return;
+ c.DrawBitmap(_icon, new SKPoint(5, 5), ImagePaint);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/SB/BaseLeague.cs b/FModel/Creator/Bases/SB/BaseLeague.cs
index 6547fe3e..583e165c 100644
--- a/FModel/Creator/Bases/SB/BaseLeague.cs
+++ b/FModel/Creator/Bases/SB/BaseLeague.cs
@@ -3,53 +3,52 @@ using CUE4Parse.UE4.Objects.Core.i18N;
using CUE4Parse.UE4.Objects.UObject;
using SkiaSharp;
-namespace FModel.Creator.Bases.SB
+namespace FModel.Creator.Bases.SB;
+
+public class BaseLeague : UCreator
{
- public class BaseLeague : UCreator
+ private int _promotionXp, _xpLostPerMatch;
+
+ public BaseLeague(UObject uObject, EIconStyle style) : base(uObject, style)
{
- private int _promotionXp, _xpLostPerMatch;
-
- public BaseLeague(UObject uObject, EIconStyle style) : base(uObject, style)
- {
- _promotionXp = 0;
- _xpLostPerMatch = 0;
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out int promotionXp, "PromotionXP"))
- _promotionXp = promotionXp;
- if (Object.TryGetValue(out int xpLostPerMatch, "XPLostPerMatch"))
- _xpLostPerMatch = xpLostPerMatch;
-
- if (Object.TryGetValue(out FPackageIndex division, "Division") &&
- Utils.TryGetPackageIndexExport(division, out UObject div))
- {
- var d = new BaseDivision(div, Style);
- d.ParseForInfo();
- Preview = d.Preview;
- Background = d.Background;
- Border = d.Border;
- }
-
- if (Object.TryGetValue(out FText displayName, "DisplayName"))
- DisplayName = displayName.Text;
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
-
- DrawToBottom(c, SKTextAlign.Left, $"PromotionXP: {_promotionXp}");
- DrawToBottom(c, SKTextAlign.Right, $"XPLostPerMatch: {_xpLostPerMatch}");
-
- return new []{ret};
- }
+ _promotionXp = 0;
+ _xpLostPerMatch = 0;
}
-}
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out int promotionXp, "PromotionXP"))
+ _promotionXp = promotionXp;
+ if (Object.TryGetValue(out int xpLostPerMatch, "XPLostPerMatch"))
+ _xpLostPerMatch = xpLostPerMatch;
+
+ if (Object.TryGetValue(out FPackageIndex division, "Division") &&
+ Utils.TryGetPackageIndexExport(division, out UObject div))
+ {
+ var d = new BaseDivision(div, Style);
+ d.ParseForInfo();
+ Preview = d.Preview;
+ Background = d.Background;
+ Border = d.Border;
+ }
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName"))
+ DisplayName = displayName.Text;
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+
+ DrawToBottom(c, SKTextAlign.Left, $"PromotionXP: {_promotionXp}");
+ DrawToBottom(c, SKTextAlign.Right, $"XPLostPerMatch: {_xpLostPerMatch}");
+
+ return new[] { ret };
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/SB/BaseSpellIcon.cs b/FModel/Creator/Bases/SB/BaseSpellIcon.cs
index 61a3ca82..a9461c13 100644
--- a/FModel/Creator/Bases/SB/BaseSpellIcon.cs
+++ b/FModel/Creator/Bases/SB/BaseSpellIcon.cs
@@ -7,92 +7,91 @@ using CUE4Parse.UE4.Objects.UObject;
using FModel.Creator.Bases.FN;
using SkiaSharp;
-namespace FModel.Creator.Bases.SB
+namespace FModel.Creator.Bases.SB;
+
+public class BaseSpellIcon : BaseIcon
{
- public class BaseSpellIcon : BaseIcon
+ private SKBitmap _seriesBackground2;
+
+ private readonly SKPaint _overlayPaint = new()
{
- private SKBitmap _seriesBackground2;
+ FilterQuality = SKFilterQuality.High,
+ IsAntialias = true,
+ Color = SKColors.Transparent.WithAlpha(75)
+ };
- private readonly SKPaint _overlayPaint = new()
+ public BaseSpellIcon(UObject uObject, EIconStyle style) : base(uObject, style)
+ {
+ Background = new[] { SKColor.Parse("FFFFFF"), SKColor.Parse("636363") };
+ Border = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF") };
+ Width = Object.ExportType.StartsWith("GCosmeticCard") ? 1536 : 512;
+ Height = Object.ExportType.StartsWith("GCosmeticCard") ? 450 : 512;
+ }
+
+ public override void ParseForInfo()
+ {
+ if (Object.TryGetValue(out FName rarity, "Rarity"))
+ GetRarity(rarity);
+
+ if (Object.TryGetValue(out FSoftObjectPath preview, "IconTexture", "OfferTexture", "PortraitTexture"))
+ Preview = Utils.GetBitmap(preview);
+ else if (Object.TryGetValue(out FPackageIndex icon, "IconTexture", "OfferTexture", "PortraitTexture"))
+ Preview = Utils.GetBitmap(icon);
+
+ if (Object.TryGetValue(out FText displayName, "DisplayName", "Title", "Name"))
+ DisplayName = displayName.Text;
+ if (Object.TryGetValue(out FText description, "Description"))
+ Description = description.Text;
+
+ SeriesBackground = Utils.GetBitmap("g3/Content/UI/Textures/assets/HUDAccentFillBox.HUDAccentFillBox");
+ _seriesBackground2 = Utils.GetBitmap("g3/Content/UI/Textures/assets/store/ItemBGStatic_UIT.ItemBGStatic_UIT");
+ }
+
+ public override SKBitmap[] Draw()
+ {
+ var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
+ using var c = new SKCanvas(ret);
+
+ DrawBackgrounds(c);
+ DrawBackground(c);
+ DrawPreview(c);
+ DrawTextBackground(c);
+ DrawDisplayName(c);
+ DrawDescription(c);
+
+ return new[] { ret };
+ }
+
+ private void DrawBackgrounds(SKCanvas c)
+ {
+ if (SeriesBackground != null)
+ c.DrawBitmap(SeriesBackground, new SKRect(0, 0, Width, Height), ImagePaint);
+ if (_seriesBackground2 != null)
+ c.DrawBitmap(_seriesBackground2, new SKRect(0, 0, Width, Height), _overlayPaint);
+
+ var x = Margin * (int) 2.5;
+ const int radi = 15;
+ c.DrawCircle(x + radi, x + radi, radi, new SKPaint
{
- FilterQuality = SKFilterQuality.High,
IsAntialias = true,
- Color = SKColors.Transparent.WithAlpha(75)
- };
+ FilterQuality = SKFilterQuality.High,
+ Shader = SKShader.CreateRadialGradient(
+ new SKPoint(radi, radi), radi * 2 / 5 * 4,
+ Background, SKShaderTileMode.Clamp)
+ });
+ }
- public BaseSpellIcon(UObject uObject, EIconStyle style) : base(uObject, style)
+ private void GetRarity(FName n)
+ {
+ if (!Utils.TryLoadObject("g3/Content/UI/UIKit/DT_RarityColors.DT_RarityColors", out UDataTable rarity)) return;
+
+ if (rarity.TryGetDataTableRow(n.Text["EXRarity::".Length..], StringComparison.Ordinal, out var row))
{
- Background = new[] {SKColor.Parse("FFFFFF"), SKColor.Parse("636363")};
- Border = new[] {SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF")};
- Width = Object.ExportType.StartsWith("GCosmeticCard") ? 1536 : 512;
- Height = Object.ExportType.StartsWith("GCosmeticCard") ? 450 : 512;
- }
-
- public override void ParseForInfo()
- {
- if (Object.TryGetValue(out FName rarity, "Rarity"))
- GetRarity(rarity);
-
- if (Object.TryGetValue(out FSoftObjectPath preview, "IconTexture", "OfferTexture", "PortraitTexture"))
- Preview = Utils.GetBitmap(preview);
- else if (Object.TryGetValue(out FPackageIndex icon, "IconTexture", "OfferTexture", "PortraitTexture"))
- Preview = Utils.GetBitmap(icon);
-
- if (Object.TryGetValue(out FText displayName, "DisplayName", "Title", "Name"))
- DisplayName = displayName.Text;
- if (Object.TryGetValue(out FText description, "Description"))
- Description = description.Text;
-
- SeriesBackground = Utils.GetBitmap("g3/Content/UI/Textures/assets/HUDAccentFillBox.HUDAccentFillBox");
- _seriesBackground2 = Utils.GetBitmap("g3/Content/UI/Textures/assets/store/ItemBGStatic_UIT.ItemBGStatic_UIT");
- }
-
- public override SKBitmap[] Draw()
- {
- var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
- using var c = new SKCanvas(ret);
-
- DrawBackgrounds(c);
- DrawBackground(c);
- DrawPreview(c);
- DrawTextBackground(c);
- DrawDisplayName(c);
- DrawDescription(c);
-
- return new []{ret};
- }
-
- private void DrawBackgrounds(SKCanvas c)
- {
- if (SeriesBackground != null)
- c.DrawBitmap(SeriesBackground, new SKRect(0, 0, Width, Height), ImagePaint);
- if (_seriesBackground2 != null)
- c.DrawBitmap(_seriesBackground2, new SKRect(0, 0, Width, Height), _overlayPaint);
-
- var x = Margin * (int) 2.5;
- const int radi = 15;
- c.DrawCircle(x + radi, x + radi, radi, new SKPaint
+ if (row.TryGetValue(out FLinearColor[] colors, "Colors"))
{
- IsAntialias = true,
- FilterQuality = SKFilterQuality.High,
- Shader = SKShader.CreateRadialGradient(
- new SKPoint(radi, radi), radi * 2 / 5 * 4,
- Background, SKShaderTileMode.Clamp)
- });
- }
-
- private void GetRarity(FName n)
- {
- if (!Utils.TryLoadObject("g3/Content/UI/UIKit/DT_RarityColors.DT_RarityColors", out UDataTable rarity)) return;
-
- if (rarity.TryGetDataTableRow(n.Text["EXRarity::".Length..], StringComparison.Ordinal, out var row))
- {
- if (row.TryGetValue(out FLinearColor[] colors, "Colors"))
- {
- Background = new[] {SKColor.Parse(colors[0].Hex), SKColor.Parse(colors[2].Hex)};
- Border = new[] {SKColor.Parse(colors[1].Hex), SKColor.Parse(colors[0].Hex)};
- }
+ Background = new[] { SKColor.Parse(colors[0].Hex), SKColor.Parse(colors[2].Hex) };
+ Border = new[] { SKColor.Parse(colors[1].Hex), SKColor.Parse(colors[0].Hex) };
}
}
}
-}
+}
\ No newline at end of file
diff --git a/FModel/Creator/Bases/UCreator.cs b/FModel/Creator/Bases/UCreator.cs
index cbd3b156..d0577b37 100644
--- a/FModel/Creator/Bases/UCreator.cs
+++ b/FModel/Creator/Bases/UCreator.cs
@@ -6,225 +6,224 @@ using FModel.Framework;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator.Bases
+namespace FModel.Creator.Bases;
+
+public abstract class UCreator
{
- public abstract class UCreator
+ protected UObject Object { get; }
+ protected EIconStyle Style { get; }
+ public SKBitmap DefaultPreview { get; set; }
+ public SKBitmap Preview { get; set; }
+ public SKColor[] Background { get; protected set; }
+ public SKColor[] Border { get; protected set; }
+ public string DisplayName { get; protected set; }
+ public string Description { get; protected set; }
+ public int Margin { get; protected set; }
+ public int Width { get; protected set; }
+ public int Height { get; protected set; }
+
+ public abstract void ParseForInfo();
+ public abstract SKBitmap[] Draw();
+
+ protected UCreator(UObject uObject, EIconStyle style)
{
- protected UObject Object { get; }
- protected EIconStyle Style { get; }
- public SKBitmap DefaultPreview { get; set; }
- public SKBitmap Preview { get; set; }
- public SKColor[] Background { get; protected set; }
- public SKColor[] Border { get; protected set; }
- public string DisplayName { get; protected set; }
- public string Description { get; protected set; }
- public int Margin { get; protected set; }
- public int Width { get; protected set; }
- public int Height { get; protected set; }
+ DefaultPreview = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png"))?.Stream);
+ Background = new[] { SKColor.Parse("5BFD00"), SKColor.Parse("003700") };
+ Border = new[] { SKColor.Parse("1E8500"), SKColor.Parse("5BFD00") };
+ DisplayName = string.Empty;
+ Description = string.Empty;
+ Width = 512;
+ Height = 512;
+ Margin = 2;
+ Object = uObject;
+ Style = style;
+ }
- public abstract void ParseForInfo();
- public abstract SKBitmap[] Draw();
+ 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
+ };
+ protected readonly SKPaint DescriptionPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Typeface = Utils.Typefaces.Description, TextSize = 13,
+ Color = SKColors.White
+ };
+ protected readonly SKPaint ImagePaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High
+ };
+ private readonly SKPaint _textBackgroundPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = new SKColor(0, 0, 50, 75)
+ };
+ private readonly SKPaint _shortDescriptionPaint = new()
+ {
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Color = SKColors.White
+ };
- protected UCreator(UObject uObject, EIconStyle style)
- {
- DefaultPreview = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png"))?.Stream);
- Background = new[] {SKColor.Parse("5BFD00"), SKColor.Parse("003700")};
- Border = new[] {SKColor.Parse("1E8500"), SKColor.Parse("5BFD00")};
- DisplayName = string.Empty;
- Description = string.Empty;
- Width = 512;
- Height = 512;
- Margin = 2;
- Object = uObject;
- Style = style;
- }
+ public void DrawBackground(SKCanvas c)
+ {
+ // reverse doesn't affect basic rarities
+ 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;
- 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
- };
- protected readonly SKPaint DescriptionPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Typeface = Utils.Typefaces.Description, TextSize = 13,
- Color = SKColors.White
- };
- protected readonly SKPaint ImagePaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High
- };
- private readonly SKPaint _textBackgroundPaint = new()
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = new SKColor(0, 0, 50, 75)
- };
- private readonly SKPaint _shortDescriptionPaint = new()
- {
- 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];
- Background[0].ToHsl(out _, out _, out var l1);
- Background[1].ToHsl(out _, out _, out var l2);
- var reverse = l1 > l2;
-
- // border
- c.DrawRect(new SKRect(0, 0, Width, Height),
- new SKPaint
- {
- IsAntialias = true, FilterQuality = SKFilterQuality.High,
- Shader = SKShader.CreateLinearGradient(
- new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Border, SKShaderTileMode.Clamp)
- });
-
- if (this is BaseIcon {SeriesBackground: { }} baseIcon)
- c.DrawBitmap(baseIcon.SeriesBackground, new SKRect(baseIcon.Margin, baseIcon.Margin, baseIcon.Width - baseIcon.Margin,
- baseIcon.Height - baseIcon.Margin), ImagePaint);
- else
+ // border
+ c.DrawRect(new SKRect(0, 0, Width, Height),
+ new SKPaint
{
- 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;
+ IsAntialias = true, FilterQuality = SKFilterQuality.High,
+ Shader = SKShader.CreateLinearGradient(
+ new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Border, SKShaderTileMode.Clamp)
+ });
- 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
- {
- 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;
- }
- }
- }
- }
-
- protected void DrawPreview(SKCanvas c)
- => c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, Margin, Width - Margin, Height - Margin), ImagePaint);
-
- protected void DrawTextBackground(SKCanvas c)
+ if (this is BaseIcon { SeriesBackground: { } } baseIcon)
+ c.DrawBitmap(baseIcon.SeriesBackground, new SKRect(baseIcon.Margin, baseIcon.Margin, baseIcon.Width - baseIcon.Margin,
+ baseIcon.Height - baseIcon.Margin), ImagePaint);
+ else
{
- 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);
+ 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, _STARTER_TEXT_POSITION, Width - Margin, Height - Margin), _textBackgroundPaint);
- break;
- }
- }
-
- protected void DrawDisplayName(SKCanvas c)
- {
- if (string.IsNullOrWhiteSpace(DisplayName)) return;
-
- while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
- {
- DisplayNamePaint.TextSize -= 1;
- }
-
- var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
- var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
- var x = (Width - shapedText.Points[^1].X) / 2;
- var y = _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE;
-
- switch (Style)
- {
- case EIconStyle.Flat:
{
- DisplayNamePaint.TextAlign = SKTextAlign.Right;
- x = Width - Margin * 2 - shapedText.Points[^1].X;
+ 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;
}
}
-
-#if DEBUG
- c.DrawLine(x, 0, x, Width, new SKPaint {Color = SKColors.Blue, IsStroke = true});
- c.DrawLine(x + shapedText.Points[^1].X, 0, x + shapedText.Points[^1].X, Width, new SKPaint {Color = SKColors.Blue, IsStroke = true});
- c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, y), new SKPaint {Color = SKColors.Blue, IsStroke = true});
-#endif
-
- c.DrawShapedText(shaper, DisplayName, x, y, DisplayNamePaint);
- }
-
- protected void DrawDescription(SKCanvas c)
- {
- if (string.IsNullOrWhiteSpace(Description)) return;
-
- var maxLine = string.IsNullOrEmpty(DisplayName) ? 8 : 4;
- var side = SKTextAlign.Center;
- switch (Style)
- {
- case EIconStyle.Flat:
- side = SKTextAlign.Right;
- break;
- }
-
- Utils.DrawCenteredMultilineText(c, Description, maxLine, Width, Margin, side,
- new SKRect(Margin, string.IsNullOrEmpty(DisplayName) ? _STARTER_TEXT_POSITION : _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE, Width - Margin, Height - _BOTTOM_TEXT_SIZE), DescriptionPaint);
- }
-
- protected void DrawToBottom(SKCanvas c, SKTextAlign side, string text)
- {
- if (string.IsNullOrEmpty(text)) return;
-
- _shortDescriptionPaint.TextAlign = side;
- _shortDescriptionPaint.TextSize = Utils.Typefaces.Bottom == null ? 15 : 13;
- switch (side)
- {
- case SKTextAlign.Left:
- _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
- var shaper = new CustomSKShaper(_shortDescriptionPaint.Typeface);
- shaper.Shape(text, _shortDescriptionPaint);
-
- c.DrawShapedText(shaper, text, Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
- break;
- case SKTextAlign.Right:
- _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.Default;
- c.DrawText(text, Width - Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
- break;
- }
}
}
-}
+
+ protected void DrawPreview(SKCanvas c)
+ => c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, Margin, Width - Margin, Height - Margin), ImagePaint);
+
+ protected void DrawTextBackground(SKCanvas c)
+ {
+ 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;
+ }
+ default:
+ c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, Height - Margin), _textBackgroundPaint);
+ break;
+ }
+ }
+
+ protected void DrawDisplayName(SKCanvas c)
+ {
+ if (string.IsNullOrWhiteSpace(DisplayName)) return;
+
+ while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
+ {
+ DisplayNamePaint.TextSize -= 1;
+ }
+
+ var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
+ var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
+ var x = (Width - shapedText.Points[^1].X) / 2;
+ var y = _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE;
+
+ switch (Style)
+ {
+ case EIconStyle.Flat:
+ {
+ DisplayNamePaint.TextAlign = SKTextAlign.Right;
+ x = Width - Margin * 2 - shapedText.Points[^1].X;
+ break;
+ }
+ }
+
+#if DEBUG
+ c.DrawLine(x, 0, x, Width, new SKPaint { Color = SKColors.Blue, IsStroke = true });
+ c.DrawLine(x + shapedText.Points[^1].X, 0, x + shapedText.Points[^1].X, Width, new SKPaint { Color = SKColors.Blue, IsStroke = true });
+ c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, y), new SKPaint { Color = SKColors.Blue, IsStroke = true });
+#endif
+
+ c.DrawShapedText(shaper, DisplayName, x, y, DisplayNamePaint);
+ }
+
+ protected void DrawDescription(SKCanvas c)
+ {
+ if (string.IsNullOrWhiteSpace(Description)) return;
+
+ var maxLine = string.IsNullOrEmpty(DisplayName) ? 8 : 4;
+ var side = SKTextAlign.Center;
+ switch (Style)
+ {
+ case EIconStyle.Flat:
+ side = SKTextAlign.Right;
+ break;
+ }
+
+ Utils.DrawCenteredMultilineText(c, Description, maxLine, Width, Margin, side,
+ new SKRect(Margin, string.IsNullOrEmpty(DisplayName) ? _STARTER_TEXT_POSITION : _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE, Width - Margin, Height - _BOTTOM_TEXT_SIZE), DescriptionPaint);
+ }
+
+ protected void DrawToBottom(SKCanvas c, SKTextAlign side, string text)
+ {
+ if (string.IsNullOrEmpty(text)) return;
+
+ _shortDescriptionPaint.TextAlign = side;
+ _shortDescriptionPaint.TextSize = Utils.Typefaces.Bottom == null ? 15 : 13;
+ switch (side)
+ {
+ case SKTextAlign.Left:
+ _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
+ var shaper = new CustomSKShaper(_shortDescriptionPaint.Typeface);
+ shaper.Shape(text, _shortDescriptionPaint);
+
+ c.DrawShapedText(shaper, text, Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
+ break;
+ case SKTextAlign.Right:
+ _shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.Default;
+ c.DrawText(text, Width - Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/CreatorPackage.cs b/FModel/Creator/CreatorPackage.cs
index c19bea00..22a842e7 100644
--- a/FModel/Creator/CreatorPackage.cs
+++ b/FModel/Creator/CreatorPackage.cs
@@ -6,247 +6,246 @@ using FModel.Creator.Bases.BB;
using FModel.Creator.Bases.FN;
using FModel.Creator.Bases.SB;
-namespace FModel.Creator
+namespace FModel.Creator;
+
+public class CreatorPackage : IDisposable
{
- public class CreatorPackage : IDisposable
+ private UObject _object;
+ private EIconStyle _style;
+
+ public CreatorPackage(UObject uObject, EIconStyle style)
{
- private UObject _object;
- private EIconStyle _style;
+ _object = uObject;
+ _style = style;
+ }
- public CreatorPackage(UObject uObject, EIconStyle style)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public UCreator ConstructCreator()
+ {
+ TryConstructCreator(out var creator);
+ return creator;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryConstructCreator(out UCreator creator)
+ {
+ switch (_object.ExportType)
{
- _object = uObject;
- _style = style;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public UCreator ConstructCreator()
- {
- TryConstructCreator(out var creator);
- return creator;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryConstructCreator(out UCreator creator)
- {
- switch (_object.ExportType)
- {
- // Fortnite
- case "FortCreativeWeaponMeleeItemDefinition":
- case "AthenaConsumableEmoteItemDefinition":
- case "AthenaSkyDiveContrailItemDefinition":
- case "AthenaLoadingScreenItemDefinition":
- case "AthenaVictoryPoseItemDefinition":
- case "AthenaPetCarrierItemDefinition":
- case "AthenaMusicPackItemDefinition":
- case "AthenaBattleBusItemDefinition":
- case "AthenaCharacterItemDefinition":
- case "AthenaMapMarkerItemDefinition":
- case "AthenaBackpackItemDefinition":
- case "AthenaPickaxeItemDefinition":
- case "AthenaGadgetItemDefinition":
- case "AthenaGliderItemDefinition":
- case "AthenaSprayItemDefinition":
- case "AthenaDanceItemDefinition":
- case "AthenaEmojiItemDefinition":
- case "AthenaItemWrapDefinition":
- case "AthenaToyItemDefinition":
- case "FortHeroType":
- case "FortTokenType":
- case "FortAbilityKit":
- case "FortWorkerType":
- case "RewardGraphToken":
- case "FortBannerTokenType":
- case "FortVariantTokenType":
- case "FortDecoItemDefinition":
- case "FortStatItemDefinition":
- case "FortAmmoItemDefinition":
- case "FortEmoteItemDefinition":
- case "FortBadgeItemDefinition":
- case "FortAwardItemDefinition":
- case "FortGadgetItemDefinition":
- case "AthenaCharmItemDefinition":
- case "FortPlaysetItemDefinition":
- case "FortGiftBoxItemDefinition":
- case "FortOutpostItemDefinition":
- case "FortVehicleItemDefinition":
- case "FortCardPackItemDefinition":
- case "FortDefenderItemDefinition":
- case "FortCurrencyItemDefinition":
- case "FortResourceItemDefinition":
- case "FortBackpackItemDefinition":
- case "FortEventQuestMapDataAsset":
- case "FortWeaponModItemDefinition":
- case "FortCodeTokenItemDefinition":
- case "FortSchematicItemDefinition":
- case "FortWorldMultiItemDefinition":
- case "FortAlterationItemDefinition":
- case "FortExpeditionItemDefinition":
- case "FortIngredientItemDefinition":
- case "FortAccountBuffItemDefinition":
- case "FortWeaponMeleeItemDefinition":
- case "FortPlayerPerksItemDefinition":
- case "FortPlaysetPropItemDefinition":
- case "FortHomebaseNodeItemDefinition":
- case "FortNeverPersistItemDefinition":
- case "RadioContentSourceItemDefinition":
- case "FortPlaysetGrenadeItemDefinition":
- case "FortPersonalVehicleItemDefinition":
- case "FortGameplayModifierItemDefinition":
- case "FortHardcoreModifierItemDefinition":
- case "FortConsumableAccountItemDefinition":
- case "FortConversionControlItemDefinition":
- case "FortAccountBuffCreditItemDefinition":
- case "FortEventCurrencyItemDefinitionRedir":
- case "FortPersistentResourceItemDefinition":
- case "FortHomebaseBannerIconItemDefinition":
- case "FortCampaignHeroLoadoutItemDefinition":
- case "FortConditionalResourceItemDefinition":
- case "FortChallengeBundleScheduleDefinition":
- case "FortWeaponMeleeDualWieldItemDefinition":
- case "FortDailyRewardScheduleTokenDefinition":
- case "FortCreativeRealEstatePlotItemDefinition":
- case "AthenaDanceItemDefinition_AdHocSquadsJoin_C":
- creator = _style switch
- {
- EIconStyle.Cataba => new BaseCommunity(_object, _style, "Cataba"),
- _ => new BaseIcon(_object, _style)
- };
- return true;
- case "FortTandemCharacterData":
- creator = new BaseTandem(_object, _style);
- return true;
- case "FortTrapItemDefinition":
- case "FortSpyTechItemDefinition":
- case "FortAccoladeItemDefinition":
- case "FortContextTrapItemDefinition":
- case "FortWeaponRangedItemDefinition":
- case "Daybreak_LevelExitVehicle_PartItemDefinition_C":
- creator = new BaseIconStats(_object, _style);
- return true;
- case "FortItemSeriesDefinition":
- creator = new BaseSeries(_object, _style);
- return true;
- case "MaterialInstanceConstant"
- when _object.Owner != null &&
- (_object.Owner.Name.EndsWith($"/MI_OfferImages/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
- _object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
- _object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}", StringComparison.OrdinalIgnoreCase)):
- creator = new BaseMaterialInstance(_object, _style);
- return true;
- case "AthenaItemShopOfferDisplayData":
- creator = new BaseOfferDisplayData(_object, _style);
- return true;
- case "FortMtxOfferData":
- creator = new BaseMtxOffer(_object, _style);
- return true;
- case "FortPlaylistAthena":
- creator = new BasePlaylist(_object, _style);
- return true;
- case "FortFeatItemDefinition":
- case "FortQuestItemDefinition":
- case "FortQuestItemDefinition_Athena":
- case "AthenaDailyQuestDefinition":
- case "FortUrgentQuestItemDefinition":
- creator = new BaseQuest(_object, _style);
- return true;
- case "FortCompendiumItemDefinition":
- case "FortChallengeBundleItemDefinition":
- creator = new BaseBundle(_object, _style);
- return true;
- case "AthenaSeasonItemDefinition":
- creator = new BaseSeason(_object, _style);
- return true;
- case "FortItemAccessTokenType":
- creator = new BaseItemAccessToken(_object, _style);
- return true;
- 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, _style);
- return true;
- // Battle Breakers
- case "WExpGenericAccountItemDefinition":
- case "WExpGearAccountItemDefinition":
- case "WExpHQWorkerLodgesDefinition":
- case "WExpPersonalEventDefinition":
- case "WExpUpgradePotionDefinition":
- case "WExpAccountRewardDefinition":
- case "WExpHQBlacksmithDefinition":
- case "WExpHQSecretShopDefinition":
- case "WExpHQMonsterPitDefinition":
- case "WExpHQHeroTowerDefinition":
- case "WExpVoucherItemDefinition":
- case "WExpTreasureMapDefinition":
- case "WExpHammerChestDefinition":
- case "WExpHQWorkshopDefinition":
- case "WExpUnlockableDefinition":
- case "WExpHQSmelterDefinition":
- case "WExpContainerDefinition":
- case "WExpCharacterDefinition":
- case "WExpHQMarketDefinition":
- case "WExpGiftboxDefinition":
- case "WExpStandInDefinition":
- case "WExpRegionDefinition":
- case "WExpHQMineDefinition":
- case "WExpXpBookDefinition":
- case "WExpTokenDefinition":
- case "WExpItemDefinition":
- case "WExpZoneDefinition":
- creator = new BaseBreakersIcon(_object, EIconStyle.Default);
- return true;
- // Spellbreak
- case "GTargetedTeleportActiveSkill":
- case "GChronomasterV2ActiveSkill":
- case "GShadowstepActiveSkill":
- case "GGatewayActiveSkill":
- case "GStealthActiveSkill":
- case "GFeatherActiveSkill":
- case "GCosmeticDropTrail":
- case "GFlightActiveSkill":
- case "GCosmeticRunTrail":
- case "GCosmeticArtifact":
- case "GCosmeticTriumph":
- case "GWolfsbloodSkill":
- case "GDashActiveSkill":
- case "GCharacterPerk":
- case "GCosmeticTitle":
- case "GCosmeticBadge":
- case "GRMTStoreOffer":
- case "GCosmeticEmote":
- case "GCosmeticCard":
- case "GCosmeticSkin":
- case "GStoreOffer":
- case "GAccolade":
- case "GRuneItem":
- case "GQuest":
- creator = new BaseSpellIcon(_object, EIconStyle.Default);
- return true;
- case "GLeagueTier":
- creator = new BaseLeague(_object, EIconStyle.Default);
- return true;
- case "GLeagueDivision":
- creator = new BaseDivision(_object, EIconStyle.Default);
- return true;
- default:
- creator = null;
- return false;
- }
- }
-
- public override string ToString() => $"{_object.ExportType} | {_style}";
-
- public void Dispose()
- {
- _object = null;
+ // Fortnite
+ case "FortCreativeWeaponMeleeItemDefinition":
+ case "AthenaConsumableEmoteItemDefinition":
+ case "AthenaSkyDiveContrailItemDefinition":
+ case "AthenaLoadingScreenItemDefinition":
+ case "AthenaVictoryPoseItemDefinition":
+ case "AthenaPetCarrierItemDefinition":
+ case "AthenaMusicPackItemDefinition":
+ case "AthenaBattleBusItemDefinition":
+ case "AthenaCharacterItemDefinition":
+ case "AthenaMapMarkerItemDefinition":
+ case "AthenaBackpackItemDefinition":
+ case "AthenaPickaxeItemDefinition":
+ case "AthenaGadgetItemDefinition":
+ case "AthenaGliderItemDefinition":
+ case "AthenaSprayItemDefinition":
+ case "AthenaDanceItemDefinition":
+ case "AthenaEmojiItemDefinition":
+ case "AthenaItemWrapDefinition":
+ case "AthenaToyItemDefinition":
+ case "FortHeroType":
+ case "FortTokenType":
+ case "FortAbilityKit":
+ case "FortWorkerType":
+ case "RewardGraphToken":
+ case "FortBannerTokenType":
+ case "FortVariantTokenType":
+ case "FortDecoItemDefinition":
+ case "FortStatItemDefinition":
+ case "FortAmmoItemDefinition":
+ case "FortEmoteItemDefinition":
+ case "FortBadgeItemDefinition":
+ case "FortAwardItemDefinition":
+ case "FortGadgetItemDefinition":
+ case "AthenaCharmItemDefinition":
+ case "FortPlaysetItemDefinition":
+ case "FortGiftBoxItemDefinition":
+ case "FortOutpostItemDefinition":
+ case "FortVehicleItemDefinition":
+ case "FortCardPackItemDefinition":
+ case "FortDefenderItemDefinition":
+ case "FortCurrencyItemDefinition":
+ case "FortResourceItemDefinition":
+ case "FortBackpackItemDefinition":
+ case "FortEventQuestMapDataAsset":
+ case "FortWeaponModItemDefinition":
+ case "FortCodeTokenItemDefinition":
+ case "FortSchematicItemDefinition":
+ case "FortWorldMultiItemDefinition":
+ case "FortAlterationItemDefinition":
+ case "FortExpeditionItemDefinition":
+ case "FortIngredientItemDefinition":
+ case "FortAccountBuffItemDefinition":
+ case "FortWeaponMeleeItemDefinition":
+ case "FortPlayerPerksItemDefinition":
+ case "FortPlaysetPropItemDefinition":
+ case "FortHomebaseNodeItemDefinition":
+ case "FortNeverPersistItemDefinition":
+ case "RadioContentSourceItemDefinition":
+ case "FortPlaysetGrenadeItemDefinition":
+ case "FortPersonalVehicleItemDefinition":
+ case "FortGameplayModifierItemDefinition":
+ case "FortHardcoreModifierItemDefinition":
+ case "FortConsumableAccountItemDefinition":
+ case "FortConversionControlItemDefinition":
+ case "FortAccountBuffCreditItemDefinition":
+ case "FortEventCurrencyItemDefinitionRedir":
+ case "FortPersistentResourceItemDefinition":
+ case "FortHomebaseBannerIconItemDefinition":
+ case "FortCampaignHeroLoadoutItemDefinition":
+ case "FortConditionalResourceItemDefinition":
+ case "FortChallengeBundleScheduleDefinition":
+ case "FortWeaponMeleeDualWieldItemDefinition":
+ case "FortDailyRewardScheduleTokenDefinition":
+ case "FortCreativeRealEstatePlotItemDefinition":
+ case "AthenaDanceItemDefinition_AdHocSquadsJoin_C":
+ creator = _style switch
+ {
+ EIconStyle.Cataba => new BaseCommunity(_object, _style, "Cataba"),
+ _ => new BaseIcon(_object, _style)
+ };
+ return true;
+ case "FortTandemCharacterData":
+ creator = new BaseTandem(_object, _style);
+ return true;
+ case "FortTrapItemDefinition":
+ case "FortSpyTechItemDefinition":
+ case "FortAccoladeItemDefinition":
+ case "FortContextTrapItemDefinition":
+ case "FortWeaponRangedItemDefinition":
+ case "Daybreak_LevelExitVehicle_PartItemDefinition_C":
+ creator = new BaseIconStats(_object, _style);
+ return true;
+ case "FortItemSeriesDefinition":
+ creator = new BaseSeries(_object, _style);
+ return true;
+ case "MaterialInstanceConstant"
+ when _object.Owner != null &&
+ (_object.Owner.Name.EndsWith($"/MI_OfferImages/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
+ _object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
+ _object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}", StringComparison.OrdinalIgnoreCase)):
+ creator = new BaseMaterialInstance(_object, _style);
+ return true;
+ case "AthenaItemShopOfferDisplayData":
+ creator = new BaseOfferDisplayData(_object, _style);
+ return true;
+ case "FortMtxOfferData":
+ creator = new BaseMtxOffer(_object, _style);
+ return true;
+ case "FortPlaylistAthena":
+ creator = new BasePlaylist(_object, _style);
+ return true;
+ case "FortFeatItemDefinition":
+ case "FortQuestItemDefinition":
+ case "FortQuestItemDefinition_Athena":
+ case "AthenaDailyQuestDefinition":
+ case "FortUrgentQuestItemDefinition":
+ creator = new BaseQuest(_object, _style);
+ return true;
+ case "FortCompendiumItemDefinition":
+ case "FortChallengeBundleItemDefinition":
+ creator = new BaseBundle(_object, _style);
+ return true;
+ case "AthenaSeasonItemDefinition":
+ creator = new BaseSeason(_object, _style);
+ return true;
+ case "FortItemAccessTokenType":
+ creator = new BaseItemAccessToken(_object, _style);
+ return true;
+ 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, _style);
+ return true;
+ // Battle Breakers
+ case "WExpGenericAccountItemDefinition":
+ case "WExpGearAccountItemDefinition":
+ case "WExpHQWorkerLodgesDefinition":
+ case "WExpPersonalEventDefinition":
+ case "WExpUpgradePotionDefinition":
+ case "WExpAccountRewardDefinition":
+ case "WExpHQBlacksmithDefinition":
+ case "WExpHQSecretShopDefinition":
+ case "WExpHQMonsterPitDefinition":
+ case "WExpHQHeroTowerDefinition":
+ case "WExpVoucherItemDefinition":
+ case "WExpTreasureMapDefinition":
+ case "WExpHammerChestDefinition":
+ case "WExpHQWorkshopDefinition":
+ case "WExpUnlockableDefinition":
+ case "WExpHQSmelterDefinition":
+ case "WExpContainerDefinition":
+ case "WExpCharacterDefinition":
+ case "WExpHQMarketDefinition":
+ case "WExpGiftboxDefinition":
+ case "WExpStandInDefinition":
+ case "WExpRegionDefinition":
+ case "WExpHQMineDefinition":
+ case "WExpXpBookDefinition":
+ case "WExpTokenDefinition":
+ case "WExpItemDefinition":
+ case "WExpZoneDefinition":
+ creator = new BaseBreakersIcon(_object, EIconStyle.Default);
+ return true;
+ // Spellbreak
+ case "GTargetedTeleportActiveSkill":
+ case "GChronomasterV2ActiveSkill":
+ case "GShadowstepActiveSkill":
+ case "GGatewayActiveSkill":
+ case "GStealthActiveSkill":
+ case "GFeatherActiveSkill":
+ case "GCosmeticDropTrail":
+ case "GFlightActiveSkill":
+ case "GCosmeticRunTrail":
+ case "GCosmeticArtifact":
+ case "GCosmeticTriumph":
+ case "GWolfsbloodSkill":
+ case "GDashActiveSkill":
+ case "GCharacterPerk":
+ case "GCosmeticTitle":
+ case "GCosmeticBadge":
+ case "GRMTStoreOffer":
+ case "GCosmeticEmote":
+ case "GCosmeticCard":
+ case "GCosmeticSkin":
+ case "GStoreOffer":
+ case "GAccolade":
+ case "GRuneItem":
+ case "GQuest":
+ creator = new BaseSpellIcon(_object, EIconStyle.Default);
+ return true;
+ case "GLeagueTier":
+ creator = new BaseLeague(_object, EIconStyle.Default);
+ return true;
+ case "GLeagueDivision":
+ creator = new BaseDivision(_object, EIconStyle.Default);
+ return true;
+ default:
+ creator = null;
+ return false;
}
}
-}
+
+ public override string ToString() => $"{_object.ExportType} | {_style}";
+
+ public void Dispose()
+ {
+ _object = null;
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Typefaces.cs b/FModel/Creator/Typefaces.cs
index 45f5e552..2b84b600 100644
--- a/FModel/Creator/Typefaces.cs
+++ b/FModel/Creator/Typefaces.cs
@@ -6,274 +6,273 @@ using FModel.Settings;
using FModel.ViewModels;
using SkiaSharp;
-namespace FModel.Creator
+namespace FModel.Creator;
+
+public class Typefaces
{
- public class Typefaces
+ private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new("pack://application:,,,/Resources/BurbankBigCondensed-Bold.ttf");
+ private const string _EXT = ".ufont";
+
+ // FortniteGame
+ private const string _FORTNITE_BASE_PATH = "/Game/UI/Foundation/Fonts/";
+ private const string _ASIA_ERINM = "AsiaERINM"; // korean fortnite
+ private const string _BURBANK_BIG_CONDENSED_BLACK = "BurbankBigCondensed-Black"; // russian
+ private const string _BURBANK_BIG_REGULAR_BLACK = "BurbankBigRegular-Black";
+ private const string _BURBANK_BIG_REGULAR_BOLD = "BurbankBigRegular-Bold"; // official fortnite ig
+ private const string _BURBANK_SMALL_MEDIUM = "BurbankSmall-Medium";
+ private const string _DROID_SANS_FORTNITE_SUBSET = "DroidSans-Fortnite-Subset";
+ private const string _NIS_JYAU = "NIS_JYAU"; // japanese fortnite
+ private const string _NOTO_COLOR_EMOJI = "NotoColorEmoji";
+ private const string _NOTO_SANS_BOLD = "NotoSans-Bold";
+ private const string _NOTO_SANS_FORTNITE_BOLD = "NotoSans-Fortnite-Bold";
+ private const string _NOTO_SANS_FORTNITE_ITALIC = "NotoSans-Fortnite-Italic";
+ private const string _NOTO_SANS_FORTNITE_REGULAR = "NotoSans-Fortnite-Regular";
+ private const string _NOTO_SANS_ITALIC = "NotoSans-Italic";
+ private const string _NOTO_SANS_REGULAR = "NotoSans-Regular";
+ private const string _NOTO_SANS_ARABIC_BLACK = "NotoSansArabic-Black"; // arabic fortnite
+ private const string _NOTO_SANS_ARABIC_BOLD = "NotoSansArabic-Bold";
+ private const string _NOTO_SANS_ARABIC_REGULAR = "NotoSansArabic-Regular";
+ private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold";
+ private const string _NOTO_SANS_KR_REGULAR = "NotoSansKR-Regular";
+ private const string _NOTO_SANS_SC_BLACK = "NotoSansSC-Black"; // simplified chinese fortnite
+ private const string _NOTO_SANS_SC_REGULAR = "NotoSansSC-Regular";
+ private const string _NOTO_SANS_TC_BLACK = "NotoSansTC-Black"; // traditional chinese fortnite
+ private const string _NOTO_SANS_TC_REGULAR = "NotoSansTC-Regular";
+ private const string _BURBANK_SMALL_BLACK = "burbanksmall-black";
+ private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold";
+
+ // Spellbreak
+ private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/";
+ private const string _MONTSERRAT_SEMIBOLD = "Montserrat-Semibold";
+ private const string _MONTSERRAT_SEMIBOLD_ITALIC = "Montserrat-SemiBoldItalic";
+ private const string _NANUM_GOTHIC = "NanumGothic";
+ private const string _QUADRAT_BOLD = "Quadrat_Bold";
+ private const string _SEGOE_BOLD_ITALIC = "Segoe_Bold_Italic";
+
+ // WorldExplorers
+ private const string _BATTLE_BREAKERS_BASE_PATH = "/Game/UMG/Fonts/Faces/";
+ private const string _HEMIHEAD426 = "HemiHead426";
+ private const string _NOTO_SANS_JP_REGULAR = "NotoSansJP-Regular";
+ private const string _LATO_BLACK = "Lato-Black";
+ private const string _LATO_BLACK_ITALIC = "Lato-BlackItalic";
+ private const string _LATO_LIGHT = "Lato-Light";
+ private const string _LATO_MEDIUM = "Lato-Medium";
+
+ private readonly CUE4ParseViewModel _viewModel;
+
+ public readonly SKTypeface Default; // used as a fallback font for all untranslated strings (item source, ...)
+ public readonly SKTypeface DisplayName;
+ public readonly SKTypeface Description;
+ public readonly SKTypeface Bottom; // must be null for non-latin base languages
+ public readonly SKTypeface Bundle;
+ public readonly SKTypeface BundleNumber;
+ public readonly SKTypeface TandemDisplayName;
+ public readonly SKTypeface TandemGenDescription;
+ public readonly SKTypeface TandemAddDescription;
+
+ public Typefaces(CUE4ParseViewModel viewModel)
{
- private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new("pack://application:,,,/Resources/BurbankBigCondensed-Bold.ttf");
- private const string _EXT = ".ufont";
+ byte[] data;
+ _viewModel = viewModel;
+ var language = UserSettings.Default.AssetLanguage;
+ Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream);
- // FortniteGame
- private const string _FORTNITE_BASE_PATH = "/Game/UI/Foundation/Fonts/";
- private const string _ASIA_ERINM = "AsiaERINM"; // korean fortnite
- private const string _BURBANK_BIG_CONDENSED_BLACK = "BurbankBigCondensed-Black"; // russian
- private const string _BURBANK_BIG_REGULAR_BLACK = "BurbankBigRegular-Black";
- private const string _BURBANK_BIG_REGULAR_BOLD = "BurbankBigRegular-Bold"; // official fortnite ig
- private const string _BURBANK_SMALL_MEDIUM = "BurbankSmall-Medium";
- private const string _DROID_SANS_FORTNITE_SUBSET = "DroidSans-Fortnite-Subset";
- private const string _NIS_JYAU = "NIS_JYAU"; // japanese fortnite
- private const string _NOTO_COLOR_EMOJI = "NotoColorEmoji";
- private const string _NOTO_SANS_BOLD = "NotoSans-Bold";
- private const string _NOTO_SANS_FORTNITE_BOLD = "NotoSans-Fortnite-Bold";
- private const string _NOTO_SANS_FORTNITE_ITALIC = "NotoSans-Fortnite-Italic";
- private const string _NOTO_SANS_FORTNITE_REGULAR = "NotoSans-Fortnite-Regular";
- private const string _NOTO_SANS_ITALIC = "NotoSans-Italic";
- private const string _NOTO_SANS_REGULAR = "NotoSans-Regular";
- private const string _NOTO_SANS_ARABIC_BLACK = "NotoSansArabic-Black"; // arabic fortnite
- private const string _NOTO_SANS_ARABIC_BOLD = "NotoSansArabic-Bold";
- private const string _NOTO_SANS_ARABIC_REGULAR = "NotoSansArabic-Regular";
- private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold";
- private const string _NOTO_SANS_KR_REGULAR = "NotoSansKR-Regular";
- private const string _NOTO_SANS_SC_BLACK = "NotoSansSC-Black"; // simplified chinese fortnite
- private const string _NOTO_SANS_SC_REGULAR = "NotoSansSC-Regular";
- private const string _NOTO_SANS_TC_BLACK = "NotoSansTC-Black"; // traditional chinese fortnite
- private const string _NOTO_SANS_TC_REGULAR = "NotoSansTC-Regular";
- private const string _BURBANK_SMALL_BLACK = "burbanksmall-black";
- private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold";
-
- // Spellbreak
- private const string _SPELLBREAK_BASE_PATH = "/Game/UI/Fonts/";
- private const string _MONTSERRAT_SEMIBOLD = "Montserrat-Semibold";
- private const string _MONTSERRAT_SEMIBOLD_ITALIC = "Montserrat-SemiBoldItalic";
- private const string _NANUM_GOTHIC = "NanumGothic";
- private const string _QUADRAT_BOLD = "Quadrat_Bold";
- private const string _SEGOE_BOLD_ITALIC = "Segoe_Bold_Italic";
-
- // WorldExplorers
- private const string _BATTLE_BREAKERS_BASE_PATH = "/Game/UMG/Fonts/Faces/";
- private const string _HEMIHEAD426 = "HemiHead426";
- private const string _NOTO_SANS_JP_REGULAR = "NotoSansJP-Regular";
- private const string _LATO_BLACK = "Lato-Black";
- private const string _LATO_BLACK_ITALIC = "Lato-BlackItalic";
- private const string _LATO_LIGHT = "Lato-Light";
- private const string _LATO_MEDIUM = "Lato-Medium";
-
- private readonly CUE4ParseViewModel _viewModel;
-
- public readonly SKTypeface Default; // used as a fallback font for all untranslated strings (item source, ...)
- public readonly SKTypeface DisplayName;
- public readonly SKTypeface Description;
- public readonly SKTypeface Bottom; // must be null for non-latin base languages
- public readonly SKTypeface Bundle;
- public readonly SKTypeface BundleNumber;
- public readonly SKTypeface TandemDisplayName;
- public readonly SKTypeface TandemGenDescription;
- public readonly SKTypeface TandemAddDescription;
-
- public Typefaces(CUE4ParseViewModel viewModel)
+ switch (viewModel.Game)
{
- byte[] data;
- _viewModel = viewModel;
- var language = UserSettings.Default.AssetLanguage;
- Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream);
-
- switch (viewModel.Game)
+ case FGame.FortniteGame:
{
- case FGame.FortniteGame:
+ var namePath = _FORTNITE_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _ASIA_ERINM,
+ ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
+ ELanguage.Japanese => _NIS_JYAU,
+ ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
+ ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
+ ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
+ _ => string.Empty
+ };
+ if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data))
{
- var namePath = _FORTNITE_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _ASIA_ERINM,
- ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
- ELanguage.Japanese => _NIS_JYAU,
- ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
- ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
- ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
- _ => string.Empty
- };
- if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- DisplayName = SKTypeface.FromStream(m);
- }
- else DisplayName = Default;
+ var m = new MemoryStream(data) { Position = 0 };
+ DisplayName = SKTypeface.FromStream(m);
+ }
+ else DisplayName = Default;
- var descriptionPath = _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
- };
- if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- Description = SKTypeface.FromStream(m);
- }
- else Description = Default;
+ var descriptionPath = _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
+ };
+ if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ Description = SKTypeface.FromStream(m);
+ }
+ else Description = Default;
- var bottomPath = _FORTNITE_BASE_PATH +
+ var bottomPath = _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
+ };
+ if (viewModel.Provider.TrySaveAsset(bottomPath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ Bottom = SKTypeface.FromStream(m);
+ }
+ // else keep it null
+
+ if (viewModel.Provider.TrySaveAsset(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ BundleNumber = SKTypeface.FromStream(m);
+ }
+ else BundleNumber = Default;
+
+ var bundleNamePath = _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
+ ELanguage.Korean => _ASIA_ERINM,
+ ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
+ ELanguage.Japanese => _NIS_JYAU,
+ ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
+ ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
+ ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
+ _ => string.Empty
};
- if (viewModel.Provider.TrySaveAsset(bottomPath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- Bottom = SKTypeface.FromStream(m);
- }
- // else keep it null
-
- if (viewModel.Provider.TrySaveAsset(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- BundleNumber = SKTypeface.FromStream(m);
- }
- else BundleNumber = Default;
-
- var bundleNamePath = _FORTNITE_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _ASIA_ERINM,
- ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
- ELanguage.Japanese => _NIS_JYAU,
- ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
- ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
- ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
- _ => string.Empty
- };
- if (viewModel.Provider.TrySaveAsset(bundleNamePath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- Bundle = SKTypeface.FromStream(m);
- }
- else Bundle = BundleNumber;
-
- var tandemDisplayNamePath = _FORTNITE_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _ASIA_ERINM,
- ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
- ELanguage.Japanese => _NIS_JYAU,
- ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
- ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
- ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
- _ => _BURBANK_BIG_REGULAR_BLACK
- };
- if (viewModel.Provider.TrySaveAsset(tandemDisplayNamePath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- TandemDisplayName = SKTypeface.FromStream(m);
- }
- else TandemDisplayName = Default;
-
- var tandemGeneralDescPath = _FORTNITE_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _ASIA_ERINM,
- ELanguage.Japanese => _NIS_JYAU,
- ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
- ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
- ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
- _ => _BURBANK_SMALL_BLACK
- };
- if (viewModel.Provider.TrySaveAsset(tandemGeneralDescPath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- TandemGenDescription = SKTypeface.FromStream(m);
- }
- else TandemGenDescription = Default;
-
- var tandemAdditionalDescPath = _FORTNITE_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _ASIA_ERINM,
- ELanguage.Japanese => _NIS_JYAU,
- ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
- ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
- ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
- _ => _BURBANK_SMALL_BOLD
- };
- if (viewModel.Provider.TrySaveAsset(tandemAdditionalDescPath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- TandemAddDescription = SKTypeface.FromStream(m);
- }
- else TandemAddDescription = Default;
-
- break;
- }
- case FGame.WorldExplorers:
+ if (viewModel.Provider.TrySaveAsset(bundleNamePath + _EXT, out data))
{
- var namePath = _BATTLE_BREAKERS_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
- ELanguage.Russian => _LATO_BLACK,
- ELanguage.Japanese => _NOTO_SANS_JP_REGULAR,
- ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
- _ => _HEMIHEAD426
- };
- if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- DisplayName = SKTypeface.FromStream(m);
- }
- else DisplayName = Default;
-
- var descriptionPath = _BATTLE_BREAKERS_BASE_PATH +
- language switch
- {
- ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
- ELanguage.Russian => _LATO_BLACK,
- ELanguage.Japanese => _NOTO_SANS_JP_REGULAR,
- ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
- _ => _HEMIHEAD426
- };
- if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- Description = SKTypeface.FromStream(m);
- }
- else Description = Default;
-
- break;
+ var m = new MemoryStream(data) { Position = 0 };
+ Bundle = SKTypeface.FromStream(m);
}
- case FGame.g3:
+ else Bundle = BundleNumber;
+
+ var tandemDisplayNamePath = _FORTNITE_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _ASIA_ERINM,
+ ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
+ ELanguage.Japanese => _NIS_JYAU,
+ ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
+ ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
+ ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
+ _ => _BURBANK_BIG_REGULAR_BLACK
+ };
+ if (viewModel.Provider.TrySaveAsset(tandemDisplayNamePath + _EXT, out data))
{
- if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _QUADRAT_BOLD + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- DisplayName = SKTypeface.FromStream(m);
- }
- else DisplayName = Default;
-
- if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _MONTSERRAT_SEMIBOLD + _EXT, out data))
- {
- var m = new MemoryStream(data) { Position = 0 };
- Description = SKTypeface.FromStream(m);
- }
- else Description = Default;
-
- break;
+ var m = new MemoryStream(data) { Position = 0 };
+ TandemDisplayName = SKTypeface.FromStream(m);
}
+ else TandemDisplayName = Default;
+
+ var tandemGeneralDescPath = _FORTNITE_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _ASIA_ERINM,
+ ELanguage.Japanese => _NIS_JYAU,
+ ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
+ ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
+ ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
+ _ => _BURBANK_SMALL_BLACK
+ };
+ if (viewModel.Provider.TrySaveAsset(tandemGeneralDescPath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ TandemGenDescription = SKTypeface.FromStream(m);
+ }
+ else TandemGenDescription = Default;
+
+ var tandemAdditionalDescPath = _FORTNITE_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _ASIA_ERINM,
+ ELanguage.Japanese => _NIS_JYAU,
+ ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
+ ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
+ ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
+ _ => _BURBANK_SMALL_BOLD
+ };
+ if (viewModel.Provider.TrySaveAsset(tandemAdditionalDescPath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ TandemAddDescription = SKTypeface.FromStream(m);
+ }
+ else TandemAddDescription = Default;
+
+ break;
+ }
+ case FGame.WorldExplorers:
+ {
+ var namePath = _BATTLE_BREAKERS_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
+ ELanguage.Russian => _LATO_BLACK,
+ ELanguage.Japanese => _NOTO_SANS_JP_REGULAR,
+ ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
+ _ => _HEMIHEAD426
+ };
+ if (viewModel.Provider.TrySaveAsset(namePath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ DisplayName = SKTypeface.FromStream(m);
+ }
+ else DisplayName = Default;
+
+ var descriptionPath = _BATTLE_BREAKERS_BASE_PATH +
+ language switch
+ {
+ ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
+ ELanguage.Russian => _LATO_BLACK,
+ ELanguage.Japanese => _NOTO_SANS_JP_REGULAR,
+ ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
+ _ => _HEMIHEAD426
+ };
+ if (viewModel.Provider.TrySaveAsset(descriptionPath + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ Description = SKTypeface.FromStream(m);
+ }
+ else Description = Default;
+
+ break;
+ }
+ case FGame.g3:
+ {
+ if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _QUADRAT_BOLD + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ DisplayName = SKTypeface.FromStream(m);
+ }
+ else DisplayName = Default;
+
+ if (viewModel.Provider.TrySaveAsset(_SPELLBREAK_BASE_PATH + _MONTSERRAT_SEMIBOLD + _EXT, out data))
+ {
+ var m = new MemoryStream(data) { Position = 0 };
+ Description = SKTypeface.FromStream(m);
+ }
+ else Description = Default;
+
+ break;
}
}
-
- public SKTypeface OnTheFly(string path)
- {
- if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return Default;
- var m = new MemoryStream(data) { Position = 0 };
- return SKTypeface.FromStream(m);
- }
}
-}
+
+ public SKTypeface OnTheFly(string path)
+ {
+ if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return Default;
+ var m = new MemoryStream(data) { Position = 0 };
+ return SKTypeface.FromStream(m);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Creator/Utils.cs b/FModel/Creator/Utils.cs
index 57ebfe14..96757c73 100644
--- a/FModel/Creator/Utils.cs
+++ b/FModel/Creator/Utils.cs
@@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using System.Windows;
using CUE4Parse.UE4.Assets.Exports;
using CUE4Parse.UE4.Assets.Exports.Material;
using CUE4Parse.UE4.Assets.Exports.Texture;
@@ -19,382 +18,377 @@ using FModel.ViewModels;
using SkiaSharp;
using SkiaSharp.HarfBuzz;
-namespace FModel.Creator
+namespace FModel.Creator;
+
+public static class Utils
{
- public static class Utils
+ private static ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
+ private static readonly Regex _htmlRegex = new("<.*?>");
+ public static Typefaces Typefaces;
+
+ public static string RemoveHtmlTags(string s)
{
- private static ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
- private static readonly Regex _htmlRegex = new("<.*?>");
- public static Typefaces Typefaces;
-
- public static string RemoveHtmlTags(string s)
+ var match = _htmlRegex.Match(s);
+ while (match.Success)
{
- var match = _htmlRegex.Match(s);
- while (match.Success)
- {
- s = s.Replace(match.Value, string.Empty);
- match = match.NextMatch();
- }
-
- return s;
+ s = s.Replace(match.Value, string.Empty);
+ match = match.NextMatch();
}
- public static bool TryGetDisplayAsset(UObject uObject, out SKBitmap preview)
+ return s;
+ }
+
+ public static bool TryGetDisplayAsset(UObject uObject, out SKBitmap preview)
+ {
+ if (uObject.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
{
- if (uObject.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
- {
- preview = GetBitmap(sidePanelIcon);
- return preview != null;
- }
-
- var path = $"/Game/Catalog/MI_OfferImages/MI_{uObject.Name.Replace("Athena_Commando_", string.Empty)}";
- if (!TryLoadObject(path, out UMaterialInstanceConstant material)) // non-obfuscated item definition
- {
- if (!TryLoadObject($"{path[..path.LastIndexOf('_')]}_{path.SubstringAfterLast('_').ToLower()}", out material)) // Try to get MI with lowercase obfuscation
- TryLoadObject(path[..path.LastIndexOf('_')], out material); // hopefully gets obfuscated item definition
- }
-
- preview = GetBitmap(material);
+ preview = GetBitmap(sidePanelIcon);
return preview != null;
}
- public static SKBitmap GetBitmap(FPackageIndex packageIndex)
+ var path = $"/Game/Catalog/MI_OfferImages/MI_{uObject.Name.Replace("Athena_Commando_", string.Empty)}";
+ if (!TryLoadObject(path, out UMaterialInstanceConstant material)) // non-obfuscated item definition
{
- while (true)
+ if (!TryLoadObject($"{path[..path.LastIndexOf('_')]}_{path.SubstringAfterLast('_').ToLower()}", out material)) // Try to get MI with lowercase obfuscation
+ TryLoadObject(path[..path.LastIndexOf('_')], out material); // hopefully gets obfuscated item definition
+ }
+
+ preview = GetBitmap(material);
+ return preview != null;
+ }
+
+ public static SKBitmap GetBitmap(FPackageIndex packageIndex)
+ {
+ while (true)
+ {
+ if (!TryGetPackageIndexExport(packageIndex, out UObject export)) return null;
+ switch (export)
{
- if (!TryGetPackageIndexExport(packageIndex, out UObject export)) return null;
- switch (export)
+ case UTexture2D texture:
+ return GetBitmap(texture);
+ case UMaterialInstanceConstant material:
+ return GetBitmap(material);
+ default:
{
- case UTexture2D texture:
- return GetBitmap(texture);
- case UMaterialInstanceConstant material:
- return GetBitmap(material);
- default:
+ if (export.TryGetValue(out FSoftObjectPath previewImage, "LargePreviewImage", "SmallPreviewImage")) return GetBitmap(previewImage);
+ if (export.TryGetValue(out string largePreview, "LargePreviewImage")) return GetBitmap(largePreview);
+ if (export.TryGetValue(out FPackageIndex smallPreview, "SmallPreviewImage"))
{
- if (export.TryGetValue(out FSoftObjectPath previewImage, "LargePreviewImage", "SmallPreviewImage")) return GetBitmap(previewImage);
- if (export.TryGetValue(out string largePreview, "LargePreviewImage")) return GetBitmap(largePreview);
- if (export.TryGetValue(out FPackageIndex smallPreview, "SmallPreviewImage"))
- {
- packageIndex = smallPreview;
- continue;
- }
-
- return null;
+ packageIndex = smallPreview;
+ continue;
}
+
+ return null;
}
}
}
- public static SKBitmap GetBitmap(UMaterialInstanceConstant material)
- {
- if (material == null) return null;
- foreach (var textureParameter in material.TextureParameterValues)
- {
- if (!textureParameter.ParameterValue.TryLoad(out var texture)) continue;
- switch (textureParameter.ParameterInfo.Name.Text)
- {
- case "MainTex":
- case "TextureA":
- case "TextureB":
- case "OfferImage":
- case "KeyArtTexture":
- case "NPC-Portrait":
- {
- return GetBitmap(texture);
- }
- }
- }
-
- return null;
- }
- public static SKBitmap GetB64Bitmap(string b64) => SKBitmap.Decode(new MemoryStream(Convert.FromBase64String(b64)) {Position = 0});
- public static SKBitmap GetBitmap(FSoftObjectPath softObjectPath) => GetBitmap(softObjectPath.AssetPathName.Text);
- public static SKBitmap GetBitmap(string fullPath) => TryLoadObject(fullPath, out UTexture2D texture) ? GetBitmap(texture) : null;
- public static SKBitmap GetBitmap(UTexture2D texture) => texture.IsVirtual ? null : texture.Decode(UserSettings.Default.OverridedPlatform);
- public static SKBitmap GetBitmap(byte[] data) => SKBitmap.Decode(data);
-
- public static SKBitmap ResizeWithRatio(this SKBitmap me, double width, double height)
- {
- var ratioX = width / me.Width;
- var ratioY = height / me.Height;
- var ratio = ratioX < ratioY ? ratioX : ratioY;
- return me.Resize(Convert.ToInt32(me.Width * ratio), Convert.ToInt32(me.Height * ratio));
- }
- public static SKBitmap Resize(this SKBitmap me, int size) => me.Resize(size, size);
- public static SKBitmap Resize(this SKBitmap me, int width, int height)
- {
- var bmp = new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels);
- using var pixmap = bmp.PeekPixels();
- me.ScalePixels(pixmap, SKFilterQuality.Medium);
- return bmp;
- }
-
- public static void ClearToTransparent(this SKBitmap me, SKColor colorToDelete) {
- var colors = me.Pixels;
- for (var n = 0; n < colors.Length; n++) {
- if (colors[n] != colorToDelete) continue;
- colors[n] = SKColors.Transparent;
- }
- me.Pixels = colors;
- }
-
- public static bool TryGetPackageIndexExport(FPackageIndex packageIndex, out T export) where T : UObject
- {
- return packageIndex.TryLoad(out export);
- }
-
- // fullpath must be either without any extension or with the export objectname
- public static bool TryLoadObject(string fullPath, out T export) where T : UObject
- {
- return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export);
- }
-
- public static IEnumerable LoadExports(string fullPath)
- {
- return _applicationView.CUE4Parse.Provider.LoadObjectExports(fullPath);
- }
-
- public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f)
- {
- var max = maxFont;
- var min = 0f;
- var last = -1f;
- float value;
- while (true)
- {
- value = min + ((max - min) / 2);
- using (SKFont ft = new SKFont(typeface, value))
- using (SKPaint paint = new SKPaint(ft))
- {
- if (paint.MeasureText(text) > sectorSize)
- {
- last = value;
- max = value;
- }
- else
- {
- min = value;
- if (Math.Abs(last - value) <= degreeOfCertainty)
- return last;
-
- last = value;
- }
- }
- }
- }
-
- public static string GetLocalizedResource(string @namespace, string key, string defaultValue)
- {
- return _applicationView.CUE4Parse.Provider.GetLocalizedString(@namespace, key, defaultValue);
- }
-
- public static string GetFullPath(string partialPath)
- {
- var regex = new Regex(partialPath, RegexOptions.IgnoreCase | RegexOptions.Compiled);
- foreach (var path in _applicationView.CUE4Parse.Provider.Files.Keys)
- {
- if (regex.IsMatch(path))
- {
- return path;
- }
- }
-
- return string.Empty;
- }
-
- public static void DrawCenteredMultilineText(SKCanvas c, string text, int maxCount, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint)
- {
- var lineHeight = paint.TextSize * 1.2f;
- var lines = SplitLines(text, paint, area.Width - margin);
-
-#if DEBUG
- c.DrawRect(new SKRect(area.Left, area.Top, area.Right, area.Bottom), new SKPaint {Color = SKColors.Red, IsStroke = true});
-#endif
-
- if (lines == null) return;
- if (lines.Count <= maxCount) maxCount = lines.Count;
- var height = maxCount * lineHeight;
- var y = area.MidY - height / 2;
-
- var shaper = new CustomSKShaper(paint.Typeface);
- for (var i = 0; i < maxCount; i++)
- {
- var line = lines[i];
- if (line == null) continue;
-
- var lineText = line.Trim();
- var shapedText = shaper.Shape(lineText, paint);
-
- y += lineHeight;
- var x = side switch
- {
- SKTextAlign.Center => area.MidX - shapedText.Points[^1].X / 2,
- SKTextAlign.Right => size - margin - shapedText.Points[^1].X,
- SKTextAlign.Left => margin,
- _ => throw new NotImplementedException()
- };
-
- c.DrawShapedText(shaper, lineText, x, y, paint);
- }
- }
-
- public static void DrawMultilineText(SKCanvas c, string text, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint, out float yPos)
- {
- yPos = area.Top;
- var lineHeight = paint.TextSize * 1.2f;
- var lines = SplitLines(text, paint, area.Width);
- if (lines == null) return;
-
- foreach (var line in lines)
- {
- var fontSize = GetMaxFontSize(area.Width, paint.Typeface, line);
- if (paint.TextSize > fontSize) // if the text is not fitting in the line decrease the font size (CKJ languages)
- {
- paint.TextSize = fontSize;
- lineHeight = paint.TextSize * 1.2f;
- }
-
- if (line == null) continue;
- var lineText = line.Trim();
- var shaper = new CustomSKShaper(paint.Typeface);
- var shapedText = shaper.Shape(lineText, paint);
-
- var x = side switch
- {
- SKTextAlign.Center => area.MidX - shapedText.Points[^1].X / 2,
- SKTextAlign.Right => size - margin - shapedText.Points[^1].X,
- SKTextAlign.Left => area.Left,
- _ => throw new NotImplementedException()
- };
-
- c.DrawShapedText(shaper, lineText, x, yPos, paint);
- yPos += lineHeight;
- }
-
-#if DEBUG
- c.DrawRect(new SKRect(area.Left, area.Top - paint.TextSize, area.Right, yPos), new SKPaint {Color = SKColors.Red, IsStroke = true});
-#endif
- }
-
- #region Chinese, Korean and Japanese text split
- // https://github.com/YoungjaeKim/mikan.sharp/blob/master/MikanSharp/Mikan/Mikan.cs
-
- static string joshi = @"(でなければ|について|かしら|くらい|けれど|なのか|ばかり|ながら|ことよ|こそ|こと|さえ|しか|した|たり|だけ|だに|だの|つつ|ても|てよ|でも|とも|から|など|なり|ので|のに|ほど|まで|もの|やら|より|って|で|と|な|に|ね|の|も|は|ば|へ|や|わ|を|か|が|さ|し|ぞ|て)";
- static string keywords = @"(\ |[a-zA-Z0-9]+\.[a-z]{2,}|[一-龠々〆ヵヶゝ]+|[ぁ-んゝ]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+)";
- static string periods = @"([\.\,。、!\!?\?]+)$";
- static string bracketsBegin = @"([〈《「『「((\[【〔〚〖〘❮❬❪❨(<{❲❰{❴])";
- static string bracketsEnd = @"([〉》」』」))\]】〕〗〙〛}>\)❩❫❭❯❱❳❵}])";
-
- public static string[] SplitCKJText(string str)
- {
-
- var line1 = Regex.Split(str, keywords).ToList();
- var line2 = line1.SelectMany((o, _) => Regex.Split(o, joshi)).ToList();
- var line3 = line2.SelectMany((o, _) => Regex.Split(o, bracketsBegin)).ToList();
- var line4 = line3.SelectMany((o, _) => Regex.Split(o, bracketsEnd)).ToList();
- var words = line4.Where(o => !string.IsNullOrEmpty(o)).ToList();
-
- var prevType = string.Empty;
- var prevWord = string.Empty;
- List result = new List();
-
- words.ForEach(word =>
- {
- var token = Regex.IsMatch(word, periods) || Regex.IsMatch(word, joshi);
-
- if (Regex.IsMatch(word, bracketsBegin))
- {
- prevType = "braketBegin";
- prevWord = word;
- return;
- }
-
- if (Regex.IsMatch(word, bracketsEnd))
- {
- result[result.Count - 1] += word;
- prevType = "braketEnd";
- prevWord = word;
- return;
- }
-
- if (prevType == "braketBegin")
- {
- word = prevWord + word;
- prevWord = string.Empty;
- prevType = string.Empty;
- }
-
- // すでに文字が入っている上で助詞が続く場合は結合する
- if (result.Count > 0 && token && prevType == string.Empty)
- {
- result[result.Count - 1] += word;
- prevType = "keyword";
- prevWord = word;
- return;
- }
-
- // 単語のあとの文字がひらがななら結合する
- if (result.Count > 1 && token || (prevType == "keyword" && Regex.IsMatch(word, @"[ぁ-んゝ]+")))
- {
- result[result.Count - 1] += word;
- prevType = string.Empty;
- prevWord = word;
- return;
- }
-
- result.Add(word);
- prevType = "keyword";
- prevWord = word;
- });
- return result.ToArray();
- }
- #endregion
-
- public static List SplitLines(string text, SKPaint paint, float maxWidth)
- {
- if (string.IsNullOrEmpty(text)) return null;
-
- var spaceWidth = paint.MeasureText(" ");
- var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries);
-
- var ret = new List(lines.Length);
- foreach (var line in lines)
- {
- if (string.IsNullOrWhiteSpace(line)) continue;
-
- float width = 0;
- bool isCJK = false;
- var words = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
-
- if (words.Length <= 1 && UserSettings.Default.AssetLanguage is ELanguage.Japanese or ELanguage.Korean or ELanguage.Chinese or ELanguage.TraditionalChinese)
- {
- words = SplitCKJText(line);
- isCJK = true;
- }
-
- var lineResult = new StringBuilder();
- foreach (var word in words)
- {
- var wordWidth = paint.MeasureText(word);
- var wordWithSpaceWidth = wordWidth + spaceWidth;
- var wordWithSpace = isCJK ? word : word + " ";
-
- if (width + wordWidth > maxWidth)
- {
- ret.Add(lineResult.ToString());
- lineResult = new StringBuilder(wordWithSpace);
- width = wordWithSpaceWidth;
- }
- else
- {
- lineResult.Append(wordWithSpace);
- width += wordWithSpaceWidth;
- }
- }
-
- ret.Add(lineResult.ToString());
- }
-
- return ret;
- }
}
-}
+
+ public static SKBitmap GetBitmap(UMaterialInstanceConstant material)
+ {
+ if (material == null) return null;
+ foreach (var textureParameter in material.TextureParameterValues)
+ {
+ if (!textureParameter.ParameterValue.TryLoad(out var texture)) continue;
+ switch (textureParameter.ParameterInfo.Name.Text)
+ {
+ case "MainTex":
+ case "TextureA":
+ case "TextureB":
+ case "OfferImage":
+ case "KeyArtTexture":
+ case "NPC-Portrait":
+ {
+ return GetBitmap(texture);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static SKBitmap GetB64Bitmap(string b64) => SKBitmap.Decode(new MemoryStream(Convert.FromBase64String(b64)) { Position = 0 });
+ public static SKBitmap GetBitmap(FSoftObjectPath softObjectPath) => GetBitmap(softObjectPath.AssetPathName.Text);
+ public static SKBitmap GetBitmap(string fullPath) => TryLoadObject(fullPath, out UTexture2D texture) ? GetBitmap(texture) : null;
+ public static SKBitmap GetBitmap(UTexture2D texture) => texture.IsVirtual ? null : texture.Decode(UserSettings.Default.OverridedPlatform);
+ public static SKBitmap GetBitmap(byte[] data) => SKBitmap.Decode(data);
+
+ public static SKBitmap ResizeWithRatio(this SKBitmap me, double width, double height)
+ {
+ var ratioX = width / me.Width;
+ var ratioY = height / me.Height;
+ var ratio = ratioX < ratioY ? ratioX : ratioY;
+ return me.Resize(Convert.ToInt32(me.Width * ratio), Convert.ToInt32(me.Height * ratio));
+ }
+
+ public static SKBitmap Resize(this SKBitmap me, int size) => me.Resize(size, size);
+
+ public static SKBitmap Resize(this SKBitmap me, int width, int height)
+ {
+ var bmp = new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels);
+ using var pixmap = bmp.PeekPixels();
+ me.ScalePixels(pixmap, SKFilterQuality.Medium);
+ return bmp;
+ }
+
+ public static bool TryGetPackageIndexExport(FPackageIndex packageIndex, out T export) where T : UObject
+ {
+ return packageIndex.TryLoad(out export);
+ }
+
+ // fullpath must be either without any extension or with the export objectname
+ public static bool TryLoadObject(string fullPath, out T export) where T : UObject
+ {
+ return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export);
+ }
+
+ public static IEnumerable LoadExports(string fullPath)
+ {
+ return _applicationView.CUE4Parse.Provider.LoadObjectExports(fullPath);
+ }
+
+ public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f)
+ {
+ var max = maxFont;
+ var min = 0f;
+ var last = -1f;
+ float value;
+ while (true)
+ {
+ value = min + ((max - min) / 2);
+ using (SKFont ft = new SKFont(typeface, value))
+ using (SKPaint paint = new SKPaint(ft))
+ {
+ if (paint.MeasureText(text) > sectorSize)
+ {
+ last = value;
+ max = value;
+ }
+ else
+ {
+ min = value;
+ if (Math.Abs(last - value) <= degreeOfCertainty)
+ return last;
+
+ last = value;
+ }
+ }
+ }
+ }
+
+ public static string GetLocalizedResource(string @namespace, string key, string defaultValue)
+ {
+ return _applicationView.CUE4Parse.Provider.GetLocalizedString(@namespace, key, defaultValue);
+ }
+
+ public static string GetFullPath(string partialPath)
+ {
+ var regex = new Regex(partialPath, RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ foreach (var path in _applicationView.CUE4Parse.Provider.Files.Keys)
+ {
+ if (regex.IsMatch(path))
+ {
+ return path;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ public static void DrawCenteredMultilineText(SKCanvas c, string text, int maxCount, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint)
+ {
+ var lineHeight = paint.TextSize * 1.2f;
+ var lines = SplitLines(text, paint, area.Width - margin);
+
+#if DEBUG
+ c.DrawRect(new SKRect(area.Left, area.Top, area.Right, area.Bottom), new SKPaint { Color = SKColors.Red, IsStroke = true });
+#endif
+
+ if (lines == null) return;
+ if (lines.Count <= maxCount) maxCount = lines.Count;
+ var height = maxCount * lineHeight;
+ var y = area.MidY - height / 2;
+
+ var shaper = new CustomSKShaper(paint.Typeface);
+ for (var i = 0; i < maxCount; i++)
+ {
+ var line = lines[i];
+ if (line == null) continue;
+
+ var lineText = line.Trim();
+ var shapedText = shaper.Shape(lineText, paint);
+
+ y += lineHeight;
+ var x = side switch
+ {
+ SKTextAlign.Center => area.MidX - shapedText.Points[^1].X / 2,
+ SKTextAlign.Right => size - margin - shapedText.Points[^1].X,
+ SKTextAlign.Left => margin,
+ _ => throw new NotImplementedException()
+ };
+
+ c.DrawShapedText(shaper, lineText, x, y, paint);
+ }
+ }
+
+ public static void DrawMultilineText(SKCanvas c, string text, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint, out float yPos)
+ {
+ yPos = area.Top;
+ var lineHeight = paint.TextSize * 1.2f;
+ var lines = SplitLines(text, paint, area.Width);
+ if (lines == null) return;
+
+ foreach (var line in lines)
+ {
+ var fontSize = GetMaxFontSize(area.Width, paint.Typeface, line);
+ if (paint.TextSize > fontSize) // if the text is not fitting in the line decrease the font size (CKJ languages)
+ {
+ paint.TextSize = fontSize;
+ lineHeight = paint.TextSize * 1.2f;
+ }
+
+ if (line == null) continue;
+ var lineText = line.Trim();
+ var shaper = new CustomSKShaper(paint.Typeface);
+ var shapedText = shaper.Shape(lineText, paint);
+
+ var x = side switch
+ {
+ SKTextAlign.Center => area.MidX - shapedText.Points[^1].X / 2,
+ SKTextAlign.Right => size - margin - shapedText.Points[^1].X,
+ SKTextAlign.Left => area.Left,
+ _ => throw new NotImplementedException()
+ };
+
+ c.DrawShapedText(shaper, lineText, x, yPos, paint);
+ yPos += lineHeight;
+ }
+
+#if DEBUG
+ c.DrawRect(new SKRect(area.Left, area.Top - paint.TextSize, area.Right, yPos), new SKPaint { Color = SKColors.Red, IsStroke = true });
+#endif
+ }
+
+ #region Chinese, Korean and Japanese text split
+
+ // https://github.com/YoungjaeKim/mikan.sharp/blob/master/MikanSharp/Mikan/Mikan.cs
+
+ static string joshi = @"(でなければ|について|かしら|くらい|けれど|なのか|ばかり|ながら|ことよ|こそ|こと|さえ|しか|した|たり|だけ|だに|だの|つつ|ても|てよ|でも|とも|から|など|なり|ので|のに|ほど|まで|もの|やら|より|って|で|と|な|に|ね|の|も|は|ば|へ|や|わ|を|か|が|さ|し|ぞ|て)";
+ static string keywords = @"(\ |[a-zA-Z0-9]+\.[a-z]{2,}|[一-龠々〆ヵヶゝ]+|[ぁ-んゝ]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+)";
+ static string periods = @"([\.\,。、!\!?\?]+)$";
+ static string bracketsBegin = @"([〈《「『「((\[【〔〚〖〘❮❬❪❨(<{❲❰{❴])";
+ static string bracketsEnd = @"([〉》」』」))\]】〕〗〙〛}>\)❩❫❭❯❱❳❵}])";
+
+ public static string[] SplitCKJText(string str)
+ {
+ var line1 = Regex.Split(str, keywords).ToList();
+ var line2 = line1.SelectMany((o, _) => Regex.Split(o, joshi)).ToList();
+ var line3 = line2.SelectMany((o, _) => Regex.Split(o, bracketsBegin)).ToList();
+ var line4 = line3.SelectMany((o, _) => Regex.Split(o, bracketsEnd)).ToList();
+ var words = line4.Where(o => !string.IsNullOrEmpty(o)).ToList();
+
+ var prevType = string.Empty;
+ var prevWord = string.Empty;
+ List result = new List();
+
+ words.ForEach(word =>
+ {
+ var token = Regex.IsMatch(word, periods) || Regex.IsMatch(word, joshi);
+
+ if (Regex.IsMatch(word, bracketsBegin))
+ {
+ prevType = "braketBegin";
+ prevWord = word;
+ return;
+ }
+
+ if (Regex.IsMatch(word, bracketsEnd))
+ {
+ result[result.Count - 1] += word;
+ prevType = "braketEnd";
+ prevWord = word;
+ return;
+ }
+
+ if (prevType == "braketBegin")
+ {
+ word = prevWord + word;
+ prevWord = string.Empty;
+ prevType = string.Empty;
+ }
+
+ // すでに文字が入っている上で助詞が続く場合は結合する
+ if (result.Count > 0 && token && prevType == string.Empty)
+ {
+ result[result.Count - 1] += word;
+ prevType = "keyword";
+ prevWord = word;
+ return;
+ }
+
+ // 単語のあとの文字がひらがななら結合する
+ if (result.Count > 1 && token || (prevType == "keyword" && Regex.IsMatch(word, @"[ぁ-んゝ]+")))
+ {
+ result[result.Count - 1] += word;
+ prevType = string.Empty;
+ prevWord = word;
+ return;
+ }
+
+ result.Add(word);
+ prevType = "keyword";
+ prevWord = word;
+ });
+ return result.ToArray();
+ }
+
+ #endregion
+
+ public static List SplitLines(string text, SKPaint paint, float maxWidth)
+ {
+ if (string.IsNullOrEmpty(text)) return null;
+
+ var spaceWidth = paint.MeasureText(" ");
+ var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+
+ var ret = new List(lines.Length);
+ foreach (var line in lines)
+ {
+ if (string.IsNullOrWhiteSpace(line)) continue;
+
+ float width = 0;
+ var isCJK = false;
+ var words = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+
+ if (words.Length <= 1 && UserSettings.Default.AssetLanguage is ELanguage.Japanese or ELanguage.Korean or ELanguage.Chinese or ELanguage.TraditionalChinese)
+ {
+ words = SplitCKJText(line);
+ isCJK = true;
+ }
+
+ var lineResult = new StringBuilder();
+ foreach (var word in words)
+ {
+ var wordWidth = paint.MeasureText(word);
+ var wordWithSpaceWidth = wordWidth + spaceWidth;
+ var wordWithSpace = isCJK ? word : word + " ";
+
+ if (width + wordWidth > maxWidth)
+ {
+ ret.Add(lineResult.ToString());
+ lineResult = new StringBuilder(wordWithSpace);
+ width = wordWithSpaceWidth;
+ }
+ else
+ {
+ lineResult.Append(wordWithSpace);
+ width += wordWithSpaceWidth;
+ }
+ }
+
+ ret.Add(lineResult.ToString());
+ }
+
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/FModel/Enums.cs b/FModel/Enums.cs
index 1e8c9ed5..04fff3f1 100644
--- a/FModel/Enums.cs
+++ b/FModel/Enums.cs
@@ -1,142 +1,141 @@
using System.ComponentModel;
-namespace FModel
+namespace FModel;
+
+public enum EBuildKind
{
- public enum EBuildKind
- {
- Debug,
- Release,
- Unknown
- }
-
- public enum EErrorKind
- {
- Ignore,
- Restart,
- ResetSettings
- }
-
- public enum SettingsOut
- {
- Restart,
- ReloadLocres,
- CheckForUpdates,
- Nothing
- }
-
- public enum EStatusKind
- {
- Ready, // ready
- Loading, // doing stuff
- Stopping, // trying to stop
- Stopped, // stopped
- Failed, // crashed
- Completed // worked
- }
-
- public enum EAesReload
- {
- [Description("Always")]
- Always,
- [Description("Never")]
- Never,
- [Description("Once Per Day")]
- OncePerDay
- }
-
- public enum EDiscordRpc
- {
- [Description("Always")]
- Always,
- [Description("Never")]
- Never
- }
-
- public enum FGame
- {
- [Description("Unknown")]
- Unknown,
- [Description("Fortnite")]
- FortniteGame,
- [Description("Valorant")]
- ShooterGame,
- [Description("Dead By Daylight")]
- DeadByDaylight,
- [Description("Borderlands 3")]
- OakGame,
- [Description("Minecraft Dungeons")]
- Dungeons,
- [Description("Battle Breakers")]
- WorldExplorers,
- [Description("Spellbreak")]
- g3,
- [Description("State Of Decay 2")]
- StateOfDecay2,
- [Description("The Cycle")]
- Prospect,
- [Description("The Outer Worlds")]
- Indiana,
- [Description("Rogue Company")]
- RogueCompany,
- [Description("Star Wars: Jedi Fallen Order")]
- SwGame,
- [Description("Core")]
- Platform,
- [Description("Days Gone")]
- BendGame,
- [Description("PLAYERUNKNOWN'S BATTLEGROUNDS")]
- TslGame,
- [Description("Splitgate")]
- PortalWars,
- [Description("GTA: The Trilogy - Definitive Edition")]
- Gameface,
- [Description("Sea of Thieves")]
- Athena
- }
-
- public enum ELoadingMode
- {
- [Description("Single")]
- Single,
- [Description("Multiple")]
- Multiple,
- [Description("All")]
- All,
- [Description("All (New)")]
- AllButNew,
- [Description("All (Modified)")]
- AllButModified
- }
-
- public enum EUpdateMode
- {
- [Description("Stable")]
- Stable,
- [Description("Beta")]
- Beta
- }
-
- public enum ECompressedAudio
- {
- [Description("Play the decompressed data")]
- PlayDecompressed,
- [Description("Play the compressed data (might not always be a valid audio data)")]
- PlayCompressed
- }
-
- public enum EIconStyle
- {
- [Description("Default")]
- Default,
- [Description("No Background")]
- NoBackground,
- [Description("No Text")]
- NoText,
- [Description("Flat")]
- Flat,
- [Description("Cataba")]
- Cataba,
- // [Description("Community")]
- // CommunityMade
- }
+ Debug,
+ Release,
+ Unknown
}
+
+public enum EErrorKind
+{
+ Ignore,
+ Restart,
+ ResetSettings
+}
+
+public enum SettingsOut
+{
+ Restart,
+ ReloadLocres,
+ CheckForUpdates,
+ Nothing
+}
+
+public enum EStatusKind
+{
+ Ready, // ready
+ Loading, // doing stuff
+ Stopping, // trying to stop
+ Stopped, // stopped
+ Failed, // crashed
+ Completed // worked
+}
+
+public enum EAesReload
+{
+ [Description("Always")]
+ Always,
+ [Description("Never")]
+ Never,
+ [Description("Once Per Day")]
+ OncePerDay
+}
+
+public enum EDiscordRpc
+{
+ [Description("Always")]
+ Always,
+ [Description("Never")]
+ Never
+}
+
+public enum FGame
+{
+ [Description("Unknown")]
+ Unknown,
+ [Description("Fortnite")]
+ FortniteGame,
+ [Description("Valorant")]
+ ShooterGame,
+ [Description("Dead By Daylight")]
+ DeadByDaylight,
+ [Description("Borderlands 3")]
+ OakGame,
+ [Description("Minecraft Dungeons")]
+ Dungeons,
+ [Description("Battle Breakers")]
+ WorldExplorers,
+ [Description("Spellbreak")]
+ g3,
+ [Description("State Of Decay 2")]
+ StateOfDecay2,
+ [Description("The Cycle")]
+ Prospect,
+ [Description("The Outer Worlds")]
+ Indiana,
+ [Description("Rogue Company")]
+ RogueCompany,
+ [Description("Star Wars: Jedi Fallen Order")]
+ SwGame,
+ [Description("Core")]
+ Platform,
+ [Description("Days Gone")]
+ BendGame,
+ [Description("PLAYERUNKNOWN'S BATTLEGROUNDS")]
+ TslGame,
+ [Description("Splitgate")]
+ PortalWars,
+ [Description("GTA: The Trilogy - Definitive Edition")]
+ Gameface,
+ [Description("Sea of Thieves")]
+ Athena
+}
+
+public enum ELoadingMode
+{
+ [Description("Single")]
+ Single,
+ [Description("Multiple")]
+ Multiple,
+ [Description("All")]
+ All,
+ [Description("All (New)")]
+ AllButNew,
+ [Description("All (Modified)")]
+ AllButModified
+}
+
+public enum EUpdateMode
+{
+ [Description("Stable")]
+ Stable,
+ [Description("Beta")]
+ Beta
+}
+
+public enum ECompressedAudio
+{
+ [Description("Play the decompressed data")]
+ PlayDecompressed,
+ [Description("Play the compressed data (might not always be a valid audio data)")]
+ PlayCompressed
+}
+
+public enum EIconStyle
+{
+ [Description("Default")]
+ Default,
+ [Description("No Background")]
+ NoBackground,
+ [Description("No Text")]
+ NoText,
+ [Description("Flat")]
+ Flat,
+ [Description("Cataba")]
+ Cataba,
+ // [Description("Community")]
+ // CommunityMade
+}
\ No newline at end of file
diff --git a/FModel/Extensions/AvalonExtensions.cs b/FModel/Extensions/AvalonExtensions.cs
index 55c6449c..412eb7b8 100644
--- a/FModel/Extensions/AvalonExtensions.cs
+++ b/FModel/Extensions/AvalonExtensions.cs
@@ -4,47 +4,46 @@ using System.Xml;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public static class AvalonExtensions
{
- public static class AvalonExtensions
+ private static readonly IHighlightingDefinition _jsonHighlighter = LoadHighlighter("Json.xshd");
+ private static readonly IHighlightingDefinition _iniHighlighter = LoadHighlighter("Ini.xshd");
+ private static readonly IHighlightingDefinition _xmlHighlighter = LoadHighlighter("Xml.xshd");
+ private static readonly IHighlightingDefinition _cppHighlighter = LoadHighlighter("Cpp.xshd");
+ private static readonly IHighlightingDefinition _changelogHighlighter = LoadHighlighter("Changelog.xshd");
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static IHighlightingDefinition LoadHighlighter(string resourceName)
{
- private static readonly IHighlightingDefinition _jsonHighlighter = LoadHighlighter("Json.xshd");
- private static readonly IHighlightingDefinition _iniHighlighter = LoadHighlighter("Ini.xshd");
- private static readonly IHighlightingDefinition _xmlHighlighter = LoadHighlighter("Xml.xshd");
- private static readonly IHighlightingDefinition _cppHighlighter = LoadHighlighter("Cpp.xshd");
- private static readonly IHighlightingDefinition _changelogHighlighter = LoadHighlighter("Changelog.xshd");
+ var executingAssembly = Assembly.GetExecutingAssembly();
+ using var stream = executingAssembly.GetManifestResourceStream($"{executingAssembly.GetName().Name}.Resources.{resourceName}");
+ using var reader = new XmlTextReader(stream);
+ return HighlightingLoader.Load(reader, HighlightingManager.Instance);
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static IHighlightingDefinition LoadHighlighter(string resourceName)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static IHighlightingDefinition HighlighterSelector(string ext)
+ {
+ switch (ext)
{
- var executingAssembly = Assembly.GetExecutingAssembly();
- using var stream = executingAssembly.GetManifestResourceStream($"{executingAssembly.GetName().Name}.Resources.{resourceName}");
- using var reader = new XmlTextReader(stream);
- return HighlightingLoader.Load(reader, HighlightingManager.Instance);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static IHighlightingDefinition HighlighterSelector(string ext)
- {
- switch (ext)
- {
- case "ini":
- case "csv":
- return _iniHighlighter;
- case "xml":
- return _xmlHighlighter;
- case "h":
- case "cpp":
- return _cppHighlighter;
- case "changelog":
- return _changelogHighlighter;
- case "bat":
- case "txt":
- case "po":
- return null;
- default:
- return _jsonHighlighter;
- }
+ case "ini":
+ case "csv":
+ return _iniHighlighter;
+ case "xml":
+ return _xmlHighlighter;
+ case "h":
+ case "cpp":
+ return _cppHighlighter;
+ case "changelog":
+ return _changelogHighlighter;
+ case "bat":
+ case "txt":
+ case "po":
+ return null;
+ default:
+ return _jsonHighlighter;
}
}
}
\ No newline at end of file
diff --git a/FModel/Extensions/ClipboardExtensions.cs b/FModel/Extensions/ClipboardExtensions.cs
index 557784b2..5ba68b0a 100644
--- a/FModel/Extensions/ClipboardExtensions.cs
+++ b/FModel/Extensions/ClipboardExtensions.cs
@@ -1,5 +1,4 @@
using SkiaSharp;
-
using System;
using System.Drawing;
using System.Drawing.Imaging;
@@ -8,191 +7,192 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public static class ClipboardExtensions
{
- public static class ClipboardExtensions
+ public static void SetImage(byte[] pngBytes, string fileName = null)
{
- public static void SetImage(byte[] pngBytes, string fileName = null)
+ Clipboard.Clear();
+ var data = new DataObject();
+ using var pngMs = new MemoryStream(pngBytes);
+ using var image = Image.FromStream(pngMs);
+ // As standard bitmap, without transparency support
+ data.SetData(DataFormats.Bitmap, image, true);
+ // As PNG. Gimp will prefer this over the other two
+ data.SetData("PNG", pngMs, false);
+ // As DIB. This is (wrongly) accepted as ARGB by many applications
+ using var dibMemStream = new MemoryStream(ConvertToDib(image));
+ data.SetData(DataFormats.Dib, dibMemStream, false);
+ // Optional fileName
+ if (!string.IsNullOrEmpty(fileName))
{
- Clipboard.Clear();
- var data = new DataObject();
- using var pngMs = new MemoryStream(pngBytes);
- using var image = Image.FromStream(pngMs);
- // As standard bitmap, without transparency support
- data.SetData(DataFormats.Bitmap, image, true);
- // As PNG. Gimp will prefer this over the other two
- data.SetData("PNG", pngMs, false);
- // As DIB. This is (wrongly) accepted as ARGB by many applications
- using var dibMemStream = new MemoryStream(ConvertToDib(image));
- data.SetData(DataFormats.Dib, dibMemStream, false);
- // Optional fileName
- if (!string.IsNullOrEmpty(fileName))
+ var htmlFragment = GenerateHTMLFragment($"
");
+ data.SetData(DataFormats.Html, htmlFragment);
+ }
+ // The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation
+ Clipboard.SetDataObject(data, true);
+ }
+
+ public static byte[] ConvertToDib(Image image)
+ {
+ byte[] bm32bData;
+ var width = image.Width;
+ var height = image.Height;
+
+ // Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
+ using (var bm32b = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
+ {
+ using (var gr = Graphics.FromImage(bm32b))
{
- var htmlFragment = GenerateHTMLFragment($"
");
- data.SetData(DataFormats.Html, htmlFragment);
+ gr.DrawImage(image, new Rectangle(0, 0, width, height));
}
- // The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation
- Clipboard.SetDataObject(data, true);
+
+ // Bitmap format has its lines reversed.
+ bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
+ bm32bData = GetRawBytes(bm32b);
}
- public static byte[] ConvertToDib(Image image)
+ // BITMAPINFOHEADER struct for DIB.
+ const int hdrSize = 0x28;
+ var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
+ //Int32 biSize;
+ WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
+ //Int32 biWidth;
+ WriteIntToByteArray(fullImage, 0x04, 4, true, (uint) width);
+ //Int32 biHeight;
+ WriteIntToByteArray(fullImage, 0x08, 4, true, (uint) height);
+ //Int16 biPlanes;
+ WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
+ //Int16 biBitCount;
+ WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
+ //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
+ WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
+ //Int32 biSizeImage;
+ WriteIntToByteArray(fullImage, 0x14, 4, true, (uint) bm32bData.Length);
+ // These are all 0. Since .net clears new arrays, don't bother writing them.
+ //Int32 biXPelsPerMeter = 0;
+ //Int32 biYPelsPerMeter = 0;
+ //Int32 biClrUsed = 0;
+ //Int32 biClrImportant = 0;
+
+ // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
+ WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
+ WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
+ WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
+
+ Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint) bm32bData.Length);
+ return fullImage;
+ }
+
+ private static byte[] ConvertToDib(byte[] pngBytes = null)
+ {
+ byte[] bm32bData;
+ int width, height;
+
+ using (var skBmp = SKBitmap.Decode(pngBytes))
{
- byte[] bm32bData;
- var width = image.Width;
- var height = image.Height;
-
- // Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
- using (var bm32b = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
- {
- using (var gr = Graphics.FromImage(bm32b))
- {
- gr.DrawImage(image, new Rectangle(0, 0, width, height));
- }
- // Bitmap format has its lines reversed.
- bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
- bm32bData = GetRawBytes(bm32b);
- }
-
- // BITMAPINFOHEADER struct for DIB.
- const int hdrSize = 0x28;
- var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
- //Int32 biSize;
- WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
- //Int32 biWidth;
- WriteIntToByteArray(fullImage, 0x04, 4, true, (uint)width);
- //Int32 biHeight;
- WriteIntToByteArray(fullImage, 0x08, 4, true, (uint)height);
- //Int16 biPlanes;
- WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
- //Int16 biBitCount;
- WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
- //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
- WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
- //Int32 biSizeImage;
- WriteIntToByteArray(fullImage, 0x14, 4, true, (uint)bm32bData.Length);
- // These are all 0. Since .net clears new arrays, don't bother writing them.
- //Int32 biXPelsPerMeter = 0;
- //Int32 biYPelsPerMeter = 0;
- //Int32 biClrUsed = 0;
- //Int32 biClrImportant = 0;
-
- // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
- WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
- WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
- WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
-
- Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint)bm32bData.Length);
- return fullImage;
+ width = skBmp.Width;
+ height = skBmp.Height;
+ using var rotated = new SKBitmap(new SKImageInfo(width, height, skBmp.ColorType));
+ using var canvas = new SKCanvas(rotated);
+ canvas.Scale(1, -1, 0, height / 2.0f);
+ canvas.DrawBitmap(skBmp, SKPoint.Empty);
+ canvas.Flush();
+ bm32bData = rotated.Bytes;
}
- private static byte[] ConvertToDib(byte[] pngBytes = null)
+ // BITMAPINFOHEADER struct for DIB.
+ const int hdrSize = 0x28;
+ var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
+ //Int32 biSize;
+ WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
+ //Int32 biWidth;
+ WriteIntToByteArray(fullImage, 0x04, 4, true, (uint) width);
+ //Int32 biHeight;
+ WriteIntToByteArray(fullImage, 0x08, 4, true, (uint) height);
+ //Int16 biPlanes;
+ WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
+ //Int16 biBitCount;
+ WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
+ //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
+ WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
+ //Int32 biSizeImage;
+ WriteIntToByteArray(fullImage, 0x14, 4, true, (uint) bm32bData.Length);
+ // These are all 0. Since .net clears new arrays, don't bother writing them.
+ //Int32 biXPelsPerMeter = 0;
+ //Int32 biYPelsPerMeter = 0;
+ //Int32 biClrUsed = 0;
+ //Int32 biClrImportant = 0;
+
+ // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
+ WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
+ WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
+ WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
+
+ Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint) bm32bData.Length);
+ return fullImage;
+ }
+
+ public static unsafe byte[] GetRawBytes(Bitmap bmp)
+ {
+ var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
+ var bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
+ var bytes = (uint) (Math.Abs(bmpData.Stride) * bmp.Height);
+ var buffer = new byte[bytes];
+ fixed (byte* pBuffer = buffer)
{
- byte[] bm32bData;
- int width, height;
-
- using (var skBmp = SKBitmap.Decode(pngBytes))
- {
- width = skBmp.Width;
- height = skBmp.Height;
- using var rotated = new SKBitmap(new SKImageInfo(width, height, skBmp.ColorType));
- using var canvas = new SKCanvas(rotated);
- canvas.Scale(1, -1, 0, height / 2.0f);
- canvas.DrawBitmap(skBmp, SKPoint.Empty);
- canvas.Flush();
- bm32bData = rotated.Bytes;
- }
-
- // BITMAPINFOHEADER struct for DIB.
- const int hdrSize = 0x28;
- var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
- //Int32 biSize;
- WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
- //Int32 biWidth;
- WriteIntToByteArray(fullImage, 0x04, 4, true, (uint)width);
- //Int32 biHeight;
- WriteIntToByteArray(fullImage, 0x08, 4, true, (uint)height);
- //Int16 biPlanes;
- WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
- //Int16 biBitCount;
- WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
- //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
- WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
- //Int32 biSizeImage;
- WriteIntToByteArray(fullImage, 0x14, 4, true, (uint)bm32bData.Length);
- // These are all 0. Since .net clears new arrays, don't bother writing them.
- //Int32 biXPelsPerMeter = 0;
- //Int32 biYPelsPerMeter = 0;
- //Int32 biClrUsed = 0;
- //Int32 biClrImportant = 0;
-
- // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
- WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
- WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
- WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
-
- Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint)bm32bData.Length);
- return fullImage;
+ Unsafe.CopyBlockUnaligned(pBuffer, bmpData.Scan0.ToPointer(), bytes);
}
- public static unsafe byte[] GetRawBytes(Bitmap bmp)
+ bmp.UnlockBits(bmpData);
+ return buffer;
+ }
+
+ private static void WriteIntToByteArray(byte[] data, int startIndex, int bytes, bool littleEndian, uint value)
+ {
+ var lastByte = bytes - 1;
+
+ if (data.Length < startIndex + bytes)
{
- var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
- var bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
- var bytes = (uint)(Math.Abs(bmpData.Stride) * bmp.Height);
- var buffer = new byte[bytes];
- fixed (byte* pBuffer = buffer)
- {
- Unsafe.CopyBlockUnaligned(pBuffer, bmpData.Scan0.ToPointer(), bytes);
- }
- bmp.UnlockBits(bmpData);
- return buffer;
+ throw new ArgumentOutOfRangeException(nameof(startIndex), "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
}
- private static void WriteIntToByteArray(byte[] data, int startIndex, int bytes, bool littleEndian, uint value)
+ for (var index = 0; index < bytes; index++)
{
- var lastByte = bytes - 1;
-
- if (data.Length < startIndex + bytes)
- {
- throw new ArgumentOutOfRangeException(nameof(startIndex), "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
- }
-
- for (var index = 0; index < bytes; index++)
- {
- var offs = startIndex + (littleEndian ? index : lastByte - index);
- data[offs] = (byte)(value >> 8 * index & 0xFF);
- }
- }
-
- private static string GenerateHTMLFragment(string html)
- {
- var sb = new StringBuilder();
-
- const string header = "Version:0.9\r\nStartHTML:<<<<<<<<<1\r\nEndHTML:<<<<<<<<<2\r\nStartFragment:<<<<<<<<<3\r\nEndFragment:<<<<<<<<<4\r\n";
- const string startHTML = "\r\n\r\n";
- const string startFragment = "";
- const string endFragment = "";
- const string endHTML = "\r\n\r\n";
-
- sb.Append(header);
-
- var startHTMLLength = header.Length;
- var startFragmentLength = startHTMLLength + startHTML.Length + startFragment.Length;
- var endFragmentLength = startFragmentLength + Encoding.UTF8.GetByteCount(html);
- var endHTMLLength = endFragmentLength + endFragment.Length + endHTML.Length;
-
- sb.Replace("<<<<<<<<<1", startHTMLLength.ToString("D10"));
- sb.Replace("<<<<<<<<<2", endHTMLLength.ToString("D10"));
- sb.Replace("<<<<<<<<<3", startFragmentLength.ToString("D10"));
- sb.Replace("<<<<<<<<<4", endFragmentLength.ToString("D10"));
-
- sb.Append(startHTML);
- sb.Append(startFragment);
- sb.Append(html);
- sb.Append(endFragment);
- sb.Append(endHTML);
-
- return sb.ToString();
+ var offs = startIndex + (littleEndian ? index : lastByte - index);
+ data[offs] = (byte) (value >> 8 * index & 0xFF);
}
}
+
+ private static string GenerateHTMLFragment(string html)
+ {
+ var sb = new StringBuilder();
+
+ const string header = "Version:0.9\r\nStartHTML:<<<<<<<<<1\r\nEndHTML:<<<<<<<<<2\r\nStartFragment:<<<<<<<<<3\r\nEndFragment:<<<<<<<<<4\r\n";
+ const string startHTML = "\r\n\r\n";
+ const string startFragment = "";
+ const string endFragment = "";
+ const string endHTML = "\r\n\r\n";
+
+ sb.Append(header);
+
+ var startHTMLLength = header.Length;
+ var startFragmentLength = startHTMLLength + startHTML.Length + startFragment.Length;
+ var endFragmentLength = startFragmentLength + Encoding.UTF8.GetByteCount(html);
+ var endHTMLLength = endFragmentLength + endFragment.Length + endHTML.Length;
+
+ sb.Replace("<<<<<<<<<1", startHTMLLength.ToString("D10"));
+ sb.Replace("<<<<<<<<<2", endHTMLLength.ToString("D10"));
+ sb.Replace("<<<<<<<<<3", startFragmentLength.ToString("D10"));
+ sb.Replace("<<<<<<<<<4", endFragmentLength.ToString("D10"));
+
+ sb.Append(startHTML);
+ sb.Append(startFragment);
+ sb.Append(html);
+ sb.Append(endFragment);
+ sb.Append(endHTML);
+
+ return sb.ToString();
+ }
}
\ No newline at end of file
diff --git a/FModel/Extensions/CollectionExtensions.cs b/FModel/Extensions/CollectionExtensions.cs
index b26a33f5..3964f2a0 100644
--- a/FModel/Extensions/CollectionExtensions.cs
+++ b/FModel/Extensions/CollectionExtensions.cs
@@ -2,36 +2,35 @@
using System.Linq;
using System.Runtime.CompilerServices;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public static class CollectionExtensions
{
- public static class CollectionExtensions
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Next(this IList collection, T value)
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Next(this IList collection, T value)
- {
- var i = collection.IndexOf(value) + 1;
- return i >= collection.Count ? collection.First() : collection[i];
- }
+ var i = collection.IndexOf(value) + 1;
+ return i >= collection.Count ? collection.First() : collection[i];
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Next(this IList collection, int index)
- {
- var i = index + 1;
- return i >= collection.Count ? collection.First() : collection[i];
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Next(this IList collection, int index)
+ {
+ var i = index + 1;
+ return i >= collection.Count ? collection.First() : collection[i];
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Previous(this IList collection, T value)
- {
- var i = collection.IndexOf(value) - 1;
- return i < 0 ? collection.Last() : collection[i];
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Previous(this IList collection, T value)
+ {
+ var i = collection.IndexOf(value) - 1;
+ return i < 0 ? collection.Last() : collection[i];
+ }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Previous(this IList collection, int index)
- {
- var i = index - 1;
- return i < 0 ? collection.Last() : collection[i];
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Previous(this IList collection, int index)
+ {
+ var i = index - 1;
+ return i < 0 ? collection.Last() : collection[i];
}
}
\ No newline at end of file
diff --git a/FModel/Extensions/EnumExtensions.cs b/FModel/Extensions/EnumExtensions.cs
index 400d91b1..3c3fa9a9 100644
--- a/FModel/Extensions/EnumExtensions.cs
+++ b/FModel/Extensions/EnumExtensions.cs
@@ -1,84 +1,46 @@
using System;
using System.ComponentModel;
-using System.Resources;
using System.Runtime.CompilerServices;
-using FModel.Properties;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public static class EnumExtensions
{
- public static class EnumExtensions
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string GetDescription(this Enum value)
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetDescription(this Enum value)
- {
- var fi = value.GetType().GetField(value.ToString());
- if (fi == null) return $"{value} ({value:D})";
- var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
- return attributes.Length > 0 ? attributes[0].Description : $"{value} ({value:D})";
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetLocalizedDescription(this Enum value) => value.GetLocalizedDescription(Resources.ResourceManager);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetLocalizedDescription(this Enum value, ResourceManager resourceManager)
- {
- var resourceName = value.GetType().Name + "_" + value;
- var description = resourceManager.GetString(resourceName);
-
- if (string.IsNullOrEmpty(description))
- {
- description = value.GetDescription();
- }
-
- return description;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetLocalizedCategory(this Enum value) => value.GetLocalizedCategory(Resources.ResourceManager);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetLocalizedCategory(this Enum value, ResourceManager resourceManager)
- {
- var resourceName = value.GetType().Name + "_" + value + "_Category";
- var description = resourceManager.GetString(resourceName);
-
- return description;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T ToEnum(this string value, T defaultValue) where T : struct
- {
- if (!Enum.TryParse(value, true, out T ret))
- return defaultValue;
-
- return ret;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool HasAnyFlags(this T flags, T contains) where T : Enum, IConvertible => (flags.ToInt32(null) & contains.ToInt32(null)) != 0;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetIndex(this Enum value)
- {
- var values = Enum.GetValues(value.GetType());
- return Array.IndexOf(values, value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Next(this Enum value)
- {
- var values = Enum.GetValues(value.GetType());
- var i = Array.IndexOf(values, value) + 1;
- return i == values.Length ? (T) values.GetValue(0) : (T) values.GetValue(i);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T Previous(this Enum value)
- {
- var values = Enum.GetValues(value.GetType());
- var i = Array.IndexOf(values, value) - 1;
- return i == -1 ? (T) values.GetValue(values.Length - 1) : (T) values.GetValue(i);
- }
+ var fi = value.GetType().GetField(value.ToString());
+ if (fi == null) return $"{value} ({value:D})";
+ var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
+ return attributes.Length > 0 ? attributes[0].Description : $"{value} ({value:D})";
}
-}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ToEnum(this string value, T defaultValue) where T : struct => !Enum.TryParse(value, true, out T ret) ? defaultValue : ret;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool HasAnyFlags(this T flags, T contains) where T : Enum, IConvertible => (flags.ToInt32(null) & contains.ToInt32(null)) != 0;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetIndex(this Enum value)
+ {
+ var values = Enum.GetValues(value.GetType());
+ return Array.IndexOf(values, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Next(this Enum value)
+ {
+ var values = Enum.GetValues(value.GetType());
+ var i = Array.IndexOf(values, value) + 1;
+ return i == values.Length ? (T) values.GetValue(0) : (T) values.GetValue(i);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Previous(this Enum value)
+ {
+ var values = Enum.GetValues(value.GetType());
+ var i = Array.IndexOf(values, value) - 1;
+ return i == -1 ? (T) values.GetValue(values.Length - 1) : (T) values.GetValue(i);
+ }
+}
\ No newline at end of file
diff --git a/FModel/Extensions/StreamExtensions.cs b/FModel/Extensions/StreamExtensions.cs
index 21c95978..21517936 100644
--- a/FModel/Extensions/StreamExtensions.cs
+++ b/FModel/Extensions/StreamExtensions.cs
@@ -2,30 +2,29 @@
using System.IO;
using System.Runtime.CompilerServices;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public enum Endianness
{
- public enum Endianness
- {
- LittleEndian,
- BigEndian,
- }
+ LittleEndian,
+ BigEndian
+}
- public static class StreamExtensions
+public static class StreamExtensions
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint ReadUInt32(this Stream s, Endianness endian = Endianness.LittleEndian)
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static uint ReadUInt32(this Stream s, Endianness endian = Endianness.LittleEndian)
+ var b1 = s.ReadByte();
+ var b2 = s.ReadByte();
+ var b3 = s.ReadByte();
+ var b4 = s.ReadByte();
+
+ return endian switch
{
- var b1 = s.ReadByte();
- var b2 = s.ReadByte();
- var b3 = s.ReadByte();
- var b4 = s.ReadByte();
-
- return endian switch
- {
- Endianness.LittleEndian => (uint) (b4 << 24 | b3 << 16 | b2 << 8 | b1),
- Endianness.BigEndian => (uint) (b1 << 24 | b2 << 16 | b3 << 8 | b4),
- _ => throw new Exception("unknown endianness")
- };
- }
+ Endianness.LittleEndian => (uint) (b4 << 24 | b3 << 16 | b2 << 8 | b1),
+ Endianness.BigEndian => (uint) (b1 << 24 | b2 << 16 | b3 << 8 | b4),
+ _ => throw new Exception("unknown endianness")
+ };
}
}
\ No newline at end of file
diff --git a/FModel/Extensions/StringExtensions.cs b/FModel/Extensions/StringExtensions.cs
index 700c1ee2..cbfa8ddd 100644
--- a/FModel/Extensions/StringExtensions.cs
+++ b/FModel/Extensions/StringExtensions.cs
@@ -2,133 +2,132 @@
using System.IO;
using System.Runtime.CompilerServices;
-namespace FModel.Extensions
+namespace FModel.Extensions;
+
+public static class StringExtensions
{
- public static class StringExtensions
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string GetReadableSize(double size)
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string GetReadableSize(double size)
+ if (size == 0) return "0 B";
+
+ string[] sizes = { "B", "KB", "MB", "GB", "TB" };
+ var order = 0;
+ while (size >= 1024 && order < sizes.Length - 1)
{
- if (size == 0) return "0 B";
-
- string[] sizes = {"B", "KB", "MB", "GB", "TB"};
- var order = 0;
- while (size >= 1024 && order < sizes.Length - 1)
- {
- order++;
- size /= 1024;
- }
-
- return $"{size:# ###.##} {sizes[order]}".TrimStart();
+ order++;
+ size /= 1024;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBefore(this string s, char delimiter)
+ return $"{size:# ###.##} {sizes[order]}".TrimStart();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBefore(this string s, char delimiter)
+ {
+ var index = s.IndexOf(delimiter);
+ return index == -1 ? s : s[..index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBefore(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var index = s.IndexOf(delimiter, comparisonType);
+ return index == -1 ? s : s[..index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringAfter(this string s, char delimiter)
+ {
+ var index = s.IndexOf(delimiter);
+ return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringAfter(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var index = s.IndexOf(delimiter, comparisonType);
+ return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBeforeLast(this string s, char delimiter)
+ {
+ var index = s.LastIndexOf(delimiter);
+ return index == -1 ? s : s[..index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBeforeLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var index = s.LastIndexOf(delimiter, comparisonType);
+ return index == -1 ? s : s[..index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBeforeWithLast(this string s, char delimiter)
+ {
+ var index = s.LastIndexOf(delimiter);
+ return index == -1 ? s : s[..(index + 1)];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringBeforeWithLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var index = s.LastIndexOf(delimiter, comparisonType);
+ return index == -1 ? s : s[..(index + 1)];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringAfterLast(this string s, char delimiter)
+ {
+ var index = s.LastIndexOf(delimiter);
+ return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string SubstringAfterLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var index = s.LastIndexOf(delimiter, comparisonType);
+ return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetLineNumber(this string s, string lineToFind)
+ {
+ if (int.TryParse(lineToFind, out var index))
+ return s.GetLineNumber(index);
+
+ lineToFind = $" \"Name\": \"{lineToFind}\",";
+ using var reader = new StringReader(s);
+ var lineNum = 0;
+ string line;
+ while ((line = reader.ReadLine()) != null)
{
- var index = s.IndexOf(delimiter);
- return index == -1 ? s : s[..index];
+ lineNum++;
+ if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase))
+ return lineNum;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBefore(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
+ return 1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int GetLineNumber(this string s, int index)
+ {
+ using var reader = new StringReader(s);
+ var lineNum = 0;
+ string line;
+ while ((line = reader.ReadLine()) != null)
{
- var index = s.IndexOf(delimiter, comparisonType);
- return index == -1 ? s : s[..index];
+ lineNum++;
+ if (line.Equals(" {"))
+ index--;
+
+ if (index == -1)
+ return lineNum + 1;
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringAfter(this string s, char delimiter)
- {
- var index = s.IndexOf(delimiter);
- return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringAfter(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
- {
- var index = s.IndexOf(delimiter, comparisonType);
- return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBeforeLast(this string s, char delimiter)
- {
- var index = s.LastIndexOf(delimiter);
- return index == -1 ? s : s[..index];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBeforeLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
- {
- var index = s.LastIndexOf(delimiter, comparisonType);
- return index == -1 ? s : s[..index];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBeforeWithLast(this string s, char delimiter)
- {
- var index = s.LastIndexOf(delimiter);
- return index == -1 ? s : s[..(index + 1)];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringBeforeWithLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
- {
- var index = s.LastIndexOf(delimiter, comparisonType);
- return index == -1 ? s : s[..(index + 1)];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringAfterLast(this string s, char delimiter)
- {
- var index = s.LastIndexOf(delimiter);
- return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string SubstringAfterLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
- {
- var index = s.LastIndexOf(delimiter, comparisonType);
- return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetLineNumber(this string s, string lineToFind)
- {
- if (int.TryParse(lineToFind, out var index))
- return s.GetLineNumber(index);
-
- lineToFind = $" \"Name\": \"{lineToFind}\",";
- using var reader = new StringReader(s);
- var lineNum = 0;
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- lineNum++;
- if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase))
- return lineNum;
- }
-
- return 1;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetLineNumber(this string s, int index)
- {
- using var reader = new StringReader(s);
- var lineNum = 0;
- string line;
- while ((line = reader.ReadLine()) != null)
- {
- lineNum++;
- if (line.Equals(" {"))
- index--;
-
- if (index == -1)
- return lineNum + 1;
- }
-
- return 1;
- }
+ return 1;
}
}
\ No newline at end of file
diff --git a/FModel/FModel.csproj b/FModel/FModel.csproj
index 4ad0944b..0ed78b14 100644
--- a/FModel/FModel.csproj
+++ b/FModel/FModel.csproj
@@ -2,7 +2,7 @@
WinExe
- net6.0-windows
+ net7.0-windows
true
FModel.ico
4.2.2
diff --git a/FModel/Framework/AsyncQueue.cs b/FModel/Framework/AsyncQueue.cs
index a24d30ea..78de380b 100644
--- a/FModel/Framework/AsyncQueue.cs
+++ b/FModel/Framework/AsyncQueue.cs
@@ -2,32 +2,31 @@
using System.Threading;
using System.Threading.Tasks.Dataflow;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class AsyncQueue : IAsyncEnumerable
{
- public class AsyncQueue : IAsyncEnumerable
+ private readonly SemaphoreSlim _semaphore = new(1);
+ private readonly BufferBlock _buffer = new();
+
+ public int Count => _buffer.Count;
+
+ public void Enqueue(T item) => _buffer.Post(item);
+
+ public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken token = default)
{
- private readonly SemaphoreSlim _semaphore = new(1);
- private readonly BufferBlock _buffer = new();
-
- public int Count => _buffer.Count;
-
- public void Enqueue(T item) => _buffer.Post(item);
-
- public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken token = default)
+ await _semaphore.WaitAsync(token);
+ try
{
- await _semaphore.WaitAsync(token);
- try
+ while (Count > 0)
{
- while (Count > 0)
- {
- token.ThrowIfCancellationRequested();
- yield return await _buffer.ReceiveAsync(token);
- }
- }
- finally
- {
- _semaphore.Release();
+ token.ThrowIfCancellationRequested();
+ yield return await _buffer.ReceiveAsync(token);
}
}
+ finally
+ {
+ _semaphore.Release();
+ }
}
-}
+}
\ No newline at end of file
diff --git a/FModel/Framework/Command.cs b/FModel/Framework/Command.cs
index 90396053..ed4b1d61 100644
--- a/FModel/Framework/Command.cs
+++ b/FModel/Framework/Command.cs
@@ -1,19 +1,18 @@
using System;
using System.Windows.Input;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public abstract class Command : ICommand
{
- public abstract class Command : ICommand
+ public abstract void Execute(object parameter);
+
+ public abstract bool CanExecute(object parameter);
+
+ public void RaiseCanExecuteChanged()
{
- public abstract void Execute(object parameter);
-
- public abstract bool CanExecute(object parameter);
-
- public void RaiseCanExecuteChanged()
- {
- CanExecuteChanged?.Invoke(this, EventArgs.Empty);
- }
-
- public event EventHandler CanExecuteChanged;
+ CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
+
+ public event EventHandler CanExecuteChanged;
}
\ No newline at end of file
diff --git a/FModel/Framework/CustomSKShaper.cs b/FModel/Framework/CustomSKShaper.cs
index 6c92d43b..ecd8009c 100644
--- a/FModel/Framework/CustomSKShaper.cs
+++ b/FModel/Framework/CustomSKShaper.cs
@@ -4,93 +4,87 @@ using SkiaSharp;
using SkiaSharp.HarfBuzz;
using Buffer = HarfBuzzSharp.Buffer;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class CustomSKShaper : SKShaper
{
- public class CustomSKShaper : SKShaper
+ private const int _FONT_SIZE_SCALE = 512;
+ private readonly Font _font;
+
+ public CustomSKShaper(SKTypeface typeface) : base(typeface)
{
- private const int _FONT_SIZE_SCALE = 512;
- private readonly Font _font;
- private readonly Buffer _buffer;
+ using var blob = Typeface.OpenStream(out var index).ToHarfBuzzBlob();
+ using var face = new Face(blob, index);
+ face.Index = index;
+ face.UnitsPerEm = Typeface.UnitsPerEm;
- public CustomSKShaper(SKTypeface typeface) : base(typeface)
+ _font = new Font(face);
+ _font.SetScale(_FONT_SIZE_SCALE, _FONT_SIZE_SCALE);
+ _font.SetFunctionsOpenType();
+ }
+
+ public new Result Shape(Buffer buffer, float xOffset, float yOffset, SKPaint paint)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException(nameof(buffer));
+
+ if (paint == null)
+ throw new ArgumentNullException(nameof(paint));
+
+ // do the shaping
+ _font.Shape(buffer);
+
+ // get the shaping results
+ var len = buffer.Length;
+ var info = buffer.GlyphInfos;
+ var pos = buffer.GlyphPositions;
+
+ // get the sizes
+ var textSizeY = paint.TextSize / _FONT_SIZE_SCALE;
+ var textSizeX = textSizeY * paint.TextScaleX;
+
+ var points = new SKPoint[len];
+ var clusters = new uint[len];
+ var codepoints = new uint[len];
+
+ for (var i = 0; i < len; i++)
{
- using (var blob = Typeface.OpenStream(out var index).ToHarfBuzzBlob())
- using (var face = new Face(blob, index))
- {
- face.Index = index;
- face.UnitsPerEm = Typeface.UnitsPerEm;
+ // move the cursor
+ xOffset += pos[i].XAdvance * textSizeX;
+ yOffset += pos[i].YAdvance * textSizeY;
- _font = new Font(face);
- _font.SetScale(_FONT_SIZE_SCALE, _FONT_SIZE_SCALE);
- _font.SetFunctionsOpenType();
- }
-
- _buffer = new Buffer();
+ codepoints[i] = info[i].Codepoint;
+ clusters[i] = info[i].Cluster;
+ points[i] = new SKPoint(xOffset + pos[i].XOffset * textSizeX, yOffset - pos[i].YOffset * textSizeY);
}
- public new Result Shape(Buffer buffer, float xOffset, float yOffset, SKPaint paint)
+ return new Result(codepoints, clusters, points);
+ }
+
+ public new Result Shape(string text, SKPaint paint) => Shape(text, 0, 0, paint);
+
+ public new Result Shape(string text, float xOffset, float yOffset, SKPaint paint)
+ {
+ if (string.IsNullOrEmpty(text))
+ return new Result();
+
+ using var buffer = new Buffer();
+ switch (paint.TextEncoding)
{
- if (buffer == null)
- throw new ArgumentNullException(nameof(buffer));
-
- if (paint == null)
- throw new ArgumentNullException(nameof(paint));
-
- // do the shaping
- _font.Shape(buffer);
-
- // get the shaping results
- var len = buffer.Length;
- var info = buffer.GlyphInfos;
- var pos = buffer.GlyphPositions;
-
- // get the sizes
- var textSizeY = paint.TextSize / _FONT_SIZE_SCALE;
- var textSizeX = textSizeY * paint.TextScaleX;
-
- var points = new SKPoint[len];
- var clusters = new uint[len];
- var codepoints = new uint[len];
-
- for (var i = 0; i < len; i++)
- {
- // move the cursor
- xOffset += pos[i].XAdvance * textSizeX;
- yOffset += pos[i].YAdvance * textSizeY;
-
- codepoints[i] = info[i].Codepoint;
- clusters[i] = info[i].Cluster;
- points[i] = new SKPoint(xOffset + pos[i].XOffset * textSizeX, yOffset - pos[i].YOffset * textSizeY);
- }
-
- return new Result(codepoints, clusters, points);
+ case SKTextEncoding.Utf8:
+ buffer.AddUtf8(text);
+ break;
+ case SKTextEncoding.Utf16:
+ buffer.AddUtf16(text);
+ break;
+ case SKTextEncoding.Utf32:
+ buffer.AddUtf32(text);
+ break;
+ default:
+ throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
}
- public new Result Shape(string text, SKPaint paint) => Shape(text, 0, 0, paint);
-
- public new Result Shape(string text, float xOffset, float yOffset, SKPaint paint)
- {
- if (string.IsNullOrEmpty(text))
- return new Result();
-
- using var buffer = new Buffer();
- switch (paint.TextEncoding)
- {
- case SKTextEncoding.Utf8:
- buffer.AddUtf8(text);
- break;
- case SKTextEncoding.Utf16:
- buffer.AddUtf16(text);
- break;
- case SKTextEncoding.Utf32:
- buffer.AddUtf32(text);
- break;
- default:
- throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
- }
-
- buffer.GuessSegmentProperties();
- return Shape(buffer, xOffset, yOffset, paint);
- }
+ buffer.GuessSegmentProperties();
+ return Shape(buffer, xOffset, yOffset, paint);
}
}
\ No newline at end of file
diff --git a/FModel/Framework/FullyObservableCollection.cs b/FModel/Framework/FullyObservableCollection.cs
index bc1e399e..abb00e09 100644
--- a/FModel/Framework/FullyObservableCollection.cs
+++ b/FModel/Framework/FullyObservableCollection.cs
@@ -4,114 +4,113 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class FullyObservableCollection : ObservableCollection where T : INotifyPropertyChanged
{
- public class FullyObservableCollection : ObservableCollection where T : INotifyPropertyChanged
+ ///
+ /// Occurs when a property is changed within an item.
+ ///
+ public event EventHandler ItemPropertyChanged;
+
+ public FullyObservableCollection()
{
- ///
- /// Occurs when a property is changed within an item.
- ///
- public event EventHandler ItemPropertyChanged;
+ }
- public FullyObservableCollection()
+ public FullyObservableCollection(List list) : base(list)
+ {
+ ObserveAll();
+ }
+
+ public FullyObservableCollection(IEnumerable enumerable) : base(enumerable)
+ {
+ ObserveAll();
+ }
+
+ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action is NotifyCollectionChangedAction.Remove or
+ NotifyCollectionChangedAction.Replace)
{
- }
-
- public FullyObservableCollection(List list) : base(list)
- {
- ObserveAll();
- }
-
- public FullyObservableCollection(IEnumerable enumerable) : base(enumerable)
- {
- ObserveAll();
- }
-
- protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
- {
- if (e.Action is NotifyCollectionChangedAction.Remove or
- NotifyCollectionChangedAction.Replace)
- {
- foreach (T item in e.OldItems)
- item.PropertyChanged -= ChildPropertyChanged;
- }
-
- if (e.Action is NotifyCollectionChangedAction.Add or
- NotifyCollectionChangedAction.Replace)
- {
- foreach (T item in e.NewItems)
- item.PropertyChanged += ChildPropertyChanged;
- }
-
- base.OnCollectionChanged(e);
- }
-
- protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
- {
- ItemPropertyChanged?.Invoke(this, e);
- }
-
- protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
- {
- OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
- }
-
- protected override void ClearItems()
- {
- foreach (T item in Items)
+ foreach (T item in e.OldItems)
item.PropertyChanged -= ChildPropertyChanged;
-
- base.ClearItems();
}
- private void ObserveAll()
+ if (e.Action is NotifyCollectionChangedAction.Add or
+ NotifyCollectionChangedAction.Replace)
{
- foreach (var item in Items)
+ foreach (T item in e.NewItems)
item.PropertyChanged += ChildPropertyChanged;
}
- private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- var typedSender = (T) sender;
- var i = Items.IndexOf(typedSender);
+ base.OnCollectionChanged(e);
+ }
- if (i < 0)
- throw new ArgumentException("Received property notification from item not in collection");
+ protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
+ {
+ ItemPropertyChanged?.Invoke(this, e);
+ }
- OnItemPropertyChanged(i, e);
- }
+ protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
+ {
+ OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
+ }
+
+ protected override void ClearItems()
+ {
+ foreach (T item in Items)
+ item.PropertyChanged -= ChildPropertyChanged;
+
+ base.ClearItems();
+ }
+
+ private void ObserveAll()
+ {
+ foreach (var item in Items)
+ item.PropertyChanged += ChildPropertyChanged;
+ }
+
+ private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var typedSender = (T) sender;
+ var i = Items.IndexOf(typedSender);
+
+ if (i < 0)
+ throw new ArgumentException("Received property notification from item not in collection");
+
+ OnItemPropertyChanged(i, e);
+ }
+}
+
+///
+/// Provides data for the event.
+///
+public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
+{
+ ///
+ /// Gets the index in the collection for which the property change has occurred.
+ ///
+ ///
+ /// Index in parent collection.
+ ///
+ public int CollectionIndex { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The index in the collection of changed item.
+ /// The name of the property that changed.
+ public ItemPropertyChangedEventArgs(int index, string name) : base(name)
+ {
+ CollectionIndex = index;
}
///
- /// Provides data for the event.
+ /// Initializes a new instance of the class.
///
- public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
+ /// The index.
+ /// The instance containing the event data.
+ public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
{
- ///
- /// Gets the index in the collection for which the property change has occurred.
- ///
- ///
- /// Index in parent collection.
- ///
- public int CollectionIndex { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The index in the collection of changed item.
- /// The name of the property that changed.
- public ItemPropertyChangedEventArgs(int index, string name) : base(name)
- {
- CollectionIndex = index;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The index.
- /// The instance containing the event data.
- public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
- {
- }
}
}
\ No newline at end of file
diff --git a/FModel/Framework/Hotkey.cs b/FModel/Framework/Hotkey.cs
index f20116a2..11445f9c 100644
--- a/FModel/Framework/Hotkey.cs
+++ b/FModel/Framework/Hotkey.cs
@@ -1,50 +1,49 @@
using System.Text;
using System.Windows.Input;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class Hotkey : ViewModel
{
- public class Hotkey : ViewModel
+ private Key _key;
+ public Key Key
{
- private Key _key;
- public Key Key
- {
- get => _key;
- set => SetProperty(ref _key, value);
- }
+ get => _key;
+ set => SetProperty(ref _key, value);
+ }
- private ModifierKeys _modifiers;
- public ModifierKeys Modifiers
- {
- get => _modifiers;
- set => SetProperty(ref _modifiers, value);
- }
+ private ModifierKeys _modifiers;
+ public ModifierKeys Modifiers
+ {
+ get => _modifiers;
+ set => SetProperty(ref _modifiers, value);
+ }
- public Hotkey(Key key, ModifierKeys modifiers = ModifierKeys.None)
- {
- Key = key;
- Modifiers = modifiers;
- }
+ public Hotkey(Key key, ModifierKeys modifiers = ModifierKeys.None)
+ {
+ Key = key;
+ Modifiers = modifiers;
+ }
- public bool IsTriggered(Key e)
- {
- return e == Key && Keyboard.Modifiers.HasFlag(Modifiers);
- }
+ public bool IsTriggered(Key e)
+ {
+ return e == Key && Keyboard.Modifiers.HasFlag(Modifiers);
+ }
- public override string ToString()
- {
- var str = new StringBuilder();
+ public override string ToString()
+ {
+ var str = new StringBuilder();
- if (Modifiers.HasFlag(ModifierKeys.Control))
- str.Append("Ctrl + ");
- if (Modifiers.HasFlag(ModifierKeys.Shift))
- str.Append("Shift + ");
- if (Modifiers.HasFlag(ModifierKeys.Alt))
- str.Append("Alt + ");
- if (Modifiers.HasFlag(ModifierKeys.Windows))
- str.Append("Win + ");
+ if (Modifiers.HasFlag(ModifierKeys.Control))
+ str.Append("Ctrl + ");
+ if (Modifiers.HasFlag(ModifierKeys.Shift))
+ str.Append("Shift + ");
+ if (Modifiers.HasFlag(ModifierKeys.Alt))
+ str.Append("Alt + ");
+ if (Modifiers.HasFlag(ModifierKeys.Windows))
+ str.Append("Win + ");
- str.Append(Key);
- return str.ToString();
- }
+ str.Append(Key);
+ return str.ToString();
}
}
\ No newline at end of file
diff --git a/FModel/Framework/JsonNetSerializer.cs b/FModel/Framework/JsonNetSerializer.cs
index 9d9e50b8..6bc2ee4d 100644
--- a/FModel/Framework/JsonNetSerializer.cs
+++ b/FModel/Framework/JsonNetSerializer.cs
@@ -4,40 +4,39 @@ using Newtonsoft.Json.Serialization;
using RestSharp;
using RestSharp.Serialization;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class JsonNetSerializer : IRestSerializer
{
- public class JsonNetSerializer : IRestSerializer
+ public static readonly JsonSerializerSettings SerializerSettings = new()
{
- public static readonly JsonSerializerSettings SerializerSettings = new()
- {
- NullValueHandling = NullValueHandling.Ignore,
- MissingMemberHandling = MissingMemberHandling.Ignore,
- ContractResolver = new CamelCasePropertyNamesContractResolver()
- };
+ NullValueHandling = NullValueHandling.Ignore,
+ MissingMemberHandling = MissingMemberHandling.Ignore,
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ };
- public string Serialize(object obj)
- {
- return JsonConvert.SerializeObject(obj);
- }
-
- [Obsolete]
- public string Serialize(Parameter parameter)
- {
- return JsonConvert.SerializeObject(parameter.Value);
- }
-
- public T Deserialize(IRestResponse response)
- {
- return JsonConvert.DeserializeObject(response.Content, SerializerSettings);
- }
-
- public string[] SupportedContentTypes { get; } =
- {
- "application/json", "application/json; charset=UTF-8"
- };
-
- public string ContentType { get; set; } = "application/json; charset=UTF-8";
-
- public DataFormat DataFormat => DataFormat.Json;
+ public string Serialize(object obj)
+ {
+ return JsonConvert.SerializeObject(obj);
}
+
+ [Obsolete]
+ public string Serialize(Parameter parameter)
+ {
+ return JsonConvert.SerializeObject(parameter.Value);
+ }
+
+ public T Deserialize(IRestResponse response)
+ {
+ return JsonConvert.DeserializeObject(response.Content, SerializerSettings);
+ }
+
+ public string[] SupportedContentTypes { get; } =
+ {
+ "application/json", "application/json; charset=UTF-8"
+ };
+
+ public string ContentType { get; set; } = "application/json; charset=UTF-8";
+
+ public DataFormat DataFormat => DataFormat.Json;
}
\ No newline at end of file
diff --git a/FModel/Framework/NavigationList.cs b/FModel/Framework/NavigationList.cs
index 7e32beea..77a45467 100644
--- a/FModel/Framework/NavigationList.cs
+++ b/FModel/Framework/NavigationList.cs
@@ -1,34 +1,46 @@
using System.Collections.Generic;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class NavigationList : List
{
- public class NavigationList : List
+ private int _currentIndex;
+ public int CurrentIndex
{
- private int _currentIndex = 0;
- public int CurrentIndex
+ get
{
- get
+ if (_currentIndex > Count - 1)
{
- if (_currentIndex > Count - 1) { _currentIndex = Count - 1; }
- if (_currentIndex < 0) { _currentIndex = 0; }
- return _currentIndex;
+ _currentIndex = Count - 1;
}
- set { _currentIndex = value; }
- }
- public T MoveNext
- {
- get { _currentIndex++; return this[CurrentIndex]; }
- }
+ if (_currentIndex < 0)
+ {
+ _currentIndex = 0;
+ }
- public T MovePrevious
- {
- get { _currentIndex--; return this[CurrentIndex]; }
+ return _currentIndex;
}
+ set => _currentIndex = value;
+ }
- public T Current
+ public T MoveNext
+ {
+ get
{
- get { return this[CurrentIndex]; }
+ _currentIndex++;
+ return this[CurrentIndex];
}
}
-}
+
+ public T MovePrevious
+ {
+ get
+ {
+ _currentIndex--;
+ return this[CurrentIndex];
+ }
+ }
+
+ public T Current => this[CurrentIndex];
+}
\ No newline at end of file
diff --git a/FModel/Framework/RangeObservableCollection.cs b/FModel/Framework/RangeObservableCollection.cs
index 130683c1..1372b522 100644
--- a/FModel/Framework/RangeObservableCollection.cs
+++ b/FModel/Framework/RangeObservableCollection.cs
@@ -3,40 +3,39 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public sealed class RangeObservableCollection : ObservableCollection
{
- public sealed class RangeObservableCollection : ObservableCollection
+ private bool _suppressNotification;
+
+ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
- private bool _suppressNotification;
+ if (!_suppressNotification)
+ base.OnCollectionChanged(e);
+ }
- protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
- {
- if (!_suppressNotification)
- base.OnCollectionChanged(e);
- }
+ public void AddRange(IEnumerable list)
+ {
+ if (list == null)
+ throw new ArgumentNullException(nameof(list));
- public void AddRange(IEnumerable list)
- {
- if (list == null)
- throw new ArgumentNullException(nameof(list));
+ _suppressNotification = true;
- _suppressNotification = true;
+ foreach (var item in list)
+ Add(item);
- foreach (var item in list)
- Add(item);
+ _suppressNotification = false;
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
- _suppressNotification = false;
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
- }
+ public void SetSuppressionState(bool state)
+ {
+ _suppressNotification = state;
+ }
- public void SetSuppressionState(bool state)
- {
- _suppressNotification = state;
- }
-
- public void InvokeOnCollectionChanged(NotifyCollectionChangedAction changedAction = NotifyCollectionChangedAction.Reset)
- {
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(changedAction));
- }
+ public void InvokeOnCollectionChanged(NotifyCollectionChangedAction changedAction = NotifyCollectionChangedAction.Reset)
+ {
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(changedAction));
}
}
\ No newline at end of file
diff --git a/FModel/Framework/ViewModel.cs b/FModel/Framework/ViewModel.cs
index e1a55336..1efeaa8c 100644
--- a/FModel/Framework/ViewModel.cs
+++ b/FModel/Framework/ViewModel.cs
@@ -6,71 +6,70 @@ using System.Linq;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
-namespace FModel.Framework
+namespace FModel.Framework;
+
+public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErrorInfo
{
- public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErrorInfo
+ private readonly Dictionary> _validationErrors = new();
+
+ public string this[string propertyName]
{
- private readonly Dictionary> _validationErrors = new();
-
- public string this[string propertyName]
- {
- get
- {
- if (string.IsNullOrEmpty(propertyName))
- return Error;
-
- return _validationErrors.ContainsKey(propertyName) ? string.Join(Environment.NewLine, _validationErrors[propertyName]) : string.Empty;
- }
- }
-
- [JsonIgnore] public string Error => string.Join(Environment.NewLine, GetAllErrors());
- [JsonIgnore] public bool HasErrors => _validationErrors.Any();
-
- public IEnumerable GetErrors(string propertyName)
+ get
{
if (string.IsNullOrEmpty(propertyName))
- return _validationErrors.SelectMany(kvp => kvp.Value);
+ return Error;
- return _validationErrors.TryGetValue(propertyName, out var errors) ? errors : Enumerable.Empty