mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2025-06-28 15:40:47 -07:00
Source generated json serializers (#4582)
* Use source generated json serializers in order to improve code trimming * Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing * Use separate model for LogEventArgs serialization * Make dynamic object formatter static. Fix string builder pooling. * Do not inherit json version of LogEventArgs from EventArgs * Fix extra space in object formatting * Write log json directly to stream instead of using buffer writer * Rebase fixes * Rebase fixes * Rebase fixes * Enforce block-scoped namespaces in the solution. Convert style for existing code * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Rebase indent fix * Fix indent * Delete unnecessary json properties * Rebase fix * Remove overridden json property names as they are handled in the options * Apply suggestions from code review Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Use default json options in github api calls * Indentation and spacing fixes * Fix json serialization * Fix missing JsonConverter for config enums * Add double \n\n after the whole string, not inside join --------- Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
This commit is contained in:
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
|
||||
public enum AntiAliasing
|
||||
{
|
||||
None,
|
||||
@ -9,4 +13,4 @@
|
||||
SmaaHigh,
|
||||
SmaaUltra
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
||||
public enum AspectRatio
|
||||
{
|
||||
Fixed4x3,
|
||||
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
||||
public enum BackendThreading
|
||||
{
|
||||
Auto,
|
||||
|
@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(List<DownloadableContentContainer>))]
|
||||
public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||
public enum GraphicsBackend
|
||||
{
|
||||
Vulkan,
|
||||
|
@ -1,5 +1,9 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
|
||||
public enum GraphicsDebugLevel
|
||||
{
|
||||
None,
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
{
|
||||
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
||||
{
|
||||
private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
||||
{
|
||||
// Temporary reader to get the backend type
|
||||
@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
|
||||
return motionBackendType switch
|
||||
{
|
||||
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options),
|
||||
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options),
|
||||
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
|
||||
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
|
||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||
};
|
||||
}
|
||||
@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
switch (value.MotionBackend)
|
||||
{
|
||||
case MotionInputBackendType.GamepadDriver:
|
||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options);
|
||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController);
|
||||
break;
|
||||
case MotionInputBackendType.CemuHook:
|
||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options);
|
||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
||||
|
@ -1,5 +1,8 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
{
|
||||
[JsonConverter(typeof(JsonMotionConfigControllerConverter))]
|
||||
public class MotionConfigController
|
||||
{
|
||||
public MotionInputBackendType MotionBackend { get; set; }
|
||||
|
@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(MotionConfigController))]
|
||||
[JsonSerializable(typeof(CemuHookMotionConfigController))]
|
||||
[JsonSerializable(typeof(StandardMotionConfigController))]
|
||||
public partial class MotionConfigJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
|
||||
public enum MotionInputBackendType : byte
|
||||
{
|
||||
Invalid,
|
||||
|
@ -1,9 +1,12 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
[Flags]
|
||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||
[Flags]
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
||||
public enum ControllerType : int
|
||||
{
|
||||
None,
|
||||
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
||||
public enum InputBackendType
|
||||
{
|
||||
Invalid,
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
[JsonConverter(typeof(JsonInputConfigConverter))]
|
||||
public class InputConfig : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -0,0 +1,14 @@
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(InputConfig))]
|
||||
[JsonSerializable(typeof(StandardKeyboardInputConfig))]
|
||||
[JsonSerializable(typeof(StandardControllerInputConfig))]
|
||||
public partial class InputConfigJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
class JsonInputConfigConverter : JsonConverter<InputConfig>
|
||||
public class JsonInputConfigConverter : JsonConverter<InputConfig>
|
||||
{
|
||||
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
||||
{
|
||||
// Temporary reader to get the backend type
|
||||
@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
|
||||
return backendType switch
|
||||
{
|
||||
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options),
|
||||
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options),
|
||||
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
|
||||
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
|
||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||
};
|
||||
}
|
||||
@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
switch (value.Backend)
|
||||
{
|
||||
case InputBackendType.WindowKeyboard:
|
||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options);
|
||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
|
||||
break;
|
||||
case InputBackendType.GamepadSDL2:
|
||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options);
|
||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
||||
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
|
||||
public enum Key
|
||||
{
|
||||
Unknown,
|
||||
@ -136,4 +140,4 @@
|
||||
|
||||
Count
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public class KeyboardHotkeys
|
||||
public struct KeyboardHotkeys
|
||||
{
|
||||
public Key ToggleVsync { get; set; }
|
||||
public Key Screenshot { get; set; }
|
||||
@ -12,4 +12,4 @@
|
||||
public Key VolumeUp { get; set; }
|
||||
public Key VolumeDown { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
|
||||
public enum PlayerIndex : int
|
||||
{
|
||||
Player1 = 0,
|
||||
|
@ -1,5 +1,9 @@
|
||||
namespace Ryujinx.Common.Configuration
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
||||
public enum MemoryManagerMode : byte
|
||||
{
|
||||
SoftwarePageTable,
|
||||
|
@ -1,5 +1,9 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
|
||||
public enum ScalingFilter
|
||||
{
|
||||
Bilinear,
|
||||
|
@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(TitleUpdateMetadata))]
|
||||
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,22 +1,20 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
internal class DefaultLogFormatter : ILogFormatter
|
||||
{
|
||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||
|
||||
public string Format(LogEventArgs args)
|
||||
{
|
||||
StringBuilder sb = _stringBuilderPool.Allocate();
|
||||
StringBuilder sb = StringBuilderPool.Allocate();
|
||||
|
||||
try
|
||||
{
|
||||
sb.Clear();
|
||||
|
||||
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
||||
sb.Append($@"{args.Time:hh\:mm\:ss\.fff}");
|
||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
||||
|
||||
if (args.ThreadName != null)
|
||||
@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging
|
||||
|
||||
sb.Append(args.Message);
|
||||
|
||||
if (args.Data != null)
|
||||
if (args.Data is not null)
|
||||
{
|
||||
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
||||
|
||||
sb.Append(" {");
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.Append(prop.Name);
|
||||
sb.Append(": ");
|
||||
|
||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
||||
{
|
||||
Array array = (Array)prop.GetValue(args.Data);
|
||||
foreach (var item in array)
|
||||
{
|
||||
sb.Append(item.ToString());
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
if (array.Length > 0)
|
||||
{
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(prop.GetValue(args.Data));
|
||||
}
|
||||
|
||||
sb.Append(" ; ");
|
||||
}
|
||||
|
||||
// We remove the final ';' from the string
|
||||
if (props.Length > 0)
|
||||
{
|
||||
sb.Remove(sb.Length - 3, 3);
|
||||
}
|
||||
|
||||
sb.Append('}');
|
||||
sb.Append(' ');
|
||||
DynamicObjectFormatter.Format(sb, args.Data);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_stringBuilderPool.Release(sb);
|
||||
StringBuilderPool.Release(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
@ -0,0 +1,84 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
internal class DynamicObjectFormatter
|
||||
{
|
||||
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||
|
||||
public static string? Format(object? dynamicObject)
|
||||
{
|
||||
if (dynamicObject is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = StringBuilderPool.Allocate();
|
||||
|
||||
try
|
||||
{
|
||||
Format(sb, dynamicObject);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
StringBuilderPool.Release(sb);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Format(StringBuilder sb, object? dynamicObject)
|
||||
{
|
||||
if (dynamicObject is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
|
||||
|
||||
sb.Append('{');
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.Append(prop.Name);
|
||||
sb.Append(": ");
|
||||
|
||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
||||
{
|
||||
Array? array = (Array?) prop.GetValue(dynamicObject);
|
||||
|
||||
if (array is not null)
|
||||
{
|
||||
foreach (var item in array)
|
||||
{
|
||||
sb.Append(item);
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
if (array.Length > 0)
|
||||
{
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(prop.GetValue(dynamicObject));
|
||||
}
|
||||
|
||||
sb.Append(" ; ");
|
||||
}
|
||||
|
||||
// We remove the final ';' from the string
|
||||
if (props.Length > 0)
|
||||
{
|
||||
sb.Remove(sb.Length - 3, 3);
|
||||
}
|
||||
|
||||
sb.Append('}');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
|
||||
public enum LogClass
|
||||
{
|
||||
Application,
|
||||
|
@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging
|
||||
public readonly string Message;
|
||||
public readonly object Data;
|
||||
|
||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message)
|
||||
{
|
||||
Level = level;
|
||||
Time = time;
|
||||
ThreadName = threadName;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data)
|
||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null)
|
||||
{
|
||||
Level = level;
|
||||
Time = time;
|
||||
|
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
internal class LogEventArgsJson
|
||||
{
|
||||
public LogLevel Level { get; }
|
||||
public TimeSpan Time { get; }
|
||||
public string ThreadName { get; }
|
||||
|
||||
public string Message { get; }
|
||||
public string Data { get; }
|
||||
|
||||
[JsonConstructor]
|
||||
public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null)
|
||||
{
|
||||
Level = level;
|
||||
Time = time;
|
||||
ThreadName = threadName;
|
||||
Message = message;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public static LogEventArgsJson FromLogEventArgs(LogEventArgs args)
|
||||
{
|
||||
return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data));
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
[JsonSerializable(typeof(LogEventArgsJson))]
|
||||
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
|
||||
public enum LogLevel
|
||||
{
|
||||
Debug,
|
||||
|
@ -1,5 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
{
|
||||
@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging
|
||||
|
||||
public void Log(object sender, LogEventArgs e)
|
||||
{
|
||||
string text = JsonSerializer.Serialize(e);
|
||||
|
||||
using (BinaryWriter writer = new BinaryWriter(_stream))
|
||||
{
|
||||
writer.Write(text);
|
||||
}
|
||||
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
||||
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
[JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")]
|
||||
[JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")]
|
||||
public partial class CommonJsonContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
}
|
@ -1,15 +1,62 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public class JsonHelper
|
||||
{
|
||||
public static JsonNamingPolicy SnakeCase { get; }
|
||||
private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
|
||||
private const int DefaultFileWriteBufferSize = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Creates new serializer options with default settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
||||
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
||||
/// </remarks>
|
||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
|
||||
{
|
||||
JsonSerializerOptions options = new()
|
||||
{
|
||||
DictionaryKeyPolicy = SnakeCasePolicy,
|
||||
PropertyNamingPolicy = SnakeCasePolicy,
|
||||
WriteIndented = indented,
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
};
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
return JsonSerializer.Serialize(value, typeInfo);
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
return JsonSerializer.Deserialize(value, typeInfo);
|
||||
}
|
||||
|
||||
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough);
|
||||
JsonSerializer.Serialize(file, value, typeInfo);
|
||||
}
|
||||
|
||||
public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
using FileStream file = File.OpenRead(filePath);
|
||||
return JsonSerializer.Deserialize(file, typeInfo);
|
||||
}
|
||||
|
||||
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
|
||||
{
|
||||
JsonSerializer.Serialize(stream, value, typeInfo);
|
||||
}
|
||||
|
||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||
{
|
||||
@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities
|
||||
return name;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilder builder = new();
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("_");
|
||||
builder.Append('_');
|
||||
builder.Append(char.ToLowerInvariant(c));
|
||||
}
|
||||
}
|
||||
@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities
|
||||
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());
|
||||
options.Converters.Add(new JsonInputConfigConverter());
|
||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
34
Ryujinx.Common/Utilities/TypedStringEnumConverter.cs
Normal file
@ -0,0 +1,34 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>.
|
||||
/// Get rid of this converter if dotnet supports similar functionality out of the box.
|
||||
/// </remarks>
|
||||
/// <typeparam name="TEnum">Type of enum to serialize</typeparam>
|
||||
public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
|
||||
{
|
||||
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var enumValue = reader.GetString();
|
||||
if (string.IsNullOrEmpty(enumValue))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return Enum.Parse<TEnum>(enumValue);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user