From 58f588ef7ccb9f0b3314745ae024a007bf6c563e Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Fri, 31 May 2024 19:55:54 +0200
Subject: [PATCH 1/8] Switch from Newtonsoft.Json to System.Text.Json
---
Directory.Packages.props | 1 +
src/BizHawk.Client.Common/OpenAdvanced.cs | 9 +-
src/BizHawk.Client.Common/RecentFiles.cs | 2 +-
src/BizHawk.Client.Common/config/Config.cs | 12 +--
.../config/ConfigExtensions.cs | 20 ++---
.../config/ConfigService.cs | 81 +++++++++--------
.../config/FeedbackBind.cs | 2 +-
src/BizHawk.Client.Common/config/PathEntry.cs | 3 +-
.../config/PathEntryCollection.cs | 4 +-
.../config/ToolDialogSettings.cs | 8 +-
.../lua/LuaDocumentation.cs | 8 +-
.../movie/tasproj/TasBranch.cs | 32 ++-----
.../movie/tasproj/TasLagLog.cs | 11 ++-
.../movie/tasproj/TasMovie.IO.cs | 13 ++-
.../RetroAchievements/RAIntegration.Update.cs | 5 +-
src/BizHawk.Client.EmuHawk/UpdateChecker.cs | 7 +-
.../tools/ToolManager.cs | 6 +-
.../Array2DJsonConverter.cs | 87 +++++++++++++++++++
.../BizHawk.Emulation.Common.csproj | 2 +-
.../ByteArrayAsNormalArrayJsonConverter.cs | 44 ++++++++++
src/BizHawk.Emulation.Common/TextState.cs | 4 -
.../U8ArrayAsNormalJSONListConverter.cs | 63 --------------
.../Atari/2600/Atari2600.ISettable.cs | 6 --
.../Atari/A7800Hawk/A7800Hawk.ISettable.cs | 2 +-
.../Consoles/Atari/Stella/Stella.ISettable.cs | 6 --
.../Consoles/Atari/lynx/Lynx.IStatable.cs | 8 +-
.../Consoles/Coleco/ColecoVision.ISettable.cs | 2 +-
.../GCE/Vectrex/VectrexHawk.ISettable.cs | 5 +-
.../Intellivision/Intellivision.ISettable.cs | 2 +-
.../Nintendo/GBHawk/GBHawk.ISettable.cs | 5 +-
.../GBHawkLink/GBHawkLink.ISettable.cs | 7 +-
.../GBHawkLink3x/GBHawkLink3x.ISettable.cs | 9 +-
.../GBHawkLink4x/GBHawkLink4x.ISettable.cs | 11 +--
.../Nintendo/Gameboy/Gambatte.IStatable.cs | 9 +-
.../Gameboy/GambatteLink.IStatable.cs | 10 +--
.../N64/N64SyncSettings.Controller.cs | 3 -
.../Nintendo/NDS/MelonDS.ISettable.cs | 15 +---
.../Consoles/Nintendo/NES/NES.ISettable.cs | 11 ++-
.../Consoles/Nintendo/NES/NESControllers.cs | 6 +-
.../Nintendo/QuickNES/QuickNES.ISettable.cs | 4 -
.../Nintendo/SameBoy/SameBoy.ISettable.cs | 6 +-
.../Consoles/Sega/gpgx64/GPGX.ISettable.cs | 8 --
.../Consoles/Sony/PSX/Octoshock.cs | 5 +-
.../WonderSwan/WonderSwan.IStatable.cs | 9 +-
.../config/SerializationStabilityTests.cs | 19 ++--
45 files changed, 279 insertions(+), 313 deletions(-)
create mode 100644 src/BizHawk.Emulation.Common/Array2DJsonConverter.cs
create mode 100644 src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs
delete mode 100644 src/BizHawk.Emulation.Common/U8ArrayAsNormalJSONListConverter.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 57fba17f291..98182c0fd5d 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -40,6 +40,7 @@
+
diff --git a/src/BizHawk.Client.Common/OpenAdvanced.cs b/src/BizHawk.Client.Common/OpenAdvanced.cs
index a33b5cb1de2..f82c5a0142f 100644
--- a/src/BizHawk.Client.Common/OpenAdvanced.cs
+++ b/src/BizHawk.Client.Common/OpenAdvanced.cs
@@ -1,9 +1,8 @@
using System.IO;
+using System.Text.Json;
using BizHawk.Common.StringExtensions;
-using Newtonsoft.Json;
-
//this file contains some cumbersome self-"serialization" in order to gain a modicum of control over what the serialized output looks like
//I don't want them to look like crufty json
@@ -94,12 +93,12 @@ public struct Token
public void Deserialize(string str)
{
- token = JsonConvert.DeserializeObject(str);
+ token = JsonSerializer.Deserialize(str);
}
public void Serialize(TextWriter tw)
{
- tw.Write(JsonConvert.SerializeObject(token));
+ tw.Write(JsonSerializer.Serialize(token));
}
public string CorePath
@@ -186,4 +185,4 @@ public void Serialize(TextWriter tw)
tw.Write(Path);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/BizHawk.Client.Common/RecentFiles.cs b/src/BizHawk.Client.Common/RecentFiles.cs
index 5f37582d1b0..e0c092070f4 100644
--- a/src/BizHawk.Client.Common/RecentFiles.cs
+++ b/src/BizHawk.Client.Common/RecentFiles.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace BizHawk.Client.Common
{
diff --git a/src/BizHawk.Client.Common/config/Config.cs b/src/BizHawk.Client.Common/config/Config.cs
index 978734440c2..8d1faaef1ec 100644
--- a/src/BizHawk.Client.Common/config/Config.cs
+++ b/src/BizHawk.Client.Common/config/Config.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using BizHawk.Bizware.Graphics;
using BizHawk.Common;
@@ -9,9 +11,6 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
namespace BizHawk.Client.Common
{
public class Config
@@ -368,8 +367,10 @@ public void SetWindowScaleFor(string sysID, int windowScale)
public bool VideoWriterAudioSyncEffective;
// Emulation core settings
- internal Dictionary CoreSettings { get; set; } = new Dictionary();
- internal Dictionary CoreSyncSettings { get; set; } = new Dictionary();
+ [JsonInclude]
+ internal Dictionary CoreSettings { get; set; } = new();
+ [JsonInclude]
+ internal Dictionary CoreSyncSettings { get; set; } = new();
public Dictionary CommonToolSettings { get; set; } = new Dictionary();
public Dictionary> CustomToolSettings { get; set; } = new Dictionary>();
@@ -405,6 +406,7 @@ public void SetWindowScaleFor(string sysID, int windowScale)
public bool GbAsSgb { get; set; }
public string LibretroCore { get; set; }
+ [JsonPropertyOrder(-1)]
public Dictionary PreferredCores = GenDefaultCorePreferences();
public bool DontTryOtherCores { get; set; }
diff --git a/src/BizHawk.Client.Common/config/ConfigExtensions.cs b/src/BizHawk.Client.Common/config/ConfigExtensions.cs
index 17176e6e436..c443aae4657 100644
--- a/src/BizHawk.Client.Common/config/ConfigExtensions.cs
+++ b/src/BizHawk.Client.Common/config/ConfigExtensions.cs
@@ -1,33 +1,23 @@
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common;
-using Newtonsoft.Json.Linq;
-
namespace BizHawk.Client.Common
{
public static class ConfigExtensions
{
- private class TypeNameEncapsulator
- {
- public object o;
- }
- private static JToken Serialize(object o)
+ private static JsonElement Serialize(object o)
{
- var tne = new TypeNameEncapsulator { o = o };
- return JToken.FromObject(tne, ConfigService.Serializer)["o"];
-
- // Maybe todo: This code is identical to the code above, except that it does not emit the legacy "$type"
- // parameter that we no longer need here. Leaving that in to make bisecting during this dev phase easier, and such.
- // return JToken.FromObject(o, ConfigService.Serializer);
+ return JsonSerializer.SerializeToElement(o, ConfigService.SerializerOptions);
}
- private static object Deserialize(JToken j, Type type)
+ private static object Deserialize(JsonElement json, Type type)
{
try
{
- return j?.ToObject(type, ConfigService.Serializer);
+ return json.Deserialize(type, ConfigService.SerializerOptions);
}
catch
{
diff --git a/src/BizHawk.Client.Common/config/ConfigService.cs b/src/BizHawk.Client.Common/config/ConfigService.cs
index fda1ba59ed8..ac120c23df9 100644
--- a/src/BizHawk.Client.Common/config/ConfigService.cs
+++ b/src/BizHawk.Client.Common/config/ConfigService.cs
@@ -1,37 +1,45 @@
+using System.Globalization;
using System.IO;
-using System.Reflection;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using BizHawk.Common;
-
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Newtonsoft.Json.Serialization;
-
-#pragma warning disable 618
+using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
- public static class ConfigService
+ internal class FloatConverter : JsonConverter
{
- internal static readonly JsonSerializer Serializer;
+ public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetSingle();
- static ConfigService()
+ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
{
- Serializer = new JsonSerializer
- {
- MissingMemberHandling = MissingMemberHandling.Ignore,
- TypeNameHandling = TypeNameHandling.Auto,
- ConstructorHandling = ConstructorHandling.Default,
-
- // because of the peculiar setup of Binding.cs and PathEntry.cs
- ObjectCreationHandling = ObjectCreationHandling.Replace,
-
- ContractResolver = new DefaultContractResolver
- {
- DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic
- },
- };
+#if NETCOREAPP
+ writer.WriteNumberValue(value);
+#else
+ // gotta love the fact .net framework can't even format floats correctly by default
+ // can't use G7 here because it may be too low accuracy, and can't use G8 because it may be too high, see 1.0000003f or 0.8f
+ writer.WriteRawValue(value.ToString("R", NumberFormatInfo.InvariantInfo));
+#endif
}
+ }
+
+ public static class ConfigService
+ {
+ internal static readonly JsonSerializerOptions SerializerOptions = new()
+ {
+ IncludeFields = true,
+ AllowTrailingCommas = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = true,
+ Converters =
+ {
+ new FloatConverter(), // this serializes floats with minimum required precision, e.g. 1.8000000012 -> 1.8
+ new ByteArrayAsNormalArrayJsonConverter() // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
+ }
+ };
public static bool IsFromSameVersion(string filepath, out string msg)
{
@@ -48,7 +56,7 @@ public static bool IsFromSameVersion(string filepath, out string msg)
string cfgVersionStr = null;
try
{
- cfgVersionStr = JObject.Parse(File.ReadAllText(filepath))["LastWrittenFrom"]?.Value();
+ cfgVersionStr = JsonNode.Parse(File.ReadAllText(filepath))["LastWrittenFrom"]?.GetValue();
}
catch (Exception)
{
@@ -84,16 +92,15 @@ public static bool IsFromSameVersion(string filepath, out string msg)
/// internal error
public static T Load(string filepath) where T : new()
{
- T config = default(T);
+ T config = default;
try
{
var file = new FileInfo(filepath);
if (file.Exists)
{
- using var reader = file.OpenText();
- var r = new JsonTextReader(reader);
- config = (T)Serializer.Deserialize(r, typeof(T));
+ using var reader = file.OpenRead();
+ config = JsonSerializer.Deserialize(reader, SerializerOptions);
}
}
catch (Exception ex)
@@ -106,12 +113,10 @@ public static bool IsFromSameVersion(string filepath, out string msg)
public static void Save(string filepath, object config)
{
- var file = new FileInfo(filepath);
try
{
- using var writer = file.CreateText();
- var w = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
- Serializer.Serialize(w, config);
+ using var writer = File.Create(filepath);
+ JsonSerializer.Serialize(writer, config, SerializerOptions);
}
catch
{
@@ -127,9 +132,7 @@ private class TypeNameEncapsulator
public static object LoadWithType(string serialized)
{
- using var tr = new StringReader(serialized);
- using var jr = new JsonTextReader(tr);
- var tne = (TypeNameEncapsulator)Serializer.Deserialize(jr, typeof(TypeNameEncapsulator));
+ var tne = JsonSerializer.Deserialize(serialized, SerializerOptions);
// in the case of trying to deserialize nothing, tne will be nothing
// we want to return nothing
@@ -138,12 +141,8 @@ public static object LoadWithType(string serialized)
public static string SaveWithType(object o)
{
- using var sw = new StringWriter();
- using var jw = new JsonTextWriter(sw) { Formatting = Formatting.None };
var tne = new TypeNameEncapsulator { o = o };
- Serializer.Serialize(jw, tne, typeof(TypeNameEncapsulator));
- sw.Flush();
- return sw.ToString();
+ return JsonSerializer.Serialize(tne, SerializerOptions);
}
}
}
diff --git a/src/BizHawk.Client.Common/config/FeedbackBind.cs b/src/BizHawk.Client.Common/config/FeedbackBind.cs
index 207e94d763a..d7095194fbf 100644
--- a/src/BizHawk.Client.Common/config/FeedbackBind.cs
+++ b/src/BizHawk.Client.Common/config/FeedbackBind.cs
@@ -1,6 +1,6 @@
#nullable enable
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace BizHawk.Client.Common
{
diff --git a/src/BizHawk.Client.Common/config/PathEntry.cs b/src/BizHawk.Client.Common/config/PathEntry.cs
index 2759c56fa15..af54507d412 100644
--- a/src/BizHawk.Client.Common/config/PathEntry.cs
+++ b/src/BizHawk.Client.Common/config/PathEntry.cs
@@ -1,11 +1,10 @@
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace BizHawk.Client.Common
{
public sealed class PathEntry
{
public string Type { get; set; }
- [JsonIgnore]
private string _path;
public string Path
{
diff --git a/src/BizHawk.Client.Common/config/PathEntryCollection.cs b/src/BizHawk.Client.Common/config/PathEntryCollection.cs
index cba2e3a97cf..43c65d972d6 100644
--- a/src/BizHawk.Client.Common/config/PathEntryCollection.cs
+++ b/src/BizHawk.Client.Common/config/PathEntryCollection.cs
@@ -1,13 +1,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.Json.Serialization;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
-using Newtonsoft.Json;
-
namespace BizHawk.Client.Common
{
public class PathEntryCollection
@@ -164,7 +163,6 @@ public void ResolveWithDefaults()
[JsonIgnore]
public string FirmwaresPathFragment => this[GLOBAL, "Firmware"].Path;
- [JsonIgnore]
internal string TempFilesFragment => this[GLOBAL, "Temp Files"].Path;
public static readonly Lazy> Defaults = new(() => new[]
diff --git a/src/BizHawk.Client.Common/config/ToolDialogSettings.cs b/src/BizHawk.Client.Common/config/ToolDialogSettings.cs
index 39f44327aca..7501ef40e9a 100644
--- a/src/BizHawk.Client.Common/config/ToolDialogSettings.cs
+++ b/src/BizHawk.Client.Common/config/ToolDialogSettings.cs
@@ -1,14 +1,18 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace BizHawk.Client.Common
{
public class ToolDialogSettings
{
+ // one may wonder why the public property is getting JsonIgnored and the private one JsonIncluded
+ [JsonInclude]
+ [JsonPropertyOrder(-2)]
private int? _wndx;
+ [JsonInclude]
+ [JsonPropertyOrder(-1)]
private int? _wndy;
public ToolDialogSettings()
diff --git a/src/BizHawk.Client.Common/lua/LuaDocumentation.cs b/src/BizHawk.Client.Common/lua/LuaDocumentation.cs
index fb6d7dd6cd5..75a7854173a 100644
--- a/src/BizHawk.Client.Common/lua/LuaDocumentation.cs
+++ b/src/BizHawk.Client.Common/lua/LuaDocumentation.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Reflection;
using System.Text;
-using Newtonsoft.Json;
+using System.Text.Json;
namespace BizHawk.Client.Common
{
@@ -117,18 +117,14 @@ public SublimeCompletions()
Scope = "source.lua - string";
}
- [JsonProperty(PropertyName = "scope")]
public string Scope { get; set; }
- [JsonProperty(PropertyName = "completions")]
public List Completions { get; set; } = new List();
public class Completion
{
- [JsonProperty(PropertyName = "trigger")]
public string Trigger { get; set; }
- [JsonProperty(PropertyName = "contents")]
public string Contents { get; set; }
}
}
@@ -184,7 +180,7 @@ public string ToSublime2CompletionList()
sc.Completions.Add(completion);
}
- return JsonConvert.SerializeObject(sc);
+ return JsonSerializer.Serialize(sc, new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
}
public string ToNotepadPlusPlusAutoComplete()
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs b/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
index 56cb358f213..575e5bd5b92 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-
-using Newtonsoft.Json;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using BizHawk.Bizware.Graphics;
using BizHawk.Common.IOExtensions;
@@ -137,7 +137,7 @@ public void Save(ZipStateSaver bs)
var nusertext = new IndexedStateLump(BinaryStateLump.BranchUserText);
foreach (var b in this)
{
- bs.PutLump(nheader, tw => tw.WriteLine(JsonConvert.SerializeObject(b.ForSerial)));
+ bs.PutLump(nheader, tw => tw.WriteLine(JsonSerializer.Serialize(b.ForSerial)));
bs.PutLump(ncore, (Stream s) => s.Write(b.CoreData, 0, b.CoreData.Length));
@@ -194,29 +194,15 @@ public void Load(ZipStateLoader bl, ITasMovie movie)
if (!bl.GetLump(nheader, abort: false, tr =>
{
- var header = (dynamic)JsonConvert.DeserializeObject(tr.ReadLine());
- b.Frame = (int)header.Frame;
+ var header = JsonSerializer.Deserialize(tr.ReadLine());
+ b.Frame = header["Frame"]!.GetValue();
- var timestamp = header.TimeStamp;
+ var timestamp = header["TimeStamp"];
- if (timestamp != null)
- {
- b.TimeStamp = (DateTime)timestamp;
- }
- else
- {
- b.TimeStamp = DateTime.Now;
- }
+ b.TimeStamp = timestamp?.GetValue() ?? DateTime.Now;
- var identifier = header.UniqueIdentifier;
- if (identifier != null)
- {
- b.Uuid = (Guid)identifier;
- }
- else
- {
- b.Uuid = Guid.NewGuid();
- }
+ var identifier = header["UniqueIdentifier"];
+ b.Uuid = identifier?.GetValue() ?? Guid.NewGuid();
}))
{
return;
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs b/src/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs
index 9e784ead9b6..9a68a1f626d 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasLagLog.cs
@@ -1,11 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.Json;
using BizHawk.Common.CollectionExtensions;
-using Newtonsoft.Json;
-
namespace BizHawk.Client.Common
{
public class TasLagLog
@@ -63,14 +62,14 @@ public void InsertHistoryAt(int frame, bool isLag)
public void Save(TextWriter tw)
{
- tw.WriteLine(JsonConvert.SerializeObject(_lagLog));
- tw.WriteLine(JsonConvert.SerializeObject(_wasLag));
+ tw.WriteLine(JsonSerializer.Serialize(_lagLog));
+ tw.WriteLine(JsonSerializer.Serialize(_wasLag));
}
public void Load(TextReader tr)
{
- _lagLog = JsonConvert.DeserializeObject>(tr.ReadLine());
- _wasLag = JsonConvert.DeserializeObject>(tr.ReadLine());
+ _lagLog = JsonSerializer.Deserialize>(tr.ReadLine());
+ _wasLag = JsonSerializer.Deserialize>(tr.ReadLine());
}
public bool? History(int frame)
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
index a8c49517292..e359d8dbb40 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
@@ -1,10 +1,9 @@
-using System.IO;
+using System.IO;
using System.Linq;
+using System.Text.Json;
using BizHawk.Common.StringExtensions;
-using Newtonsoft.Json;
-
namespace BizHawk.Client.Common
{
internal partial class TasMovie
@@ -22,7 +21,7 @@ private void AddTasProjLumps(ZipStateSaver bs, bool isBackup = false)
{
// at this point, TasStateManager may be null if we're currently importing a .bk2
- var settings = JsonConvert.SerializeObject(TasStateManager?.Settings ?? Session.Settings.DefaultTasStateManagerSettings);
+ var settings = JsonSerializer.Serialize(TasStateManager?.Settings ?? Session.Settings.DefaultTasStateManagerSettings);
bs.PutLump(BinaryStateLump.StateHistorySettings, tw => tw.WriteLine(settings));
bs.PutLump(BinaryStateLump.LagLog, tw => LagLog.Save(tw));
bs.PutLump(BinaryStateLump.Markers, tw => tw.WriteLine(Markers.ToString()));
@@ -43,7 +42,7 @@ private void AddTasProjLumps(ZipStateSaver bs, bool isBackup = false)
Branches.Save(bs);
}
- bs.PutLump(BinaryStateLump.Session, tw => tw.WriteLine(JsonConvert.SerializeObject(TasSession)));
+ bs.PutLump(BinaryStateLump.Session, tw => tw.WriteLine(JsonSerializer.Serialize(TasSession)));
if (!isBackup && TasStateManager is not null)
{
@@ -135,7 +134,7 @@ private void LoadTasprojExtras(ZipStateLoader bl)
var json = tr.ReadToEnd();
try
{
- TasSession = JsonConvert.DeserializeObject(json);
+ TasSession = JsonSerializer.Deserialize(json);
Branches.Current = TasSession.CurrentBranch;
}
catch
@@ -150,7 +149,7 @@ private void LoadTasprojExtras(ZipStateLoader bl)
var json = tr.ReadToEnd();
try
{
- settings = JsonConvert.DeserializeObject(json);
+ settings = JsonSerializer.Deserialize(json);
}
catch
{
diff --git a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.Update.cs b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.Update.cs
index 668df2be2da..7f7bcdc6949 100644
--- a/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.Update.cs
+++ b/src/BizHawk.Client.EmuHawk/RetroAchievements/RAIntegration.Update.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
-
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.BizInvoke;
using BizHawk.Common;
@@ -62,7 +61,7 @@ public static bool CheckUpdateRA(IDialogParent dialogParent)
try
{
var http = new HttpCommunication(null, "https://retroachievements.org/dorequest.php?r=latestintegration", null);
- var info = JsonConvert.DeserializeObject>(http.ExecGet());
+ var info = JsonSerializer.Deserialize>(http.ExecGet());
if (info.TryGetValue("Success", out var success) && (bool)success)
{
var lastestVer = new Version((string)info["LatestVersion"]);
diff --git a/src/BizHawk.Client.EmuHawk/UpdateChecker.cs b/src/BizHawk.Client.EmuHawk/UpdateChecker.cs
index b1bff70609f..0fe48c3b684 100644
--- a/src/BizHawk.Client.EmuHawk/UpdateChecker.cs
+++ b/src/BizHawk.Client.EmuHawk/UpdateChecker.cs
@@ -1,12 +1,11 @@
using System.IO;
using System.Net;
+using System.Text.Json.Nodes;
using System.Threading;
using BizHawk.Client.Common;
using BizHawk.Common;
-using Newtonsoft.Json.Linq;
-
namespace BizHawk.Client.EmuHawk
{
public static class UpdateChecker
@@ -73,9 +72,9 @@ private static void CheckInternal()
{
try
{
- JObject response = JObject.Parse(DownloadURLAsString(_latestVersionInfoURL));
+ var response = JsonNode.Parse(DownloadURLAsString(_latestVersionInfoURL));
- LatestVersion = ValidateVersionNumberString((string)response["name"]);
+ LatestVersion = ValidateVersionNumberString(response["name"].GetValue());
}
catch
{
diff --git a/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs b/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
index 4db66f30adc..3b2915f026f 100644
--- a/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
@@ -391,8 +391,8 @@ private static bool HasCustomConfig(IToolForm tool)
private static void InstallCustomConfig(IToolForm tool, Dictionary data)
{
Type type = tool.GetType();
- var props = type.GetPropertiesWithAttrib(typeof(ConfigPersistAttribute)).ToList();
- if (props.Count == 0)
+ var props = type.GetPropertiesWithAttrib(typeof(ConfigPersistAttribute)).ToArray();
+ if (props.Length == 0)
{
return;
}
@@ -425,7 +425,7 @@ private static void InstallCustomConfig(IToolForm tool, Dictionary SaveCustomConfig(tool, data, props);
}
- private static void SaveCustomConfig(IToolForm tool, Dictionary data, List props)
+ private static void SaveCustomConfig(IToolForm tool, Dictionary data, PropertyInfo[] props)
{
data.Clear();
foreach (var prop in props)
diff --git a/src/BizHawk.Emulation.Common/Array2DJsonConverter.cs b/src/BizHawk.Emulation.Common/Array2DJsonConverter.cs
new file mode 100644
index 00000000000..168165cc55b
--- /dev/null
+++ b/src/BizHawk.Emulation.Common/Array2DJsonConverter.cs
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace BizHawk.Emulation.Common
+{
+ // heavily inspired by https://stackoverflow.com/questions/66280645/how-can-i-serialize-a-double-2d-array-to-json-using-system-text-json
+ public class Array2DJsonConverter : JsonConverter
+ {
+ public override void Write(Utf8JsonWriter writer, T[,]? array, JsonSerializerOptions options)
+ {
+ if (array is null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ int rowsFirstIndex = array.GetLowerBound(0);
+ int rowsLastIndex = array.GetUpperBound(0);
+ var arrayConverter = (JsonConverter)options.GetConverter(typeof(T[]));
+
+ writer.WriteStartArray();
+ for (int i = rowsFirstIndex; i <= rowsLastIndex; i++)
+ {
+ arrayConverter.Write(writer, array.SliceRow(i).ToArray(), options);
+ }
+ writer.WriteEndArray();
+ }
+
+ public override T[,]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType is JsonTokenType.Null) return null;
+ if (reader.TokenType is not JsonTokenType.StartArray)
+ throw new JsonException($"Unexpected token when reading bytes: expected {nameof(JsonTokenType.StartArray)}, got {reader.TokenType}");
+
+ var listConverter = (JsonConverter)options.GetConverter(typeof(T[]));
+ List fullList = new();
+ while (reader.Read())
+ switch (reader.TokenType)
+ {
+ // handle both base64 string and array formats here and trust the converter to handle it
+ case JsonTokenType.String:
+ case JsonTokenType.StartArray:
+ var result = listConverter.Read(ref reader, typeof(T[]), options)!;
+ fullList.Add(result);
+ continue;
+ case JsonTokenType.EndArray:
+ return fullList.To2D();
+ case JsonTokenType.Comment:
+ continue;
+ default:
+ throw new JsonException($"Unexpected token when reading bytes: {reader.TokenType}");
+ }
+
+ throw new JsonException("Unexpected end when reading bytes");
+ }
+ }
+
+ internal static class ArrayExtensions
+ {
+ public static T[,] To2D(this IList source)
+ {
+ int firstDimension = source.Count;
+ int secondDimension = source.FirstOrDefault()?.Length ?? 0;
+
+ // sanity check; the input must consist of arrays of the same size
+ if (source.Any(row => row.Length != secondDimension))
+ throw new InvalidOperationException();
+
+ var result = new T[firstDimension, secondDimension];
+ for (int i = 0; i < firstDimension; i++)
+ for (int j = 0; j < source[i].Length; j++)
+ result[i, j] = source[i][j];
+
+ return result;
+ }
+
+ public static IEnumerable SliceRow(this T[,] array, int row)
+ {
+ for (int i = array.GetLowerBound(1); i <= array.GetUpperBound(1); i++)
+ {
+ yield return array[row, i];
+ }
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj b/src/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
index 8790dd2cae6..f811bf49e58 100644
--- a/src/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
+++ b/src/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj
@@ -8,8 +8,8 @@
-
+
diff --git a/src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs b/src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs
new file mode 100644
index 00000000000..1ecae3cde73
--- /dev/null
+++ b/src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace BizHawk.Emulation.Common
+{
+ /// based on this SO answer
+ public sealed class ByteArrayAsNormalArrayJsonConverter : JsonConverter
+ {
+ public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType is JsonTokenType.Null) return null;
+ if (reader.TokenType is not JsonTokenType.StartArray) throw new JsonException($"Unexpected token when reading bytes: expected {nameof(JsonTokenType.StartArray)}, got {reader.TokenType}");
+
+ List list = new();
+ while (reader.Read()) switch (reader.TokenType)
+ {
+ case JsonTokenType.Number:
+ list.Add(reader.GetByte());
+ continue;
+ case JsonTokenType.EndArray:
+ return list.ToArray();
+ case JsonTokenType.Comment:
+ continue;
+ default:
+ throw new JsonException($"Unexpected token when reading bytes: {reader.TokenType}");
+ }
+ throw new JsonException("Unexpected end when reading bytes");
+ }
+
+ public override void Write(Utf8JsonWriter writer, byte[]? value, JsonSerializerOptions options)
+ {
+ if (value is null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ writer.WriteStartArray();
+ foreach (byte b in value) writer.WriteNumberValue(b);
+ writer.WriteEndArray();
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/TextState.cs b/src/BizHawk.Emulation.Common/TextState.cs
index d2374142752..e8fa77ce5e7 100644
--- a/src/BizHawk.Emulation.Common/TextState.cs
+++ b/src/BizHawk.Emulation.Common/TextState.cs
@@ -5,8 +5,6 @@
using BizHawk.Common.CollectionExtensions;
-using Newtonsoft.Json;
-
namespace BizHawk.Emulation.Common
{
// managed counterpart to unmanaged serialization code in GB and WSWAN cores
@@ -42,10 +40,8 @@ public bool ShouldSerializeObjects()
public readonly Node Root = new Node();
- [JsonIgnore]
private Stack Nodes;
- [JsonIgnore]
private Node Current => Nodes.Peek();
public void Prepare()
diff --git a/src/BizHawk.Emulation.Common/U8ArrayAsNormalJSONListConverter.cs b/src/BizHawk.Emulation.Common/U8ArrayAsNormalJSONListConverter.cs
deleted file mode 100644
index 083e50876c0..00000000000
--- a/src/BizHawk.Emulation.Common/U8ArrayAsNormalJSONListConverter.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System.Collections.Generic;
-
-using Newtonsoft.Json;
-using Newtonsoft.Json.Serialization;
-
-namespace BizHawk.Emulation.Common
-{
- /// seems unnecessary, but suggested by official docs so sure why not
- public sealed class U8ArrayAsNormalJSONListResolver : DefaultContractResolver
- {
- public static readonly U8ArrayAsNormalJSONListResolver INSTANCE = new();
-
- protected override JsonContract CreateContract(Type objectType)
- {
- var contract = base.CreateContract(objectType);
- if (objectType == typeof(byte[])) contract.Converter = U8ArrayAsNormalJSONListConverter.INSTANCE;
- return contract;
- }
- }
-
- /// based on this SO answer
- public sealed class U8ArrayAsNormalJSONListConverter : JsonConverter
- {
- public static readonly U8ArrayAsNormalJSONListConverter INSTANCE = new();
-
- public override byte[]? ReadJson(JsonReader reader, Type objectType, byte[]? existingValue, bool hasExistingValue, JsonSerializer serializer)
- {
- if (reader.TokenType is JsonToken.Null) return null;
- if (reader.TokenType is not JsonToken.StartArray) throw new Exception($"Unexpected token when reading bytes: expected {nameof(JsonToken.StartArray)}, got {reader.TokenType}");
- List list = new();
- while (reader.Read()) switch (reader.TokenType)
- {
- case JsonToken.Integer:
- list.Add(reader.Value switch
- {
- byte b => b,
- long l and >= byte.MinValue and <= byte.MaxValue => unchecked((byte) l),
- var o => throw new Exception($"Integer literal outside u8 range: {o}")
- });
- continue;
- case JsonToken.EndArray:
- return list.ToArray();
- case JsonToken.Comment:
- continue;
- default:
- throw new Exception($"Unexpected token when reading bytes: {reader.TokenType}");
- }
- throw new Exception("Unexpected end when reading bytes");
- }
-
- public override void WriteJson(JsonWriter writer, byte[]? value, JsonSerializer serializer)
- {
- if (value is null)
- {
- writer.WriteNull();
- return;
- }
- writer.WriteStartArray();
- for (var i = 0; i < value.Length; i++) writer.WriteValue(value[i]);
- writer.WriteEndArray();
- }
- }
-}
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.ISettable.cs
index 37635102def..92e0ecde61c 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.ISettable.cs
@@ -1,8 +1,6 @@
using System.ComponentModel;
using System.Drawing;
-using Newtonsoft.Json;
-
using BizHawk.Emulation.Common;
using BizHawk.Common;
@@ -44,16 +42,12 @@ public PutSettingsDirtyBits PutSyncSettings(A2600SyncSettings o)
[CoreSettings]
public class A2600Settings
{
- [JsonIgnore]
private int _ntscTopLine;
- [JsonIgnore]
private int _ntscBottomLine;
- [JsonIgnore]
private int _palTopLine;
- [JsonIgnore]
private int _palBottomLine;
[DisplayName("Show Background")]
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs
index b9c06464ed3..1ec80f89f24 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.ISettable.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/Stella/Stella.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/Stella/Stella.ISettable.cs
index 10c6a8db935..09c87727ab3 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Atari/Stella/Stella.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/Stella/Stella.ISettable.cs
@@ -1,8 +1,6 @@
using System.ComponentModel;
using System.Drawing;
-using Newtonsoft.Json;
-
using BizHawk.Emulation.Common;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Consoles.Atari.Stella;
@@ -40,16 +38,12 @@ public PutSettingsDirtyBits PutSyncSettings(A2600SyncSettings o)
[CoreSettings]
public class A2600Settings
{
- [JsonIgnore]
private int _ntscTopLine;
- [JsonIgnore]
private int _ntscBottomLine;
- [JsonIgnore]
private int _palTopLine;
- [JsonIgnore]
private int _palBottomLine;
[DisplayName("Show Background")]
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IStatable.cs
index df0d12f4565..88a0d0e133c 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IStatable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Atari/lynx/Lynx.IStatable.cs
@@ -1,5 +1,5 @@
using System.IO;
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.Emulation.Common;
@@ -19,12 +19,12 @@ public void SaveStateText(TextWriter writer)
s.ExtraData.LagCount = LagCount;
s.ExtraData.Frame = Frame;
- _ser.Serialize(writer, s);
+ writer.Write(JsonSerializer.Serialize(s, _options));
}
public void LoadStateText(TextReader reader)
{
- var s = (TextState)_ser.Deserialize(reader, typeof(TextState));
+ var s = JsonSerializer.Deserialize>(reader.ReadToEnd(), _options);
s.Prepare();
var ff = s.GetFunctionPointersLoad();
LibLynx.TxtStateLoad(Core, ref ff);
@@ -69,7 +69,7 @@ public void LoadStateBinary(BinaryReader reader)
Frame = reader.ReadInt32();
}
- private readonly JsonSerializer _ser = new JsonSerializer { Formatting = Formatting.Indented };
+ private readonly JsonSerializerOptions _options = new() { WriteIndented = true };
private readonly byte[] _saveBuff;
private class TextStateData
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs
index 8ea7707b973..5906a0db7b3 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.ISettable.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISettable.cs
index 8c41a7c0712..a0fcc7823d3 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/GCE/Vectrex/VectrexHawk.ISettable.cs
@@ -1,6 +1,5 @@
using System.ComponentModel;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -35,6 +34,7 @@ public PutSettingsDirtyBits PutSyncSettings(VectrexSyncSettings o)
[CoreSettings]
public class VectrexSyncSettings
{
+ // TODO: should Port2 also be JsonIgnored here?
[JsonIgnore]
public string Port1 = VectrexHawkControllerDeck.DefaultControllerName;
public string Port2 = VectrexHawkControllerDeck.DefaultControllerName;
@@ -45,7 +45,6 @@ public enum ControllerType
Analog
}
- [JsonIgnore]
private ControllerType _VectrexController1;
private ControllerType _VectrexController2;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.ISettable.cs
index ad9f177cfcc..f150510ee77 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.ISettable.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs
index da9375d6f66..9936ddd4b00 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs
@@ -1,6 +1,5 @@
using System.ComponentModel;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -125,9 +124,7 @@ public int RTCOffset
[DefaultValue(true)]
public bool Use_SRAM { get; set; }
- [JsonIgnore]
private int _RTCInitialTime;
- [JsonIgnore]
private int _RTCOffset;
[JsonIgnore]
public ushort _DivInitialTime = 8;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs
index 3f87bf2e5cc..974f29f7045 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs
@@ -1,6 +1,5 @@
using System.ComponentModel;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -130,13 +129,9 @@ public int RTCOffset_R
[DefaultValue(true)]
public bool Use_SRAM { get; set; }
- [JsonIgnore]
private int _RTCInitialTime_L;
- [JsonIgnore]
private int _RTCInitialTime_R;
- [JsonIgnore]
private int _RTCOffset_L;
- [JsonIgnore]
private int _RTCOffset_R;
[JsonIgnore]
public ushort _DivInitialTime_L = 8;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs
index 2505b2354c1..7452fb3d1af 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs
@@ -1,6 +1,5 @@
using System.ComponentModel;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -147,17 +146,11 @@ public int RTCOffset_R
[DefaultValue(true)]
public bool Use_SRAM { get; set; }
- [JsonIgnore]
private int _RTCInitialTime_L;
- [JsonIgnore]
private int _RTCInitialTime_C;
- [JsonIgnore]
private int _RTCInitialTime_R;
- [JsonIgnore]
private int _RTCOffset_L;
- [JsonIgnore]
private int _RTCOffset_C;
- [JsonIgnore]
private int _RTCOffset_R;
[JsonIgnore]
public ushort _DivInitialTime_L = 8;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISettable.cs
index b89cd3c0cb8..13119c9a651 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.ISettable.cs
@@ -1,6 +1,5 @@
using System.ComponentModel;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -176,21 +175,13 @@ public int RTCOffset_D
[DefaultValue(true)]
public bool Use_SRAM { get; set; }
- [JsonIgnore]
private int _RTCInitialTime_A;
- [JsonIgnore]
private int _RTCInitialTime_B;
- [JsonIgnore]
private int _RTCInitialTime_C;
- [JsonIgnore]
private int _RTCInitialTime_D;
- [JsonIgnore]
private int _RTCOffset_A;
- [JsonIgnore]
private int _RTCOffset_B;
- [JsonIgnore]
private int _RTCOffset_C;
- [JsonIgnore]
private int _RTCOffset_D;
[JsonIgnore]
public ushort _DivInitialTime_A = 8;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
index 3350697e027..3b6e5ccb7de 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs
@@ -1,8 +1,7 @@
//#define USE_UPSTREAM_STATES // really more for testing due to needing to use these anyways for initial state code. could potentially be used outright for states
using System.IO;
-
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.Emulation.Common;
@@ -15,12 +14,12 @@ public partial class Gameboy : IStatable, ITextStatable
public void SaveStateText(TextWriter writer)
{
var s = SaveState();
- _ser.Serialize(writer, s);
+ writer.Write(JsonSerializer.Serialize(s, _options));
}
public void LoadStateText(TextReader reader)
{
- var s = (TextState)_ser.Deserialize(reader, typeof(TextState));
+ var s = JsonSerializer.Deserialize>(reader.ReadToEnd(), _options);
LoadState(s);
reader.ReadToEnd();
}
@@ -94,7 +93,7 @@ private void NewSaveCoreSetBuff()
=> _stateBuf = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
#endif
- private readonly JsonSerializer _ser = new() { Formatting = Formatting.Indented };
+ private readonly JsonSerializerOptions _options = new() { WriteIndented = true };
// other data in the text state besides core
internal class TextStateData
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IStatable.cs
index 3817e78acac..220a3cd5e72 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IStatable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IStatable.cs
@@ -1,6 +1,5 @@
using System.IO;
-
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.Emulation.Common;
@@ -24,12 +23,12 @@ public bool AvoidRewind
public void SaveStateText(TextWriter writer)
{
- ser.Serialize(writer, new GBLSerialized(this));
+ writer.Write(JsonSerializer.Serialize(new GBLSerialized(this), _options));
}
public void LoadStateText(TextReader reader)
{
- var s = (GBLSerialized)ser.Deserialize(reader, typeof(GBLSerialized));
+ var s = JsonSerializer.Deserialize(reader.ReadToEnd(), _options);
if (s.NumCores != _numCores)
{
throw new InvalidOperationException("Core number mismatch!");
@@ -49,7 +48,6 @@ public void LoadStateText(TextReader reader)
_linkShiftSignal = s.LinkShiftSignal;
_linkSpaced = s.LinkSpaced;
_linkSpaceSignal = s.LinkSpaceSignal;
- reader.ReadToEnd();
}
public void SaveStateBinary(BinaryWriter writer)
@@ -97,7 +95,7 @@ public void LoadStateBinary(BinaryReader reader)
_linkSpaceSignal = reader.ReadBoolean();
}
- private readonly JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented };
+ private readonly JsonSerializerOptions _options = new() { WriteIndented = true };
private class GBLSerialized
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.Controller.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.Controller.cs
index 3dc259f0521..b27de5725e1 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.Controller.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.Controller.cs
@@ -1,5 +1,4 @@
using System.ComponentModel;
-using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Nintendo.N64
{
@@ -25,7 +24,6 @@ public enum N64ControllerPakType
TRANSFER_PAK = 4
}
- [JsonIgnore]
private N64ControllerPakType _type = N64ControllerPakType.NO_PAK;
///
@@ -44,7 +42,6 @@ public N64ControllerPakType PakType
set => _type = value;
}
- [JsonIgnore]
private bool _isConnected = true;
///
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs
index b0a22006f4c..1ce0831eacf 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs
@@ -1,8 +1,6 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
-using Newtonsoft.Json;
-
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -61,7 +59,6 @@ public class NDSSettings
[TypeConverter(typeof(DescribableEnumConverter))]
public ScreenRotationKind ScreenRotation { get; set; }
- [JsonIgnore]
private int _screengap;
[DisplayName("Screen Gap")]
@@ -185,7 +182,6 @@ public enum ThreeDeeRendererType : int
[DefaultValue(true)]
public bool ThreadedRendering { get; set; }
- [JsonIgnore]
private int _glScaleFactor;
[DisplayName("OpenGL Scale Factor")]
@@ -207,8 +203,7 @@ public int GLScaleFactor
[DefaultValue(false)]
public bool GLHiResCoordinates { get; set; }
- [JsonIgnore]
- private DateTime _initaltime;
+ private DateTime _initialtime;
[DisplayName("Initial Time")]
[Description("Initial time of emulation. Not used if Use Real Time is true")]
@@ -216,8 +211,8 @@ public int GLScaleFactor
[TypeConverter(typeof(BizDateTimeConverter))]
public DateTime InitialTime
{
- get => _initaltime;
- set => _initaltime = value < minDate ? minDate : (value > maxDate ? maxDate : value);
+ get => _initialtime;
+ set => _initialtime = value < minDate ? minDate : (value > maxDate ? maxDate : value);
}
[DisplayName("Use Real Time")]
@@ -264,7 +259,6 @@ public enum StartUp : int
[TypeConverter(typeof(DescribableEnumConverter))]
public StartUp FirmwareStartUp { get; set; }
- [JsonIgnore]
private string _firmwareusername;
[DisplayName("Firmware Username")]
@@ -310,10 +304,8 @@ public enum Month : int
December,
}
- [JsonIgnore]
private Month _firmwarebirthdaymonth;
- [JsonIgnore]
private int _firmwarebirthdayday;
[DisplayName("Firmware Birthday Month")]
@@ -373,7 +365,6 @@ public enum Color : int
[TypeConverter(typeof(DescribableEnumConverter))]
public Color FirmwareFavouriteColour { get; set; }
- [JsonIgnore]
private string _firmwaremessage;
[DisplayName("Firmware Message")]
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISettable.cs
index 2a4aa1873bd..7f2ab8c1b8f 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISettable.cs
@@ -1,11 +1,10 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
-using Newtonsoft.Json;
-
namespace BizHawk.Emulation.Cores.Nintendo.NES
{
public partial class NES : ISettable
@@ -61,10 +60,9 @@ public enum Region
public NESControlSettings Controls = new NESControlSettings();
- [JsonIgnore]
private byte[]/*?*/ _initialWRamStatePattern;
- [JsonConverter(typeof(U8ArrayAsNormalJSONListConverter))] // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
+ [JsonConverter(typeof(ByteArrayAsNormalArrayJsonConverter))] // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
public byte[] InitialWRamStatePattern
{
get => _initialWRamStatePattern ?? [ ];
@@ -150,6 +148,7 @@ public class NESSettings
public int PAL_TopLine = 0;
public int PAL_BottomLine = 239;
+ [JsonConverter(typeof(Array2DJsonConverter))]
public byte[,] Palette;
public int APU_vol = 1;
@@ -166,7 +165,7 @@ public NESSettings()
Palette = (byte[,])Palettes.QuickNESPalette.Clone();
}
- [Newtonsoft.Json.JsonConstructor]
+ [JsonConstructor]
public NESSettings(byte[,] Palette)
{
if (Palette == null)
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NESControllers.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NESControllers.cs
index bd4c8937f6d..0620029619f 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NESControllers.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NESControllers.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
+
using BizHawk.Emulation.Common;
using BizHawk.Common;
-using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Nintendo.NES
{
@@ -1065,16 +1065,13 @@ public static IList GetNesPortValues()
return new List(NesPortDevices.Keys).AsReadOnly();
}
- [JsonIgnore]
private bool _Famicom;
public bool Famicom
{
get => _Famicom;
set => _Famicom = value;
}
- [JsonIgnore]
private string _NesLeftPort;
- [JsonIgnore]
private string _NesRightPort;
public string NesLeftPort
{
@@ -1098,7 +1095,6 @@ public string NesRightPort
throw new InvalidOperationException();
}
}
- [JsonIgnore]
private string _FamicomExpPort;
public string FamicomExpPort
{
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
index d0b84df2d04..7a03aae4c0a 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.ISettable.cs
@@ -1,8 +1,6 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
-using Newtonsoft.Json;
-
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -61,7 +59,6 @@ public int NumSprites
set => _NumSprites = Math.Min(64, Math.Max(0, value));
}
- [JsonIgnore]
private int _NumSprites;
[DefaultValue(false)]
@@ -87,7 +84,6 @@ public byte[] Palette
}
}
- [JsonIgnore]
private byte[] _Palette;
public QuickNESSettings Clone()
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISettable.cs
index 93b9ce2c4d5..e44074c00bf 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/SameBoy/SameBoy.ISettable.cs
@@ -1,7 +1,6 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
-
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
using BizHawk.Common;
using BizHawk.Emulation.Common;
@@ -61,6 +60,7 @@ public enum GBPaletteType : uint
CUSTOM,
}
+ [JsonInclude]
private int[] _customPal;
[DisplayName("GB Mono Palette")]
@@ -93,7 +93,6 @@ public enum ColorCorrectionMode : uint
[TypeConverter(typeof(DescribableEnumConverter))]
public ColorCorrectionMode ColorCorrection { get; set; }
- [JsonIgnore]
private int _lighttemperature;
[DisplayName("Ambient Light Temperature")]
@@ -126,7 +125,6 @@ public enum HighPassFilterMode : uint
[TypeConverter(typeof(DescribableEnumConverter))]
public HighPassFilterMode HighPassFilter { get; set; }
- [JsonIgnore]
private int _interferencevolume;
[DisplayName("Audio Interference Volume")]
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs
index c43a2e187cc..8be3680cbe6 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs
@@ -3,7 +3,6 @@
using BizHawk.Common;
using BizHawk.Emulation.Common;
-using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
@@ -108,7 +107,6 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c
public class GPGXSettings
{
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _DrawBGA;
[DisplayName("Background Layer A")]
@@ -121,7 +119,6 @@ public bool DrawBGA
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _DrawBGB;
[DisplayName("Background Layer B")]
@@ -134,7 +131,6 @@ public bool DrawBGB
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _DrawBGW;
[DisplayName("Background Layer W")]
@@ -147,7 +143,6 @@ public bool DrawBGW
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _DrawObj;
[DisplayName("Sprite Layer")]
@@ -160,7 +155,6 @@ public bool DrawObj
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _PadScreen320;
[DisplayName("Pad screen to 320")]
@@ -173,7 +167,6 @@ public bool PadScreen320
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _Backdrop;
[DisplayName("Use custom backdrop color")]
@@ -186,7 +179,6 @@ public bool Backdrop
}
[DeepEqualsIgnore]
- [JsonIgnore]
private bool _noSpriteLimit;
[DisplayName("Remove Per-Line Sprite Limit")]
diff --git a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs
index 31a8c88adee..339fa55d920 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs
@@ -15,8 +15,7 @@
using System.IO;
using System.Collections.Generic;
using System.Linq;
-
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.Emulation.Common;
using BizHawk.Common;
@@ -1065,7 +1064,7 @@ public class SyncSettings
{
public SyncSettings Clone()
{
- return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(this));
+ return JsonSerializer.Deserialize(JsonSerializer.Serialize(this)); // what am I witnessing
}
public bool EnableLEC;
diff --git a/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.IStatable.cs b/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.IStatable.cs
index 31066bd3219..edd31ab04df 100644
--- a/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.IStatable.cs
+++ b/src/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.IStatable.cs
@@ -1,7 +1,6 @@
using System.IO;
using System.Runtime.InteropServices;
-
-using Newtonsoft.Json;
+using System.Text.Json;
using BizHawk.Emulation.Common;
@@ -12,7 +11,7 @@ partial class WonderSwan: ITextStatable
private void InitIStatable()
=> savebuff = new byte[BizSwan.bizswan_binstatesize(Core)];
- private readonly JsonSerializer ser = new() { Formatting = Formatting.Indented };
+ private readonly JsonSerializerOptions _options = new() { WriteIndented = true };
[StructLayout(LayoutKind.Sequential)]
private class TextStateData
@@ -43,12 +42,12 @@ public void SaveStateText(TextWriter writer)
var ff = s.GetFunctionPointersSave();
BizSwan.bizswan_txtstatesave(Core, ref ff);
SaveTextStateData(s.ExtraData);
- ser.Serialize(writer, s);
+ writer.Write(JsonSerializer.Serialize(s, _options));
}
public void LoadStateText(TextReader reader)
{
- var s = (TextState)ser.Deserialize(reader, typeof(TextState));
+ var s = JsonSerializer.Deserialize>(reader.ReadToEnd(), _options);
if (s is not null)
{
s.Prepare();
diff --git a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
index 2e41605caa9..df7fc3acb63 100644
--- a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
+++ b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
@@ -1,13 +1,12 @@
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using BizHawk.Client.Common;
using BizHawk.Common;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
namespace BizHawk.Tests.Client.Common.config
{
[TestClass]
@@ -29,7 +28,7 @@ public sealed class SerializationStabilityTests
typeof(DateTime),
typeof(Dictionary<,>),
typeof(int),
- typeof(JToken),
+ typeof(JsonElement),
typeof(List<>),
typeof(Nullable<>),
typeof(object),
@@ -44,7 +43,7 @@ public sealed class SerializationStabilityTests
{
[typeof(AnalogBind)] = @"{""Value"":""X1 LeftThumbX Axis"",""Mult"":0.8,""Deadzone"":0.1}",
[typeof(CheatConfig)] = $@"{{""DisableOnLoad"":false,""LoadFileByGame"":true,""AutoSaveOnClose"":true,""Recent"":{RECENT_SER}}}",
- [typeof(FeedbackBind)] = @"{""Channels"":""Left+Right"",""GamepadPrefix"":""X1 "",""Prescale"":1.0}",
+ [typeof(FeedbackBind)] = @"{""Channels"":""Left+Right"",""GamepadPrefix"":""X1 "",""Prescale"":1}",
[typeof(MessagePosition)] = @"{""X"":0,""Y"":0,""Anchor"":0}",
[typeof(MovieConfig)] = $@"{{""MovieEndAction"":3,""EnableBackupMovies"":true,""MoviesOnDisk"":false,""MovieCompressionLevel"":2,""VBAStyleMovieLoadState"":false,""DefaultTasStateManagerSettings"":{ZWINDER_SER}}}",
[typeof(PathEntry)] = PATHENTRY_SER,
@@ -84,11 +83,17 @@ static void CheckAll(string? groupDesc = null)
CheckAll();
}
+ private static readonly JsonSerializerOptions SerializerOptions = new(ConfigService.SerializerOptions)
+ {
+ WriteIndented = false
+ };
+
[TestMethod]
public void TestRoundTripSerialization()
{
- static object Deser(string s, Type type) => JToken.Parse(s).ToObject(type, ConfigService.Serializer)!;
- static string Ser(object o) => JToken.FromObject(o, ConfigService.Serializer).ToString(Formatting.None);
+ static object? Deser(string s, Type type) => JsonSerializer.Deserialize(s, type, SerializerOptions);
+ static string Ser(object? o) => JsonSerializer.Serialize(o, SerializerOptions);
+
foreach (var (type, s) in KnownGoodFromBizHawk)
{
if (s == "TODO") continue;
From 6f81a4324a46c6edae70994cb7df6ab4a58473c4 Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Fri, 31 May 2024 21:42:26 +0200
Subject: [PATCH 2/8] fix TAStudio loading
---
.../config/ConfigService.cs | 33 ++++++++++++++-----
.../savestates/SavestateFile.cs | 2 +-
.../CustomControls/InputRoll/InputRoll.cs | 19 ++++++-----
src/BizHawk.Client.EmuHawk/MainForm.cs | 2 +-
.../tools/BasicBot/BasicBot.cs | 2 +-
.../config/SerializationStabilityTests.cs | 9 ++---
6 files changed, 41 insertions(+), 26 deletions(-)
diff --git a/src/BizHawk.Client.Common/config/ConfigService.cs b/src/BizHawk.Client.Common/config/ConfigService.cs
index ac120c23df9..7706b4bc123 100644
--- a/src/BizHawk.Client.Common/config/ConfigService.cs
+++ b/src/BizHawk.Client.Common/config/ConfigService.cs
@@ -28,12 +28,11 @@ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOpt
public static class ConfigService
{
- internal static readonly JsonSerializerOptions SerializerOptions = new()
+ private static readonly JsonSerializerOptions NonIndentedSerializerOptions = new()
{
IncludeFields = true,
AllowTrailingCommas = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
- WriteIndented = true,
Converters =
{
new FloatConverter(), // this serializes floats with minimum required precision, e.g. 1.8000000012 -> 1.8
@@ -41,6 +40,13 @@ public static class ConfigService
}
};
+ private static readonly JsonSerializerOptions IndentedSerializerOptions = new(NonIndentedSerializerOptions)
+ {
+ WriteIndented = true
+ };
+
+ internal static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;
+
public static bool IsFromSameVersion(string filepath, out string msg)
{
const string MSGFMT_NEWER = "Your config file ({0}) is from a newer version of EmuHawk, {2} (this is {1}). It may fail to load.";
@@ -100,7 +106,7 @@ public static bool IsFromSameVersion(string filepath, out string msg)
if (file.Exists)
{
using var reader = file.OpenRead();
- config = JsonSerializer.Deserialize(reader, SerializerOptions);
+ config = JsonSerializer.Deserialize(reader, IndentedSerializerOptions);
}
}
catch (Exception ex)
@@ -116,7 +122,7 @@ public static void Save(string filepath, object config)
try
{
using var writer = File.Create(filepath);
- JsonSerializer.Serialize(writer, config, SerializerOptions);
+ JsonSerializer.Serialize(writer, config, IndentedSerializerOptions);
}
catch
{
@@ -130,13 +136,24 @@ private class TypeNameEncapsulator
public object o;
}
- public static object LoadWithType(string serialized)
+ public static T LoadWithType(string serialized)
{
var tne = JsonSerializer.Deserialize(serialized, SerializerOptions);
- // in the case of trying to deserialize nothing, tne will be nothing
- // we want to return nothing
- return tne?.o;
+ if (tne?.o is JsonElement jsonElement)
+ return jsonElement.Deserialize(SerializerOptions);
+
+ return default;
+ }
+
+ public static object LoadWithType(string serialized, Type deserializedType)
+ {
+ var tne = JsonSerializer.Deserialize(serialized, SerializerOptions);
+
+ if (tne?.o is JsonElement jsonElement)
+ return jsonElement.Deserialize(deserializedType, SerializerOptions);
+
+ return null;
}
public static string SaveWithType(object o)
diff --git a/src/BizHawk.Client.Common/savestates/SavestateFile.cs b/src/BizHawk.Client.Common/savestates/SavestateFile.cs
index a2af4d74a1f..a5c86d7be5c 100644
--- a/src/BizHawk.Client.Common/savestates/SavestateFile.cs
+++ b/src/BizHawk.Client.Common/savestates/SavestateFile.cs
@@ -181,7 +181,7 @@ public bool Load(string path, IDialogParent dialogParent)
if (!string.IsNullOrWhiteSpace(userData))
{
- var bag = (Dictionary)ConfigService.LoadWithType(userData);
+ var bag = ConfigService.LoadWithType>(userData);
_userBag.Clear();
foreach (var (k, v) in bag) _userBag.Add(k, v);
}
diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
index c1da1815617..f06559732c7 100644
--- a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
+++ b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
@@ -659,16 +659,19 @@ public string UserSettingsSerialized()
public void LoadSettingsSerialized(string settingsJson)
{
- var settings = ConfigService.LoadWithType(settingsJson);
-
- // TODO: don't silently fail, inform the user somehow
- if (settings is InputRollSettings rollSettings)
+ try
{
- _columns = rollSettings.Columns;
+ InputRollSettings settings = ConfigService.LoadWithType(settingsJson);
+
+ _columns = settings.Columns;
_columns.ChangedCallback = ColumnChangedCallback;
- HorizontalOrientation = rollSettings.HorizontalOrientation;
- LagFramesToHide = rollSettings.LagFramesToHide;
- HideWasLagFrames = rollSettings.HideWasLagFrames;
+ HorizontalOrientation = settings.HorizontalOrientation;
+ LagFramesToHide = settings.LagFramesToHide;
+ HideWasLagFrames = settings.HideWasLagFrames;
+ }
+ catch
+ {
+ // TODO: don't silently fail, inform the user somehow
}
}
diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs
index 21f53d04efa..7486ad5ccb5 100644
--- a/src/BizHawk.Client.EmuHawk/MainForm.cs
+++ b/src/BizHawk.Client.EmuHawk/MainForm.cs
@@ -2365,7 +2365,7 @@ private void CoreSyncSettings(object sender, RomLoader.SettingsLoadArgs e)
{
if (!string.IsNullOrWhiteSpace(MovieSession.QueuedSyncSettings))
{
- e.Settings = ConfigService.LoadWithType(MovieSession.QueuedSyncSettings);
+ e.Settings = ConfigService.LoadWithType(MovieSession.QueuedSyncSettings, e.SettingsType);
}
else
{
diff --git a/src/BizHawk.Client.EmuHawk/tools/BasicBot/BasicBot.cs b/src/BizHawk.Client.EmuHawk/tools/BasicBot/BasicBot.cs
index 1c74d610344..fd46a934e35 100644
--- a/src/BizHawk.Client.EmuHawk/tools/BasicBot/BasicBot.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/BasicBot/BasicBot.cs
@@ -572,7 +572,7 @@ private bool LoadBotFile(string path)
BotData botData;
try
{
- botData = (BotData) ConfigService.LoadWithType(File.ReadAllText(path));
+ botData = ConfigService.LoadWithType(File.ReadAllText(path));
}
catch (Exception e)
{
diff --git a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
index df7fc3acb63..2be0cd7ad58 100644
--- a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
+++ b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
@@ -83,16 +83,11 @@ static void CheckAll(string? groupDesc = null)
CheckAll();
}
- private static readonly JsonSerializerOptions SerializerOptions = new(ConfigService.SerializerOptions)
- {
- WriteIndented = false
- };
-
[TestMethod]
public void TestRoundTripSerialization()
{
- static object? Deser(string s, Type type) => JsonSerializer.Deserialize(s, type, SerializerOptions);
- static string Ser(object? o) => JsonSerializer.Serialize(o, SerializerOptions);
+ static object? Deser(string s, Type type) => JsonSerializer.Deserialize(s, type, ConfigService.SerializerOptions);
+ static string Ser(object? o) => JsonSerializer.Serialize(o, ConfigService.SerializerOptions);
foreach (var (type, s) in KnownGoodFromBizHawk)
{
From 48545bf9cdb761d9c9101b5334ee8dce3f8d8905 Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Sat, 1 Jun 2024 14:25:37 +0200
Subject: [PATCH 3/8] Move JsonConverters, support TypeConverter, fix custom
tool settings load
---
.../config/ConfigService.cs | 25 ++-------
.../tools/ToolManager.cs | 18 ++-----
.../{ => Json}/Array2DJsonConverter.cs | 2 +-
.../ByteArrayAsNormalArrayJsonConverter.cs | 2 +-
.../Json/FloatConverter.cs | 22 ++++++++
.../Json/TypeConverterJsonAdapter.cs | 52 +++++++++++++++++++
.../Consoles/Nintendo/NES/NES.ISettable.cs | 1 +
7 files changed, 84 insertions(+), 38 deletions(-)
rename src/BizHawk.Emulation.Common/{ => Json}/Array2DJsonConverter.cs (98%)
rename src/BizHawk.Emulation.Common/{ => Json}/ByteArrayAsNormalArrayJsonConverter.cs (97%)
create mode 100644 src/BizHawk.Emulation.Common/Json/FloatConverter.cs
create mode 100644 src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
diff --git a/src/BizHawk.Client.Common/config/ConfigService.cs b/src/BizHawk.Client.Common/config/ConfigService.cs
index 7706b4bc123..df04bb247b5 100644
--- a/src/BizHawk.Client.Common/config/ConfigService.cs
+++ b/src/BizHawk.Client.Common/config/ConfigService.cs
@@ -1,31 +1,13 @@
-using System.Globalization;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
-using System.Text.Json.Serialization;
using BizHawk.Common;
-using BizHawk.Emulation.Common;
+using BizHawk.Emulation.Common.Json;
namespace BizHawk.Client.Common
{
- internal class FloatConverter : JsonConverter
- {
- public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetSingle();
-
- public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
- {
-#if NETCOREAPP
- writer.WriteNumberValue(value);
-#else
- // gotta love the fact .net framework can't even format floats correctly by default
- // can't use G7 here because it may be too low accuracy, and can't use G8 because it may be too high, see 1.0000003f or 0.8f
- writer.WriteRawValue(value.ToString("R", NumberFormatInfo.InvariantInfo));
-#endif
- }
- }
-
public static class ConfigService
{
private static readonly JsonSerializerOptions NonIndentedSerializerOptions = new()
@@ -36,7 +18,8 @@ public static class ConfigService
Converters =
{
new FloatConverter(), // this serializes floats with minimum required precision, e.g. 1.8000000012 -> 1.8
- new ByteArrayAsNormalArrayJsonConverter() // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
+ new ByteArrayAsNormalArrayJsonConverter(), // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
+ new TypeConverterJsonAdapterFactory(), // allows serialization using `[TypeConverter]` attributes
}
};
@@ -45,7 +28,7 @@ public static class ConfigService
WriteIndented = true
};
- internal static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;
+ public static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;
public static bool IsFromSameVersion(string filepath, out string msg)
{
diff --git a/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs b/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
index 3b2915f026f..6a017377237 100644
--- a/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/ToolManager.cs
@@ -4,7 +4,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
-using System.ComponentModel;
+using System.Text.Json;
using System.Windows.Forms;
using BizHawk.Client.Common;
@@ -401,21 +401,9 @@ private static void InstallCustomConfig(IToolForm tool, Dictionary : JsonConverter
diff --git a/src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs b/src/BizHawk.Emulation.Common/Json/ByteArrayAsNormalArrayJsonConverter.cs
similarity index 97%
rename from src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs
rename to src/BizHawk.Emulation.Common/Json/ByteArrayAsNormalArrayJsonConverter.cs
index 1ecae3cde73..0011fa9014f 100644
--- a/src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs
+++ b/src/BizHawk.Emulation.Common/Json/ByteArrayAsNormalArrayJsonConverter.cs
@@ -2,7 +2,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-namespace BizHawk.Emulation.Common
+namespace BizHawk.Emulation.Common.Json
{
/// based on this SO answer
public sealed class ByteArrayAsNormalArrayJsonConverter : JsonConverter
diff --git a/src/BizHawk.Emulation.Common/Json/FloatConverter.cs b/src/BizHawk.Emulation.Common/Json/FloatConverter.cs
new file mode 100644
index 00000000000..106d3e64b1a
--- /dev/null
+++ b/src/BizHawk.Emulation.Common/Json/FloatConverter.cs
@@ -0,0 +1,22 @@
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace BizHawk.Emulation.Common.Json
+{
+ public class FloatConverter : JsonConverter
+ {
+ public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetSingle();
+
+ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
+ {
+#if NETCOREAPP
+ writer.WriteNumberValue(value);
+#else
+ // gotta love the fact .net framework can't even format floats correctly by default
+ // can't use G7 here because it may be too low accuracy, and can't use G8 because it may be too high, see 1.0000003f or 0.8f
+ writer.WriteRawValue(value.ToString("R", NumberFormatInfo.InvariantInfo));
+#endif
+ }
+ }
+}
diff --git a/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs b/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
new file mode 100644
index 00000000000..75562c46d2d
--- /dev/null
+++ b/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
@@ -0,0 +1,52 @@
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+// System.Text.Json does not respect `[TypeConverter]`s by default, so it's required to use a JsonConverter that acts as an adapter
+// see also https://github.com/dotnet/runtime/issues/38812 or https://github.com/dotnet/runtime/issues/1761
+namespace BizHawk.Emulation.Common.Json
+{
+ ///
+ /// Adapter between and .
+ ///
+ public class TypeConverterJsonAdapter : JsonConverter
+ {
+ ///
+ public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var converter = TypeDescriptor.GetConverter(typeToConvert);
+ return (T)converter.ConvertFromString(reader.GetString())!;
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, T objectToWrite, JsonSerializerOptions options)
+ {
+ var converter = TypeDescriptor.GetConverter(objectToWrite!);
+ writer.WriteStringValue(converter.ConvertToString(objectToWrite!));
+ }
+
+ ///
+ public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttributes(inherit: true).Any();
+ }
+
+ ///
+ public class TypeConverterJsonAdapter : TypeConverterJsonAdapter
public bool Rotatable { get; set; }
-// [JsonConstructor]
+ [JsonConstructor]
private RollColumn()
{
Name = default!;
From f4b0238fd6cb6932815bded78257bfa94fea128f Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Sat, 1 Jun 2024 19:53:02 +0200
Subject: [PATCH 5/8] More deserialization fixes
---
.../CustomControls/InputRoll/RollColumn.cs | 7 ++++---
.../tools/TAStudio/TAStudioPalette.cs | 2 ++
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs
index df67836dede..ed72b528696 100644
--- a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs
+++ b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs
@@ -31,10 +31,11 @@ public class RollColumn
public bool Rotatable { get; set; }
[JsonConstructor]
- private RollColumn()
+ private RollColumn(string name, string text, ColumnType type)
{
- Name = default!;
- Text = default!;
+ Name = name;
+ Text = text;
+ Type = type;
}
public RollColumn(string name, int widthUnscaled, ColumnType type, string text)
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioPalette.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioPalette.cs
index 016c183830e..de18c1f2bc3 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioPalette.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioPalette.cs
@@ -1,4 +1,5 @@
using System.Drawing;
+using System.Text.Json.Serialization;
namespace BizHawk.Client.EmuHawk
{
@@ -49,6 +50,7 @@ public readonly struct TAStudioPalette
public readonly Color AnalogEdit_Col;
+ [JsonConstructor]
public TAStudioPalette(
// Color currentFrame_FrameCol,
Color currentFrame_InputLog,
From 49d6bbf20e2c4fc91ccf9b891ca90142f54a560d Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Thu, 6 Jun 2024 15:30:11 +0200
Subject: [PATCH 6/8] Fix tests and analyzers
---
src/BizHawk.Client.Common/RecentFiles.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/BizHawk.Client.Common/RecentFiles.cs b/src/BizHawk.Client.Common/RecentFiles.cs
index e0c092070f4..2b459ef4f37 100644
--- a/src/BizHawk.Client.Common/RecentFiles.cs
+++ b/src/BizHawk.Client.Common/RecentFiles.cs
@@ -6,6 +6,8 @@ namespace BizHawk.Client.Common
{
public class RecentFiles
{
+ [JsonInclude]
+ [JsonPropertyOrder(-1)]
// ReSharper disable once FieldCanBeMadeReadOnly.Local
private List recentlist;
From 77cda915d913c50a41a28125f9d6407c2a951e25 Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Fri, 7 Jun 2024 19:45:08 +0200
Subject: [PATCH 7/8] Add serialization tests for the added JsonConverters
---
.../config/SerializationStabilityTests.cs | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
index 2be0cd7ad58..51228e41e8d 100644
--- a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
+++ b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Drawing;
+using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using BizHawk.Client.Common;
using BizHawk.Common;
+using BizHawk.Emulation.Common.Json;
namespace BizHawk.Tests.Client.Common.config
{
@@ -95,5 +97,75 @@ public void TestRoundTripSerialization()
Assert.AreEqual(s, Ser(Deser(s, type)), $"{type} failed serialization round-trip");
}
}
+
+ [TestMethod]
+ [DataRow("0.8")]
+ [DataRow("1.00000036")]
+ [DataRow("1.8")]
+ public void TestRoundTripSerializationFloatConverter(string floatValue)
+ {
+ float deserialized = JsonSerializer.Deserialize(floatValue, ConfigService.SerializerOptions);
+ string serialized = JsonSerializer.Serialize(deserialized, ConfigService.SerializerOptions);
+ Assert.AreEqual(floatValue, serialized);
+ }
+
+ [TestMethod]
+ [DataRow("[1,2,3]")]
+ [DataRow("[]")]
+ [DataRow("null")]
+ [DataRow("[255,0,127,128,1]")]
+ public void TestRoundTripSerializationByteArrayConverter(string byteArrayValue)
+ {
+ byte[]? deserialized = JsonSerializer.Deserialize(byteArrayValue, ConfigService.SerializerOptions);
+ string serialized = JsonSerializer.Serialize(deserialized, ConfigService.SerializerOptions);
+ Assert.AreEqual(byteArrayValue, serialized);
+ }
+
+ [TestMethod]
+ public void TestSerializationTypeConverter()
+ {
+ var color = Color.FromArgb(200, 255, 13, 42);
+ string serialized = JsonSerializer.Serialize(color, ConfigService.SerializerOptions);
+ Assert.AreEqual("\"200; 255; 13; 42\"", serialized);
+
+ var newColor = JsonSerializer.Deserialize(serialized, ConfigService.SerializerOptions);
+ Assert.AreEqual(color, newColor);
+ }
+
+ private static bool Equals(T[,] array1, T[,] array2)
+ {
+ return array1.Rank == array2.Rank
+ && Enumerable.Range(0, array1.Rank).All(dimension => array1.GetLength(dimension) == array2.GetLength(dimension))
+ && array1.Cast().SequenceEqual(array2.Cast());
+ }
+
+ [TestMethod]
+ public void TestSerialization2DArrayConverter()
+ {
+ var options = new JsonSerializerOptions
+ {
+ Converters = { new Array2DJsonConverter() }
+ };
+ var optionsWithByteArrayConverter = new JsonSerializerOptions
+ {
+ Converters = { new Array2DJsonConverter(), new ByteArrayAsNormalArrayJsonConverter() }
+ };
+
+ byte[,] testByteArray =
+ {
+ { 1, 2, 3 },
+ { 255, 0, 128 }
+ };
+
+ string serialized = JsonSerializer.Serialize(testByteArray, options);
+ Assert.AreEqual("[\"AQID\",\"/wCA\"]", serialized);
+ byte[,] deserialized = JsonSerializer.Deserialize(serialized, options)!;
+ Assert.IsTrue(Equals(testByteArray, deserialized));
+
+ string serialized2 = JsonSerializer.Serialize(testByteArray, optionsWithByteArrayConverter);
+ Assert.AreEqual("[[1,2,3],[255,0,128]]", serialized2);
+ byte[,] deserialized2 = JsonSerializer.Deserialize(serialized2, optionsWithByteArrayConverter)!;
+ Assert.IsTrue(Equals(testByteArray, deserialized2));
+ }
}
}
From ad4ad58540f631d6eae867d5d70d22da5dd72af8 Mon Sep 17 00:00:00 2001
From: Morilli <35152647+Morilli@users.noreply.github.com>
Date: Fri, 7 Jun 2024 20:11:05 +0200
Subject: [PATCH 8/8] Fix typeconverter culture handling
I guess tests are actually useful?
---
.../Json/TypeConverterJsonAdapter.cs | 7 ++++---
.../Client.Common/config/SerializationStabilityTests.cs | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs b/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
index 75562c46d2d..21b10cb8522 100644
--- a/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
+++ b/src/BizHawk.Emulation.Common/Json/TypeConverterJsonAdapter.cs
@@ -1,4 +1,5 @@
-using System.ComponentModel;
+using System.ComponentModel;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.Json;
@@ -17,14 +18,14 @@ public class TypeConverterJsonAdapter : JsonConverter
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var converter = TypeDescriptor.GetConverter(typeToConvert);
- return (T)converter.ConvertFromString(reader.GetString())!;
+ return (T)converter.ConvertFromString(null!, CultureInfo.InvariantCulture, reader.GetString())!;
}
///
public override void Write(Utf8JsonWriter writer, T objectToWrite, JsonSerializerOptions options)
{
var converter = TypeDescriptor.GetConverter(objectToWrite!);
- writer.WriteStringValue(converter.ConvertToString(objectToWrite!));
+ writer.WriteStringValue(converter.ConvertToString(null!, CultureInfo.InvariantCulture, objectToWrite!));
}
///
diff --git a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
index 51228e41e8d..c4104245a92 100644
--- a/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
+++ b/src/BizHawk.Tests/Client.Common/config/SerializationStabilityTests.cs
@@ -126,7 +126,7 @@ public void TestSerializationTypeConverter()
{
var color = Color.FromArgb(200, 255, 13, 42);
string serialized = JsonSerializer.Serialize(color, ConfigService.SerializerOptions);
- Assert.AreEqual("\"200; 255; 13; 42\"", serialized);
+ Assert.AreEqual("\"200, 255, 13, 42\"", serialized);
var newColor = JsonSerializer.Deserialize(serialized, ConfigService.SerializerOptions);
Assert.AreEqual(color, newColor);