mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-26 04:39:17 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ca6af843e |
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 120 KiB |
@@ -216,10 +216,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharpemu.hle": {
|
"sharpemu.hle": {
|
||||||
"type": "Project",
|
"type": "Project"
|
||||||
"dependencies": {
|
|
||||||
"SharpEmu.Logging": "[1.0.0, )"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sharpemu.libs": {
|
"sharpemu.libs": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
|
|||||||
@@ -705,7 +705,6 @@ public sealed partial class DirectExecutionBackend
|
|||||||
"eE4Szl8sil8" or // sceKernelAprSubmitCommandBuffer
|
"eE4Szl8sil8" or // sceKernelAprSubmitCommandBuffer
|
||||||
"qvMUCyyaCSI" or // sceKernelAprSubmitCommandBufferAndGetId
|
"qvMUCyyaCSI" or // sceKernelAprSubmitCommandBufferAndGetId
|
||||||
"Q2V+iqvjgC0" or // vsnprintf
|
"Q2V+iqvjgC0" or // vsnprintf
|
||||||
"AV6ipCNa4Rw" or // strcasecmp
|
|
||||||
"q1cHNfGycLI" or // scePadRead
|
"q1cHNfGycLI" or // scePadRead
|
||||||
"xk0AcarP3V4" or // scePadOpen
|
"xk0AcarP3V4" or // scePadOpen
|
||||||
"yH17Q6NWtVg" or // sceUserServiceGetEvent
|
"yH17Q6NWtVg" or // sceUserServiceGetEvent
|
||||||
@@ -870,7 +869,6 @@ public sealed partial class DirectExecutionBackend
|
|||||||
"WkkeywLJcgU" or // wcslen
|
"WkkeywLJcgU" or // wcslen
|
||||||
"Ovb2dSJOAuE" or // strcmp
|
"Ovb2dSJOAuE" or // strcmp
|
||||||
"aesyjrHVWy4" or // strncmp
|
"aesyjrHVWy4" or // strncmp
|
||||||
"AV6ipCNa4Rw" or // strcasecmp
|
|
||||||
"pNtJdE3x49E" or // wcscmp
|
"pNtJdE3x49E" or // wcscmp
|
||||||
"fV2xHER+bKE" or // wcscoll
|
"fV2xHER+bKE" or // wcscoll
|
||||||
"E8wCoUEbfzk" or // wcsncmp
|
"E8wCoUEbfzk" or // wcsncmp
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public sealed class SharpEmuRuntime : ISharpEmuRuntime
|
|||||||
LastMilestoneLog = null;
|
LastMilestoneLog = null;
|
||||||
KernelModuleRegistry.Reset();
|
KernelModuleRegistry.Reset();
|
||||||
var image = LoadImage(normalizedEbootPath);
|
var image = LoadImage(normalizedEbootPath);
|
||||||
VideoOutExports.ConfigureApplicationInfo(image.Title, image.TitleId, image.Version, BuildInfo.CommitSha);
|
VideoOutExports.ConfigureApplicationInfo(image.Title, image.TitleId, image.Version);
|
||||||
SaveDataExports.ConfigureApplicationInfo(image.TitleId);
|
SaveDataExports.ConfigureApplicationInfo(image.TitleId);
|
||||||
LogAppBundleInfo(normalizedEbootPath, image);
|
LogAppBundleInfo(normalizedEbootPath, image);
|
||||||
RegisterLoadedModule(normalizedEbootPath, image, isMain: true, isSystemModule: false);
|
RegisterLoadedModule(normalizedEbootPath, image, isMain: true, isSystemModule: false);
|
||||||
|
|||||||
@@ -65,10 +65,7 @@
|
|||||||
"contentHash": "Iy22JopynbOJ32vA0lBhFEzGi65GQJBuJHYBYRBpydrDpNoTiHnjIXfA65Gu+8qsOr/ZEoIF8r9aHCgAXuO6DA=="
|
"contentHash": "Iy22JopynbOJ32vA0lBhFEzGi65GQJBuJHYBYRBpydrDpNoTiHnjIXfA65Gu+8qsOr/ZEoIF8r9aHCgAXuO6DA=="
|
||||||
},
|
},
|
||||||
"sharpemu.hle": {
|
"sharpemu.hle": {
|
||||||
"type": "Project",
|
"type": "Project"
|
||||||
"dependencies": {
|
|
||||||
"SharpEmu.Logging": "[1.0.0, )"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sharpemu.libs": {
|
"sharpemu.libs": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.Templates;
|
|
||||||
using Avalonia.Data;
|
|
||||||
using Avalonia.Layout;
|
|
||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.Platform;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
|
|
||||||
namespace SharpEmu.GUI;
|
|
||||||
|
|
||||||
public sealed class ConsoleWindow : Window
|
|
||||||
{
|
|
||||||
private readonly AvaloniaList<LogLine> _sourceLines;
|
|
||||||
private readonly AvaloniaList<LogLine> _visibleLines = new();
|
|
||||||
private readonly ListBox _list;
|
|
||||||
private readonly TextBox _searchBox;
|
|
||||||
private readonly CheckBox _autoScrollCheck;
|
|
||||||
|
|
||||||
public ConsoleWindow(
|
|
||||||
AvaloniaList<LogLine> lines,
|
|
||||||
Action clear,
|
|
||||||
bool autoScroll)
|
|
||||||
{
|
|
||||||
_sourceLines = lines;
|
|
||||||
Title = "SharpEmu Console";
|
|
||||||
Width = 980;
|
|
||||||
Height = 620;
|
|
||||||
MinWidth = 520;
|
|
||||||
MinHeight = 320;
|
|
||||||
Background = new SolidColorBrush(Color.Parse("#0D1017"));
|
|
||||||
Icon = new WindowIcon(AssetLoader.Open(new Uri("avares://SharpEmu.GUI/Assets/SharpEmu.ico")));
|
|
||||||
|
|
||||||
_searchBox = new TextBox { Watermark = "Search...", Width = 320, Margin = new Thickness(0, 0, 12, 0) };
|
|
||||||
_autoScrollCheck = new CheckBox
|
|
||||||
{
|
|
||||||
Content = "Auto-scroll",
|
|
||||||
IsChecked = autoScroll,
|
|
||||||
FontSize = 12,
|
|
||||||
Margin = new Thickness(0, 0, 12, 0),
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
};
|
|
||||||
var copyButton = new Button
|
|
||||||
{
|
|
||||||
Classes = { "ghost" },
|
|
||||||
Content = "Copy",
|
|
||||||
Padding = new Thickness(10, 4),
|
|
||||||
Margin = new Thickness(0, 0, 8, 0),
|
|
||||||
};
|
|
||||||
var clearButton = new Button { Classes = { "ghost" }, Content = "Clear", Padding = new Thickness(10, 4) };
|
|
||||||
copyButton.Click += async (_, _) => await CopyAsync();
|
|
||||||
clearButton.Click += (_, _) => clear();
|
|
||||||
_searchBox.TextChanged += (_, _) => RefreshVisibleLines();
|
|
||||||
|
|
||||||
_list = new ListBox
|
|
||||||
{
|
|
||||||
Classes = { "console" },
|
|
||||||
ItemsSource = _visibleLines,
|
|
||||||
BorderThickness = new Thickness(1),
|
|
||||||
BorderBrush = new SolidColorBrush(Color.Parse("#232B3A")),
|
|
||||||
ItemTemplate = new FuncDataTemplate<LogLine>((_, _) =>
|
|
||||||
{
|
|
||||||
var text = new TextBlock { TextWrapping = TextWrapping.NoWrap };
|
|
||||||
text.Bind(TextBlock.TextProperty, new Binding(nameof(LogLine.Text)));
|
|
||||||
text.Bind(TextBlock.ForegroundProperty, new Binding(nameof(LogLine.Brush)));
|
|
||||||
return text;
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
Content = new Grid
|
|
||||||
{
|
|
||||||
Margin = new Thickness(12),
|
|
||||||
RowDefinitions = new RowDefinitions("Auto,*"),
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new Grid
|
|
||||||
{
|
|
||||||
Margin = new Thickness(0, 0, 0, 8),
|
|
||||||
ColumnDefinitions = new ColumnDefinitions("*,Auto,Auto,Auto,Auto"),
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
new TextBlock
|
|
||||||
{
|
|
||||||
Classes = { "sectionTitle" },
|
|
||||||
Text = "CONSOLE",
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
|
||||||
},
|
|
||||||
_searchBox.WithGridColumn(1),
|
|
||||||
_autoScrollCheck.WithGridColumn(2),
|
|
||||||
copyButton.WithGridColumn(3),
|
|
||||||
clearButton.WithGridColumn(4),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_list.WithGridRow(1),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
lines.CollectionChanged += OnLinesChanged;
|
|
||||||
Closed += (_, _) => lines.CollectionChanged -= OnLinesChanged;
|
|
||||||
RefreshVisibleLines();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLinesChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
RefreshVisibleLines();
|
|
||||||
if (_autoScrollCheck.IsChecked == true)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(() => (_list.Scroll as ScrollViewer)?.ScrollToEnd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshVisibleLines()
|
|
||||||
{
|
|
||||||
var query = _searchBox.Text ?? string.Empty;
|
|
||||||
_visibleLines.Clear();
|
|
||||||
_visibleLines.AddRange(string.IsNullOrWhiteSpace(query)
|
|
||||||
? _sourceLines
|
|
||||||
: _sourceLines.Where(line => line.Text.Contains(query, StringComparison.OrdinalIgnoreCase)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CopyAsync()
|
|
||||||
{
|
|
||||||
if (_visibleLines.Count == 0 || Clipboard is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Clipboard.SetTextAsync(string.Join(Environment.NewLine, _visibleLines.Select(line => line.Text)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file static class GridExtensions
|
|
||||||
{
|
|
||||||
public static T WithGridColumn<T>(this T control, int column) where T : Control
|
|
||||||
{
|
|
||||||
Grid.SetColumn(control, column);
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T WithGridRow<T>(this T control, int row) where T : Control
|
|
||||||
{
|
|
||||||
Grid.SetRow(control, row);
|
|
||||||
return control;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -160,17 +160,15 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<Border Grid.Row="2" x:Name="ConsolePanel" Classes="card" Padding="0" Height="240"
|
<Border Grid.Row="2" x:Name="ConsolePanel" Classes="card" Padding="0" Height="240"
|
||||||
Margin="0,12,0,0" IsVisible="False">
|
Margin="0,12,0,0" IsVisible="False">
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto,Auto,Auto" Margin="16,12,16,8">
|
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto, Auto" Margin="16,12,16,8">
|
||||||
<TextBlock Classes="sectionTitle" Text="CONSOLE" VerticalAlignment="Center" />
|
<TextBlock Classes="sectionTitle" Text="CONSOLE" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Column="1" FontSize="12" Margin="0,0,12,0" x:Name="ConsoleSearchBox"
|
<TextBox Grid.Column="1" FontSize="12" Margin="0,0,12,0" x:Name="ConsoleSearchBox"
|
||||||
Watermark="Search..." Width="320" />
|
Watermark="Search..." MaxWidth="5500" />
|
||||||
<CheckBox Grid.Column="2" x:Name="AutoScrollCheck" Content="Auto-scroll" IsChecked="True"
|
<CheckBox Grid.Column="2" x:Name="AutoScrollCheck" Content="Auto-scroll" IsChecked="True"
|
||||||
FontSize="12" Margin="0,0,12,0" />
|
FontSize="12" Margin="0,0,12,0" />
|
||||||
<Button Grid.Column="3" x:Name="DetachConsoleButton" Classes="ghost" Content="Split" FontSize="12"
|
<Button Grid.Column="3" x:Name="CopyLogButton" Classes="ghost" Content="Copy" FontSize="12"
|
||||||
Padding="10,4" Margin="0,0,8,0" />
|
Padding="10,4" Margin="0,0,8,0" />
|
||||||
<Button Grid.Column="4" x:Name="CopyLogButton" Classes="ghost" Content="Copy" FontSize="12"
|
<Button Grid.Column="4" x:Name="ClearLogButton" Classes="ghost" Content="Clear" FontSize="12"
|
||||||
Padding="10,4" Margin="0,0,8,0" />
|
|
||||||
<Button Grid.Column="5" x:Name="ClearLogButton" Classes="ghost" Content="Clear" FontSize="12"
|
|
||||||
Padding="10,4" />
|
Padding="10,4" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<ListBox Grid.Row="1" x:Name="ConsoleList" Classes="console" BorderThickness="0,1,0,0"
|
<ListBox Grid.Row="1" x:Name="ConsoleList" Classes="console" BorderThickness="0,1,0,0"
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private GuiSettings _settings = new();
|
private GuiSettings _settings = new();
|
||||||
private EmulatorProcess? _emulator;
|
private EmulatorProcess? _emulator;
|
||||||
private ConsoleWindow? _consoleWindow;
|
|
||||||
private StreamWriter? _fileLog;
|
private StreamWriter? _fileLog;
|
||||||
private readonly SndPreviewPlayer _sndPreview = new();
|
private readonly SndPreviewPlayer _sndPreview = new();
|
||||||
private string? _emulatorExePath;
|
private string? _emulatorExePath;
|
||||||
@@ -99,9 +98,8 @@ public partial class MainWindow : Window
|
|||||||
StopButton.Click += (_, _) => StopEmulator();
|
StopButton.Click += (_, _) => StopEmulator();
|
||||||
ClearLogButton.Click += (_, _) => _consoleLines.Clear();
|
ClearLogButton.Click += (_, _) => _consoleLines.Clear();
|
||||||
CopyLogButton.Click += async (_, _) => await CopyConsoleAsync();
|
CopyLogButton.Click += async (_, _) => await CopyConsoleAsync();
|
||||||
DetachConsoleButton.Click += (_, _) => ShowConsoleWindow();
|
|
||||||
OptionsToggle.IsCheckedChanged += (_, _) => OptionsPanel.IsVisible = OptionsToggle.IsChecked == true;
|
OptionsToggle.IsCheckedChanged += (_, _) => OptionsPanel.IsVisible = OptionsToggle.IsChecked == true;
|
||||||
ConsoleToggle.IsCheckedChanged += (_, _) => ConsolePanel.IsVisible = ConsoleToggle.IsChecked == true && _consoleWindow is null;
|
ConsoleToggle.IsCheckedChanged += (_, _) => ConsolePanel.IsVisible = ConsoleToggle.IsChecked == true;
|
||||||
SelectLogFilePathButton.Click += async (_, _) => await SelectFilePathAsync();
|
SelectLogFilePathButton.Click += async (_, _) => await SelectFilePathAsync();
|
||||||
TitleMusicToggle.IsCheckedChanged += (_, _) => OnTitleMusicToggled();
|
TitleMusicToggle.IsCheckedChanged += (_, _) => OnTitleMusicToggled();
|
||||||
DiscordToggle.IsCheckedChanged += (_, _) =>
|
DiscordToggle.IsCheckedChanged += (_, _) =>
|
||||||
@@ -331,7 +329,6 @@ public partial class MainWindow : Window
|
|||||||
_gamepadTimer.Stop();
|
_gamepadTimer.Stop();
|
||||||
_sndPreview.Stop();
|
_sndPreview.Stop();
|
||||||
_discord?.Dispose();
|
_discord?.Dispose();
|
||||||
_consoleWindow?.Close();
|
|
||||||
_emulator?.Dispose();
|
_emulator?.Dispose();
|
||||||
DropFileLog();
|
DropFileLog();
|
||||||
}
|
}
|
||||||
@@ -1473,28 +1470,4 @@ public partial class MainWindow : Window
|
|||||||
var text = string.Join(Environment.NewLine, _consoleLines.Select(line => line.Text));
|
var text = string.Join(Environment.NewLine, _consoleLines.Select(line => line.Text));
|
||||||
await Clipboard.SetTextAsync(text);
|
await Clipboard.SetTextAsync(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowConsoleWindow()
|
|
||||||
{
|
|
||||||
if (_consoleWindow is { } window)
|
|
||||||
{
|
|
||||||
window.Activate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConsoleSearchBox.Text = string.Empty;
|
|
||||||
ConsoleToggle.IsChecked = false;
|
|
||||||
ConsolePanel.IsVisible = false;
|
|
||||||
_consoleWindow = new ConsoleWindow(
|
|
||||||
_consoleLines,
|
|
||||||
() => { _consoleLines.Clear(); _allConsoleLines.Clear(); },
|
|
||||||
AutoScrollCheck.IsChecked == true);
|
|
||||||
_consoleWindow.Closed += (_, _) =>
|
|
||||||
{
|
|
||||||
_consoleWindow = null;
|
|
||||||
ConsoleToggle.IsChecked = true;
|
|
||||||
ConsolePanel.IsVisible = true;
|
|
||||||
};
|
|
||||||
_consoleWindow.Show(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@
|
|||||||
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using SharpEmu.Logging;
|
|
||||||
|
|
||||||
namespace SharpEmu.HLE;
|
namespace SharpEmu.HLE;
|
||||||
|
|
||||||
public sealed class Aerolib : ISymbolCatalog
|
public sealed class Aerolib : ISymbolCatalog
|
||||||
{
|
{
|
||||||
private static readonly SharpEmuLogger Log = SharpEmuLog.For("Aerolib");
|
|
||||||
private static readonly Lazy<Aerolib> _instance = new(() => new Aerolib());
|
private static readonly Lazy<Aerolib> _instance = new(() => new Aerolib());
|
||||||
private static readonly Aerolib EmptyCatalog = new Aerolib(empty: true);
|
private static readonly Aerolib EmptyCatalog = new Aerolib(empty: true);
|
||||||
|
|
||||||
@@ -110,14 +108,14 @@ public sealed class Aerolib : ISymbolCatalog
|
|||||||
|
|
||||||
if (resourceName == null)
|
if (resourceName == null)
|
||||||
{
|
{
|
||||||
Log.Error("Embedded resource 'aerolib.bin' not found");
|
Console.Error.WriteLine("[AEROLIB] Embedded resource 'aerolib.bin' not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to open embedded resource stream");
|
Console.Error.WriteLine("[AEROLIB] Failed to open embedded resource stream");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,11 +145,11 @@ public sealed class Aerolib : ISymbolCatalog
|
|||||||
_byExportName[name] = symbol;
|
_byExportName[name] = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Info($"Loaded {_byNid.Count} NID entries from binary resource");
|
Console.Error.WriteLine($"[AEROLIB] Loaded {_byNid.Count} NID entries from binary resource");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error($"Failed to load embedded aerolib.bin: {ex.Message}", ex);
|
Console.Error.WriteLine($"[AEROLIB] Failed to load embedded aerolib.bin: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,11 @@
|
|||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using SharpEmu.Logging;
|
|
||||||
|
|
||||||
namespace SharpEmu.HLE;
|
namespace SharpEmu.HLE;
|
||||||
|
|
||||||
public sealed class ModuleManager : IModuleManager
|
public sealed class ModuleManager : IModuleManager
|
||||||
{
|
{
|
||||||
private static readonly SharpEmuLogger Log = SharpEmuLog.For("HLE");
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, Delegate> _dispatchTable = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, Delegate> _dispatchTable = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentDictionary<string, ExportedFunction> _exportTable = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, ExportedFunction> _exportTable = new(StringComparer.Ordinal);
|
||||||
private readonly ConcurrentDictionary<string, ExportedFunction> _exportNameTable = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, ExportedFunction> _exportNameTable = new(StringComparer.Ordinal);
|
||||||
@@ -50,7 +47,7 @@ public sealed class ModuleManager : IModuleManager
|
|||||||
var handler = CreateHandler(type, method, instances);
|
var handler = CreateHandler(type, method, instances);
|
||||||
if (!_dispatchTable.TryAdd(exportInfo.Value.Nid, handler))
|
if (!_dispatchTable.TryAdd(exportInfo.Value.Nid, handler))
|
||||||
{
|
{
|
||||||
Log.Warning($"Duplicate NID '{exportInfo.Value.Nid}' ({exportInfo.Value.ExportName}) — already registered, skipping.");
|
Console.Error.WriteLine($"[HLE] Duplicate NID '{exportInfo.Value.Nid}' ({exportInfo.Value.ExportName}) — already registered, skipping.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +106,7 @@ public sealed class ModuleManager : IModuleManager
|
|||||||
|
|
||||||
if (!_dispatchTable.TryGetValue(nid, out var function) || !_exportTable.TryGetValue(nid, out var export))
|
if (!_dispatchTable.TryGetValue(nid, out var function) || !_exportTable.TryGetValue(nid, out var export))
|
||||||
{
|
{
|
||||||
Log.Warning($"NID '{nid}' not found in dispatch table.");
|
Console.Error.WriteLine($"[HLE] NID '{nid}' not found in dispatch table.");
|
||||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||||
return false;
|
return false;
|
||||||
@@ -117,7 +114,7 @@ public sealed class ModuleManager : IModuleManager
|
|||||||
|
|
||||||
if ((export.Target & context.TargetGeneration) == 0)
|
if ((export.Target & context.TargetGeneration) == 0)
|
||||||
{
|
{
|
||||||
Log.Warning($"NID '{nid}' ({export.Name}) found but not implemented for generation {context.TargetGeneration} (targets: {export.Target}).");
|
Console.Error.WriteLine($"[HLE] NID '{nid}' ({export.Name}) found but not implemented for generation {context.TargetGeneration} (targets: {export.Target}).");
|
||||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED);
|
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED);
|
||||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED;
|
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -12,10 +12,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\SharpEmu.Logging\SharpEmu.Logging.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Aerolib\aerolib.bin" />
|
<EmbeddedResource Include="Aerolib\aerolib.bin" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net10.0": {
|
"net10.0": {}
|
||||||
"sharpemu.logging": {
|
|
||||||
"type": "Project"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3296,60 +3296,10 @@ public static class AgcExports
|
|||||||
globalMemoryBindings,
|
globalMemoryBindings,
|
||||||
vertexInputs,
|
vertexInputs,
|
||||||
renderTargets,
|
renderTargets,
|
||||||
ApplyTransparentPremultipliedFillClear(
|
CreateRenderState(state.CxRegisters, renderTargets.FirstOrDefault()));
|
||||||
CreateRenderState(state.CxRegisters, renderTargets.FirstOrDefault()),
|
|
||||||
textures,
|
|
||||||
vertexInputs,
|
|
||||||
pixelEvaluation.InitialScalarRegisters));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly bool _transparentFillClearEnabled = !string.Equals(
|
|
||||||
Environment.GetEnvironmentVariable("SHARPEMU_DISABLE_TRANSPARENT_FILL_CLEAR"),
|
|
||||||
"1",
|
|
||||||
StringComparison.Ordinal);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Chowdren resets its effect layers with an untextured transparent-black
|
|
||||||
/// fill using premultiplied blending. With One/OneMinusSrcAlpha that draw
|
|
||||||
/// is otherwise a no-op, causing fog and vignette layers to accumulate.
|
|
||||||
/// Treat precisely that draw shape as an overwrite.
|
|
||||||
/// </summary>
|
|
||||||
private static VulkanGuestRenderState ApplyTransparentPremultipliedFillClear(
|
|
||||||
VulkanGuestRenderState renderState,
|
|
||||||
IReadOnlyList<TranslatedImageBinding> textures,
|
|
||||||
IReadOnlyList<Gen5VertexInputBinding> vertexInputs,
|
|
||||||
IReadOnlyList<uint> pixelUserData)
|
|
||||||
{
|
|
||||||
if (!_transparentFillClearEnabled ||
|
|
||||||
textures.Count != 0 ||
|
|
||||||
vertexInputs.Count != 0 ||
|
|
||||||
pixelUserData.Count < 4 ||
|
|
||||||
renderState.Blend is not
|
|
||||||
{
|
|
||||||
Enable: true,
|
|
||||||
ColorSrcFactor: 1,
|
|
||||||
ColorDstFactor: 5,
|
|
||||||
ColorFunc: 0,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return renderState;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var index = 0; index < 4; index++)
|
|
||||||
{
|
|
||||||
if ((pixelUserData[index] & 0x7FFF_FFFFu) != 0)
|
|
||||||
{
|
|
||||||
return renderState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderState with
|
|
||||||
{
|
|
||||||
Blend = renderState.Blend with { Enable = false },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static VulkanGuestIndexBuffer? CreateVulkanIndexBuffer(
|
private static VulkanGuestIndexBuffer? CreateVulkanIndexBuffer(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
SubmittedDcbState state,
|
SubmittedDcbState state,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ internal static class AmprFileRegistry
|
|||||||
return _hostPathsById.TryGetValue(id, out hostPath!);
|
return _hostPathsById.TryGetValue(id, out hostPath!);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static uint ComputeFileId(string guestPath)
|
private static uint ComputeFileId(string guestPath)
|
||||||
{
|
{
|
||||||
var bytes = System.Text.Encoding.UTF8.GetBytes(guestPath);
|
var bytes = System.Text.Encoding.UTF8.GetBytes(guestPath);
|
||||||
|
|
||||||
|
|||||||
@@ -1689,15 +1689,15 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entryCount = (int)count;
|
|
||||||
Span<uint> localIds = count <= 256 ? stackalloc uint[entryCount] : new uint[entryCount];
|
|
||||||
Span<ulong> localSizes = count <= 128 ? stackalloc ulong[entryCount] : new ulong[entryCount];
|
|
||||||
var resolvedGuestPaths = new string[entryCount];
|
|
||||||
var resolvedHostPaths = new string[entryCount];
|
|
||||||
|
|
||||||
for (ulong i = 0; i < count; i++)
|
for (ulong i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var index = (int)i;
|
if (idsAddress != 0 &&
|
||||||
|
!TryWriteUInt32Compat(ctx, idsAddress + (i * sizeof(uint)), uint.MaxValue))
|
||||||
|
{
|
||||||
|
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
if (!TryResolveAprFilepath(ctx, pathListAddress, i, out var guestPath))
|
if (!TryResolveAprFilepath(ctx, pathListAddress, i, out var guestPath))
|
||||||
{
|
{
|
||||||
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
||||||
@@ -1712,45 +1712,21 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileId = AmprFileRegistry.ComputeFileId(guestPath);
|
var fileId = AmprFileRegistry.Register(guestPath, hostPath);
|
||||||
LogIoTrace("apr_resolve", guestPath, $"host='{hostPath}' index={i} count={count} id=0x{fileId:X8} size={fileSize}");
|
LogIoTrace("apr_resolve", guestPath, $"host='{hostPath}' index={i} count={count} id=0x{fileId:X8} size={fileSize}");
|
||||||
|
|
||||||
localIds[index] = fileId;
|
if (idsAddress != 0 &&
|
||||||
localSizes[index] = fileSize;
|
!TryWriteUInt32Compat(ctx, idsAddress + (i * sizeof(uint)), fileId))
|
||||||
resolvedGuestPaths[index] = guestPath;
|
|
||||||
resolvedHostPaths[index] = hostPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<byte> sizePayload = count <= 64 ? stackalloc byte[entryCount * sizeof(ulong)] : new byte[entryCount * sizeof(ulong)];
|
|
||||||
for (ulong i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt64LittleEndian(sizePayload[(int)(i * sizeof(ulong))..], localSizes[(int)i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteCompat(ctx, sizesAddress, sizePayload))
|
|
||||||
{
|
|
||||||
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idsAddress != 0)
|
|
||||||
{
|
|
||||||
Span<byte> idPayload = count <= 128 ? stackalloc byte[entryCount * sizeof(uint)] : new byte[entryCount * sizeof(uint)];
|
|
||||||
for (ulong i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(idPayload[(int)(i * sizeof(uint))..], localIds[(int)i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryWriteCompat(ctx, idsAddress, idPayload))
|
|
||||||
{
|
{
|
||||||
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < entryCount; i++)
|
if (!TryWriteUInt64Compat(ctx, sizesAddress + (i * sizeof(ulong)), fileSize))
|
||||||
{
|
{
|
||||||
AmprFileRegistry.Register(resolvedGuestPaths[i], resolvedHostPaths[i]);
|
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
ctx[CpuRegister.Rax] = 0;
|
||||||
@@ -6374,19 +6350,19 @@ public static class KernelMemoryCompatExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentIndex = directory.NextIndex;
|
var currentIndex = directory.NextIndex;
|
||||||
|
if (basePointerAddress != 0 && !TryWriteUInt64Compat(ctx, basePointerAddress, (ulong)currentIndex))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentIndex >= directory.Entries.Length)
|
if (currentIndex >= directory.Entries.Length)
|
||||||
{
|
{
|
||||||
if (basePointerAddress != 0 &&
|
|
||||||
!TryWriteUInt64Compat(ctx, basePointerAddress, (ulong)currentIndex))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx[CpuRegister.Rax] = 0;
|
ctx[CpuRegister.Rax] = 0;
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entryName = directory.Entries[currentIndex];
|
var entryName = directory.Entries[currentIndex];
|
||||||
|
directory.NextIndex = currentIndex + 1;
|
||||||
|
|
||||||
var entryBytes = Encoding.UTF8.GetBytes(entryName);
|
var entryBytes = Encoding.UTF8.GetBytes(entryName);
|
||||||
var nameLength = Math.Min(entryBytes.Length, 255);
|
var nameLength = Math.Min(entryBytes.Length, 255);
|
||||||
@@ -6405,13 +6381,6 @@ public static class KernelMemoryCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (basePointerAddress != 0 &&
|
|
||||||
!TryWriteUInt64Compat(ctx, basePointerAddress, (ulong)currentIndex))
|
|
||||||
{
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
directory.NextIndex = currentIndex + 1;
|
|
||||||
ctx[CpuRegister.Rax] = 512;
|
ctx[CpuRegister.Rax] = 512;
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public static class VideoOutExports
|
|||||||
private static long _vblankSignalCount;
|
private static long _vblankSignalCount;
|
||||||
private static long _flipSubmitCount;
|
private static long _flipSubmitCount;
|
||||||
|
|
||||||
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version, string? emulatorCommitSha)
|
public static void ConfigureApplicationInfo(string? title, string? titleId, string? version)
|
||||||
{
|
{
|
||||||
var parts = new List<string>();
|
var parts = new List<string>();
|
||||||
if (!string.IsNullOrWhiteSpace(title))
|
if (!string.IsNullOrWhiteSpace(title))
|
||||||
@@ -79,10 +79,9 @@ public static class VideoOutExports
|
|||||||
|
|
||||||
var application = parts.Count == 0 ? "VideoOut" : string.Join(' ', parts);
|
var application = parts.Count == 0 ? "VideoOut" : string.Join(' ', parts);
|
||||||
var versionSuffix = string.IsNullOrWhiteSpace(version) ? string.Empty : $" v{version.Trim()}";
|
var versionSuffix = string.IsNullOrWhiteSpace(version) ? string.Empty : $" v{version.Trim()}";
|
||||||
var commitSuffix = string.IsNullOrWhiteSpace(emulatorCommitSha) ? string.Empty : $" \u00b7 {emulatorCommitSha.Trim()}";
|
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
{
|
{
|
||||||
_windowTitle = $"SharpEmu{commitSuffix} - {application}{versionSuffix}";
|
_windowTitle = $"SharpEmu - {application}{versionSuffix}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,12 +98,6 @@
|
|||||||
"contentHash": "Iy22JopynbOJ32vA0lBhFEzGi65GQJBuJHYBYRBpydrDpNoTiHnjIXfA65Gu+8qsOr/ZEoIF8r9aHCgAXuO6DA=="
|
"contentHash": "Iy22JopynbOJ32vA0lBhFEzGi65GQJBuJHYBYRBpydrDpNoTiHnjIXfA65Gu+8qsOr/ZEoIF8r9aHCgAXuO6DA=="
|
||||||
},
|
},
|
||||||
"sharpemu.hle": {
|
"sharpemu.hle": {
|
||||||
"type": "Project",
|
|
||||||
"dependencies": {
|
|
||||||
"SharpEmu.Logging": "[1.0.0, )"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sharpemu.logging": {
|
|
||||||
"type": "Project"
|
"type": "Project"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user