Compare commits

...

1 Commits

Author SHA1 Message Date
ParantezTech 9b8ccf71b1 [GUI/CLI] Add support for console settings and language selection 2026-09-25 23:48:02 +03:00
27 changed files with 324 additions and 7 deletions

View File

@ -1000,7 +1000,7 @@ internal static partial class Program
private static void PrintUsage()
{
Log.Info("Usage: SharpEmu.CLI [--strict] [--trace-imports[=N]] [--cpu-engine=<native>] [--log-level=<level>] [--log-file[=<path>]] [--window-mode=<windowed|borderless|exclusive>] [--resolution=<WIDTHxHEIGHT>] [--display=<N>] [--refresh-rate=<HZ>] [--scaling=<fit|cover|stretch|integer>] [--vsync=<on|off>] [--hdr=<auto|on|off>] [--overlay=<on|off>] [--overlay-mode=<full|minimal|titlebar>] [--overlay-corner=<topleft|topright|bottomright|bottomleft>] [--debug-server[=host:port]] <path-to-eboot.bin>");
Log.Info("Usage: SharpEmu.CLI [--strict] [--trace-imports[=N]] [--cpu-engine=<native>] [--console=<ps5>] [--console-language=<language>] [--log-level=<level>] [--log-file[=<path>]] [--window-mode=<windowed|borderless|exclusive>] [--resolution=<WIDTHxHEIGHT>] [--display=<N>] [--refresh-rate=<HZ>] [--scaling=<fit|cover|stretch|integer>] [--vsync=<on|off>] [--hdr=<auto|on|off>] [--overlay=<on|off>] [--overlay-mode=<full|minimal|titlebar>] [--overlay-corner=<topleft|topright|bottomright|bottomleft>] [--debug-server[=host:port]] <path-to-eboot.bin>");
Log.Info(@"Example: SharpEmu.CLI --cpu-engine=native --trace-imports=64 --log-level=debug --log-file ""E:\Games\...\eboot.bin""");
Log.Info("Debug server: --debug-server starts a live debug listener (default 127.0.0.1:5714); connect with SharpEmu.DebugClient.");
}
@ -1062,6 +1062,7 @@ internal static partial class Program
var strictDynlibResolution = false;
var importTraceLimit = 0;
var cpuEngine = CpuExecutionEngine.NativeOnly;
var systemLanguage = 1;
HostWindowMode? windowModeOverride = null;
HostScalingMode? scalingModeOverride = null;
int? windowWidthOverride = null;
@ -1080,6 +1081,26 @@ internal static partial class Program
for (var i = 0; i < args.Length; i++)
{
var argument = args[i];
if (TrySplitOption(argument, "--console", out var consoleText))
{
if (!string.Equals(consoleText, "ps5", StringComparison.OrdinalIgnoreCase))
{
ebootPath = string.Empty;
runtimeOptions = default;
return false;
}
continue;
}
if (TrySplitOption(argument, "--console-language", out var consoleLanguageText))
{
if (!TryParseSystemLanguage(consoleLanguageText, out systemLanguage))
{
ebootPath = string.Empty;
runtimeOptions = default;
return false;
}
continue;
}
if (TrySplitOption(argument, "--window-mode", out var windowModeText))
{
if (!TryParseWindowMode(windowModeText, out var windowMode))
@ -1351,6 +1372,7 @@ internal static partial class Program
runtimeOptions = new SharpEmuRuntimeOptions
{
CpuEngine = cpuEngine,
SystemLanguage = systemLanguage,
StrictDynlibResolution = strictDynlibResolution,
ImportTraceLimit = importTraceLimit,
};
@ -1445,6 +1467,46 @@ internal static partial class Program
return Enum.IsDefined(mode);
}
private static bool TryParseSystemLanguage(string value, out int language)
{
language = value.ToLowerInvariant() switch
{
"japanese" => 0,
"englishus" => 1,
"french" => 2,
"spanishspain" => 3,
"german" => 4,
"italian" => 5,
"dutch" => 6,
"portugueseportugal" => 7,
"russian" => 8,
"korean" => 9,
"chinesetraditional" => 10,
"chinesesimplified" => 11,
"finnish" => 12,
"swedish" => 13,
"danish" => 14,
"norwegian" => 15,
"polish" => 16,
"portuguesebrazil" => 17,
"englishuk" => 18,
"turkish" => 19,
"spanishlatinamerica" => 20,
"arabic" => 21,
"frenchcanada" => 22,
"czech" => 23,
"hungarian" => 24,
"greek" => 25,
"romanian" => 26,
"thai" => 27,
"vietnamese" => 28,
"indonesian" => 29,
"ukrainian" => 30,
_ => -1,
};
return language >= 0;
}
private static bool TryParseScalingMode(string value, out HostScalingMode mode)
{
mode = value.ToLowerInvariant() switch

View File

@ -42,6 +42,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
private readonly CpuExecutionOptions _cpuExecutionOptions;
private readonly IFileSystem _fileSystem;
private readonly GuestGpuMemory? _gpuMemory;
private readonly int _systemLanguage;
private readonly object _dynamicModuleGate = new();
private bool _disposed;
@ -63,7 +64,8 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
ISymbolCatalog? symbolCatalog = null,
CpuExecutionOptions cpuExecutionOptions = default,
IFileSystem? fileSystem = null,
GuestGpuMemory? gpuMemory = null)
GuestGpuMemory? gpuMemory = null,
int systemLanguage = 1)
{
_gpuMemory = gpuMemory;
_selfLoader = selfLoader ?? throw new ArgumentNullException(nameof(selfLoader));
@ -79,6 +81,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
DebugHook = cpuExecutionOptions.DebugHook,
};
_fileSystem = fileSystem ?? new PhysicalFileSystem();
_systemLanguage = systemLanguage;
}
public static ISharpEmuRuntime CreateDefault(SharpEmuRuntimeOptions options = default)
@ -112,7 +115,8 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
Aerolib.Instance,
cpuExecutionOptions,
fileSystem,
gpuMemory);
gpuMemory,
options.SystemLanguage ?? 1);
}
public SelfImage LoadImage(string ebootPath)
@ -160,7 +164,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
VideoOutExports.ConfigureApplicationInfo(image.Title, image.TitleId, image.Version);
KernelMemoryCompatExports.ConfigureApplicationInfo(image.TitleId);
SaveDataExports.ConfigureApplicationInfo(image.TitleId);
SystemServiceExports.ConfigureApplicationInfo(image.TitleId);
SystemServiceExports.ConfigureApplicationInfo(image.TitleId, _systemLanguage);
_ = RegisterLoadedModule(normalizedEbootPath, image, isMain: true, isSystemModule: false);
KernelRuntimeCompatExports.ConfigureProcessProcParamAddress(image.ProcParamAddress);
Console.Error.WriteLine($"[RUNTIME] Entry: 0x{image.EntryPoint:X16}");

View File

@ -10,6 +10,8 @@ public readonly struct SharpEmuRuntimeOptions
{
public CpuExecutionEngine CpuEngine { get; init; }
public int? SystemLanguage { get; init; }
public bool StrictDynlibResolution { get; init; }
public int ImportTraceLimit { get; init; }

View File

@ -54,6 +54,10 @@ public sealed class GuiSettings
/// <summary>UI language, matching a file code under Languages/ (e.g. "en", "tr").</summary>
public string Language { get; set; } = "en";
public string ConsoleType { get; set; } = "PS5";
public string ConsoleLanguage { get; set; } = "EnglishUS";
/// <summary>Default text-entry profile exposed to games.</summary>
public string DefaultProfile { get; set; } = "Sharp";
@ -144,6 +148,40 @@ public sealed class GuiSettings
legacyProfile is { Length: 2 } ? legacyProfile[1] : settings.DefaultProfile);
settings.DiscordClientId ??= "1525606762248540221";
settings.LibraryLayout = NormalizeChoice(settings.LibraryLayout, "Carousel", "Grid");
settings.ConsoleType = NormalizeChoice(settings.ConsoleType, "PS5");
settings.ConsoleLanguage = NormalizeChoice(
settings.ConsoleLanguage,
"EnglishUS",
"Japanese",
"French",
"SpanishSpain",
"German",
"Italian",
"Dutch",
"PortuguesePortugal",
"Russian",
"Korean",
"ChineseTraditional",
"ChineseSimplified",
"Finnish",
"Swedish",
"Danish",
"Norwegian",
"Polish",
"PortugueseBrazil",
"EnglishUK",
"Turkish",
"SpanishLatinAmerica",
"Arabic",
"FrenchCanada",
"Czech",
"Hungarian",
"Greek",
"Romanian",
"Thai",
"Vietnamese",
"Indonesian",
"Ukrainian");
settings.WindowMode = NormalizeChoice(settings.WindowMode, "Windowed", "Borderless", "Exclusive");
settings.Resolution = NormalizeResolution(settings.Resolution);
settings.ScalingMode = NormalizeChoice(settings.ScalingMode, "Fit", "Cover", "Stretch", "Integer");

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "فشل التحديث المنزَّل في اجتياز تحقق SHA-256.",
"Updater.Status.Unsupported": "يتطلب التحديث التلقائي إصدار x64 لنظام Windows أو Linux أو macOS.",
"Options.Graphics": "الرسومات",
"Options.Console.Type.Label": "وحدة التحكم",
"Options.Console.Type.Desc": "طراز وحدة التحكم الذي يتم إبلاغ الألعاب به.",
"Options.Console.Language.Label": "لغة وحدة التحكم",
"Options.Console.Language.Desc": "لغة النظام التي يتم إبلاغ الألعاب بها.",
"Options.Section.Rendering": "التصيير",
"Options.Section.Display": "العرض",
"Options.WindowMode.Label": "وضع النافذة",

View File

@ -59,6 +59,11 @@
"Options.Section.Display": "Ekran",
"Options.Graphics": "Qrafika",
"Options.Console.Type.Label": "Konsol",
"Options.Console.Type.Desc": "Oyunlara bildirilən konsol modeli.",
"Options.Console.Language.Label": "Konsol dili",
"Options.Console.Language.Desc": "Oyunlara bildirilən sistem dili.",
"Options.RenderResolution.Label": "Daxili təsvir ölçüsü",
"Options.RenderResolution.Desc": "Ekrandan kənar təsvir hədəflərini ilkin ölçüdən aşağı ölçüdə yaradıb göstərərkən böyüdün. Aşağı dəyərlər təsvir keyfiyyətini azaldaraq GPU yükünü yüngülləşdirir; növbəti işə salınmada qüvvəyə minir.",
"Options.RenderResolution.Native": "100% (ilkin)",

View File

@ -206,6 +206,11 @@
"Updater.Status.ChecksumFailed": "A atualização baixada falhou na verificação SHA-256.",
"Updater.Status.Unsupported": "A atualização automática requer um build x64 para Windows, Linux ou macOS.",
"Options.Graphics": "Gráficos",
"Options.Console.Type.Label": "Console",
"Options.Console.Type.Desc": "Modelo de console informado aos jogos.",
"Options.Console.Language.Label": "Idioma do console",
"Options.Console.Language.Desc": "Idioma do sistema informado aos jogos.",
"Options.Section.Rendering": "RENDERIZAÇÃO",
"Options.Section.Display": "TELA",
"Options.WindowMode.Label": "Modo de janela",

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "Das heruntergeladene Update hat die SHA-256-Prüfung nicht bestanden.",
"Updater.Status.Unsupported": "Automatische Updates erfordern einen x64-Build für Windows, Linux oder macOS.",
"Options.Graphics": "Grafik",
"Options.Console.Type.Label": "Konsole",
"Options.Console.Type.Desc": "Konsolenmodell, das Spielen gemeldet wird.",
"Options.Console.Language.Label": "Konsolensprache",
"Options.Console.Language.Desc": "Systemsprache, die Spielen gemeldet wird.",
"Options.Section.Rendering": "DARSTELLUNG",
"Options.Section.Display": "ANZEIGE",
"Options.WindowMode.Label": "Fenstermodus",

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "Den downloadede opdatering bestod ikke SHA-256-verifikationen.",
"Updater.Status.Unsupported": "Automatisk opdatering kræver et x64-build til Windows, Linux eller macOS.",
"Options.Graphics": "Grafik",
"Options.Console.Type.Label": "Konsol",
"Options.Console.Type.Desc": "Konsolmodel der rapporteres til spil.",
"Options.Console.Language.Label": "Konsolsprog",
"Options.Console.Language.Desc": "Systemsprog der rapporteres til spil.",
"Options.Section.Rendering": "GENGIVELSE",
"Options.Section.Display": "SKÆRM",
"Options.WindowMode.Label": "Vinduestilstand",

View File

@ -73,6 +73,11 @@
"Options.Section.Display": "KUVA",
"Options.Graphics": "Graafika",
"Options.Console.Type.Label": "Konsool",
"Options.Console.Type.Desc": "Mängudele teatatud konsoolimudel.",
"Options.Console.Language.Label": "Konsooli keel",
"Options.Console.Language.Desc": "Mängudele teatatud süsteemikeel.",
"Options.WindowMode.Label": "Aknarežiim",
"Options.WindowMode.Desc": "Tavaline aken, äärteta töölauavaade või eksklusiivne täisekraan.",
"Options.WindowMode.Windowed": "Aken",

View File

@ -73,6 +73,11 @@
"Options.Section.Display": "DISPLAY",
"Options.Graphics": "Graphics",
"Options.Console.Type.Label": "Console",
"Options.Console.Type.Desc": "Console model exposed to games.",
"Options.Console.Language.Label": "Console language",
"Options.Console.Language.Desc": "System language reported to games.",
"Options.WindowMode.Label": "Window mode",
"Options.WindowMode.Desc": "Regular window, desktop borderless, or exclusive fullscreen.",
"Options.WindowMode.Windowed": "Windowed",

View File

@ -206,6 +206,11 @@
"Updater.Status.ChecksumFailed": "La actualización descargada no superó la verificación SHA-256.",
"Updater.Status.Unsupported": "La actualización automática requiere un build x64 de Windows, Linux o macOS.",
"Options.Graphics": "Gráficos",
"Options.Console.Type.Label": "Consola",
"Options.Console.Type.Desc": "Modelo de consola que se comunica a los juegos.",
"Options.Console.Language.Label": "Idioma de la consola",
"Options.Console.Language.Desc": "Idioma del sistema que se comunica a los juegos.",
"Options.Section.Rendering": "RENDERIZADO",
"Options.Section.Display": "PANTALLA",
"Options.WindowMode.Label": "Modo de ventana",

View File

@ -206,6 +206,11 @@
"Updater.Status.ChecksumFailed": "La mise à jour téléchargée a échoué à la vérification SHA-256.",
"Updater.Status.Unsupported": "La mise à jour automatique nécessite un build x64 pour Windows, Linux ou macOS.",
"Options.Graphics": "Graphismes",
"Options.Console.Type.Label": "Console",
"Options.Console.Type.Desc": "Modèle de console signalé aux jeux.",
"Options.Console.Language.Label": "Langue de la console",
"Options.Console.Language.Desc": "Langue système signalée aux jeux.",
"Options.Section.Rendering": "RENDU",
"Options.Section.Display": "AFFICHAGE",
"Options.WindowMode.Label": "Mode fenêtre",

View File

@ -206,6 +206,11 @@
"Updater.Status.ChecksumFailed": "A letöltött frissítés nem ment át az SHA-256-ellenőrzésen.",
"Updater.Status.Unsupported": "Az automatikus frissítéshez Windows, Linux vagy macOS x64 build szükséges.",
"Options.Graphics": "Grafika",
"Options.Console.Type.Label": "Konzol",
"Options.Console.Type.Desc": "A játékoknak jelentett konzolmodell.",
"Options.Console.Language.Label": "Konzol nyelve",
"Options.Console.Language.Desc": "A játékoknak jelentett rendszer nyelve.",
"Options.Section.Rendering": "RENDERELÉS",
"Options.Section.Display": "KIJELZŐ",
"Options.WindowMode.Label": "Ablakmód",

View File

@ -210,6 +210,11 @@
"Updater.Status.ChecksumFailed": "L'aggiornamento scaricato non ha superato la verifica SHA-256.",
"Updater.Status.Unsupported": "L'aggiornamento automatico richiede un build x64 per Windows, Linux o macOS.",
"Options.Graphics": "Grafica",
"Options.Console.Type.Label": "Console",
"Options.Console.Type.Desc": "Modello di console comunicato ai giochi.",
"Options.Console.Language.Label": "Lingua della console",
"Options.Console.Language.Desc": "Lingua di sistema comunicata ai giochi.",
"Options.Section.Rendering": "RENDERING",
"Options.Section.Display": "SCHERMO",
"Options.WindowMode.Label": "Modalità finestra",

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "ダウンロードしたアップデートはSHA-256検証に失敗しました。",
"Updater.Status.Unsupported": "自動アップデートにはWindows、Linux、またはmacOSのx64ビルドが必要です。",
"Options.Graphics": "グラフィックス",
"Options.Console.Type.Label": "コンソール",
"Options.Console.Type.Desc": "ゲームに通知するコンソールモデル。",
"Options.Console.Language.Label": "コンソールの言語",
"Options.Console.Language.Desc": "ゲームに通知するシステム言語。",
"Options.Section.Rendering": "レンダリング",
"Options.Section.Display": "ディスプレイ",
"Options.WindowMode.Label": "ウィンドウモード",

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "다운로드한 업데이트가 SHA-256 검증에 실패했습니다.",
"Updater.Status.Unsupported": "자동 업데이트에는 Windows, Linux 또는 macOS x64 빌드가 필요합니다.",
"Options.Graphics": "그래픽",
"Options.Console.Type.Label": "콘솔",
"Options.Console.Type.Desc": "게임에 보고할 콘솔 모델입니다.",
"Options.Console.Language.Label": "콘솔 언어",
"Options.Console.Language.Desc": "게임에 보고할 시스템 언어입니다.",
"Options.Section.Rendering": "렌더링",
"Options.Section.Display": "디스플레이",
"Options.WindowMode.Label": "창 모드",

View File

@ -205,6 +205,11 @@
"Updater.Status.ChecksumFailed": "De gedownloade update is niet door de SHA-256-verificatie gekomen.",
"Updater.Status.Unsupported": "Automatisch updaten vereist een x64-build voor Windows, Linux of macOS.",
"Options.Graphics": "Grafisch",
"Options.Console.Type.Label": "Console",
"Options.Console.Type.Desc": "Consolemodel dat aan games wordt gemeld.",
"Options.Console.Language.Label": "Consoletaal",
"Options.Console.Language.Desc": "Systeemtaal die aan games wordt gemeld.",
"Options.Section.Rendering": "RENDERING",
"Options.Section.Display": "BEELDSCHERM",
"Options.WindowMode.Label": "Venstermodus",

View File

@ -73,6 +73,11 @@
"Options.Section.Display": "WYŚWIETLANIE",
"Options.Graphics": "Grafika",
"Options.Console.Type.Label": "Konsola",
"Options.Console.Type.Desc": "Model konsoli zgłaszany grom.",
"Options.Console.Language.Label": "Język konsoli",
"Options.Console.Language.Desc": "Język systemu zgłaszany grom.",
"Options.WindowMode.Label": "Tryb okna",
"Options.WindowMode.Desc": "Zwykłe okno, bezramkowe na pulpicie lub pełny ekran wyłączny.",
"Options.WindowMode.Windowed": "W oknie",

View File

@ -206,6 +206,11 @@
"Updater.Status.ChecksumFailed": "A atualização transferida falhou a verificação SHA-256.",
"Updater.Status.Unsupported": "A atualização automática requer um build x64 para Windows, Linux ou macOS.",
"Options.Graphics": "Gráficos",
"Options.Console.Type.Label": "Consola",
"Options.Console.Type.Desc": "Modelo de consola comunicado aos jogos.",
"Options.Console.Language.Label": "Idioma da consola",
"Options.Console.Language.Desc": "Idioma do sistema comunicado aos jogos.",
"Options.Section.Rendering": "RENDERIZAÇÃO",
"Options.Section.Display": "ECRÃ",
"Options.WindowMode.Label": "Modo de janela",

View File

@ -69,6 +69,11 @@
"Options.Section.Display": "ЭКРАН",
"Options.Graphics": "Графика",
"Options.Console.Type.Label": "Консоль",
"Options.Console.Type.Desc": "Модель консоли, сообщаемая играм.",
"Options.Console.Language.Label": "Язык консоли",
"Options.Console.Language.Desc": "Системный язык, сообщаемый играм.",
"Options.WindowMode.Label": "Режим окна",
"Options.WindowMode.Desc": "Обычное окно, безрамочный режим рабочего стола или эксклюзивный полноэкранный режим.",
"Options.WindowMode.Windowed": "Оконный",

View File

@ -54,6 +54,11 @@
"Options.Section.Display": "GÖRÜNTÜ",
"Options.Graphics": "Grafik",
"Options.Console.Type.Label": "Konsol",
"Options.Console.Type.Desc": "Oyunlara bildirilen konsol modeli.",
"Options.Console.Language.Label": "Konsol dili",
"Options.Console.Language.Desc": "Oyunlara bildirilen sistem dili.",
"Options.WindowMode.Label": "Pencere modu",
"Options.WindowMode.Desc": "Normal pencere, kenarlıksız masaüstü veya özel tam ekran.",
"Options.Resolution.Label": "Çözünürlük",

View File

@ -1185,6 +1185,28 @@ SPDX-License-Identifier: GPL-2.0-or-later
VerticalAlignment="Center" />
</local:SettingRow>
<local:SettingRow x:Name="ConsoleTypeRow"
Classes="optionRow"
Label="{Binding [Options.Console.Type.Label], Source={x:Static local:Localization.Instance}}"
Description="{Binding [Options.Console.Type.Desc], Source={x:Static local:Localization.Instance}}">
<ComboBox x:Name="ConsoleTypeBox"
Classes="optionValue"
ItemTemplate="{StaticResource LocalizedChoiceTemplate}"
HorizontalContentAlignment="Left"
VerticalAlignment="Center" />
</local:SettingRow>
<local:SettingRow x:Name="ConsoleLanguageRow"
Classes="optionRow"
Label="{Binding [Options.Console.Language.Label], Source={x:Static local:Localization.Instance}}"
Description="{Binding [Options.Console.Language.Desc], Source={x:Static local:Localization.Instance}}">
<ComboBox x:Name="ConsoleLanguageBox"
Classes="optionValue"
ItemTemplate="{StaticResource LocalizedChoiceTemplate}"
HorizontalContentAlignment="Left"
VerticalAlignment="Center" />
</local:SettingRow>
<local:SettingRow x:Name="StrictRow"
Classes="optionRow"
Label="{Binding [Options.Strict.Label], Source={x:Static local:Localization.Instance}}"

View File

@ -48,6 +48,44 @@ public partial class MainWindow : Window
[
LocalizedChoice.FromKey("Native", "Options.CpuEngine.Native"),
];
private readonly LocalizedChoice[] _consoleTypeChoices =
[
LocalizedChoice.Literal("PS5", "PlayStation 5"),
];
private readonly LocalizedChoice[] _consoleLanguageChoices =
[
LocalizedChoice.Literal("Japanese", "日本語"),
LocalizedChoice.Literal("EnglishUS", "English (United States)"),
LocalizedChoice.Literal("French", "Français"),
LocalizedChoice.Literal("SpanishSpain", "Español (España)"),
LocalizedChoice.Literal("German", "Deutsch"),
LocalizedChoice.Literal("Italian", "Italiano"),
LocalizedChoice.Literal("Dutch", "Nederlands"),
LocalizedChoice.Literal("PortuguesePortugal", "Português (Portugal)"),
LocalizedChoice.Literal("Russian", "Русский"),
LocalizedChoice.Literal("Korean", "한국어"),
LocalizedChoice.Literal("ChineseTraditional", "繁體中文"),
LocalizedChoice.Literal("ChineseSimplified", "简体中文"),
LocalizedChoice.Literal("Finnish", "Suomi"),
LocalizedChoice.Literal("Swedish", "Svenska"),
LocalizedChoice.Literal("Danish", "Dansk"),
LocalizedChoice.Literal("Norwegian", "Norsk"),
LocalizedChoice.Literal("Polish", "Polski"),
LocalizedChoice.Literal("PortugueseBrazil", "Português (Brasil)"),
LocalizedChoice.Literal("EnglishUK", "English (United Kingdom)"),
LocalizedChoice.Literal("Turkish", "Türkçe"),
LocalizedChoice.Literal("SpanishLatinAmerica", "Español (Latinoamérica)"),
LocalizedChoice.Literal("Arabic", "العربية"),
LocalizedChoice.Literal("FrenchCanada", "Français (Canada)"),
LocalizedChoice.Literal("Czech", "Čeština"),
LocalizedChoice.Literal("Hungarian", "Magyar"),
LocalizedChoice.Literal("Greek", "Ελληνικά"),
LocalizedChoice.Literal("Romanian", "Română"),
LocalizedChoice.Literal("Thai", "ไทย"),
LocalizedChoice.Literal("Vietnamese", "Tiếng Việt"),
LocalizedChoice.Literal("Indonesian", "Bahasa Indonesia"),
LocalizedChoice.Literal("Ukrainian", "Українська"),
];
private readonly LocalizedChoice[] _logLevelChoices =
[
LocalizedChoice.FromKey("Trace", "Options.LogLevel.Trace"),
@ -161,6 +199,8 @@ public partial class MainWindow : Window
string EbootPath,
string DisplayName,
string? TitleId,
string ConsoleType,
string ConsoleLanguage,
EffectiveLaunchSettings Settings,
SharpEmuRuntimeOptions RuntimeOptions);
@ -275,6 +315,10 @@ public partial class MainWindow : Window
};
AutoUpdateToggle.IsCheckedChanged += (_, _) =>
_settings.CheckForUpdatesOnStartup = AutoUpdateToggle.IsChecked == true;
ConsoleTypeBox.SelectionChanged += (_, _) =>
_settings.ConsoleType = SelectedComboText(ConsoleTypeBox, "PS5");
ConsoleLanguageBox.SelectionChanged += (_, _) =>
_settings.ConsoleLanguage = SelectedComboText(ConsoleLanguageBox, "EnglishUS");
WindowModeBox.SelectionChanged += (_, _) => _settings.WindowMode = SelectedComboText(WindowModeBox, "Windowed");
DisplayBox.SelectionChanged += (_, _) => OnHostDisplayChanged();
ResolutionBox.SelectionChanged += (_, _) => OnHostResolutionChanged();
@ -1200,6 +1244,8 @@ public partial class MainWindow : Window
private void InitializeLocalizedChoiceBoxes()
{
CpuEngineBox.ItemsSource = _cpuEngineChoices;
ConsoleTypeBox.ItemsSource = _consoleTypeChoices;
ConsoleLanguageBox.ItemsSource = _consoleLanguageChoices;
LogLevelBox.ItemsSource = _logLevelChoices;
WindowModeBox.ItemsSource = _windowModeChoices;
ScalingModeBox.ItemsSource = _scalingModeChoices;
@ -1212,6 +1258,8 @@ public partial class MainWindow : Window
private void RefreshLocalizedChoices()
{
RefreshChoices(_cpuEngineChoices);
RefreshChoices(_consoleTypeChoices);
RefreshChoices(_consoleLanguageChoices);
RefreshChoices(_logLevelChoices);
RefreshChoices(_windowModeChoices);
RefreshChoices(_scalingModeChoices);
@ -1232,6 +1280,10 @@ public partial class MainWindow : Window
private void ApplySettingsToControls()
{
CpuEngineBox.SelectedIndex = 0;
ConsoleTypeBox.SelectedIndex = 0;
ConsoleLanguageBox.SelectedIndex = ChoiceIndex(
_settings.ConsoleLanguage,
_consoleLanguageChoices.Select(choice => choice.Value).ToArray());
LogLevelBox.SelectedIndex = _settings.LogLevel.ToLowerInvariant() switch
{
"trace" => 0,
@ -2545,6 +2597,8 @@ public partial class MainWindow : Window
Path.GetFullPath(ebootPath),
displayName,
_runningGameTitleId,
_settings.ConsoleType,
_settings.ConsoleLanguage,
effective,
runtimeOptions);
@ -2711,6 +2765,8 @@ public partial class MainWindow : Window
{
"--cpu-engine=native",
$"--log-level={launch.Settings.LogLevel}",
$"--console={launch.ConsoleType.ToLowerInvariant()}",
$"--console-language={launch.ConsoleLanguage}",
};
if (launch.RuntimeOptions.StrictDynlibResolution)
{

View File

@ -19,10 +19,12 @@ public static class SystemServiceExports
private static string? _mainAppTitleId;
private static int _noticeScreenSkipFlag;
private static int _systemLanguage = 1;
public static void ConfigureApplicationInfo(string? titleId)
public static void ConfigureApplicationInfo(string? titleId, int systemLanguage = 1)
{
_mainAppTitleId = string.IsNullOrWhiteSpace(titleId) ? null : titleId.Trim();
Volatile.Write(ref _systemLanguage, systemLanguage);
}
[SysAbiExport(
@ -114,7 +116,8 @@ public static class SystemServiceExports
var value = parameterId switch
{
1 or 2 or 3 or 1000 => 1,
1 or 400 => Volatile.Read(ref _systemLanguage),
2 or 3 or 1000 => 1,
4 => 180,
_ => 0,
};
@ -240,6 +243,9 @@ public static class SystemServiceExports
LibraryName = "libSceSystemService")]
public static int SystemServiceReportAbnormalTermination(CpuContext ctx) => ctx.SetReturn(0);
internal static void ResetForTests() =>
internal static void ResetForTests()
{
Volatile.Write(ref _noticeScreenSkipFlag, 0);
Volatile.Write(ref _systemLanguage, 1);
}
}

View File

@ -93,6 +93,8 @@ public sealed class GuiSettingsTests
Assert.Equal("Info", settings.LogLevel);
Assert.Equal("en", settings.Language);
Assert.Equal("PS5", settings.ConsoleType);
Assert.Equal("EnglishUS", settings.ConsoleLanguage);
Assert.Equal("1525606762248540221", settings.DiscordClientId);
Assert.Empty(settings.GameFolders);
Assert.Empty(settings.ExcludedGames);
@ -128,6 +130,20 @@ public sealed class GuiSettingsTests
Assert.Equal(1000, settings.RefreshRate);
}
[Fact]
public void NormalizeFromJson_ConsoleValuesNormalizeAndPreserveValidLanguage()
{
var invalid = GuiSettings.NormalizeFromJson(
"""{ "ConsoleType": "ps6", "ConsoleLanguage": "klingon" }""");
var turkish = GuiSettings.NormalizeFromJson(
"""{ "ConsoleType": "ps5", "ConsoleLanguage": "turkish" }""");
Assert.Equal("PS5", invalid.ConsoleType);
Assert.Equal("EnglishUS", invalid.ConsoleLanguage);
Assert.Equal("PS5", turkish.ConsoleType);
Assert.Equal("Turkish", turkish.ConsoleLanguage);
}
[Theory]
[InlineData("""{ }""", "Carousel")]
[InlineData("""{ "LibraryLayout": null }""", "Carousel")]

View File

@ -3,6 +3,7 @@
using SharpEmu.HLE;
using SharpEmu.Libs.SystemService;
using System.Buffers.Binary;
using Xunit;
namespace SharpEmu.Libs.Tests.SystemService;
@ -50,4 +51,24 @@ public sealed class SystemServiceExportsTests
Assert.True(memory.TryRead(MemoryBase, flag));
Assert.Equal(1, flag[0]);
}
[Theory]
[InlineData(1)]
[InlineData(400)]
public void ParamGetIntReturnsConfiguredSystemLanguage(int parameterId)
{
var memory = new FakeCpuMemory(MemoryBase, sizeof(int));
var context = new CpuContext(memory, Generation.Gen5)
{
[CpuRegister.Rdi] = unchecked((ulong)parameterId),
[CpuRegister.Rsi] = MemoryBase,
};
SystemServiceExports.ConfigureApplicationInfo(null, 19);
Assert.Equal(0, SystemServiceExports.SystemServiceParamGetInt(context));
Span<byte> value = stackalloc byte[sizeof(int)];
Assert.True(memory.TryRead(MemoryBase, value));
Assert.Equal(19, BinaryPrimitives.ReadInt32LittleEndian(value));
}
}