mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-12 12:38:42 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 373100a6b0 |
@@ -48,6 +48,9 @@ public sealed class GuiSettings
|
|||||||
/// <summary>Publish launcher/game status to Discord Rich Presence.</summary>
|
/// <summary>Publish launcher/game status to Discord Rich Presence.</summary>
|
||||||
public bool DiscordRichPresence { get; set; } = true;
|
public bool DiscordRichPresence { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Names of SHARPEMU_* switches set to "1" in the emulator's environment at launch.</summary>
|
||||||
|
public List<string> EnvironmentToggles { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Discord application ID used for Rich Presence; the default is the
|
/// Discord application ID used for Rich Presence; the default is the
|
||||||
/// SharpEmu application. Override to rebrand what Discord shows as
|
/// SharpEmu application. Override to rebrand what Discord shows as
|
||||||
|
|||||||
@@ -26,6 +26,15 @@
|
|||||||
"Library.Loading": "Loading library…",
|
"Library.Loading": "Loading library…",
|
||||||
|
|
||||||
"Options.General": "General",
|
"Options.General": "General",
|
||||||
|
"Options.Env.Tab": "Environment",
|
||||||
|
"Options.Section.Environment": "ENVIRONMENT VARIABLES",
|
||||||
|
"Options.Env.Desc": "Switches passed to the emulator as environment variables at launch.",
|
||||||
|
"Options.Env.Bthid.Desc": "Report Bluetooth HID as unavailable for titles whose wheel/FFB middleware polls forever.\nLeave off normally. Some titles freeze when init fails.",
|
||||||
|
"Options.Env.LoopGuard.Desc": "Do not force quit titles that repeat the same call for too long.\nTry this when a game exits on its own while loading.",
|
||||||
|
"Options.Env.VkValidation.Desc": "Enable Vulkan validation layers for GPU debugging.\nSlow. Requires the Vulkan SDK to be installed.",
|
||||||
|
"Options.Env.DumpSpirv.Desc": "Dump AGC shaders and their SPIR-V translations to the shader-dumps folder.\nUse when reporting shader or rendering bugs.",
|
||||||
|
"Options.Env.LogDirectMemory.Desc": "Log direct memory allocations and failures to the console.\nUse when a game aborts or exits during boot.",
|
||||||
|
"Options.Env.LogNp.Desc": "Log NP (PlayStation Network) library calls to the console.",
|
||||||
"Options.Section.Emulation": "EMULATION",
|
"Options.Section.Emulation": "EMULATION",
|
||||||
"Options.Section.Logging": "LOGGING",
|
"Options.Section.Logging": "LOGGING",
|
||||||
"Options.Section.Launcher": "LAUNCHER",
|
"Options.Section.Launcher": "LAUNCHER",
|
||||||
|
|||||||
@@ -387,6 +387,88 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem x:Name="EnvTabItem" Header="Environment" FontSize="15">
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
||||||
|
|
||||||
|
<Border Classes="card">
|
||||||
|
<StackPanel Spacing="14">
|
||||||
|
<TextBlock x:Name="EnvSectionTitle" Classes="sectionTitle" Text="ENVIRONMENT VARIABLES" />
|
||||||
|
<TextBlock x:Name="EnvDesc"
|
||||||
|
Text="Switches passed to the emulator as environment variables at launch."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_BTHID_UNAVAILABLE" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvBthidDesc"
|
||||||
|
Text="Report Bluetooth HID as unavailable for titles whose wheel/FFB middleware polls forever. Leave off normally. Some titles freeze when init fails."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvBthidToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_DISABLE_IMPORT_LOOP_GUARD" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvLoopGuardDesc"
|
||||||
|
Text="Do not force quit titles that repeat the same call for too long. Try this when a game exits on its own while loading."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvLoopGuardToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_VK_VALIDATION" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvVkValidationDesc"
|
||||||
|
Text="Enable Vulkan validation layers for GPU debugging. Slow. Requires the Vulkan SDK to be installed."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvVkValidationToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_DUMP_SPIRV" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvDumpSpirvDesc"
|
||||||
|
Text="Dump AGC shaders and their SPIR-V translations to the shader-dumps folder. Use when reporting shader or rendering bugs."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvDumpSpirvToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_LOG_DIRECT_MEMORY" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvLogDirectMemoryDesc"
|
||||||
|
Text="Log direct memory allocations and failures to the console. Use when a game aborts or exits during boot."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvLogDirectMemoryToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<StackPanel VerticalAlignment="Center" Spacing="2" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="SHARPEMU_LOG_NP" FontSize="13" FontFamily="Consolas,monospace" />
|
||||||
|
<TextBlock x:Name="EnvLogNpDesc"
|
||||||
|
Text="Log NP (PlayStation Network) library calls to the console."
|
||||||
|
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||||||
|
</StackPanel>
|
||||||
|
<ToggleSwitch Grid.Column="1" x:Name="EnvLogNpToggle" OnContent="On" OffContent="Off"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
@@ -125,6 +125,18 @@ public partial class MainWindow : Window
|
|||||||
UpdateDiscordPresence();
|
UpdateDiscordPresence();
|
||||||
};
|
};
|
||||||
SelectLogFilePathButton.Click += async (_, _) => await SelectLogFilePathAsync();
|
SelectLogFilePathButton.Click += async (_, _) => await SelectLogFilePathAsync();
|
||||||
|
EnvBthidToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_BTHID_UNAVAILABLE", EnvBthidToggle.IsChecked == true);
|
||||||
|
EnvLoopGuardToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_DISABLE_IMPORT_LOOP_GUARD", EnvLoopGuardToggle.IsChecked == true);
|
||||||
|
EnvVkValidationToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_VK_VALIDATION", EnvVkValidationToggle.IsChecked == true);
|
||||||
|
EnvDumpSpirvToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_DUMP_SPIRV", EnvDumpSpirvToggle.IsChecked == true);
|
||||||
|
EnvLogDirectMemoryToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_LOG_DIRECT_MEMORY", EnvLogDirectMemoryToggle.IsChecked == true);
|
||||||
|
EnvLogNpToggle.IsCheckedChanged += (_, _) =>
|
||||||
|
SetEnvironmentToggle("SHARPEMU_LOG_NP", EnvLogNpToggle.IsChecked == true);
|
||||||
LanguageBox.SelectionChanged += (_, _) => OnLanguageChanged();
|
LanguageBox.SelectionChanged += (_, _) => OnLanguageChanged();
|
||||||
|
|
||||||
GameList.AddHandler(ContextRequestedEvent, OnGameContextRequested, RoutingStrategies.Tunnel);
|
GameList.AddHandler(ContextRequestedEvent, OnGameContextRequested, RoutingStrategies.Tunnel);
|
||||||
@@ -403,6 +415,15 @@ public partial class MainWindow : Window
|
|||||||
LoadingStateText.Text = loc.Get("Library.Loading");
|
LoadingStateText.Text = loc.Get("Library.Loading");
|
||||||
|
|
||||||
GeneralTabItem.Header = loc.Get("Options.General");
|
GeneralTabItem.Header = loc.Get("Options.General");
|
||||||
|
EnvTabItem.Header = loc.Get("Options.Env.Tab");
|
||||||
|
EnvSectionTitle.Text = loc.Get("Options.Section.Environment");
|
||||||
|
EnvDesc.Text = loc.Get("Options.Env.Desc");
|
||||||
|
EnvBthidDesc.Text = loc.Get("Options.Env.Bthid.Desc");
|
||||||
|
EnvLoopGuardDesc.Text = loc.Get("Options.Env.LoopGuard.Desc");
|
||||||
|
EnvVkValidationDesc.Text = loc.Get("Options.Env.VkValidation.Desc");
|
||||||
|
EnvDumpSpirvDesc.Text = loc.Get("Options.Env.DumpSpirv.Desc");
|
||||||
|
EnvLogDirectMemoryDesc.Text = loc.Get("Options.Env.LogDirectMemory.Desc");
|
||||||
|
EnvLogNpDesc.Text = loc.Get("Options.Env.LogNp.Desc");
|
||||||
EmulationSectionTitle.Text = loc.Get("Options.Section.Emulation");
|
EmulationSectionTitle.Text = loc.Get("Options.Section.Emulation");
|
||||||
LoggingSectionTitle.Text = loc.Get("Options.Section.Logging");
|
LoggingSectionTitle.Text = loc.Get("Options.Section.Logging");
|
||||||
LauncherSectionTitle.Text = loc.Get("Options.Section.Launcher");
|
LauncherSectionTitle.Text = loc.Get("Options.Section.Launcher");
|
||||||
@@ -584,9 +605,34 @@ public partial class MainWindow : Window
|
|||||||
OverrideLogFileToggle.IsChecked = _settings.OverrideLogFile;
|
OverrideLogFileToggle.IsChecked = _settings.OverrideLogFile;
|
||||||
TitleMusicToggle.IsChecked = _settings.PlayTitleMusic;
|
TitleMusicToggle.IsChecked = _settings.PlayTitleMusic;
|
||||||
DiscordToggle.IsChecked = _settings.DiscordRichPresence;
|
DiscordToggle.IsChecked = _settings.DiscordRichPresence;
|
||||||
|
EnvBthidToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_BTHID_UNAVAILABLE");
|
||||||
|
EnvLoopGuardToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_DISABLE_IMPORT_LOOP_GUARD");
|
||||||
|
EnvVkValidationToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_VK_VALIDATION");
|
||||||
|
EnvDumpSpirvToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_DUMP_SPIRV");
|
||||||
|
EnvLogDirectMemoryToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_LOG_DIRECT_MEMORY");
|
||||||
|
EnvLogNpToggle.IsChecked = _settings.EnvironmentToggles.Contains("SHARPEMU_LOG_NP");
|
||||||
UpdateLogFilePathText();
|
UpdateLogFilePathText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Environment variables set on this process at the previous launch; children
|
||||||
|
// inherit the process environment, so stale names must be cleared explicitly.
|
||||||
|
private readonly HashSet<string> _appliedEnvironmentVariables = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
private void SetEnvironmentToggle(string name, bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
if (!_settings.EnvironmentToggles.Contains(name))
|
||||||
|
{
|
||||||
|
_settings.EnvironmentToggles.Add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_settings.EnvironmentToggles.Remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string SelectedLogLevel()
|
private string SelectedLogLevel()
|
||||||
{
|
{
|
||||||
return LogLevelBox.SelectedIndex switch
|
return LogLevelBox.SelectedIndex switch
|
||||||
@@ -1402,6 +1448,24 @@ public partial class MainWindow : Window
|
|||||||
Localization.Instance.Format("Launch.Command", string.Join(' ', arguments)),
|
Localization.Instance.Format("Launch.Command", string.Join(' ', arguments)),
|
||||||
DimLineBrush);
|
DimLineBrush);
|
||||||
|
|
||||||
|
// Apply the enabled switches to this process; both emulator launch paths
|
||||||
|
// (CreateProcessW and Process.Start) inherit it. Clear switches turned
|
||||||
|
// off since the previous launch.
|
||||||
|
foreach (var staleName in _appliedEnvironmentVariables)
|
||||||
|
{
|
||||||
|
if (!_settings.EnvironmentToggles.Contains(staleName))
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable(staleName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_appliedEnvironmentVariables.Clear();
|
||||||
|
foreach (var name in _settings.EnvironmentToggles)
|
||||||
|
{
|
||||||
|
Environment.SetEnvironmentVariable(name, "1");
|
||||||
|
_appliedEnvironmentVariables.Add(name);
|
||||||
|
}
|
||||||
|
|
||||||
var emulator = new EmulatorProcess();
|
var emulator = new EmulatorProcess();
|
||||||
emulator.OutputReceived += (line, isError) => _pendingLines.Enqueue((line, isError));
|
emulator.OutputReceived += (line, isError) => _pendingLines.Enqueue((line, isError));
|
||||||
emulator.Exited += code => Dispatcher.UIThread.Post(() => OnEmulatorExited(code));
|
emulator.Exited += code => Dispatcher.UIThread.Post(() => OnEmulatorExited(code));
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public static class AgcExports
|
|||||||
private const uint ItDispatchDirect = 0x15;
|
private const uint ItDispatchDirect = 0x15;
|
||||||
private const uint ItDispatchIndirect = 0x16;
|
private const uint ItDispatchIndirect = 0x16;
|
||||||
private const uint ItWaitRegMem = 0x3C;
|
private const uint ItWaitRegMem = 0x3C;
|
||||||
|
private const uint ItIndirectBuffer = 0x3F;
|
||||||
private const uint ItEventWrite = 0x46;
|
private const uint ItEventWrite = 0x46;
|
||||||
private const uint ItDmaData = 0x50;
|
private const uint ItDmaData = 0x50;
|
||||||
private const uint ItSetContextReg = 0x69;
|
private const uint ItSetContextReg = 0x69;
|
||||||
@@ -1943,6 +1944,96 @@ public static class AgcExports
|
|||||||
return ReturnPointer(ctx, commandAddress);
|
return ReturnPointer(ctx, commandAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guest draw-command builders: emit valid (skippable) packets and return a live
|
||||||
|
// command pointer so the guest's command-buffer build succeeds; full draw processing is TODO.
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "8N2tmT3jmC8",
|
||||||
|
ExportName = "sceAgcDcbSetIndexCount",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DcbSetIndexCount(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var dcb = ctx[CpuRegister.Rdi];
|
||||||
|
var indexCount = (uint)ctx[CpuRegister.Rsi];
|
||||||
|
if (dcb == 0)
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryAllocateCommandDwords(ctx, dcb, 2, out var cmd) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd, Pm4(2, ItNop, RZero)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 4, indexCount))
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnPointer(ctx, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "xSAR0LTcRKM",
|
||||||
|
ExportName = "sceAgcDcbJump",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DcbJump(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var dcb = ctx[CpuRegister.Rdi];
|
||||||
|
var target = ctx[CpuRegister.Rsi];
|
||||||
|
var sizeDwords = (uint)ctx[CpuRegister.Rdx];
|
||||||
|
if (dcb == 0)
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryAllocateCommandDwords(ctx, dcb, 4, out var cmd) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd, Pm4(4, ItIndirectBuffer, RZero)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 4, (uint)(target & 0xFFFF_FFFFUL)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 8, (uint)((target >> 32) & 0xFFFFUL)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 12, sizeDwords & 0xFFFFF))
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnPointer(ctx, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "bbFueFP+J4k",
|
||||||
|
ExportName = "sceAgcDcbSetPredication",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int DcbSetPredication(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var dcb = ctx[CpuRegister.Rdi];
|
||||||
|
var address = ctx[CpuRegister.Rsi];
|
||||||
|
if (dcb == 0)
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryAllocateCommandDwords(ctx, dcb, 3, out var cmd) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd, Pm4(3, ItNop, RZero)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 4, (uint)(address & 0xFFFF_FFFFUL)) ||
|
||||||
|
!ctx.TryWriteUInt32(cmd + 8, (uint)(address >> 32)))
|
||||||
|
{
|
||||||
|
return ReturnPointer(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnPointer(ctx, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "w6Dj1VJt5qY",
|
||||||
|
ExportName = "sceAgcSetPacketPredication",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAgc")]
|
||||||
|
public static int SetPacketPredication(CpuContext ctx)
|
||||||
|
{
|
||||||
|
// Global predication toggle on a packet; a no-op is safe for rendering.
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "w2rJhmD+dsE",
|
Nid = "w2rJhmD+dsE",
|
||||||
ExportName = "sceAgcDriverAddEqEvent",
|
ExportName = "sceAgcDriverAddEqEvent",
|
||||||
|
|||||||
@@ -348,6 +348,18 @@ public static class AmprExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "Zi3dBUjgyXI",
|
||||||
|
ExportName = "sceAmprMeasureCommandSizeWriteKernelEventQueueOnCompletion",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAmpr")]
|
||||||
|
public static int MeasureCommandSizeWriteKernelEventQueueOnCompletion(CpuContext ctx)
|
||||||
|
{
|
||||||
|
TraceAmpr(ctx, "measure_write_equeue_complete", 0, KernelEventQueueRecordSize, 0);
|
||||||
|
ctx[CpuRegister.Rax] = KernelEventQueueRecordSize;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "C+IEj+BsAFM",
|
Nid = "C+IEj+BsAFM",
|
||||||
ExportName = "sceAmprMeasureCommandSizeWriteAddressOnCompletion",
|
ExportName = "sceAmprMeasureCommandSizeWriteAddressOnCompletion",
|
||||||
@@ -440,6 +452,40 @@ public static class AmprExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "o67gODLFpls",
|
||||||
|
ExportName = "sceAmprCommandBufferWriteKernelEventQueueOnCompletion",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAmpr")]
|
||||||
|
public static int CommandBufferWriteKernelEventQueueOnCompletion(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var commandBuffer = ctx[CpuRegister.Rdi];
|
||||||
|
var equeue = ctx[CpuRegister.Rsi];
|
||||||
|
var ident = ctx[CpuRegister.Rdx];
|
||||||
|
var completionToken = ctx[CpuRegister.Rcx];
|
||||||
|
var userData = ctx[CpuRegister.R8];
|
||||||
|
|
||||||
|
if (commandBuffer == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AppendKernelEventQueueRecord(
|
||||||
|
ctx,
|
||||||
|
commandBuffer,
|
||||||
|
equeue,
|
||||||
|
ident,
|
||||||
|
completionToken,
|
||||||
|
userData))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAmpr(ctx, "write_equeue_complete", commandBuffer, ident, completionToken);
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "sJXyWHjP-F8",
|
Nid = "sJXyWHjP-F8",
|
||||||
ExportName = "sceAmprCommandBufferWriteAddressOnCompletion",
|
ExportName = "sceAmprCommandBufferWriteAddressOnCompletion",
|
||||||
|
|||||||
@@ -1834,6 +1834,45 @@ public static class KernelMemoryCompatExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "fgIsQ10xYVA",
|
||||||
|
ExportName = "sceKernelChmod",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libKernel")]
|
||||||
|
public static int KernelChmod(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var pathAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var mode = unchecked((uint)ctx[CpuRegister.Rsi]);
|
||||||
|
if (pathAddress == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryReadNullTerminatedUtf8(ctx, pathAddress, MaxGuestStringLength, out var guestPath))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostPath = ResolveGuestPath(guestPath);
|
||||||
|
if (IsReadOnlyGuestMutationPath(guestPath))
|
||||||
|
{
|
||||||
|
LogOpenTrace($"chmod readonly path='{guestPath}' host='{hostPath}' mode=0x{mode:X}");
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_PERMISSION_DENIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(hostPath) && !Directory.Exists(hostPath))
|
||||||
|
{
|
||||||
|
AddNegativeStatCacheForGuestPath(guestPath);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// POSIX permission bits have no host equivalent on Windows; accept the call
|
||||||
|
// so guests that chmod their freshly created files/directories can proceed.
|
||||||
|
LogOpenTrace($"chmod path='{guestPath}' host='{hostPath}' mode=0x{mode:X}");
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "NNtFaKJbPt0",
|
Nid = "NNtFaKJbPt0",
|
||||||
ExportName = "_close",
|
ExportName = "_close",
|
||||||
|
|||||||
@@ -382,6 +382,47 @@ public static class KernelPthreadExtendedCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "oIRFTjoILbg",
|
||||||
|
ExportName = "scePthreadSetschedparam",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libKernel")]
|
||||||
|
public static int PthreadSetschedparam(CpuContext ctx) => PosixPthreadSetschedparam(ctx);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "P41kTWUS3EI",
|
||||||
|
ExportName = "scePthreadGetschedparam",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libKernel")]
|
||||||
|
public static int PthreadGetschedparam(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var thread = ctx[CpuRegister.Rdi];
|
||||||
|
var outPolicyAddress = ctx[CpuRegister.Rsi];
|
||||||
|
var outSchedParamAddress = ctx[CpuRegister.Rdx];
|
||||||
|
if (thread == 0 || outPolicyAddress == 0 || outSchedParamAddress == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int policy;
|
||||||
|
int priority;
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
var state = GetOrCreateThreadStateLocked(thread);
|
||||||
|
policy = state.Attributes.SchedPolicy;
|
||||||
|
priority = state.Priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteInt32(outPolicyAddress, policy) ||
|
||||||
|
!ctx.TryWriteInt32(outSchedParamAddress, priority))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "nsYoNRywwNg",
|
Nid = "nsYoNRywwNg",
|
||||||
ExportName = "scePthreadAttrInit",
|
ExportName = "scePthreadAttrInit",
|
||||||
|
|||||||
@@ -7,6 +7,17 @@ namespace SharpEmu.Libs.Mouse;
|
|||||||
|
|
||||||
public static class MouseExports
|
public static class MouseExports
|
||||||
{
|
{
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "Qs0wWulgl7U",
|
||||||
|
ExportName = "sceMouseInit",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceMouse")]
|
||||||
|
public static int MouseInit(CpuContext ctx)
|
||||||
|
{
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns 0 read entries: no mouse is connected. This NID was previously misbound
|
// Returns 0 read entries: no mouse is connected. This NID was previously misbound
|
||||||
// as an sceNgs2VoiceGetState alias.
|
// as an sceNgs2VoiceGetState alias.
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
|
|||||||
@@ -146,6 +146,13 @@ public static class NetCtlExports
|
|||||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_OK, typeof(long));
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_OK, typeof(long));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "1NE9OWdBIww",
|
||||||
|
ExportName = "sceNetCtlRegisterCallbackV6",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNetCtl")]
|
||||||
|
public static int NetCtlRegisterCallbackV6(CpuContext ctx) => NetCtlRegisterCallback(ctx);
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "obuxdTiwkF8",
|
Nid = "obuxdTiwkF8",
|
||||||
ExportName = "sceNetCtlGetInfo",
|
ExportName = "sceNetCtlGetInfo",
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using SharpEmu.HLE;
|
||||||
|
|
||||||
|
namespace SharpEmu.Libs.Np;
|
||||||
|
|
||||||
|
// Stub for sce::Np::CppWebApi: titles abort PS5-component startup if
|
||||||
|
// Common::initialize returns a negative SCE error, so no-op success is required to boot.
|
||||||
|
public static class NpCppWebApiExports
|
||||||
|
{
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "UYPxv8MIzGo",
|
||||||
|
ExportName = "_ZN3sce2Np9CppWebApi6Common10initializeERKNS2_10InitParamsERNS2_10LibContextE",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpCppWebApi")]
|
||||||
|
public static int CppWebApiCommonInitialize(CpuContext ctx)
|
||||||
|
{
|
||||||
|
// int Common::initialize(const InitParams&, LibContext&) — 0 on success.
|
||||||
|
TraceCppWebApi("common_initialize", ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi]);
|
||||||
|
return ctx.SetReturn(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TraceCppWebApi(string operation, ulong arg0, ulong arg1)
|
||||||
|
{
|
||||||
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_NP"), "1", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][TRACE] np_cppwebapi.{operation} arg0=0x{arg0:X16} arg1=0x{arg1:X16}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using SharpEmu.HLE;
|
||||||
|
|
||||||
|
namespace SharpEmu.Libs.Pad;
|
||||||
|
|
||||||
|
// Minimal libSceBluetoothHid stub: no host Bluetooth passthrough, so report
|
||||||
|
// success and let the Pad path provide input (SHARPEMU_BTHID_UNAVAILABLE=1 fails instead).
|
||||||
|
public static class BluetoothHidExports
|
||||||
|
{
|
||||||
|
private const int BluetoothHidUnavailable = unchecked((int)0x80960001);
|
||||||
|
|
||||||
|
private static readonly bool _reportUnavailable = string.Equals(
|
||||||
|
Environment.GetEnvironmentVariable("SHARPEMU_BTHID_UNAVAILABLE"),
|
||||||
|
"1",
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
|
||||||
|
private static int Result(CpuContext ctx) =>
|
||||||
|
ctx.SetReturn(_reportUnavailable ? BluetoothHidUnavailable : 0);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "tul3-GzejQc",
|
||||||
|
ExportName = "sceBluetoothHidInit",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceBluetoothHid")]
|
||||||
|
public static int BluetoothHidInit(CpuContext ctx) => Result(ctx);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "4FUZ+c52d2k",
|
||||||
|
ExportName = "sceBluetoothHidRegisterDevice",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceBluetoothHid")]
|
||||||
|
public static int BluetoothHidRegisterDevice(CpuContext ctx) => Result(ctx);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "4Ypfo9RIwfM",
|
||||||
|
ExportName = "sceBluetoothHidRegisterCallback",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceBluetoothHid")]
|
||||||
|
public static int BluetoothHidRegisterCallback(CpuContext ctx) => Result(ctx);
|
||||||
|
}
|
||||||
@@ -48,7 +48,18 @@ public static class PadExports
|
|||||||
ExportName = "scePadOpen",
|
ExportName = "scePadOpen",
|
||||||
Target = Generation.Gen4 | Generation.Gen5,
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
LibraryName = "libScePad")]
|
LibraryName = "libScePad")]
|
||||||
public static int PadOpen(CpuContext ctx)
|
public static int PadOpen(CpuContext ctx) => PadOpenCore(ctx, extended: false);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "WFIiSfXGUq8",
|
||||||
|
ExportName = "scePadOpenExt",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libScePad")]
|
||||||
|
public static int PadOpenExt(CpuContext ctx) => PadOpenCore(ctx, extended: true);
|
||||||
|
|
||||||
|
// scePadOpen rejects a non-null 4th arg and non-standard ports; scePadOpenExt accepts a
|
||||||
|
// ScePadOpenExtParam* plus ports 1/2 (racing titles retry scePadOpenExt(type=2) forever if rejected).
|
||||||
|
private static int PadOpenCore(CpuContext ctx, bool extended)
|
||||||
{
|
{
|
||||||
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
|
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
var type = unchecked((int)ctx[CpuRegister.Rsi]);
|
var type = unchecked((int)ctx[CpuRegister.Rsi]);
|
||||||
@@ -64,7 +75,8 @@ public static class PadExports
|
|||||||
return ctx.SetReturn(OrbisPadErrorDeviceNoHandle);
|
return ctx.SetReturn(OrbisPadErrorDeviceNoHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userId != PrimaryUserId || type != StandardPortType || index != 0 || parameterAddress != 0)
|
var typeAccepted = extended ? type is 0 or 1 or 2 : type == StandardPortType;
|
||||||
|
if (userId != PrimaryUserId || !typeAccepted || index != 0 || (!extended && parameterAddress != 0))
|
||||||
{
|
{
|
||||||
return ctx.SetReturn(OrbisPadErrorDeviceNotConnected);
|
return ctx.SetReturn(OrbisPadErrorDeviceNotConnected);
|
||||||
}
|
}
|
||||||
@@ -83,6 +95,19 @@ public static class PadExports
|
|||||||
return ctx.SetReturn(PrimaryPadHandle);
|
return ctx.SetReturn(PrimaryPadHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "6ncge5+l5Qs",
|
||||||
|
ExportName = "scePadClose",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libScePad")]
|
||||||
|
public static int PadClose(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
return handle == PrimaryPadHandle
|
||||||
|
? ctx.SetReturn(0)
|
||||||
|
: ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "clVvL4ZDntw",
|
Nid = "clVvL4ZDntw",
|
||||||
ExportName = "scePadSetMotionSensorState",
|
ExportName = "scePadSetMotionSensorState",
|
||||||
@@ -131,6 +156,47 @@ public static class PadExports
|
|||||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "hGbf2QTBmqc",
|
||||||
|
ExportName = "scePadGetExtControllerInformation",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libScePad")]
|
||||||
|
public static int PadGetExtControllerInformation(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
var informationAddress = ctx[CpuRegister.Rsi];
|
||||||
|
if (handle != PrimaryPadHandle)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (informationAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base ScePadControllerInformation + device-class/connection fields: report a connected
|
||||||
|
// DualSense so the guest's open -> get-ext-info -> close probe loop resolves.
|
||||||
|
Span<byte> information = stackalloc byte[0x40];
|
||||||
|
information.Clear();
|
||||||
|
BinaryPrimitives.WriteSingleLittleEndian(information[0x00..], 44.86f);
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(information[0x04..], 1920);
|
||||||
|
BinaryPrimitives.WriteUInt16LittleEndian(information[0x06..], 943);
|
||||||
|
information[0x08] = 30;
|
||||||
|
information[0x09] = 30;
|
||||||
|
information[0x0A] = StandardPortType;
|
||||||
|
information[0x0B] = 1; // connected count
|
||||||
|
information[0x0C] = 1; // connected
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(information[0x10..], 0);
|
||||||
|
information[0x1C] = 0; // deviceClass: 0 = standard controller / DualSense
|
||||||
|
information[0x1D] = 1; // connected (ext)
|
||||||
|
information[0x1E] = 0; // connectionType: local
|
||||||
|
|
||||||
|
return ctx.Memory.TryWrite(informationAddress, information)
|
||||||
|
? ctx.SetReturn(0)
|
||||||
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "YndgXqQVV7c",
|
Nid = "YndgXqQVV7c",
|
||||||
ExportName = "scePadReadState",
|
ExportName = "scePadReadState",
|
||||||
|
|||||||
@@ -185,6 +185,31 @@ public static class UserServiceExports
|
|||||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "woNpu+45RLk",
|
||||||
|
ExportName = "sceUserServiceGetAgeLevel",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceUserService")]
|
||||||
|
public static int UserServiceGetAgeLevel(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var userId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
var ageLevelAddress = ctx[CpuRegister.Rsi];
|
||||||
|
if (userId != 1000)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisUserServiceErrorInvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ageLevelAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report an adult account so titles skip parental-restriction paths.
|
||||||
|
return ctx.TryWriteInt32(ageLevelAddress, 21)
|
||||||
|
? ctx.SetReturn(0)
|
||||||
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
private static void TraceUserService(string message)
|
private static void TraceUserService(string message)
|
||||||
{
|
{
|
||||||
if (_traceUserService)
|
if (_traceUserService)
|
||||||
|
|||||||
@@ -403,6 +403,34 @@ public static class VideoOutExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "+I4K03i3EL0",
|
||||||
|
ExportName = "sceVideoOutInitializeOutputOptions",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceVideoOut")]
|
||||||
|
public static int VideoOutInitializeOutputOptions(CpuContext ctx)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "w0hLuNarQxY",
|
||||||
|
ExportName = "sceVideoOutConfigureOutput",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceVideoOut")]
|
||||||
|
public static int VideoOutConfigureOutput(CpuContext ctx)
|
||||||
|
{
|
||||||
|
// Accept the requested output configuration; the presenter always renders
|
||||||
|
// at the display buffer's native size.
|
||||||
|
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
if (!TryGetPort(handle, out _))
|
||||||
|
{
|
||||||
|
return OrbisVideoOutErrorInvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "utPrVdxio-8",
|
Nid = "utPrVdxio-8",
|
||||||
ExportName = "sceVideoOutGetOutputStatus",
|
ExportName = "sceVideoOutGetOutputStatus",
|
||||||
|
|||||||
Reference in New Issue
Block a user