Compare commits

..

1 Commits

Author SHA1 Message Date
ParantezTech f69f81f682 [HLE] Add RandomExports HLE 2026-07-18 23:33:45 +03:00
3 changed files with 118 additions and 46 deletions
+10 -46
View File
@@ -92,11 +92,6 @@ public partial class MainWindow : Window
// plain window color remains the fallback when the asset fails to load.
private Bitmap? _defaultBackdrop;
// Whether the native loading/closing popup should be showing; it is a
// desktop-topmost popup, so it closes while the launcher is in the
// background or minimized and reopens from this flag on activation.
private bool _sessionLoadingActive;
// Controller navigation state.
private readonly DispatcherTimer _gamepadTimer;
private HostGamepadButtons _previousPadButtons;
@@ -155,18 +150,8 @@ public partial class MainWindow : Window
};
_libraryBlurTimer.Tick += (_, _) => AdvanceLibraryBlur();
// Native popups float above every window on the desktop; they must
// follow the launcher into the background or a minimized state.
Activated += (_, _) =>
{
UpdateSessionBarVisibility();
SessionLoadingPopup.IsOpen = _sessionLoadingActive;
};
Deactivated += (_, _) =>
{
SessionBarPopup.IsOpen = false;
SessionLoadingPopup.IsOpen = false;
};
Activated += (_, _) => UpdateSessionBarVisibility();
Deactivated += (_, _) => SessionBarPopup.IsOpen = false;
TitleBar.PointerPressed += OnTitleBarPointerPressed;
GameList.SelectionChanged += (_, _) => UpdateSelectedGame();
@@ -429,15 +414,6 @@ public partial class MainWindow : Window
return;
}
if (_isRunning || _isStopping)
{
// The game renders inside the launcher window, so the launcher
// stays active while playing. The controller belongs to the game
// then: no navigation, and Circle/B must never stop the session.
_previousPadButtons = pad.Buttons;
return;
}
var shoulderPressed = pad.Buttons & ~_previousPadButtons;
if ((shoulderPressed & HostGamepadButtons.L1) != 0)
{
@@ -487,6 +463,11 @@ public partial class MainWindow : Window
LaunchSelected();
}
if ((pressed & HostGamepadButtons.Circle) != 0)
{
StopEmulator();
}
_previousPadButtons = pad.Buttons;
}
@@ -1645,23 +1626,13 @@ public partial class MainWindow : Window
base.OnPropertyChanged(change);
if (change.Property == WindowStateProperty)
{
// The XAML WindowState="Maximized" assignment raises this change
// during InitializeComponent, before named controls are wired up.
if (WindowState == WindowState.Minimized)
{
_sndPreview.Pause();
if (SessionLoadingPopup is { } popup)
{
popup.IsOpen = false;
}
}
else
{
_sndPreview.Resume();
if (SessionLoadingPopup is { } popup)
{
popup.IsOpen = _sessionLoadingActive;
}
}
}
}
@@ -2038,7 +2009,7 @@ public partial class MainWindow : Window
ContentToolbar.IsVisible = false;
ConsolePanel.IsVisible = false;
LaunchBar.IsVisible = false;
HideSessionLoading();
SessionLoadingPopup.IsOpen = false;
UpdateSessionBarVisibility();
}
});
@@ -2138,7 +2109,7 @@ public partial class MainWindow : Window
GameView.IsVisible = false;
GameView.IsHitTestVisible = true;
SessionBarPopup.IsOpen = false;
HideSessionLoading();
SessionLoadingPopup.IsOpen = false;
AnimateLibraryBlur(0, clearWhenComplete: true);
MainContent.Margin = new Thickness(32, 24, 32, 20);
ContentToolbar.IsVisible = true;
@@ -2222,14 +2193,7 @@ public partial class MainWindow : Window
{
SessionLoadingTitle.Text = title;
SessionLoadingDetail.Text = detail;
_sessionLoadingActive = true;
SessionLoadingPopup.IsOpen = IsActive && WindowState != WindowState.Minimized;
}
private void HideSessionLoading()
{
_sessionLoadingActive = false;
SessionLoadingPopup.IsOpen = false;
SessionLoadingPopup.IsOpen = true;
}
private void ReturnToLibraryWhileStopping()
+39
View File
@@ -0,0 +1,39 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Security.Cryptography;
using SharpEmu.HLE;
namespace SharpEmu.Libs.Random;
public static class RandomExports
{
private const int RandomErrorInvalid = unchecked((int)0x817C0016);
private const int MaxRandomBytes = 64;
[SysAbiExport(
Nid = "PI7jIZj4pcE",
ExportName = "sceRandomGetRandomNumber",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceRandom")]
public static int RandomGetRandomNumber(CpuContext ctx)
{
var destination = ctx[CpuRegister.Rdi];
var size = ctx[CpuRegister.Rsi];
if ((destination == 0 && size != 0) || size > MaxRandomBytes)
{
return ctx.SetReturn(RandomErrorInvalid);
}
if (size == 0)
{
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
}
Span<byte> bytes = stackalloc byte[(int)size];
RandomNumberGenerator.Fill(bytes);
return ctx.Memory.TryWrite(destination, bytes)
? ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK)
: ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
}
@@ -0,0 +1,69 @@
// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Random;
using Xunit;
namespace SharpEmu.Libs.Tests.Random;
public sealed class RandomExportsTests
{
private const ulong BaseAddress = 0x1000;
private const int RandomErrorInvalid = unchecked((int)0x817C0016);
[Fact]
public void GetRandomNumberWritesRequestedBytes()
{
var memory = new FakeCpuMemory(BaseAddress, 64);
var ctx = CreateContext(memory, BaseAddress, 64);
Assert.Equal(0, RandomExports.RandomGetRandomNumber(ctx));
var bytes = new byte[64];
Assert.True(memory.TryRead(BaseAddress, bytes));
Assert.NotEqual(new byte[64], bytes);
}
[Theory]
[InlineData(0, 1)]
[InlineData(BaseAddress, 65)]
public void GetRandomNumberRejectsInvalidArguments(ulong address, ulong size)
{
var memory = new FakeCpuMemory(BaseAddress, 64);
var ctx = CreateContext(memory, address, size);
Assert.Equal(RandomErrorInvalid, RandomExports.RandomGetRandomNumber(ctx));
}
[Fact]
public void GetRandomNumberAcceptsEmptyRequest()
{
var memory = new FakeCpuMemory(BaseAddress, 64);
var ctx = CreateContext(memory, 0, 0);
Assert.Equal(0, RandomExports.RandomGetRandomNumber(ctx));
}
[Fact]
public void GetRandomNumberReportsUnmappedDestination()
{
var memory = new FakeCpuMemory(BaseAddress, 64);
var ctx = CreateContext(memory, BaseAddress + 64, 1);
Assert.Equal(
(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT,
RandomExports.RandomGetRandomNumber(ctx));
}
private static CpuContext CreateContext(
ICpuMemory memory,
ulong destination,
ulong size)
{
var ctx = new CpuContext(memory, Generation.Gen5);
ctx[CpuRegister.Rdi] = destination;
ctx[CpuRegister.Rsi] = size;
return ctx;
}
}