mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-30 14:39:42 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbf2c2d00a | |||
| 8c1507777c | |||
| 8ebc43e758 | |||
| 5aadb7495a |
@@ -88,7 +88,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Last Milestone / First Error
|
label: Last Milestone / First Error
|
||||||
description: Paste the **last useful milestone line** or the **first `WARNING`, `ERROR`, or `CRITICAL` line connected to the stop** from the log. `DEBUG`/`TRACE` lines do not count unless a real error follows them.
|
description: Paste the **last useful milestone line** or the **first `WARNING`, `ERROR`, or `CRITICAL` line connected to the stop** from the log. `DEBUG`/`TRACE` lines do not count unless a real error follows them.
|
||||||
placeholder: e.g. CpuEngine: native-only OR [ERROR] Native backend FAILED: ...
|
placeholder: "e.g. CpuEngine: native-only OR [ERROR] Native backend FAILED: ..."
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
// 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,15 +160,17 @@ 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" Margin="16,12,16,8">
|
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,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..." MaxWidth="5500" />
|
Watermark="Search..." Width="320" />
|
||||||
<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="CopyLogButton" Classes="ghost" Content="Copy" FontSize="12"
|
<Button Grid.Column="3" x:Name="DetachConsoleButton" Classes="ghost" Content="Split" FontSize="12"
|
||||||
Padding="10,4" Margin="0,0,8,0" />
|
Padding="10,4" Margin="0,0,8,0" />
|
||||||
<Button Grid.Column="4" x:Name="ClearLogButton" Classes="ghost" Content="Clear" FontSize="12"
|
<Button Grid.Column="4" x:Name="CopyLogButton" Classes="ghost" Content="Copy" 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,6 +42,7 @@ 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;
|
||||||
@@ -98,8 +99,9 @@ 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;
|
ConsoleToggle.IsCheckedChanged += (_, _) => ConsolePanel.IsVisible = ConsoleToggle.IsChecked == true && _consoleWindow is null;
|
||||||
SelectLogFilePathButton.Click += async (_, _) => await SelectFilePathAsync();
|
SelectLogFilePathButton.Click += async (_, _) => await SelectFilePathAsync();
|
||||||
TitleMusicToggle.IsCheckedChanged += (_, _) => OnTitleMusicToggled();
|
TitleMusicToggle.IsCheckedChanged += (_, _) => OnTitleMusicToggled();
|
||||||
DiscordToggle.IsCheckedChanged += (_, _) =>
|
DiscordToggle.IsCheckedChanged += (_, _) =>
|
||||||
@@ -329,6 +331,7 @@ 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();
|
||||||
}
|
}
|
||||||
@@ -1470,4 +1473,28 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3296,10 +3296,60 @@ public static class AgcExports
|
|||||||
globalMemoryBindings,
|
globalMemoryBindings,
|
||||||
vertexInputs,
|
vertexInputs,
|
||||||
renderTargets,
|
renderTargets,
|
||||||
CreateRenderState(state.CxRegisters, renderTargets.FirstOrDefault()));
|
ApplyTransparentPremultipliedFillClear(
|
||||||
|
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,
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using SharpEmu.HLE;
|
||||||
|
|
||||||
|
namespace SharpEmu.Libs.DiscMap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HLE implementation of libSceDiscMap, ported from Kyty's LibDiscMap.cpp
|
||||||
|
/// (https://github.com/InoriRus/Kyty, MIT). Titles installed to internal
|
||||||
|
/// storage query these entry points on nearly every file access to decide
|
||||||
|
/// whether a read must be redirected to the Blu-ray drive. Reporting every
|
||||||
|
/// request as already resident on HDD lets the regular file system path
|
||||||
|
/// service the read.
|
||||||
|
/// </summary>
|
||||||
|
public static class DiscMapExports
|
||||||
|
{
|
||||||
|
private const int DiscMapErrorInvalidArgument = unchecked((int)0x81100001);
|
||||||
|
private const int DiscMapErrorLocationNotMapped = unchecked((int)0x81100002);
|
||||||
|
private const int DiscMapErrorFileNotFound = unchecked((int)0x81100003);
|
||||||
|
private const int DiscMapErrorNoBitmapInfo = unchecked((int)0x81100004);
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "lbQKqsERhtE",
|
||||||
|
ExportName = "sceDiscMapIsRequestOnHDD",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceDiscMap")]
|
||||||
|
public static int DiscMapIsRequestOnHDD(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var pathAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var offset = ctx[CpuRegister.Rsi];
|
||||||
|
var size = ctx[CpuRegister.Rdx];
|
||||||
|
var resultAddress = ctx[CpuRegister.Rcx];
|
||||||
|
|
||||||
|
if (pathAddress == 0 || resultAddress == 0)
|
||||||
|
{
|
||||||
|
return DiscMapErrorInvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteInt32(resultAddress, 1))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceDiscMap(ctx, "sceDiscMapIsRequestOnHDD", pathAddress, offset, size);
|
||||||
|
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "fJgP+wqifno",
|
||||||
|
ExportName = "sceDiscMapUnknownFJgP",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceDiscMap")]
|
||||||
|
public static int DiscMapUnknownFJgP(CpuContext ctx) => WriteMappingTriple(ctx, "sceDiscMapUnknownFJgP");
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "ioKMruft1ek",
|
||||||
|
ExportName = "sceDiscMapUnknownIoKM",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceDiscMap")]
|
||||||
|
public static int DiscMapUnknownIoKM(CpuContext ctx) => WriteMappingTriple(ctx, "sceDiscMapUnknownIoKM");
|
||||||
|
|
||||||
|
private static int WriteMappingTriple(CpuContext ctx, string exportName)
|
||||||
|
{
|
||||||
|
var pathAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var offset = ctx[CpuRegister.Rsi];
|
||||||
|
var size = ctx[CpuRegister.Rdx];
|
||||||
|
var flagsAddress = ctx[CpuRegister.Rcx];
|
||||||
|
var outAddress1 = ctx[CpuRegister.R8];
|
||||||
|
var outAddress2 = ctx[CpuRegister.R9];
|
||||||
|
|
||||||
|
if (pathAddress == 0 || flagsAddress == 0 || outAddress1 == 0 || outAddress2 == 0)
|
||||||
|
{
|
||||||
|
return DiscMapErrorInvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.TryWriteUInt64(flagsAddress, 0) ||
|
||||||
|
!ctx.TryWriteUInt64(outAddress1, 0) ||
|
||||||
|
!ctx.TryWriteUInt64(outAddress2, 0))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceDiscMap(ctx, exportName, pathAddress, offset, size);
|
||||||
|
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TraceDiscMap(CpuContext ctx, string exportName, ulong pathAddress, ulong offset, ulong size)
|
||||||
|
{
|
||||||
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_DISCMAP"), "1", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = ctx.TryReadNullTerminatedUtf8(pathAddress, 1024, out var value)
|
||||||
|
? value
|
||||||
|
: $"<unreadable 0x{pathAddress:X16}>";
|
||||||
|
Console.Error.WriteLine($"[HLE][DISCMAP] {exportName} path={path} offset=0x{offset:X} size=0x{size:X}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user