diff --git a/src/SharpEmu.GUI/PerGameSettings.cs b/src/SharpEmu.GUI/PerGameSettings.cs index 878d6205..7023d778 100644 --- a/src/SharpEmu.GUI/PerGameSettings.cs +++ b/src/SharpEmu.GUI/PerGameSettings.cs @@ -49,7 +49,7 @@ public sealed class PerGameSettings var path = PathFor(titleId); if (File.Exists(path)) { - return JsonSerializer.Deserialize(File.ReadAllText(path), SerializerOptions); + return NormalizeFromJson(File.ReadAllText(path)); } } catch (Exception) @@ -59,6 +59,18 @@ public sealed class PerGameSettings return null; } + // A null list inherits global settings; only entries in a present list are sanitized. + internal static PerGameSettings? NormalizeFromJson(string json) + { + var settings = JsonSerializer.Deserialize(json, SerializerOptions); + if (settings?.EnvironmentToggles is { } toggles) + { + settings.EnvironmentToggles = toggles.Where(entry => !string.IsNullOrEmpty(entry)).ToList(); + } + + return settings; + } + public void Save(string titleId) { if (string.IsNullOrWhiteSpace(titleId)) diff --git a/tests/SharpEmu.Libs.Tests/GUI/PerGameSettingsTests.cs b/tests/SharpEmu.Libs.Tests/GUI/PerGameSettingsTests.cs new file mode 100644 index 00000000..bd905486 --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/GUI/PerGameSettingsTests.cs @@ -0,0 +1,60 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.GUI; +using Xunit; + +namespace SharpEmu.Libs.Tests.GUI; + +public sealed class PerGameSettingsTests +{ + // Invalid entries must not reach Environment.SetEnvironmentVariable. + [Fact] + public void NormalizeFromJson_NullOrEmptyToggleEntries_AreFilteredOut() + { + const string json = """ + { "EnvironmentToggles": [null, "SHARPEMU_TRACE", ""] } + """; + + var settings = PerGameSettings.NormalizeFromJson(json); + + Assert.NotNull(settings); + Assert.Equal(["SHARPEMU_TRACE"], settings.EnvironmentToggles); + } + + // A null list means that the global setting should be inherited. + [Fact] + public void NormalizeFromJson_NullToggleList_StaysNull() + { + const string json = """{ "EnvironmentToggles": null }"""; + + var settings = PerGameSettings.NormalizeFromJson(json); + + Assert.NotNull(settings); + Assert.Null(settings.EnvironmentToggles); + } + + [Fact] + public void NormalizeFromJson_EmptyToggleList_StaysEmpty() + { + const string json = """{ "EnvironmentToggles": [] }"""; + + var settings = PerGameSettings.NormalizeFromJson(json); + + Assert.NotNull(settings); + Assert.Empty(Assert.IsType>(settings.EnvironmentToggles)); + } + + [Fact] + public void NormalizeFromJson_ValidToggles_ArePreserved() + { + const string json = """ + { "EnvironmentToggles": ["SHARPEMU_TRACE", "SHARPEMU_NO_JIT"] } + """; + + var settings = PerGameSettings.NormalizeFromJson(json); + + Assert.NotNull(settings); + Assert.Equal(["SHARPEMU_TRACE", "SHARPEMU_NO_JIT"], settings.EnvironmentToggles); + } +}