From c2cee59fdaa3d3846c0affa58e3af35d5aae450c Mon Sep 17 00:00:00 2001
From: ParantezTech <12572227+par274@users.noreply.github.com>
Date: Wed, 29 Jul 2026 15:39:01 +0300
Subject: [PATCH] [ime] Add default profile
---
src/SharpEmu.GUI/GuiSettings.cs | 21 +++
src/SharpEmu.GUI/Languages/en.json | 2 +
src/SharpEmu.GUI/Languages/tr.json | 2 +
src/SharpEmu.GUI/MainWindow.axaml | 10 ++
src/SharpEmu.GUI/MainWindow.axaml.cs | 15 ++
src/SharpEmu.Libs/Ime/ImeDialogExports.cs | 175 ++++++++++++++++++++++
6 files changed, 225 insertions(+)
create mode 100644 src/SharpEmu.Libs/Ime/ImeDialogExports.cs
diff --git a/src/SharpEmu.GUI/GuiSettings.cs b/src/SharpEmu.GUI/GuiSettings.cs
index 73e98be2..2634830c 100644
--- a/src/SharpEmu.GUI/GuiSettings.cs
+++ b/src/SharpEmu.GUI/GuiSettings.cs
@@ -45,6 +45,9 @@ public sealed class GuiSettings
/// UI language, matching a file code under Languages/ (e.g. "en", "tr").
public string Language { get; set; } = "en";
+ /// Default text-entry profile exposed to games.
+ public string DefaultProfile { get; set; } = "Sharp";
+
/// Publish launcher/game status to Discord Rich Presence.
public bool DiscordRichPresence { get; set; } = true;
@@ -112,6 +115,18 @@ public sealed class GuiSettings
settings.EnvironmentToggles = FilterNullOrEmpty(settings.EnvironmentToggles);
settings.LogLevel ??= "Info";
settings.Language ??= "en";
+ var legacyProfile = settings.EnvironmentToggles
+ .Select(entry => entry.Split('=', 2, StringSplitOptions.TrimEntries))
+ .FirstOrDefault(parts =>
+ parts.Length == 2 &&
+ string.Equals(parts[0], "SHARPEMU_DEFAULT_PROFILE", StringComparison.OrdinalIgnoreCase));
+ settings.EnvironmentToggles.RemoveAll(entry =>
+ string.Equals(
+ entry.Split('=', 2, StringSplitOptions.TrimEntries)[0],
+ "SHARPEMU_DEFAULT_PROFILE",
+ StringComparison.OrdinalIgnoreCase));
+ settings.DefaultProfile = NormalizeDefaultProfile(
+ legacyProfile is { Length: 2 } ? legacyProfile[1] : settings.DefaultProfile);
settings.DiscordClientId ??= "1525606762248540221";
if (settings.RenderResolutionScale <= 0 || settings.RenderResolutionScale > 2.0)
{
@@ -152,6 +167,12 @@ public sealed class GuiSettings
return $"{width}x{height}";
}
+ internal static string NormalizeDefaultProfile(string? value)
+ {
+ var trimmed = value?.Trim();
+ return string.IsNullOrEmpty(trimmed) ? "Sharp" : trimmed;
+ }
+
public void Save()
{
try
diff --git a/src/SharpEmu.GUI/Languages/en.json b/src/SharpEmu.GUI/Languages/en.json
index fc5cc2a8..8a59c75e 100644
--- a/src/SharpEmu.GUI/Languages/en.json
+++ b/src/SharpEmu.GUI/Languages/en.json
@@ -38,6 +38,8 @@
"Options.Env.LogIo.Desc": "Log file open, read, and path-resolve activity to the console.\nUse when a game cannot find its data files during boot.",
"Options.Env.LogNp.Desc": "Log NP (PlayStation Network) library calls to the console.",
"Options.Env.GuestImageCpuSync.Desc": "Re-upload guest surfaces the game's own CPU code rewrites.\nLeave off normally. Turn on for titles whose CPU-drawn surfaces never reach the screen.\nCosts performance and regresses some titles, such as GTA V.",
+ "Options.DefaultProfile.Label": "Default profile name",
+ "Options.DefaultProfile.Desc": "Name used when a game asks for text input. Defaults to Sharp.",
"Options.Section.Emulation": "EMULATION",
"Options.Section.Logging": "LOGGING",
"Options.Section.Launcher": "LAUNCHER",
diff --git a/src/SharpEmu.GUI/Languages/tr.json b/src/SharpEmu.GUI/Languages/tr.json
index aa6fa458..3a26330a 100644
--- a/src/SharpEmu.GUI/Languages/tr.json
+++ b/src/SharpEmu.GUI/Languages/tr.json
@@ -173,6 +173,8 @@
"Options.Env.LogIo.Desc": "Dosya açma, okuma ve yol çözümleme etkinliğini konsola günlükle.\nBir oyun açılışta veri dosyalarını bulamıyorsa kullanın.",
"Options.Env.LogNp.Desc": "NP (PlayStation Network) kütüphane çağrılarını konsola günlükle.",
"Options.Env.GuestImageCpuSync.Desc": "Oyunun kendi CPU kodunun yeniden yazdığı misafir yüzeyleri tekrar yükler.\nNormalde kapalı bırakın. CPU ile çizilen yüzeyleri ekrana ulaşmayan oyunlarda açın.\nPerformansa mal olur ve GTA V gibi bazı oyunlarda soruna yol açar.",
+ "Options.DefaultProfile.Label": "Varsayilan profil adi",
+ "Options.DefaultProfile.Desc": "Oyun metin girisi istediginde kullanilacak ad. Varsayilan deger Sharp'tir.",
"Common.Save": "Kaydet",
"Common.Cancel": "İptal",
"PerGame.Title": "Oyuna özel ayarlar — {0} ({1})",
diff --git a/src/SharpEmu.GUI/MainWindow.axaml b/src/SharpEmu.GUI/MainWindow.axaml
index f398c3a8..74ead40a 100644
--- a/src/SharpEmu.GUI/MainWindow.axaml
+++ b/src/SharpEmu.GUI/MainWindow.axaml
@@ -346,6 +346,15 @@ SPDX-License-Identifier: GPL-2.0-or-later
+
+
+
+
@@ -646,6 +655,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
OffContent="{Binding [Common.Off], Source={x:Static local:Localization.Instance}}"
VerticalAlignment="Center" />
+
diff --git a/src/SharpEmu.GUI/MainWindow.axaml.cs b/src/SharpEmu.GUI/MainWindow.axaml.cs
index 086a5dca..8188e75a 100644
--- a/src/SharpEmu.GUI/MainWindow.axaml.cs
+++ b/src/SharpEmu.GUI/MainWindow.axaml.cs
@@ -298,6 +298,8 @@ public partial class MainWindow : Window
SetEnvironmentToggle(
"SHARPEMU_GUEST_IMAGE_CPU_SYNC",
EnvGuestImageCpuSyncToggle.IsChecked == true);
+ DefaultProfileBox.TextChanged += (_, _) =>
+ _settings.DefaultProfile = GuiSettings.NormalizeDefaultProfile(DefaultProfileBox.Text);
LanguageBox.SelectionChanged += (_, _) => OnLanguageChanged();
GameList.AddHandler(ContextRequestedEvent, OnGameContextRequested, RoutingStrategies.Tunnel);
@@ -883,6 +885,7 @@ public partial class MainWindow : Window
EnvLogNpToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_LOG_NP");
EnvGuestImageCpuSyncToggle.IsChecked =
_settings.EnvironmentToggles.Contains("SHARPEMU_GUEST_IMAGE_CPU_SYNC");
+ DefaultProfileBox.Text = _settings.DefaultProfile;
WindowModeBox.SelectedIndex = ChoiceIndex(_settings.WindowMode, "Windowed", "Borderless", "Exclusive");
LoadHostDisplayOptions();
ScalingModeBox.SelectedIndex = ChoiceIndex(_settings.ScalingMode, "Fit", "Cover", "Stretch", "Integer");
@@ -1109,6 +1112,8 @@ public partial class MainWindow : Window
}
}
+ private const string DefaultProfileEnvironmentName = "SHARPEMU_DEFAULT_PROFILE";
+
private string SelectedLogLevel()
{
return LogLevelBox.SelectedIndex switch
@@ -2006,10 +2011,20 @@ public partial class MainWindow : Window
continue;
}
+ if (string.Equals(name, DefaultProfileEnvironmentName, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
Environment.SetEnvironmentVariable(name, value);
_appliedEnvironmentVariables.Add(name);
}
+ Environment.SetEnvironmentVariable(
+ DefaultProfileEnvironmentName,
+ GuiSettings.NormalizeDefaultProfile(_settings.DefaultProfile));
+ _appliedEnvironmentVariables.Add(DefaultProfileEnvironmentName);
+
Environment.SetEnvironmentVariable(
"SHARPEMU_RENDER_SCALE",
_settings.RenderResolutionScale.ToString(
diff --git a/src/SharpEmu.Libs/Ime/ImeDialogExports.cs b/src/SharpEmu.Libs/Ime/ImeDialogExports.cs
new file mode 100644
index 00000000..bd756b9a
--- /dev/null
+++ b/src/SharpEmu.Libs/Ime/ImeDialogExports.cs
@@ -0,0 +1,175 @@
+// Copyright (C) 2026 SharpEmu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+using System;
+using System.Buffers.Binary;
+using System.Text;
+using SharpEmu.HLE;
+
+namespace SharpEmu.Libs.Ime;
+
+public static class ImeDialogExports
+{
+ private const int StatusNone = 0;
+ private const int StatusRunning = 1;
+ private const int StatusFinished = 2;
+
+ private const int EndStatusOk = 0;
+
+ private const ulong ParamMaxTextLengthOffset = 0x24;
+ private const ulong ParamInputTextBufferOffset = 0x28;
+
+ private const int ImeDialogErrorInvalidAddress = unchecked((int)0x80BC0001);
+
+ private const string DefaultInputText = "Sharp";
+
+ private static readonly object _gate = new();
+ private static int _status = StatusNone;
+
+ [SysAbiExport(
+ Nid = "NUeBrN7hzf0",
+ ExportName = "sceImeDialogInit",
+ Target = Generation.Gen4 | Generation.Gen5,
+ LibraryName = "libSceImeDialog")]
+ public static int ImeDialogInit(CpuContext ctx)
+ {
+ var parameterAddress = ctx[CpuRegister.Rdi];
+ if (parameterAddress == 0)
+ {
+ return SetReturn(ctx, ImeDialogErrorInvalidAddress);
+ }
+
+ TryWriteInputText(ctx, parameterAddress);
+
+ lock (_gate)
+ {
+ _status = StatusFinished;
+ }
+
+ return SetReturn(ctx, 0);
+ }
+
+ [SysAbiExport(
+ Nid = "IADmD4tScBY",
+ ExportName = "sceImeDialogGetStatus",
+ Target = Generation.Gen4 | Generation.Gen5,
+ LibraryName = "libSceImeDialog")]
+ public static int ImeDialogGetStatus(CpuContext ctx)
+ {
+ int status;
+ lock (_gate)
+ {
+ status = _status;
+ }
+
+ ctx[CpuRegister.Rax] = unchecked((ulong)(long)status);
+ return status;
+ }
+
+ [SysAbiExport(
+ Nid = "x01jxu+vxlc",
+ ExportName = "sceImeDialogGetResult",
+ Target = Generation.Gen4 | Generation.Gen5,
+ LibraryName = "libSceImeDialog")]
+ public static int ImeDialogGetResult(CpuContext ctx)
+ {
+ var resultAddress = ctx[CpuRegister.Rdi];
+ if (resultAddress == 0)
+ {
+ return SetReturn(ctx, ImeDialogErrorInvalidAddress);
+ }
+
+ Span result = stackalloc byte[8];
+ result.Clear();
+ BinaryPrimitives.WriteInt32LittleEndian(result, EndStatusOk);
+ ctx.Memory.TryWrite(resultAddress, result);
+
+ return SetReturn(ctx, 0);
+ }
+
+ [SysAbiExport(
+ Nid = "oBmw4xrmfKs",
+ ExportName = "sceImeDialogAbort",
+ Target = Generation.Gen4 | Generation.Gen5,
+ LibraryName = "libSceImeDialog")]
+ public static int ImeDialogAbort(CpuContext ctx)
+ {
+ lock (_gate)
+ {
+ _status = StatusFinished;
+ }
+
+ return SetReturn(ctx, 0);
+ }
+
+ [SysAbiExport(
+ Nid = "gyTyVn+bXMw",
+ ExportName = "sceImeDialogTerm",
+ Target = Generation.Gen4 | Generation.Gen5,
+ LibraryName = "libSceImeDialog")]
+ public static int ImeDialogTerm(CpuContext ctx)
+ {
+ lock (_gate)
+ {
+ _status = StatusNone;
+ }
+
+ return SetReturn(ctx, 0);
+ }
+
+ private static void TryWriteInputText(CpuContext ctx, ulong parameterAddress)
+ {
+ Span field = stackalloc byte[8];
+ if (!ctx.Memory.TryRead(parameterAddress + ParamInputTextBufferOffset, field))
+ {
+ return;
+ }
+
+ var bufferAddress = BinaryPrimitives.ReadUInt64LittleEndian(field);
+ if (bufferAddress == 0)
+ {
+ return;
+ }
+
+ var maxTextLength = 16u;
+ Span lengthField = stackalloc byte[4];
+ if (ctx.Memory.TryRead(parameterAddress + ParamMaxTextLengthOffset, lengthField))
+ {
+ var declared = BinaryPrimitives.ReadUInt32LittleEndian(lengthField);
+ if (declared is > 0 and <= 256)
+ {
+ maxTextLength = declared;
+ }
+ }
+
+ var text = Environment.GetEnvironmentVariable("SHARPEMU_DEFAULT_PROFILE");
+ if (string.IsNullOrEmpty(text))
+ {
+ text = DefaultInputText;
+ }
+
+ if ((uint)text.Length > maxTextLength)
+ {
+ text = text[..(int)maxTextLength];
+ }
+
+ var encoded = Encoding.Unicode.GetBytes(text);
+ Span payload = stackalloc byte[encoded.Length + 2];
+ encoded.CopyTo(payload);
+ payload[^2] = 0;
+ payload[^1] = 0;
+
+ if (ctx.Memory.TryWrite(bufferAddress, payload))
+ {
+ Console.Error.WriteLine(
+ $"[LOADER][WARN] ime.dialog_autofill buf=0x{bufferAddress:X16} " +
+ $"max={maxTextLength} text=\"{text}\"");
+ }
+ }
+
+ private static int SetReturn(CpuContext ctx, int result)
+ {
+ ctx[CpuRegister.Rax] = unchecked((ulong)(long)result);
+ return result;
+ }
+}