diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index 812dc2c308..ff5a67c4e7 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -1,16 +1,12 @@
-using JsonPrettyPrinterPlus;
-using Ryujinx.Common.Logging;
-using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text;
-using Utf8Json;
-using Utf8Json.Resolvers;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
 using Ryujinx.Configuration.System;
 using Ryujinx.Configuration.Hid;
-using Ryujinx.Common.Configuration.Hid;
-using Ryujinx.UI.Input;
 using Ryujinx.Configuration.Ui;
+using Ryujinx.UI.Input;
 
 namespace Ryujinx.Configuration
 {
@@ -179,15 +175,7 @@ namespace Ryujinx.Configuration
         /// <param name="path">The path to the JSON configuration file</param>
         public static ConfigurationFileFormat Load(string path)
         {
-            var resolver = CompositeResolver.Create(
-                new[] { new ConfigurationEnumFormatter<Key>() },
-                new[] { StandardResolver.AllowPrivateSnakeCase }
-            );
-
-            using (Stream stream = File.OpenRead(path))
-            {
-                return JsonSerializer.Deserialize<ConfigurationFileFormat>(stream, resolver);
-            }
+            return JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
         }
 
         /// <summary>
@@ -196,41 +184,7 @@ namespace Ryujinx.Configuration
         /// <param name="path">The path to the JSON configuration file</param>
         public void SaveConfig(string path)
         {
-            IJsonFormatterResolver resolver = CompositeResolver.Create(
-                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);
-            }
+            File.WriteAllText(path, JsonHelper.Serialize(this, true));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
index 1d0b050492..30cc8d8449 100644
--- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -2,6 +2,6 @@
 {
     public struct KeyboardHotkeys
     {
-        public Key ToggleVsync;
+        public Key ToggleVsync { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadController.cs b/Ryujinx.Common/Configuration/Hid/NpadController.cs
index f00865d556..94b985d55c 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadController.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadController.cs
@@ -5,31 +5,31 @@
         /// <summary>
         /// Enables or disables controller support
         /// </summary>
-        public bool Enabled;
+        public bool Enabled { get; set; }
 
         /// <summary>
         /// Controller Device Index
         /// </summary>
-        public int Index;
+        public int Index { get; set; }
 
         /// <summary>
         /// Controller Analog Stick Deadzone
         /// </summary>
-        public float Deadzone;
+        public float Deadzone { get; set; }
 
         /// <summary>
         /// Controller Trigger Threshold
         /// </summary>
-        public float TriggerThreshold;
+        public float TriggerThreshold { get; set; }
 
         /// <summary>
         /// Left JoyCon Controller Bindings
         /// </summary>
-        public NpadControllerLeft LeftJoycon;
+        public NpadControllerLeft LeftJoycon { get; set; }
 
         /// <summary>
         /// Right JoyCon Controller Bindings
         /// </summary>
-        public NpadControllerRight RightJoycon;
+        public NpadControllerRight RightJoycon { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
index 54ac0f03ae..c221b5e8a0 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
@@ -2,14 +2,14 @@
 {
     public struct NpadControllerLeft
     {
-        public ControllerInputId Stick;
-        public ControllerInputId StickButton;
-        public ControllerInputId ButtonMinus;
-        public ControllerInputId ButtonL;
-        public ControllerInputId ButtonZl;
-        public ControllerInputId DPadUp;
-        public ControllerInputId DPadDown;
-        public ControllerInputId DPadLeft;
-        public ControllerInputId DPadRight;
+        public ControllerInputId Stick       { get; set; }
+        public ControllerInputId StickButton { get; set; }
+        public ControllerInputId ButtonMinus { get; set; }
+        public ControllerInputId ButtonL     { get; set; }
+        public ControllerInputId ButtonZl    { get; set; }
+        public ControllerInputId DPadUp      { get; set; }
+        public ControllerInputId DPadDown    { get; set; }
+        public ControllerInputId DPadLeft    { get; set; }
+        public ControllerInputId DPadRight   { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
index 315136d9f6..f52f6f16df 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
@@ -2,14 +2,14 @@
 {
     public struct NpadControllerRight
     {
-        public ControllerInputId Stick;
-        public ControllerInputId StickButton;
-        public ControllerInputId ButtonA;
-        public ControllerInputId ButtonB;
-        public ControllerInputId ButtonX;
-        public ControllerInputId ButtonY;
-        public ControllerInputId ButtonPlus;
-        public ControllerInputId ButtonR;
-        public ControllerInputId ButtonZr;
+        public ControllerInputId Stick       { get; set; }
+        public ControllerInputId StickButton { get; set; }
+        public ControllerInputId ButtonA     { get; set; }
+        public ControllerInputId ButtonB     { get; set; }
+        public ControllerInputId ButtonX     { get; set; }
+        public ControllerInputId ButtonY     { get; set; }
+        public ControllerInputId ButtonPlus  { get; set; }
+        public ControllerInputId ButtonR     { get; set; }
+        public ControllerInputId ButtonZr    { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
index 911f5119ea..5ae82756b7 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
@@ -5,16 +5,16 @@ namespace Ryujinx.UI.Input
         /// <summary>
         /// Left JoyCon Keyboard Bindings
         /// </summary>
-        public Configuration.Hid.NpadKeyboardLeft LeftJoycon;
+        public Configuration.Hid.NpadKeyboardLeft LeftJoycon { get; set; }
 
         /// <summary>
         /// Right JoyCon Keyboard Bindings
         /// </summary>
-        public Configuration.Hid.NpadKeyboardRight RightJoycon;
+        public Configuration.Hid.NpadKeyboardRight RightJoycon { get; set; }
 
         /// <summary>
         /// Hotkey Keyboard Bindings
         /// </summary>
-        public Configuration.Hid.KeyboardHotkeys Hotkeys;
+        public Configuration.Hid.KeyboardHotkeys Hotkeys { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
index 799cdfdb8a..4a61d93233 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
@@ -2,17 +2,17 @@
 {
     public struct NpadKeyboardLeft
     {
-        public Key StickUp;
-        public Key StickDown;
-        public Key StickLeft;
-        public Key StickRight;
-        public Key StickButton;
-        public Key DPadUp;
-        public Key DPadDown;
-        public Key DPadLeft;
-        public Key DPadRight;
-        public Key ButtonMinus;
-        public Key ButtonL;
-        public Key ButtonZl;
+        public Key StickUp     { get; set; }
+        public Key StickDown   { get; set; }
+        public Key StickLeft   { get; set; }
+        public Key StickRight  { get; set; }
+        public Key StickButton { get; set; }
+        public Key DPadUp      { get; set; }
+        public Key DPadDown    { get; set; }
+        public Key DPadLeft    { get; set; }
+        public Key DPadRight   { get; set; }
+        public Key ButtonMinus { get; set; }
+        public Key ButtonL     { get; set; }
+        public Key ButtonZl    { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
index 311504bb7e..0677b57327 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
@@ -2,17 +2,17 @@
 {
     public struct NpadKeyboardRight
     {
-        public Key StickUp;
-        public Key StickDown;
-        public Key StickLeft;
-        public Key StickRight;
-        public Key StickButton;
-        public Key ButtonA;
-        public Key ButtonB;
-        public Key ButtonX;
-        public Key ButtonY;
-        public Key ButtonPlus;
-        public Key ButtonR;
-        public Key ButtonZr;
+        public Key StickUp     { get; set; }
+        public Key StickDown   { get; set; }
+        public Key StickLeft   { get; set; }
+        public Key StickRight  { get; set; }
+        public Key StickButton { get; set; }
+        public Key ButtonA     { get; set; }
+        public Key ButtonB     { get; set; }
+        public Key ButtonX     { get; set; }
+        public Key ButtonY     { get; set; }
+        public Key ButtonPlus  { get; set; }
+        public Key ButtonR     { get; set; }
+        public Key ButtonZr    { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
index 2b3524aa82..de4f7369b2 100644
--- a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
+++ b/Ryujinx.Common/Configuration/Ui/GuiColumns.cs
@@ -2,15 +2,15 @@
 {
     public struct GuiColumns
     {
-        public bool FavColumn;
-        public bool IconColumn;
-        public bool AppColumn;
-        public bool DevColumn;
-        public bool VersionColumn;
-        public bool TimePlayedColumn;
-        public bool LastPlayedColumn;
-        public bool FileExtColumn;
-        public bool FileSizeColumn;
-        public bool PathColumn;
+        public bool FavColumn        { get; set; }
+        public bool IconColumn       { get; set; }
+        public bool AppColumn        { get; set; }
+        public bool DevColumn        { get; set; }
+        public bool VersionColumn    { get; set; }
+        public bool TimePlayedColumn { get; set; }
+        public bool LastPlayedColumn { get; set; }
+        public bool FileExtColumn    { get; set; }
+        public bool FileSizeColumn   { get; set; }
+        public bool PathColumn       { get; set; }
     }
 }
diff --git a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
index 3729b18d13..95f96576c0 100644
--- a/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
+++ b/Ryujinx.Common/Logging/Targets/JsonLogTarget.cs
@@ -1,6 +1,5 @@
-using System;
-using System.IO;
-using Utf8Json;
+using System.IO;
+using System.Text.Json;
 
 namespace Ryujinx.Common.Logging
 {
@@ -26,7 +25,12 @@ namespace Ryujinx.Common.Logging
 
         public void Log(object sender, LogEventArgs e)
         {
-            JsonSerializer.Serialize(_stream, e);
+            string text = JsonSerializer.Serialize(e);
+
+            using (BinaryWriter writer = new BinaryWriter(_stream))
+            {
+                writer.Write(text);
+            }
         }
 
         public void Dispose()
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index e902d26ab2..3eb75730be 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -27,12 +27,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1">
-      <NoWarn>NU1701</NoWarn>
-    </PackageReference>
     <PackageReference Include="MsgPack.Cli" Version="1.0.1" />
     <PackageReference Include="System.Management" Version="4.7.0" />
-    <PackageReference Include="Utf8Json" Version="1.3.7" />
   </ItemGroup>
 
 </Project>
diff --git a/Ryujinx.Common/Utilities/JsonHelper.cs b/Ryujinx.Common/Utilities/JsonHelper.cs
new file mode 100644
index 0000000000..5aa461830e
--- /dev/null
+++ b/Ryujinx.Common/Utilities/JsonHelper.cs
@@ -0,0 +1,106 @@
+using System.IO;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Ryujinx.Common.Utilities
+{
+    public class JsonHelper
+    {
+        public static JsonNamingPolicy SnakeCase { get; }
+
+        private class SnakeCaseNamingPolicy : JsonNamingPolicy
+        {
+            public override string ConvertName(string name)
+            {
+                if (string.IsNullOrEmpty(name))
+                {
+                    return name;
+                }
+
+                StringBuilder builder = new StringBuilder();
+
+                for (int i = 0; i < name.Length; i++)
+                {
+                    char c = name[i];
+
+                    if (char.IsUpper(c))
+                    {
+                        if (i == 0 || char.IsUpper(name[i - 1]))
+                        {
+                            builder.Append(char.ToLowerInvariant(c));
+                        }
+                        else
+                        {
+                            builder.Append("_");
+                            builder.Append(char.ToLowerInvariant(c));
+                        }
+                    }
+                    else
+                    {
+                        builder.Append(c);
+                    }
+                }
+
+                return builder.ToString();
+            }
+        }
+
+        static JsonHelper()
+        {
+            SnakeCase = new SnakeCaseNamingPolicy();
+        }
+
+        public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
+        {
+            JsonSerializerOptions options = new JsonSerializerOptions
+            {
+                DictionaryKeyPolicy  = SnakeCase,
+                PropertyNamingPolicy = SnakeCase,
+                WriteIndented        = prettyPrint,
+                AllowTrailingCommas  = true,
+                ReadCommentHandling  = JsonCommentHandling.Skip
+            };
+
+            options.Converters.Add(new JsonStringEnumConverter());
+
+            return options;
+        }
+
+        public static T Deserialize<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));
+        }
+    }
+}
diff --git a/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
index e0842f2e1f..73ef8f5555 100644
--- a/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
+++ b/Ryujinx.Debugger/Profiler/ProfilerConfiguration.cs
@@ -1,8 +1,5 @@
-using Gdk;
-using System;
+using Ryujinx.Common.Utilities;
 using System.IO;
-using Utf8Json;
-using Utf8Json.Resolvers;
 
 namespace Ryujinx.Debugger.Profiler
 {
@@ -21,48 +18,12 @@ namespace Ryujinx.Debugger.Profiler
         /// <param name="path">The path to the JSON configuration file</param>
         public static ProfilerConfiguration Load(string path)
         {
-            var resolver = CompositeResolver.Create(
-                new[] { new ConfigurationEnumFormatter<Key>() },
-                new[] { StandardResolver.AllowPrivateSnakeCase }
-            );
-
             if (!File.Exists(path))
             {
                 throw new FileNotFoundException($"Profiler configuration file {path} not found");
             }
 
-            using (Stream stream = File.OpenRead(path))
-            {
-                return JsonSerializer.Deserialize<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);
-            }
+            return JsonHelper.DeserializeFromFile<ProfilerConfiguration>(path);
         }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 2dfa275713..76185d7b91 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -39,11 +39,10 @@ using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Threading;
-using Utf8Json;
-using Utf8Json.Resolvers;
 
 using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
 using NsoExecutable      = Ryujinx.HLE.Loaders.Executables.NsoExecutable;
+using JsonHelper         = Ryujinx.Common.Utilities.JsonHelper;
 
 using static LibHac.Fs.ApplicationSaveDataManagement;
 
@@ -540,49 +539,47 @@ namespace Ryujinx.HLE.HOS
             IStorage    dataStorage = null;
             IFileSystem codeFs      = null;
 
-            if (File.Exists(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json")))
+            string titleUpdateMetadataPath = System.IO.Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json");
+
+            if (File.Exists(titleUpdateMetadataPath))
             {
-                using (Stream stream = File.OpenRead(Path.Combine(Device.FileSystem.GetBasePath(), "games", mainNca.Header.TitleId.ToString("x16"), "updates.json")))
+                string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
+
+                if (File.Exists(updatePath))
                 {
-                    IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
-                    string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected;
+                    FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
+                    PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
 
-                    if (File.Exists(updatePath))
+                    foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
                     {
-                        FileStream file         = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
-                        PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
+                        Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
 
-                        foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
+                        if (result.IsSuccess())
                         {
-                            Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
+                            Ticket ticket = new Ticket(ticketFile.AsStream());
 
-                            if (result.IsSuccess())
-                            {
-                                Ticket ticket = new Ticket(ticketFile.AsStream());
+                            KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet)));
+                        }
+                    }
 
-                                KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet)));
-                            }
+                    foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
+                    {
+                        nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                        Nca nca = new Nca(KeySet, ncaFile.AsStorage());
+
+                        if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
+                        {
+                            break;
                         }
 
-                        foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
+                        if (nca.Header.ContentType == NcaContentType.Program)
                         {
-                            nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                            Nca nca = new Nca(KeySet, ncaFile.AsStorage());
-
-                            if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
-                            {
-                                break;
-                            }
-
-                            if (nca.Header.ContentType == NcaContentType.Program)
-                            {
-                                patchNca = nca;
-                            }
-                            else if (nca.Header.ContentType == NcaContentType.Control)
-                            {
-                                controlNca = nca;
-                            }
+                            patchNca = nca;
+                        }
+                        else if (nca.Header.ContentType == NcaContentType.Control)
+                        {
+                            controlNca = nca;
                         }
                     }
                 }
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 7a5fe97239..8df901e226 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,103 +1,103 @@
 {
-    "version": 5,
-    "max_anisotropy": -1,
-    "graphics_shaders_dump_path": "",
-    "logging_enable_debug": false,
-    "logging_enable_stub": true,
-    "logging_enable_info": true,
-    "logging_enable_warn": true,
-    "logging_enable_error": true,
-    "logging_enable_guest": true,
-    "logging_enable_fs_access_log": false,
-    "logging_filtered_classes": [],
-    "enable_file_log": true,
-    "system_language": "AmericanEnglish",
-    "system_region": "USA",
-    "system_time_zone": "UTC",
-    "system_time_offset": 0,
-    "docked_mode": false,
-    "enable_discord_integration": true,
-    "enable_vsync": true,
-    "enable_multicore_scheduling": true,
-    "enable_fs_integrity_checks": true,
-    "fs_global_access_log_mode": 0,
-    "ignore_missing_services": false,
-    "controller_type": "Handheld",
-    "gui_columns": {
-        "fav_column": true,
-        "icon_column": true,
-        "app_column": true,
-        "dev_column": true,
-        "version_column": true,
-        "time_played_column": true,
-        "last_played_column": true,
-        "file_ext_column": true,
-        "file_size_column": true,
-        "path_column": true
+  "version": 5,
+  "max_anisotropy": -1,
+  "graphics_shaders_dump_path": "",
+  "logging_enable_debug": false,
+  "logging_enable_stub": true,
+  "logging_enable_info": true,
+  "logging_enable_warn": true,
+  "logging_enable_error": true,
+  "logging_enable_guest": true,
+  "logging_enable_fs_access_log": false,
+  "logging_filtered_classes": [],
+  "enable_file_log": true,
+  "system_language": "AmericanEnglish",
+  "system_region": "USA",
+  "system_time_zone": "UTC",
+  "system_time_offset": 0,
+  "docked_mode": false,
+  "enable_discord_integration": true,
+  "enable_vsync": true,
+  "enable_multicore_scheduling": true,
+  "enable_fs_integrity_checks": true,
+  "fs_global_access_log_mode": 0,
+  "ignore_missing_services": false,
+  "controller_type": "Handheld",
+  "gui_columns": {
+    "fav_column": true,
+    "icon_column": true,
+    "app_column": true,
+    "dev_column": true,
+    "version_column": true,
+    "time_played_column": true,
+    "last_played_column": true,
+    "file_ext_column": true,
+    "file_size_column": true,
+    "path_column": true
+  },
+  "game_dirs": [],
+  "enable_custom_theme": false,
+  "custom_theme_path": "",
+  "enable_keyboard": false,
+  "keyboard_controls": {
+    "left_joycon": {
+      "stick_up": "W",
+      "stick_down": "S",
+      "stick_left": "A",
+      "stick_right": "D",
+      "stick_button": "F",
+      "dpad_up": "Up",
+      "dpad_down": "Down",
+      "dpad_left": "Left",
+      "dpad_right": "Right",
+      "button_minus": "Minus",
+      "button_l": "E",
+      "button_zl": "Q"
     },
-    "game_dirs": [],
-    "enable_custom_theme": false,
-    "custom_theme_path": "",
-    "enable_keyboard": false,
-    "keyboard_controls": {
-        "left_joycon": {
-            "stick_up": "W",
-            "stick_down": "S",
-            "stick_left": "A",
-            "stick_right": "D",
-            "stick_button": "F",
-            "dpad_up": "Up",
-            "dpad_down": "Down",
-            "dpad_left": "Left",
-            "dpad_right": "Right",
-            "button_minus": "Minus",
-            "button_l": "E",
-            "button_zl": "Q"
-        },
-        "right_joycon": {
-            "stick_up": "I",
-            "stick_down": "K",
-            "stick_left": "J",
-            "stick_right": "L",
-            "stick_button": "H",
-            "button_a": "Z",
-            "button_b": "X",
-            "button_x": "C",
-            "button_y": "V",
-            "button_plus": "Plus",
-            "button_r": "U",
-            "button_zr": "O"
-        },
-        "hotkeys": {
-            "toggle_vsync": "Tab"
-        }
+    "right_joycon": {
+      "stick_up": "I",
+      "stick_down": "K",
+      "stick_left": "J",
+      "stick_right": "L",
+      "stick_button": "H",
+      "button_a": "Z",
+      "button_b": "X",
+      "button_x": "C",
+      "button_y": "V",
+      "button_plus": "Plus",
+      "button_r": "U",
+      "button_zr": "O"
     },
-    "joystick_controls": {
-        "enabled": true,
-        "index": 0,
-        "deadzone": 0.05,
-        "trigger_threshold": 0.5,
-        "left_joycon": {
-            "stick": "Axis0",
-            "stick_button": "Button8",
-            "button_minus": "Button6",
-            "button_l": "Button4",
-            "button_zl": "Axis2",
-            "dpad_up": "Hat0Up",
-            "dpad_down": "Hat0Down",
-            "dpad_left": "Hat0Left",
-            "dpad_right": "Hat0Right"
-        },
-        "right_joycon": {
-            "stick": "Axis3",
-            "stick_button": "Button9",
-            "button_a": "Button1",
-            "button_b": "Button0",
-            "button_x": "Button3",
-            "button_y": "Button2",
-            "button_plus": "Button7",
-            "button_r": "Button5",
-            "button_zr": "Axis5"
-        }
+    "hotkeys": {
+      "toggle_vsync": "Tab"
     }
+  },
+  "joystick_controls": {
+    "enabled": true,
+    "index": 0,
+    "deadzone": 0.05,
+    "trigger_threshold": 0.5,
+    "left_joycon": {
+      "stick": "Axis0",
+      "stick_button": "Button8",
+      "button_minus": "Button6",
+      "button_l": "Button4",
+      "button_zl": "Axis2",
+      "dpad_up": "Hat0Up",
+      "dpad_down": "Hat0Down",
+      "dpad_left": "Hat0Left",
+      "dpad_right": "Hat0Right"
+    },
+    "right_joycon": {
+      "stick": "Axis3",
+      "stick_button": "Button9",
+      "button_a": "Button1",
+      "button_b": "Button0",
+      "button_x": "Button3",
+      "button_y": "Button2",
+      "button_plus": "Button7",
+      "button_r": "Button5",
+      "button_zr": "Axis5"
+    }
+  }
 }
\ No newline at end of file
diff --git a/Ryujinx/Ui/AboutWindow.cs b/Ryujinx/Ui/AboutWindow.cs
index cae7777666..6a18058a5a 100644
--- a/Ryujinx/Ui/AboutWindow.cs
+++ b/Ryujinx/Ui/AboutWindow.cs
@@ -1,11 +1,8 @@
 using Gtk;
 using System;
 using System.Diagnostics;
-using System.IO;
 using System.Reflection;
 using System.Runtime.InteropServices;
-using Utf8Json;
-using Utf8Json.Resolvers;
 
 using GUI = Gtk.Builder.ObjectAttribute;
 
diff --git a/Ryujinx/Ui/ApplicationLibrary.cs b/Ryujinx/Ui/ApplicationLibrary.cs
index f55fa1ec66..b4700300a8 100644
--- a/Ryujinx/Ui/ApplicationLibrary.cs
+++ b/Ryujinx/Ui/ApplicationLibrary.cs
@@ -18,10 +18,10 @@ using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Text;
-using Utf8Json;
-using Utf8Json.Resolvers;
+using System.Text.Json;
 
 using RightsId = LibHac.Fs.RightsId;
+using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
 
 namespace Ryujinx.Ui
 {
@@ -509,8 +509,6 @@ namespace Ryujinx.Ui
             string metadataFolder = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "gui");
             string metadataFile   = Path.Combine(metadataFolder, "metadata.json");
 
-            IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
-
             ApplicationMetadata appMetadata;
 
             if (!File.Exists(metadataFile))
@@ -526,27 +524,24 @@ namespace Ryujinx.Ui
 
                 using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
                 {
-                    JsonSerializer.Serialize(stream, appMetadata, resolver);
+                    JsonHelper.Serialize(stream, appMetadata, true);
                 }
             }
 
-            using (Stream stream = File.OpenRead(metadataFile))
+            try
             {
-                try
+                appMetadata = JsonHelper.DeserializeFromFile<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);
-                }
-                catch (JsonParsingException)
-                {
-                    Logger.PrintWarning(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
-                    
-                    appMetadata = new ApplicationMetadata
-                    {
-                        Favorite   = false,
-                        TimePlayed = 0,
-                        LastPlayed = "Never"
-                    };
-                }
+                    Favorite   = false,
+                    TimePlayed = 0,
+                    LastPlayed = "Never"
+                };
             }
 
             if (modifyFunction != null)
@@ -555,7 +550,7 @@ namespace Ryujinx.Ui
 
                 using (FileStream stream = File.Create(metadataFile, 4096, FileOptions.WriteThrough))
                 {
-                    JsonSerializer.Serialize(stream, appMetadata, resolver);
+                    JsonHelper.Serialize(stream, appMetadata, true);
                 }
             }
 
@@ -653,57 +648,53 @@ namespace Ryujinx.Ui
 
             if (File.Exists(jsonPath))
             {
-                using (Stream stream = File.OpenRead(jsonPath))
+                string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(jsonPath).Selected;
+
+                if (!File.Exists(updatePath))
                 {
-                    IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
-                    string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected;
+                    version = "";
 
-                    if (!File.Exists(updatePath))
+                    return false;
+                }
+
+                using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read))
+                {
+                    PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
+
+                    foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
                     {
-                        version = "";
+                        Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
 
-                        return false;
+                        if (result.IsSuccess())
+                        {
+                            Ticket ticket = new Ticket(ticketFile.AsStream());
+
+                            _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
+                        }
                     }
 
-                    using (FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read))
+                    foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
                     {
-                        PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
+                        nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-                        foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
+                        Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
+
+                        if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
                         {
-                            Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
-
-                            if (result.IsSuccess())
-                            {
-                                Ticket ticket = new Ticket(ticketFile.AsStream());
-
-                                _virtualFileSystem.KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
-                            }
+                            break;
                         }
 
-                        foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
+                        if (nca.Header.ContentType == NcaContentType.Control)
                         {
-                            nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                            ApplicationControlProperty controlData = new ApplicationControlProperty();
 
-                            Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
+                            nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-                            if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
-                            {
-                                break;
-                            }
+                            nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
 
-                            if (nca.Header.ContentType == NcaContentType.Control)
-                            {
-                                ApplicationControlProperty controlData = new ApplicationControlProperty();
+                            version = controlData.DisplayVersion.ToString();
 
-                                nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(out IFile nacpFile, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
-                                
-                                nacpFile.Read(out long _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
-
-                                version = controlData.DisplayVersion.ToString();
-
-                                return true;
-                            }
+                            return true;
                         }
                     }
                 }
diff --git a/Ryujinx/Ui/GameTableContextMenu.cs b/Ryujinx/Ui/GameTableContextMenu.cs
index 8bead1e3dd..a1433f5121 100644
--- a/Ryujinx/Ui/GameTableContextMenu.cs
+++ b/Ryujinx/Ui/GameTableContextMenu.cs
@@ -11,6 +11,7 @@ using LibHac.Ns;
 using LibHac.Spl;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
 using System;
 using System.Buffers;
@@ -19,8 +20,6 @@ using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Threading;
-using Utf8Json;
-using Utf8Json.Resolvers;
 using static LibHac.Fs.ApplicationSaveDataManagement;
 using GUI = Gtk.Builder.ObjectAttribute;
 
@@ -274,43 +273,39 @@ namespace Ryujinx.Ui
 
                         if (File.Exists(titleUpdateMetadataPath))
                         {
-                            using (Stream stream = File.OpenRead(titleUpdateMetadataPath))
+                            string updatePath = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
+
+                            if (File.Exists(updatePath))
                             {
-                                IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
-                                string updatePath = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver).Selected;
+                                FileStream updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
+                                PartitionFileSystem nsp = new PartitionFileSystem(updateFile.AsStorage());
 
-                                if (File.Exists(updatePath))
+                                foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
                                 {
-                                    FileStream updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
-                                    PartitionFileSystem nsp = new PartitionFileSystem(updateFile.AsStorage());
+                                    Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
 
-                                    foreach (DirectoryEntryEx ticketEntry in nsp.EnumerateEntries("/", "*.tik"))
+                                    if (result.IsSuccess())
                                     {
-                                        Result result = nsp.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
+                                        Ticket ticket = new Ticket(ticketFile.AsStream());
 
-                                        if (result.IsSuccess())
-                                        {
-                                            Ticket ticket = new Ticket(ticketFile.AsStream());
+                                        _virtualFileSystem.KeySet.ExternalKeySet.Add(new LibHac.Fs.RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
+                                    }
+                                }
 
-                                            _virtualFileSystem.KeySet.ExternalKeySet.Add(new LibHac.Fs.RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(_virtualFileSystem.KeySet)));
-                                        }
+                                foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
+                                {
+                                    nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                                    Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
+
+                                    if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
+                                    {
+                                        break;
                                     }
 
-                                    foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
+                                    if (nca.Header.ContentType == NcaContentType.Program)
                                     {
-                                        nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                                        Nca nca = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
-
-                                        if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != mainNca.Header.TitleId.ToString("x16"))
-                                        {
-                                            break;
-                                        }
-
-                                        if (nca.Header.ContentType == NcaContentType.Program)
-                                        {
-                                            patchNca = nca;
-                                        }
+                                        patchNca = nca;
                                     }
                                 }
                             }
diff --git a/Ryujinx/Ui/TitleUpdateWindow.cs b/Ryujinx/Ui/TitleUpdateWindow.cs
index 01025d6ddf..a6d64a793f 100644
--- a/Ryujinx/Ui/TitleUpdateWindow.cs
+++ b/Ryujinx/Ui/TitleUpdateWindow.cs
@@ -1,5 +1,4 @@
 using Gtk;
-using JsonPrettyPrinterPlus;
 using LibHac;
 using LibHac.Common;
 using LibHac.Fs;
@@ -12,11 +11,9 @@ using Ryujinx.HLE.FileSystem;
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text;
-using Utf8Json;
-using Utf8Json.Resolvers;
 
 using GUI = Gtk.Builder.ObjectAttribute;
+using JsonHelper = Ryujinx.Common.Utilities.JsonHelper;
 
 namespace Ryujinx.Ui
 {
@@ -47,12 +44,9 @@ namespace Ryujinx.Ui
 
             try
             {
-                using (Stream stream = File.OpenRead(System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json")))
-                {
-                    IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
+                string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json");
 
-                    _titleUpdateWindowData = JsonSerializer.Deserialize<TitleUpdateMetadata>(stream, resolver);
-                }
+                _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(path);
             }
             catch
             {
@@ -185,12 +179,9 @@ namespace Ryujinx.Ui
                 }
             }
 
-            IJsonFormatterResolver resolver = CompositeResolver.Create(StandardResolver.AllowPrivateSnakeCase);
-
             string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "games", _titleId, "updates.json");
-            byte[] data = JsonSerializer.Serialize(_titleUpdateWindowData, resolver);
 
-            File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
+            File.WriteAllText(path, JsonHelper.Serialize(_titleUpdateWindowData, true));
 
             MainWindow.UpdateGameTable();
             Dispose();