From 886e42fb19b5d69a6a256e20bd1b948124f790a3 Mon Sep 17 00:00:00 2001 From: Thog Date: Thu, 30 Apr 2020 14:07:41 +0200 Subject: [PATCH] Use the official JSON parser (#1151) This remove Utf8son and JsonPrettyPrinter dependencies. NOTE: the standard JSON parser doesn't support configurable indentation, as a result, all the pretty printed JSON are indented with 2 spaces. --- .../Configuration/ConfigurationFileFormat.cs | 58 +----- .../Configuration/Hid/KeyboardHotkeys.cs | 2 +- .../Configuration/Hid/NpadController.cs | 12 +- .../Configuration/Hid/NpadControllerLeft.cs | 18 +- .../Configuration/Hid/NpadControllerRight.cs | 18 +- .../Configuration/Hid/NpadKeyboard.cs | 6 +- .../Configuration/Hid/NpadKeyboardLeft.cs | 24 +-- .../Configuration/Hid/NpadKeyboardRight.cs | 24 +-- Ryujinx.Common/Configuration/Ui/GuiColumns.cs | 20 +- .../Logging/Targets/JsonLogTarget.cs | 12 +- Ryujinx.Common/Ryujinx.Common.csproj | 4 - Ryujinx.Common/Utilities/JsonHelper.cs | 106 ++++++++++ .../Profiler/ProfilerConfiguration.cs | 43 +--- Ryujinx.HLE/HOS/Horizon.cs | 65 +++--- Ryujinx/Config.json | 196 +++++++++--------- Ryujinx/Ui/AboutWindow.cs | 3 - Ryujinx/Ui/ApplicationLibrary.cs | 103 +++++---- Ryujinx/Ui/GameTableContextMenu.cs | 53 +++-- Ryujinx/Ui/TitleUpdateWindow.cs | 17 +- 19 files changed, 388 insertions(+), 396 deletions(-) create mode 100644 Ryujinx.Common/Utilities/JsonHelper.cs diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs index 812dc2c30..ff5a67c4e 100644 --- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs @@ -1,16 +1,12 @@ -using JsonPrettyPrinterPlus; -using Ryujinx.Common.Logging; -using System; using System.Collections.Generic; using System.IO; -using System.Text; -using Utf8Json; -using Utf8Json.Resolvers; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.Configuration.System; using Ryujinx.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.UI.Input; using Ryujinx.Configuration.Ui; +using Ryujinx.UI.Input; namespace Ryujinx.Configuration { @@ -179,15 +175,7 @@ namespace Ryujinx.Configuration /// The path to the JSON configuration file public static ConfigurationFileFormat Load(string path) { - var resolver = CompositeResolver.Create( - new[] { new ConfigurationEnumFormatter() }, - new[] { StandardResolver.AllowPrivateSnakeCase } - ); - - using (Stream stream = File.OpenRead(path)) - { - return JsonSerializer.Deserialize(stream, resolver); - } + return JsonHelper.DeserializeFromFile(path); } /// @@ -196,41 +184,7 @@ namespace Ryujinx.Configuration /// The path to the JSON configuration file public void SaveConfig(string path) { - IJsonFormatterResolver resolver = CompositeResolver.Create( - new[] { new ConfigurationEnumFormatter() }, - new[] { StandardResolver.AllowPrivateSnakeCase } - ); - - byte[] data = JsonSerializer.Serialize(this, resolver); - File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson()); - } - - public class ConfigurationEnumFormatter : IJsonFormatter - where T : struct - { - public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) - { - formatterResolver.GetFormatterWithVerify() - .Serialize(ref writer, value.ToString(), formatterResolver); - } - - public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) - { - if (reader.ReadIsNull()) - { - return default(T); - } - - string enumName = formatterResolver.GetFormatterWithVerify() - .Deserialize(ref reader, formatterResolver); - - if (Enum.TryParse(enumName, out T result)) - { - return result; - } - - return default(T); - } + File.WriteAllText(path, JsonHelper.Serialize(this, true)); } } } \ No newline at end of file diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 1d0b05049..30cc8d844 100644 --- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -2,6 +2,6 @@ { public struct KeyboardHotkeys { - public Key ToggleVsync; + public Key ToggleVsync { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadController.cs b/Ryujinx.Common/Configuration/Hid/NpadController.cs index f00865d55..94b985d55 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadController.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadController.cs @@ -5,31 +5,31 @@ /// /// Enables or disables controller support /// - public bool Enabled; + public bool Enabled { get; set; } /// /// Controller Device Index /// - public int Index; + public int Index { get; set; } /// /// Controller Analog Stick Deadzone /// - public float Deadzone; + public float Deadzone { get; set; } /// /// Controller Trigger Threshold /// - public float TriggerThreshold; + public float TriggerThreshold { get; set; } /// /// Left JoyCon Controller Bindings /// - public NpadControllerLeft LeftJoycon; + public NpadControllerLeft LeftJoycon { get; set; } /// /// Right JoyCon Controller Bindings /// - public NpadControllerRight RightJoycon; + public NpadControllerRight RightJoycon { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs index 54ac0f03a..c221b5e8a 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs @@ -2,14 +2,14 @@ { public struct NpadControllerLeft { - public ControllerInputId Stick; - public ControllerInputId StickButton; - public ControllerInputId ButtonMinus; - public ControllerInputId ButtonL; - public ControllerInputId ButtonZl; - public ControllerInputId DPadUp; - public ControllerInputId DPadDown; - public ControllerInputId DPadLeft; - public ControllerInputId DPadRight; + public ControllerInputId Stick { get; set; } + public ControllerInputId StickButton { get; set; } + public ControllerInputId ButtonMinus { get; set; } + public ControllerInputId ButtonL { get; set; } + public ControllerInputId ButtonZl { get; set; } + public ControllerInputId DPadUp { get; set; } + public ControllerInputId DPadDown { get; set; } + public ControllerInputId DPadLeft { get; set; } + public ControllerInputId DPadRight { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs index 315136d9f..f52f6f16d 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs @@ -2,14 +2,14 @@ { public struct NpadControllerRight { - public ControllerInputId Stick; - public ControllerInputId StickButton; - public ControllerInputId ButtonA; - public ControllerInputId ButtonB; - public ControllerInputId ButtonX; - public ControllerInputId ButtonY; - public ControllerInputId ButtonPlus; - public ControllerInputId ButtonR; - public ControllerInputId ButtonZr; + public ControllerInputId Stick { get; set; } + public ControllerInputId StickButton { get; set; } + public ControllerInputId ButtonA { get; set; } + public ControllerInputId ButtonB { get; set; } + public ControllerInputId ButtonX { get; set; } + public ControllerInputId ButtonY { get; set; } + public ControllerInputId ButtonPlus { get; set; } + public ControllerInputId ButtonR { get; set; } + public ControllerInputId ButtonZr { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs index 911f5119e..5ae82756b 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs @@ -5,16 +5,16 @@ namespace Ryujinx.UI.Input /// /// Left JoyCon Keyboard Bindings /// - public Configuration.Hid.NpadKeyboardLeft LeftJoycon; + public Configuration.Hid.NpadKeyboardLeft LeftJoycon { get; set; } /// /// Right JoyCon Keyboard Bindings /// - public Configuration.Hid.NpadKeyboardRight RightJoycon; + public Configuration.Hid.NpadKeyboardRight RightJoycon { get; set; } /// /// Hotkey Keyboard Bindings /// - public Configuration.Hid.KeyboardHotkeys Hotkeys; + public Configuration.Hid.KeyboardHotkeys Hotkeys { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs index 799cdfdb8..4a61d9323 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs @@ -2,17 +2,17 @@ { public struct NpadKeyboardLeft { - public Key StickUp; - public Key StickDown; - public Key StickLeft; - public Key StickRight; - public Key StickButton; - public Key DPadUp; - public Key DPadDown; - public Key DPadLeft; - public Key DPadRight; - public Key ButtonMinus; - public Key ButtonL; - public Key ButtonZl; + public Key StickUp { get; set; } + public Key StickDown { get; set; } + public Key StickLeft { get; set; } + public Key StickRight { get; set; } + public Key StickButton { get; set; } + public Key DPadUp { get; set; } + public Key DPadDown { get; set; } + public Key DPadLeft { get; set; } + public Key DPadRight { get; set; } + public Key ButtonMinus { get; set; } + public Key ButtonL { get; set; } + public Key ButtonZl { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs index 311504bb7..0677b5732 100644 --- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs +++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs @@ -2,17 +2,17 @@ { public struct NpadKeyboardRight { - public Key StickUp; - public Key StickDown; - public Key StickLeft; - public Key StickRight; - public Key StickButton; - public Key ButtonA; - public Key ButtonB; - public Key ButtonX; - public Key ButtonY; - public Key ButtonPlus; - public Key ButtonR; - public Key ButtonZr; + public Key StickUp { get; set; } + public Key StickDown { get; set; } + public Key StickLeft { get; set; } + public Key StickRight { get; set; } + public Key StickButton { get; set; } + public Key ButtonA { get; set; } + public Key ButtonB { get; set; } + public Key ButtonX { get; set; } + public Key ButtonY { get; set; } + public Key ButtonPlus { get; set; } + public Key ButtonR { get; set; } + public Key ButtonZr { get; set; } } } diff --git a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs index 2b3524aa8..de4f7369b 100644 --- a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs +++ b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs @@ -2,15 +2,15 @@ { public struct GuiColumns { - public bool FavColumn; - public bool IconColumn; - public bool AppColumn; - public bool DevColumn; - public bool VersionColumn; - public bool TimePlayedColumn; - public bool LastPlayedColumn; - public bool FileExtColumn; - public bool FileSizeColumn; - public bool PathColumn; + public bool FavColumn { get; set; } + public bool IconColumn { get; set; } + public bool AppColumn { get; set; } + public bool DevColumn { get; set; } + public bool VersionColumn { get; set; } + public bool TimePlayedColumn { get; set; } + public bool LastPlayedColumn { get; set; } + public bool FileExtColumn { get; set; } + public bool FileSizeColumn { get; set; } + public bool PathColumn { get; set; } } } diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs index 3729b18d1..95f96576c 100644 --- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs +++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs @@ -1,6 +1,5 @@ -using System; -using System.IO; -using Utf8Json; +using System.IO; +using System.Text.Json; namespace Ryujinx.Common.Logging { @@ -26,7 +25,12 @@ namespace Ryujinx.Common.Logging public void Log(object sender, LogEventArgs e) { - JsonSerializer.Serialize(_stream, e); + string text = JsonSerializer.Serialize(e); + + using (BinaryWriter writer = new BinaryWriter(_stream)) + { + writer.Write(text); + } } public void Dispose() diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj index e902d26ab..3eb75730b 100644 --- a/Ryujinx.Common/Ryujinx.Common.csproj +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -27,12 +27,8 @@ - - NU1701 - - diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/Ryujinx.Common/Utilities/JsonHelper.cs new file mode 100644 index 000000000..5aa461830 --- /dev/null +++ b/Ryujinx.Common/Utilities/JsonHelper.cs @@ -0,0 +1,106 @@ +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Utilities +{ + public class JsonHelper + { + public static JsonNamingPolicy SnakeCase { get; } + + private class SnakeCaseNamingPolicy : JsonNamingPolicy + { + public override string ConvertName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return name; + } + + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < name.Length; i++) + { + char c = name[i]; + + if (char.IsUpper(c)) + { + if (i == 0 || char.IsUpper(name[i - 1])) + { + builder.Append(char.ToLowerInvariant(c)); + } + else + { + builder.Append("_"); + builder.Append(char.ToLowerInvariant(c)); + } + } + else + { + builder.Append(c); + } + } + + return builder.ToString(); + } + } + + static JsonHelper() + { + SnakeCase = new SnakeCaseNamingPolicy(); + } + + public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DictionaryKeyPolicy = SnakeCase, + PropertyNamingPolicy = SnakeCase, + WriteIndented = prettyPrint, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }; + + options.Converters.Add(new JsonStringEnumConverter()); + + return options; + } + + public static T Deserialize(Stream stream) + { + using (BinaryReader reader = new BinaryReader(stream)) + { + return JsonSerializer.Deserialize(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions()); + } + } + + public static T DeserializeFromFile(string path) + { + return Deserialize(File.ReadAllText(path)); + } + + public static T Deserialize(string json) + { + return JsonSerializer.Deserialize(json, GetDefaultSerializerOptions()); + } + + public static void Serialize(Stream stream, TValue obj, bool prettyPrint = false) + { + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write(SerializeToUtf8Bytes(obj, prettyPrint)); + } + } + + public static string Serialize(TValue obj, bool prettyPrint = false) + { + return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint)); + } + + public static byte[] SerializeToUtf8Bytes(T obj, bool prettyPrint = false) + { + return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint)); + } + } +} diff --git a/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs index e0842f2e1..73ef8f555 100644 --- a/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs +++ b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs @@ -1,8 +1,5 @@ -using Gdk; -using System; +using Ryujinx.Common.Utilities; using System.IO; -using Utf8Json; -using Utf8Json.Resolvers; namespace Ryujinx.Debugger.Profiler { @@ -21,48 +18,12 @@ namespace Ryujinx.Debugger.Profiler /// The path to the JSON configuration file public static ProfilerConfiguration Load(string path) { - var resolver = CompositeResolver.Create( - new[] { new ConfigurationEnumFormatter() }, - new[] { StandardResolver.AllowPrivateSnakeCase } - ); - if (!File.Exists(path)) { throw new FileNotFoundException($"Profiler configuration file {path} not found"); } - using (Stream stream = File.OpenRead(path)) - { - return JsonSerializer.Deserialize(stream, resolver); - } - } - - private class ConfigurationEnumFormatter : IJsonFormatter - where T : struct - { - public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) - { - formatterResolver.GetFormatterWithVerify() - .Serialize(ref writer, value.ToString(), formatterResolver); - } - - public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) - { - if (reader.ReadIsNull()) - { - return default(T); - } - - string enumName = formatterResolver.GetFormatterWithVerify() - .Deserialize(ref reader, formatterResolver); - - if (Enum.TryParse(enumName, out T result)) - { - return result; - } - - return default(T); - } + return JsonHelper.DeserializeFromFile(path); } } } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 2dfa27571..76185d7b9 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -39,11 +39,10 @@ using System.IO; using System.Linq; using System.Reflection; using System.Threading; -using Utf8Json; -using Utf8Json.Resolvers; using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager; using NsoExecutable = Ryujinx.HLE.Loaders.Executables.NsoExecutable; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; using static LibHac.Fs.ApplicationSaveDataManagement; @@ -540,49 +539,47 @@ namespace Ryujinx.HLE.HOS IStorage dataStorage = null; IFileSystem codeFs = null; - if (File.Exists(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"))) + string titleUpdateMetadataPath = System.IO.Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"); + + if (File.Exists(titleUpdateMetadataPath)) { - using (Stream stream = File.OpenRead(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json"))) + string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath).Selected; + + if (File.Exists(updatePath)) { - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); - string updatePath = JsonSerializer.Deserialize(stream, resolver).Selected; + FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); + PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - if (File.Exists(updatePath)) + foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { - FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); - foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) + if (result.IsSuccess()) { - Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); + Ticket ticket = new Ticket(ticketFile.AsStream()); - if (result.IsSuccess()) - { - Ticket ticket = new Ticket(ticketFile.AsStream()); + KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); + } + } - KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); - } + foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) + { + nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new Nca(KeySet, ncaFile.AsStorage()); + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16")) + { + break; } - foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) + if (nca.Header.ContentType == NcaContentType.Program) { - nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(KeySet, ncaFile.AsStorage()); - - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16")) - { - break; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - patchNca = nca; - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } + patchNca = nca; + } + else if (nca.Header.ContentType == NcaContentType.Control) + { + controlNca = nca; } } } diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json index 7a5fe9723..8df901e22 100644 --- a/Ryujinx/Config.json +++ b/Ryujinx/Config.json @@ -1,103 +1,103 @@ { - "version": 5, - "max_anisotropy": -1, - "graphics_shaders_dump_path": "", - "logging_enable_debug": false, - "logging_enable_stub": true, - "logging_enable_info": true, - "logging_enable_warn": true, - "logging_enable_error": true, - "logging_enable_guest": true, - "logging_enable_fs_access_log": false, - "logging_filtered_classes": [], - "enable_file_log": true, - "system_language": "AmericanEnglish", - "system_region": "USA", - "system_time_zone": "UTC", - "system_time_offset": 0, - "docked_mode": false, - "enable_discord_integration": true, - "enable_vsync": true, - "enable_multicore_scheduling": true, - "enable_fs_integrity_checks": true, - "fs_global_access_log_mode": 0, - "ignore_missing_services": false, - "controller_type": "Handheld", - "gui_columns": { - "fav_column": true, - "icon_column": true, - "app_column": true, - "dev_column": true, - "version_column": true, - "time_played_column": true, - "last_played_column": true, - "file_ext_column": true, - "file_size_column": true, - "path_column": true + "version": 5, + "max_anisotropy": -1, + "graphics_shaders_dump_path": "", + "logging_enable_debug": false, + "logging_enable_stub": true, + "logging_enable_info": true, + "logging_enable_warn": true, + "logging_enable_error": true, + "logging_enable_guest": true, + "logging_enable_fs_access_log": false, + "logging_filtered_classes": [], + "enable_file_log": true, + "system_language": "AmericanEnglish", + "system_region": "USA", + "system_time_zone": "UTC", + "system_time_offset": 0, + "docked_mode": false, + "enable_discord_integration": true, + "enable_vsync": true, + "enable_multicore_scheduling": true, + "enable_fs_integrity_checks": true, + "fs_global_access_log_mode": 0, + "ignore_missing_services": false, + "controller_type": "Handheld", + "gui_columns": { + "fav_column": true, + "icon_column": true, + "app_column": true, + "dev_column": true, + "version_column": true, + "time_played_column": true, + "last_played_column": true, + "file_ext_column": true, + "file_size_column": true, + "path_column": true + }, + "game_dirs": [], + "enable_custom_theme": false, + "custom_theme_path": "", + "enable_keyboard": false, + "keyboard_controls": { + "left_joycon": { + "stick_up": "W", + "stick_down": "S", + "stick_left": "A", + "stick_right": "D", + "stick_button": "F", + "dpad_up": "Up", + "dpad_down": "Down", + "dpad_left": "Left", + "dpad_right": "Right", + "button_minus": "Minus", + "button_l": "E", + "button_zl": "Q" }, - "game_dirs": [], - "enable_custom_theme": false, - "custom_theme_path": "", - "enable_keyboard": false, - "keyboard_controls": { - "left_joycon": { - "stick_up": "W", - "stick_down": "S", - "stick_left": "A", - "stick_right": "D", - "stick_button": "F", - "dpad_up": "Up", - "dpad_down": "Down", - "dpad_left": "Left", - "dpad_right": "Right", - "button_minus": "Minus", - "button_l": "E", - "button_zl": "Q" - }, - "right_joycon": { - "stick_up": "I", - "stick_down": "K", - "stick_left": "J", - "stick_right": "L", - "stick_button": "H", - "button_a": "Z", - "button_b": "X", - "button_x": "C", - "button_y": "V", - "button_plus": "Plus", - "button_r": "U", - "button_zr": "O" - }, - "hotkeys": { - "toggle_vsync": "Tab" - } + "right_joycon": { + "stick_up": "I", + "stick_down": "K", + "stick_left": "J", + "stick_right": "L", + "stick_button": "H", + "button_a": "Z", + "button_b": "X", + "button_x": "C", + "button_y": "V", + "button_plus": "Plus", + "button_r": "U", + "button_zr": "O" }, - "joystick_controls": { - "enabled": true, - "index": 0, - "deadzone": 0.05, - "trigger_threshold": 0.5, - "left_joycon": { - "stick": "Axis0", - "stick_button": "Button8", - "button_minus": "Button6", - "button_l": "Button4", - "button_zl": "Axis2", - "dpad_up": "Hat0Up", - "dpad_down": "Hat0Down", - "dpad_left": "Hat0Left", - "dpad_right": "Hat0Right" - }, - "right_joycon": { - "stick": "Axis3", - "stick_button": "Button9", - "button_a": "Button1", - "button_b": "Button0", - "button_x": "Button3", - "button_y": "Button2", - "button_plus": "Button7", - "button_r": "Button5", - "button_zr": "Axis5" - } + "hotkeys": { + "toggle_vsync": "Tab" } + }, + "joystick_controls": { + "enabled": true, + "index": 0, + "deadzone": 0.05, + "trigger_threshold": 0.5, + "left_joycon": { + "stick": "Axis0", + "stick_button": "Button8", + "button_minus": "Button6", + "button_l": "Button4", + "button_zl": "Axis2", + "dpad_up": "Hat0Up", + "dpad_down": "Hat0Down", + "dpad_left": "Hat0Left", + "dpad_right": "Hat0Right" + }, + "right_joycon": { + "stick": "Axis3", + "stick_button": "Button9", + "button_a": "Button1", + "button_b": "Button0", + "button_x": "Button3", + "button_y": "Button2", + "button_plus": "Button7", + "button_r": "Button5", + "button_zr": "Axis5" + } + } } \ No newline at end of file diff --git a/Ryujinx/Ui/AboutWindow.cs b/Ryujinx/Ui/AboutWindow.cs index cae777766..6a18058a5 100644 --- a/Ryujinx/Ui/AboutWindow.cs +++ b/Ryujinx/Ui/AboutWindow.cs @@ -1,11 +1,8 @@ using Gtk; using System; using System.Diagnostics; -using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using Utf8Json; -using Utf8Json.Resolvers; using GUI = Gtk.Builder.ObjectAttribute; diff --git a/Ryujinx/Ui/ApplicationLibrary.cs b/Ryujinx/Ui/ApplicationLibrary.cs index f55fa1ec6..b4700300a 100644 --- a/Ryujinx/Ui/ApplicationLibrary.cs +++ b/Ryujinx/Ui/ApplicationLibrary.cs @@ -18,10 +18,10 @@ using System.Globalization; using System.IO; using System.Reflection; using System.Text; -using Utf8Json; -using Utf8Json.Resolvers; +using System.Text.Json; using RightsId = LibHac.Fs.RightsId; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; namespace Ryujinx.Ui { @@ -509,8 +509,6 @@ namespace Ryujinx.Ui string metadataFolder = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "gui"); string metadataFile = Path.Combine(metadataFolder, "metadata.json"); - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); - ApplicationMetadata appMetadata; if (!File.Exists(metadataFile)) @@ -526,27 +524,24 @@ namespace Ryujinx.Ui using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) { - JsonSerializer.Serialize(stream, appMetadata, resolver); + JsonHelper.Serialize(stream, appMetadata, true); } } - using (Stream stream = File.OpenRead(metadataFile)) + try { - try + appMetadata = JsonHelper.DeserializeFromFile(metadataFile); + } + catch (JsonException) + { + Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults."); + + appMetadata = new ApplicationMetadata { - appMetadata = JsonSerializer.Deserialize(stream, resolver); - } - catch (JsonParsingException) - { - Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults."); - - appMetadata = new ApplicationMetadata - { - Favorite = false, - TimePlayed = 0, - LastPlayed = "Never" - }; - } + Favorite = false, + TimePlayed = 0, + LastPlayed = "Never" + }; } if (modifyFunction != null) @@ -555,7 +550,7 @@ namespace Ryujinx.Ui using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) { - JsonSerializer.Serialize(stream, appMetadata, resolver); + JsonHelper.Serialize(stream, appMetadata, true); } } @@ -653,57 +648,53 @@ namespace Ryujinx.Ui if (File.Exists(jsonPath)) { - using (Stream stream = File.OpenRead(jsonPath)) + string updatePath = JsonHelper.DeserializeFromFile(jsonPath).Selected; + + if (!File.Exists(updatePath)) { - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); - string updatePath = JsonSerializer.Deserialize(stream, resolver).Selected; + version = ""; - if (!File.Exists(updatePath)) + return false; + } + + using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read)) + { + PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + + foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { - version = ""; + Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); - return false; + if (result.IsSuccess()) + { + Ticket ticket = new Ticket(ticketFile.AsStream()); + + _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); + } } - using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read)) + foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) { - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); + nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) { - Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); - - if (result.IsSuccess()) - { - Ticket ticket = new Ticket(ticketFile.AsStream()); - - _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); - } + break; } - foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) + if (nca.Header.ContentType == NcaContentType.Control) { - nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + ApplicationControlProperty controlData = new ApplicationControlProperty(); - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); + nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) - { - break; - } + nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - if (nca.Header.ContentType == NcaContentType.Control) - { - ApplicationControlProperty controlData = new ApplicationControlProperty(); + version = controlData.DisplayVersion.ToString(); - nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - version = controlData.DisplayVersion.ToString(); - - return true; - } + return true; } } } diff --git a/Ryujinx/Ui/GameTableContextMenu.cs b/Ryujinx/Ui/GameTableContextMenu.cs index 8bead1e3d..a1433f512 100644 --- a/Ryujinx/Ui/GameTableContextMenu.cs +++ b/Ryujinx/Ui/GameTableContextMenu.cs @@ -11,6 +11,7 @@ using LibHac.Ns; using LibHac.Spl; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using System; using System.Buffers; @@ -19,8 +20,6 @@ using System.Globalization; using System.IO; using System.Reflection; using System.Threading; -using Utf8Json; -using Utf8Json.Resolvers; using static LibHac.Fs.ApplicationSaveDataManagement; using GUI = Gtk.Builder.ObjectAttribute; @@ -274,43 +273,39 @@ namespace Ryujinx.Ui if (File.Exists(titleUpdateMetadataPath)) { - using (Stream stream = File.OpenRead(titleUpdateMetadataPath)) + string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath).Selected; + + if (File.Exists(updatePath)) { - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); - string updatePath = JsonSerializer.Deserialize(stream, resolver).Selected; + FileStream updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read); + PartitionFileSystem nsp = new PartitionFileSystem(updateFile.AsStorage()); - if (File.Exists(updatePath)) + foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) { - FileStream updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(updateFile.AsStorage()); + Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); - foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik")) + if (result.IsSuccess()) { - Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); + Ticket ticket = new Ticket(ticketFile.AsStream()); - if (result.IsSuccess()) - { - Ticket ticket = new Ticket(ticketFile.AsStream()); + _virtualFileSystem.KeySet.ExternalKeySet.Add(new LibHac.Fs.RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); + } + } - _virtualFileSystem.KeySet.ExternalKeySet.Add(new LibHac.Fs.RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet))); - } + foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) + { + nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); + + if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16")) + { + break; } - foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca")) + if (nca.Header.ContentType == NcaContentType.Program) { - nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()); - - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16")) - { - break; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - patchNca = nca; - } + patchNca = nca; } } } diff --git a/Ryujinx/Ui/TitleUpdateWindow.cs b/Ryujinx/Ui/TitleUpdateWindow.cs index 01025d6dd..a6d64a793 100644 --- a/Ryujinx/Ui/TitleUpdateWindow.cs +++ b/Ryujinx/Ui/TitleUpdateWindow.cs @@ -1,5 +1,4 @@ using Gtk; -using JsonPrettyPrinterPlus; using LibHac; using LibHac.Common; using LibHac.Fs; @@ -12,11 +11,9 @@ using Ryujinx.HLE.FileSystem; using System; using System.Collections.Generic; using System.IO; -using System.Text; -using Utf8Json; -using Utf8Json.Resolvers; using GUI = Gtk.Builder.ObjectAttribute; +using JsonHelper = Ryujinx.Common.Utilities.JsonHelper; namespace Ryujinx.Ui { @@ -47,12 +44,9 @@ namespace Ryujinx.Ui try { - using (Stream stream = File.OpenRead(System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json"))) - { - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); + string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json"); - _titleUpdateWindowData = JsonSerializer.Deserialize(stream, resolver); - } + _titleUpdateWindowData = JsonHelper.DeserializeFromFile(path); } catch { @@ -185,12 +179,9 @@ namespace Ryujinx.Ui } } - IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); - string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json"); - byte[] data = JsonSerializer.Serialize(_titleUpdateWindowData, resolver); - File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson()); + File.WriteAllText(path, JsonHelper.Serialize(_titleUpdateWindowData, true)); MainWindow.UpdateGameTable(); Dispose();