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:
parent
7ab3fccd4d
commit
886e42fb19
19 changed files with 388 additions and 396 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,6 @@
|
||||||
{
|
{
|
||||||
public struct KeyboardHotkeys
|
public struct KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync;
|
public Key ToggleVsync { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
106
Ryujinx.Common/Utilities/JsonHelper.cs
Normal file
106
Ryujinx.Common/Utilities/JsonHelper.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
||||||
|
|
||||||
|
appMetadata = new ApplicationMetadata
|
||||||
{
|
{
|
||||||
appMetadata = JsonSerializer.Deserialize<ApplicationMetadata>(stream, resolver);
|
Favorite = false,
|
||||||
}
|
TimePlayed = 0,
|
||||||
catch (JsonParsingException)
|
LastPlayed = "Never"
|
||||||
{
|
};
|
||||||
Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
|
||||||
|
|
||||||
appMetadata = new ApplicationMetadata
|
|
||||||
{
|
|
||||||
Favorite = false,
|
|
||||||
TimePlayed = 0,
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue