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.
This commit is contained in:
Thog 2020-04-30 14:07:41 +02:00 committed by GitHub
parent 7ab3fccd4d
commit 886e42fb19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 388 additions and 396 deletions

View file

@ -1,16 +1,12 @@
using JsonPrettyPrinterPlus;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using Ryujinx.Common.Configuration.Hid;
using Utf8Json; using Ryujinx.Common.Logging;
using Utf8Json.Resolvers; using Ryujinx.Common.Utilities;
using Ryujinx.Configuration.System; using Ryujinx.Configuration.System;
using Ryujinx.Configuration.Hid; using Ryujinx.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.UI.Input;
using Ryujinx.Configuration.Ui; using Ryujinx.Configuration.Ui;
using Ryujinx.UI.Input;
namespace Ryujinx.Configuration namespace Ryujinx.Configuration
{ {
@ -179,15 +175,7 @@ namespace Ryujinx.Configuration
/// <param name="path">The path to the JSON configuration file</param> /// <param name="path">The path to the JSON configuration file</param>
public static ConfigurationFileFormat Load(string path) public static ConfigurationFileFormat Load(string path)
{ {
var resolver = CompositeResolver.Create( return JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
using (Stream stream = File.OpenRead(path))
{
return JsonSerializer.Deserialize<ConfigurationFileFormat>(stream, resolver);
}
} }
/// <summary> /// <summary>
@ -196,41 +184,7 @@ namespace Ryujinx.Configuration
/// <param name="path">The path to the JSON configuration file</param> /// <param name="path">The path to the JSON configuration file</param>
public void SaveConfig(string path) public void SaveConfig(string path)
{ {
IJsonFormatterResolver resolver = CompositeResolver.Create( File.WriteAllText(path, JsonHelper.Serialize(this, true));
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
byte[] data = JsonSerializer.Serialize(this, resolver);
File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
}
public class ConfigurationEnumFormatter<T> : IJsonFormatter<T>
where T : struct
{
public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver)
{
formatterResolver.GetFormatterWithVerify<string>()
.Serialize(ref writer, value.ToString(), formatterResolver);
}
public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
if (reader.ReadIsNull())
{
return default(T);
}
string enumName = formatterResolver.GetFormatterWithVerify<string>()
.Deserialize(ref reader, formatterResolver);
if (Enum.TryParse<T>(enumName, out T result))
{
return result;
}
return default(T);
}
} }
} }
} }

View file

@ -2,6 +2,6 @@
{ {
public struct KeyboardHotkeys public struct KeyboardHotkeys
{ {
public Key ToggleVsync; public Key ToggleVsync { get; set; }
} }
} }

View file

@ -5,31 +5,31 @@
/// <summary> /// <summary>
/// Enables or disables controller support /// Enables or disables controller support
/// </summary> /// </summary>
public bool Enabled; public bool Enabled { get; set; }
/// <summary> /// <summary>
/// Controller Device Index /// Controller Device Index
/// </summary> /// </summary>
public int Index; public int Index { get; set; }
/// <summary> /// <summary>
/// Controller Analog Stick Deadzone /// Controller Analog Stick Deadzone
/// </summary> /// </summary>
public float Deadzone; public float Deadzone { get; set; }
/// <summary> /// <summary>
/// Controller Trigger Threshold /// Controller Trigger Threshold
/// </summary> /// </summary>
public float TriggerThreshold; public float TriggerThreshold { get; set; }
/// <summary> /// <summary>
/// Left JoyCon Controller Bindings /// Left JoyCon Controller Bindings
/// </summary> /// </summary>
public NpadControllerLeft LeftJoycon; public NpadControllerLeft LeftJoycon { get; set; }
/// <summary> /// <summary>
/// Right JoyCon Controller Bindings /// Right JoyCon Controller Bindings
/// </summary> /// </summary>
public NpadControllerRight RightJoycon; public NpadControllerRight RightJoycon { get; set; }
} }
} }

View file

@ -2,14 +2,14 @@
{ {
public struct NpadControllerLeft public struct NpadControllerLeft
{ {
public ControllerInputId Stick; public ControllerInputId Stick { get; set; }
public ControllerInputId StickButton; public ControllerInputId StickButton { get; set; }
public ControllerInputId ButtonMinus; public ControllerInputId ButtonMinus { get; set; }
public ControllerInputId ButtonL; public ControllerInputId ButtonL { get; set; }
public ControllerInputId ButtonZl; public ControllerInputId ButtonZl { get; set; }
public ControllerInputId DPadUp; public ControllerInputId DPadUp { get; set; }
public ControllerInputId DPadDown; public ControllerInputId DPadDown { get; set; }
public ControllerInputId DPadLeft; public ControllerInputId DPadLeft { get; set; }
public ControllerInputId DPadRight; public ControllerInputId DPadRight { get; set; }
} }
} }

View file

@ -2,14 +2,14 @@
{ {
public struct NpadControllerRight public struct NpadControllerRight
{ {
public ControllerInputId Stick; public ControllerInputId Stick { get; set; }
public ControllerInputId StickButton; public ControllerInputId StickButton { get; set; }
public ControllerInputId ButtonA; public ControllerInputId ButtonA { get; set; }
public ControllerInputId ButtonB; public ControllerInputId ButtonB { get; set; }
public ControllerInputId ButtonX; public ControllerInputId ButtonX { get; set; }
public ControllerInputId ButtonY; public ControllerInputId ButtonY { get; set; }
public ControllerInputId ButtonPlus; public ControllerInputId ButtonPlus { get; set; }
public ControllerInputId ButtonR; public ControllerInputId ButtonR { get; set; }
public ControllerInputId ButtonZr; public ControllerInputId ButtonZr { get; set; }
} }
} }

View file

@ -5,16 +5,16 @@ namespace Ryujinx.UI.Input
/// <summary> /// <summary>
/// Left JoyCon Keyboard Bindings /// Left JoyCon Keyboard Bindings
/// </summary> /// </summary>
public Configuration.Hid.NpadKeyboardLeft LeftJoycon; public Configuration.Hid.NpadKeyboardLeft LeftJoycon { get; set; }
/// <summary> /// <summary>
/// Right JoyCon Keyboard Bindings /// Right JoyCon Keyboard Bindings
/// </summary> /// </summary>
public Configuration.Hid.NpadKeyboardRight RightJoycon; public Configuration.Hid.NpadKeyboardRight RightJoycon { get; set; }
/// <summary> /// <summary>
/// Hotkey Keyboard Bindings /// Hotkey Keyboard Bindings
/// </summary> /// </summary>
public Configuration.Hid.KeyboardHotkeys Hotkeys; public Configuration.Hid.KeyboardHotkeys Hotkeys { get; set; }
} }
} }

View file

@ -2,17 +2,17 @@
{ {
public struct NpadKeyboardLeft public struct NpadKeyboardLeft
{ {
public Key StickUp; public Key StickUp { get; set; }
public Key StickDown; public Key StickDown { get; set; }
public Key StickLeft; public Key StickLeft { get; set; }
public Key StickRight; public Key StickRight { get; set; }
public Key StickButton; public Key StickButton { get; set; }
public Key DPadUp; public Key DPadUp { get; set; }
public Key DPadDown; public Key DPadDown { get; set; }
public Key DPadLeft; public Key DPadLeft { get; set; }
public Key DPadRight; public Key DPadRight { get; set; }
public Key ButtonMinus; public Key ButtonMinus { get; set; }
public Key ButtonL; public Key ButtonL { get; set; }
public Key ButtonZl; public Key ButtonZl { get; set; }
} }
} }

View file

@ -2,17 +2,17 @@
{ {
public struct NpadKeyboardRight public struct NpadKeyboardRight
{ {
public Key StickUp; public Key StickUp { get; set; }
public Key StickDown; public Key StickDown { get; set; }
public Key StickLeft; public Key StickLeft { get; set; }
public Key StickRight; public Key StickRight { get; set; }
public Key StickButton; public Key StickButton { get; set; }
public Key ButtonA; public Key ButtonA { get; set; }
public Key ButtonB; public Key ButtonB { get; set; }
public Key ButtonX; public Key ButtonX { get; set; }
public Key ButtonY; public Key ButtonY { get; set; }
public Key ButtonPlus; public Key ButtonPlus { get; set; }
public Key ButtonR; public Key ButtonR { get; set; }
public Key ButtonZr; public Key ButtonZr { get; set; }
} }
} }

View file

@ -2,15 +2,15 @@
{ {
public struct GuiColumns public struct GuiColumns
{ {
public bool FavColumn; public bool FavColumn { get; set; }
public bool IconColumn; public bool IconColumn { get; set; }
public bool AppColumn; public bool AppColumn { get; set; }
public bool DevColumn; public bool DevColumn { get; set; }
public bool VersionColumn; public bool VersionColumn { get; set; }
public bool TimePlayedColumn; public bool TimePlayedColumn { get; set; }
public bool LastPlayedColumn; public bool LastPlayedColumn { get; set; }
public bool FileExtColumn; public bool FileExtColumn { get; set; }
public bool FileSizeColumn; public bool FileSizeColumn { get; set; }
public bool PathColumn; public bool PathColumn { get; set; }
} }
} }

View file

@ -1,6 +1,5 @@
using System; using System.IO;
using System.IO; using System.Text.Json;
using Utf8Json;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
{ {
@ -26,7 +25,12 @@ namespace Ryujinx.Common.Logging
public void Log(object sender, LogEventArgs e) 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() public void Dispose()

View file

@ -27,12 +27,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1">
<NoWarn>NU1701</NoWarn>
</PackageReference>
<PackageReference Include="MsgPack.Cli" Version="1.0.1" /> <PackageReference Include="MsgPack.Cli" Version="1.0.1" />
<PackageReference Include="System.Management" Version="4.7.0" /> <PackageReference Include="System.Management" Version="4.7.0" />
<PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -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<T>(Stream stream)
{
using (BinaryReader reader = new BinaryReader(stream))
{
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
}
}
public static T DeserializeFromFile<T>(string path)
{
return Deserialize<T>(File.ReadAllText(path));
}
public static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
}
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
}
}
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
{
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
}
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
{
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
}
}
}

View file

@ -1,8 +1,5 @@
using Gdk; using Ryujinx.Common.Utilities;
using System;
using System.IO; using System.IO;
using Utf8Json;
using Utf8Json.Resolvers;
namespace Ryujinx.Debugger.Profiler namespace Ryujinx.Debugger.Profiler
{ {
@ -21,48 +18,12 @@ namespace Ryujinx.Debugger.Profiler
/// <param name="path">The path to the JSON configuration file</param> /// <param name="path">The path to the JSON configuration file</param>
public static ProfilerConfiguration Load(string path) public static ProfilerConfiguration Load(string path)
{ {
var resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
if (!File.Exists(path)) if (!File.Exists(path))
{ {
throw new FileNotFoundException($"Profiler configuration file {path} not found"); throw new FileNotFoundException($"Profiler configuration file {path} not found");
} }
using (Stream stream = File.OpenRead(path)) return JsonHelper.DeserializeFromFile<ProfilerConfiguration>(path);
{
return JsonSerializer.Deserialize<ProfilerConfiguration>(stream, resolver);
}
}
private class ConfigurationEnumFormatter<T> : IJsonFormatter<T>
where T : struct
{
public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver)
{
formatterResolver.GetFormatterWithVerify<string>()
.Serialize(ref writer, value.ToString(), formatterResolver);
}
public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
if (reader.ReadIsNull())
{
return default(T);
}
string enumName = formatterResolver.GetFormatterWithVerify<string>()
.Deserialize(ref reader, formatterResolver);
if (Enum.TryParse<T>(enumName, out T result))
{
return result;
}
return default(T);
}
} }
} }
} }

View file

@ -39,11 +39,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using Utf8Json;
using Utf8Json.Resolvers;
using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager; using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
using NsoExecutable = Ryujinx.HLE.Loaders.Executables.NsoExecutable; using NsoExecutable = Ryujinx.HLE.Loaders.Executables.NsoExecutable;
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
using static LibHac.Fs.ApplicationSaveDataManagement; using static LibHac.Fs.ApplicationSaveDataManagement;
@ -540,49 +539,47 @@ namespace Ryujinx.HLE.HOS
IStorage dataStorage = null; IStorage dataStorage = null;
IFileSystem codeFs = 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<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
if (File.Exists(updatePath))
{ {
IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected; 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); Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
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()) KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet)));
{ }
Ticket ticket = new Ticket(ticketFile.AsStream()); }
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(); patchNca = nca;
}
Nca nca = new Nca(KeySet, ncaFile.AsStorage()); else if (nca.Header.ContentType == NcaContentType.Control)
{
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16")) controlNca = nca;
{
break;
}
if (nca.Header.ContentType == NcaContentType.Program)
{
patchNca = nca;
}
else if (nca.Header.ContentType == NcaContentType.Control)
{
controlNca = nca;
}
} }
} }
} }

View file

@ -1,103 +1,103 @@
{ {
"version": 5, "version": 5,
"max_anisotropy": -1, "max_anisotropy": -1,
"graphics_shaders_dump_path": "", "graphics_shaders_dump_path": "",
"logging_enable_debug": false, "logging_enable_debug": false,
"logging_enable_stub": true, "logging_enable_stub": true,
"logging_enable_info": true, "logging_enable_info": true,
"logging_enable_warn": true, "logging_enable_warn": true,
"logging_enable_error": true, "logging_enable_error": true,
"logging_enable_guest": true, "logging_enable_guest": true,
"logging_enable_fs_access_log": false, "logging_enable_fs_access_log": false,
"logging_filtered_classes": [], "logging_filtered_classes": [],
"enable_file_log": true, "enable_file_log": true,
"system_language": "AmericanEnglish", "system_language": "AmericanEnglish",
"system_region": "USA", "system_region": "USA",
"system_time_zone": "UTC", "system_time_zone": "UTC",
"system_time_offset": 0, "system_time_offset": 0,
"docked_mode": false, "docked_mode": false,
"enable_discord_integration": true, "enable_discord_integration": true,
"enable_vsync": true, "enable_vsync": true,
"enable_multicore_scheduling": true, "enable_multicore_scheduling": true,
"enable_fs_integrity_checks": true, "enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0, "fs_global_access_log_mode": 0,
"ignore_missing_services": false, "ignore_missing_services": false,
"controller_type": "Handheld", "controller_type": "Handheld",
"gui_columns": { "gui_columns": {
"fav_column": true, "fav_column": true,
"icon_column": true, "icon_column": true,
"app_column": true, "app_column": true,
"dev_column": true, "dev_column": true,
"version_column": true, "version_column": true,
"time_played_column": true, "time_played_column": true,
"last_played_column": true, "last_played_column": true,
"file_ext_column": true, "file_ext_column": true,
"file_size_column": true, "file_size_column": true,
"path_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": [], "right_joycon": {
"enable_custom_theme": false, "stick_up": "I",
"custom_theme_path": "", "stick_down": "K",
"enable_keyboard": false, "stick_left": "J",
"keyboard_controls": { "stick_right": "L",
"left_joycon": { "stick_button": "H",
"stick_up": "W", "button_a": "Z",
"stick_down": "S", "button_b": "X",
"stick_left": "A", "button_x": "C",
"stick_right": "D", "button_y": "V",
"stick_button": "F", "button_plus": "Plus",
"dpad_up": "Up", "button_r": "U",
"dpad_down": "Down", "button_zr": "O"
"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"
}
}, },
"joystick_controls": { "hotkeys": {
"enabled": true, "toggle_vsync": "Tab"
"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"
}
} }
},
"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"
}
}
} }

View file

@ -1,11 +1,8 @@
using Gtk; using Gtk;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Utf8Json;
using Utf8Json.Resolvers;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;

View file

@ -18,10 +18,10 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Utf8Json; using System.Text.Json;
using Utf8Json.Resolvers;
using RightsId = LibHac.Fs.RightsId; using RightsId = LibHac.Fs.RightsId;
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
namespace Ryujinx.Ui namespace Ryujinx.Ui
{ {
@ -509,8 +509,6 @@ namespace Ryujinx.Ui
string metadataFolder = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "gui"); string metadataFolder = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "gui");
string metadataFile = Path.Combine(metadataFolder, "metadata.json"); string metadataFile = Path.Combine(metadataFolder, "metadata.json");
IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
ApplicationMetadata appMetadata; ApplicationMetadata appMetadata;
if (!File.Exists(metadataFile)) if (!File.Exists(metadataFile))
@ -526,27 +524,24 @@ namespace Ryujinx.Ui
using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) 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<ApplicationMetadata>(metadataFile);
{ }
appMetadata = JsonSerializer.Deserialize<ApplicationMetadata>(stream, resolver); catch (JsonException)
} {
catch (JsonParsingException) Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
{
Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
appMetadata = new ApplicationMetadata appMetadata = new ApplicationMetadata
{ {
Favorite = false, Favorite = false,
TimePlayed = 0, TimePlayed = 0,
LastPlayed = "Never" LastPlayed = "Never"
}; };
}
} }
if (modifyFunction != null) if (modifyFunction != null)
@ -555,7 +550,7 @@ namespace Ryujinx.Ui
using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough)) 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)) if (File.Exists(jsonPath))
{ {
using (Stream stream = File.OpenRead(jsonPath)) string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(jsonPath).Selected;
if (!File.Exists(updatePath))
{ {
IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); version = "";
string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected;
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); break;
if (result.IsSuccess())
{
Ticket ticket = new Ticket(ticketFile.AsStream());
_virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
}
} }
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) nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
{
break;
}
if (nca.Header.ContentType == NcaContentType.Control) version = controlData.DisplayVersion.ToString();
{
ApplicationControlProperty controlData = new ApplicationControlProperty();
nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); return true;
nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
version = controlData.DisplayVersion.ToString();
return true;
}
} }
} }
} }

View file

@ -11,6 +11,7 @@ using LibHac.Ns;
using LibHac.Spl; using LibHac.Spl;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System; using System;
using System.Buffers; using System.Buffers;
@ -19,8 +20,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using Utf8Json;
using Utf8Json.Resolvers;
using static LibHac.Fs.ApplicationSaveDataManagement; using static LibHac.Fs.ApplicationSaveDataManagement;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
@ -274,43 +273,39 @@ namespace Ryujinx.Ui
if (File.Exists(titleUpdateMetadataPath)) if (File.Exists(titleUpdateMetadataPath))
{ {
using (Stream stream = File.OpenRead(titleUpdateMetadataPath)) string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
if (File.Exists(updatePath))
{ {
IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase); FileStream updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected; 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); Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
PartitionFileSystem nsp = new PartitionFileSystem(updateFile.AsStorage());
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()) _virtualFileSystem.KeySet.ExternalKeySet.Add(new LibHac.Fs.RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
{ }
Ticket ticket = new Ticket(ticketFile.AsStream()); }
_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(); patchNca = nca;
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;
}
} }
} }
} }

View file

@ -1,5 +1,4 @@
using Gtk; using Gtk;
using JsonPrettyPrinterPlus;
using LibHac; using LibHac;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
@ -12,11 +11,9 @@ using Ryujinx.HLE.FileSystem;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using Utf8Json;
using Utf8Json.Resolvers;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
namespace Ryujinx.Ui namespace Ryujinx.Ui
{ {
@ -47,12 +44,9 @@ namespace Ryujinx.Ui
try try
{ {
using (Stream stream = File.OpenRead(System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json"))) string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json");
{
IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
_titleUpdateWindowData = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver); _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(path);
}
} }
catch 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"); 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(); MainWindow.UpdateGameTable();
Dispose(); Dispose();