mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-04 00:49:45 +08:00
Add per-game launch overrides (log level, import-trace limit, strict dynlib resolution, log-to-file, and SHARPEMU_* environment toggles) with three-tier resolution (per-game override -> global preference -> built-in default), stored one file per game at user/custom_configs/<titleId>.json. Editable from a new "Game settings..." context-menu dialog. Introduce a shared SettingRow control and adopt it across the Options page and the per-game dialog so the two read as one app. Fully localized (reusing the existing Options.* keys), with the actions pinned in the dialog footer.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace SharpEmu.GUI;
|
||||
|
||||
public sealed class PerGameSettings
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
public string? LogLevel { get; set; }
|
||||
|
||||
public int? ImportTraceLimit { get; set; }
|
||||
|
||||
public bool? StrictDynlibResolution { get; set; }
|
||||
|
||||
public bool? LogToFile { get; set; }
|
||||
|
||||
public List<string>? EnvironmentToggles { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsEmpty =>
|
||||
LogLevel is null &&
|
||||
ImportTraceLimit is null &&
|
||||
StrictDynlibResolution is null &&
|
||||
LogToFile is null &&
|
||||
EnvironmentToggles is null;
|
||||
|
||||
public static string DirectoryPath =>
|
||||
Path.Combine(AppContext.BaseDirectory, "user", "custom_configs");
|
||||
|
||||
public static string PathFor(string titleId) =>
|
||||
Path.Combine(DirectoryPath, SanitizeTitleId(titleId) + ".json");
|
||||
|
||||
public static PerGameSettings? Load(string? titleId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(titleId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var path = PathFor(titleId);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return JsonSerializer.Deserialize<PerGameSettings>(File.ReadAllText(path), SerializerOptions);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Save(string titleId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(titleId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var path = PathFor(titleId);
|
||||
if (IsEmpty)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(DirectoryPath);
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(this, SerializerOptions));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static string SanitizeTitleId(string titleId)
|
||||
{
|
||||
var trimmed = titleId.Trim();
|
||||
foreach (var invalid in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
trimmed = trimmed.Replace(invalid, '_');
|
||||
}
|
||||
|
||||
return trimmed.Length == 0 ? "UNKNOWN" : trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record EffectiveLaunchSettings(
|
||||
string LogLevel,
|
||||
int ImportTraceLimit,
|
||||
bool StrictDynlibResolution,
|
||||
bool LogToFile,
|
||||
IReadOnlyList<string> EnvironmentToggles)
|
||||
{
|
||||
public static EffectiveLaunchSettings Resolve(GuiSettings global, PerGameSettings? perGame) => new(
|
||||
perGame?.LogLevel ?? global.LogLevel,
|
||||
perGame?.ImportTraceLimit ?? global.ImportTraceLimit,
|
||||
perGame?.StrictDynlibResolution ?? global.StrictDynlibResolution,
|
||||
perGame?.LogToFile ?? global.LogToFile,
|
||||
perGame?.EnvironmentToggles ?? global.EnvironmentToggles);
|
||||
}
|
||||
Reference in New Issue
Block a user