From 3249f8ff41446b47cf458bbc04f8c27e005889aa Mon Sep 17 00:00:00 2001 From: Andrey Sukharev Date: Mon, 3 Apr 2023 13:14:19 +0300 Subject: Source generated json serializers (#4582) * Use source generated json serializers in order to improve code trimming * Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing * Use separate model for LogEventArgs serialization * Make dynamic object formatter static. Fix string builder pooling. * Do not inherit json version of LogEventArgs from EventArgs * Fix extra space in object formatting * Write log json directly to stream instead of using buffer writer * Rebase fixes * Rebase fixes * Rebase fixes * Enforce block-scoped namespaces in the solution. Convert style for existing code * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Rebase indent fix * Fix indent * Delete unnecessary json properties * Rebase fix * Remove overridden json property names as they are handled in the options * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Use default json options in github api calls * Indentation and spacing fixes * Fix json serialization * Fix missing JsonConverter for config enums * Add double \n\n after the whole string, not inside join --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- .../App/ApplicationJsonSerializerContext.cs | 10 ++++ Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 17 +++---- Ryujinx.Ui.Common/Configuration/AudioBackend.cs | 6 ++- .../Configuration/ConfigurationFileFormat.cs | 12 ++--- .../ConfigurationFileFormatSettings.cs | 9 ++++ .../ConfigurationJsonSerializerContext.cs | 10 ++++ .../Configuration/ConfigurationState.cs | 5 +- Ryujinx.Ui.Common/Configuration/System/Language.cs | 6 ++- Ryujinx.Ui.Common/Configuration/System/Region.cs | 6 ++- Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs | 57 ++++++++++++++++++++++ .../Models/Amiibo/AmiiboApiGamesSwitch.cs | 15 ++++++ Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs | 12 +++++ Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs | 14 ++++++ .../Models/Amiibo/AmiiboJsonSerializerContext.cs | 9 ++++ .../Github/GithubReleaseAssetJsonResponse.cs | 9 ++++ .../Models/Github/GithubReleasesJsonResponse.cs | 10 ++++ .../Github/GithubReleasesJsonSerializerContext.cs | 9 ++++ 17 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs create mode 100644 Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs create mode 100644 Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs create mode 100644 Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs (limited to 'Ryujinx.Ui.Common') diff --git a/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs new file mode 100644 index 00000000..f81121c2 --- /dev/null +++ b/Ryujinx.Ui.Common/App/ApplicationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.App.Common +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ApplicationMetadata))] + internal partial class ApplicationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index 113e9cb3..8686383e 100644 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; @@ -22,7 +23,6 @@ using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; -using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; using Path = System.IO.Path; namespace Ryujinx.Ui.App.Common @@ -42,6 +42,9 @@ namespace Ryujinx.Ui.App.Common private Language _desiredTitleLanguage; private CancellationTokenSource _cancellationToken; + private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + public ApplicationLibrary(VirtualFileSystem virtualFileSystem) { _virtualFileSystem = virtualFileSystem; @@ -489,14 +492,12 @@ namespace Ryujinx.Ui.App.Common appMetadata = new ApplicationMetadata(); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } try { - appMetadata = JsonHelper.DeserializeFromFile(metadataFile); + appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); } catch (JsonException) { @@ -509,9 +510,7 @@ namespace Ryujinx.Ui.App.Common { modifyFunction(appMetadata); - using FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough); - - JsonHelper.Serialize(stream, appMetadata, true); + JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); } return appMetadata; @@ -890,7 +889,7 @@ namespace Ryujinx.Ui.App.Common if (File.Exists(titleUpdateMetadataPath)) { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath).Selected; + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; if (File.Exists(updatePath)) { diff --git a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs index 99111ea6..1f9bd0ba 100644 --- a/Ryujinx.Ui.Common/Configuration/AudioBackend.cs +++ b/Ryujinx.Ui.Common/Configuration/AudioBackend.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration { + [JsonConverter(typeof(TypedStringEnumConverter))] public enum AudioBackend { Dummy, diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index e9aec04b..14c03957 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -5,7 +5,7 @@ using Ryujinx.Common.Utilities; using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.Ui; using System.Collections.Generic; -using System.IO; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -321,14 +321,14 @@ namespace Ryujinx.Ui.Common.Configuration /// /// Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions) /// TODO: Remove this when those older versions aren't in use anymore. - public List KeyboardConfig { get; set; } + public List KeyboardConfig { get; set; } /// /// Legacy controller control bindings /// /// Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions) /// TODO: Remove this when those older versions aren't in use anymore. - public List ControllerConfig { get; set; } + public List ControllerConfig { get; set; } /// /// Input configurations @@ -354,11 +354,12 @@ namespace Ryujinx.Ui.Common.Configuration /// Loads a configuration file from disk /// /// The path to the JSON configuration file + /// Parsed configuration file public static bool TryLoad(string path, out ConfigurationFileFormat configurationFileFormat) { try { - configurationFileFormat = JsonHelper.DeserializeFromFile(path); + configurationFileFormat = JsonHelper.DeserializeFromFile(path, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); return configurationFileFormat.Version != 0; } @@ -376,8 +377,7 @@ namespace Ryujinx.Ui.Common.Configuration /// The path to the JSON configuration file public void SaveConfig(string path) { - using FileStream fileStream = File.Create(path, 4096, FileOptions.WriteThrough); - JsonHelper.Serialize(fileStream, this, true); + JsonHelper.SerializeToFile(path, this, ConfigurationFileFormatSettings.SerializerContext.ConfigurationFileFormat); } } } diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs new file mode 100644 index 00000000..6ce2ef01 --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormatSettings.cs @@ -0,0 +1,9 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Ui.Common.Configuration +{ + internal static class ConfigurationFileFormatSettings + { + public static readonly ConfigurationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs new file mode 100644 index 00000000..bb8dfb49 --- /dev/null +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationJsonSerializerContext.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration +{ + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ConfigurationFileFormat))] + internal partial class ConfigurationJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index bcdd2e70..82a331c1 100644 --- a/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -9,6 +9,7 @@ using Ryujinx.Ui.Common.Configuration.Ui; using Ryujinx.Ui.Common.Helper; using System; using System.Collections.Generic; +using System.Text.Json.Nodes; namespace Ryujinx.Ui.Common.Configuration { @@ -631,8 +632,8 @@ namespace Ryujinx.Ui.Common.Configuration EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List(), - ControllerConfig = new List(), + KeyboardConfig = new List(), + ControllerConfig = new List(), InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, PreferredGpu = Graphics.PreferredGpu diff --git a/Ryujinx.Ui.Common/Configuration/System/Language.cs b/Ryujinx.Ui.Common/Configuration/System/Language.cs index 3d2dc991..404f8063 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Language.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Language.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter))] public enum Language { Japanese, diff --git a/Ryujinx.Ui.Common/Configuration/System/Region.cs b/Ryujinx.Ui.Common/Configuration/System/Region.cs index fb51e08e..7dfac638 100644 --- a/Ryujinx.Ui.Common/Configuration/System/Region.cs +++ b/Ryujinx.Ui.Common/Configuration/System/Region.cs @@ -1,5 +1,9 @@ -namespace Ryujinx.Ui.Common.Configuration.System +using Ryujinx.Common.Utilities; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Configuration.System { + [JsonConverter(typeof(TypedStringEnumConverter))] public enum Region { Japan, diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs new file mode 100644 index 00000000..f412b950 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApi.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboApi : IEquatable + { + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("head")] + public string Head { get; set; } + [JsonPropertyName("tail")] + public string Tail { get; set; } + [JsonPropertyName("image")] + public string Image { get; set; } + [JsonPropertyName("amiiboSeries")] + public string AmiiboSeries { get; set; } + [JsonPropertyName("character")] + public string Character { get; set; } + [JsonPropertyName("gameSeries")] + public string GameSeries { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("release")] + public Dictionary Release { get; set; } + + [JsonPropertyName("gamesSwitch")] + public List GamesSwitch { get; set; } + + public override string ToString() + { + return Name; + } + + public string GetId() + { + return Head + Tail; + } + + public bool Equals(AmiiboApi other) + { + return Head + Tail == other.Head + other.Tail; + } + + public override bool Equals(object obj) + { + return obj is AmiiboApi other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Head, Tail); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs new file mode 100644 index 00000000..def7d1bc --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiGamesSwitch.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiGamesSwitch + { + [JsonPropertyName("amiiboUsage")] + public List AmiiboUsage { get; set; } + [JsonPropertyName("gameID")] + public List GameId { get; set; } + [JsonPropertyName("gameName")] + public string GameName { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs new file mode 100644 index 00000000..814573c2 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboApiUsage.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public class AmiiboApiUsage + { + [JsonPropertyName("Usage")] + public string Usage { get; set; } + [JsonPropertyName("write")] + public bool Write { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs new file mode 100644 index 00000000..feb7993c --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJson.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + public struct AmiiboJson + { + [JsonPropertyName("amiibo")] + public List Amiibo { get; set; } + [JsonPropertyName("lastUpdated")] + public DateTime LastUpdated { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs new file mode 100644 index 00000000..4cbb5a7b --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Amiibo/AmiiboJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Amiibo +{ + [JsonSerializable(typeof(AmiiboJson))] + public partial class AmiiboJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs new file mode 100644 index 00000000..10d01478 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleaseAssetJsonResponse.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleaseAssetJsonResponse + { + public string Name { get; set; } + public string State { get; set; } + public string BrowserDownloadUrl { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs new file mode 100644 index 00000000..954d03e3 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonResponse.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ryujinx.Ui.Common.Models.Github +{ + public class GithubReleasesJsonResponse + { + public string Name { get; set; } + public List Assets { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs new file mode 100644 index 00000000..e5fd9d09 --- /dev/null +++ b/Ryujinx.Ui.Common/Models/Github/GithubReleasesJsonSerializerContext.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.Common.Models.Github +{ + [JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)] + public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext + { + } +} \ No newline at end of file -- cgit v1.2.3