mirror of
https://git.zaroz.cloud/nintendo-back-up/Ryujinx.git
synced 2025-12-25 11:43:00 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aceb534cb | ||
|
|
a0af6e4d07 | ||
|
|
f61b7818c3 | ||
|
|
a2a97e1b11 | ||
|
|
8b2625b0be | ||
|
|
651e24fed9 | ||
|
|
41b104d0fb | ||
|
|
bc44b85b0b | ||
|
|
01c2b8097c | ||
|
|
4bd2ca3f0d | ||
|
|
e63157cc33 | ||
|
|
7f2fb049f5 | ||
|
|
4744bde0e5 | ||
|
|
4a835bb2b9 | ||
|
|
ddc9ae2a83 | ||
|
|
d6d3cdd573 |
@@ -3,18 +3,18 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.0.3" />
|
<PackageVersion Include="Avalonia" Version="11.0.4" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.3" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.4" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.3" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.4" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.3" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.4" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.3" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.4" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.2" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.2" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.1" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.4" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.3.0-beta.4" />
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.31.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.2" />
|
<PackageVersion Include="System.Management" Version="7.0.2" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
if (start.CompareTo(node.End) < 0)
|
if (start.CompareTo(node.End) < 0)
|
||||||
{
|
{
|
||||||
if (overlaps.Length >= overlapCount)
|
if (overlaps.Length <= overlapCount)
|
||||||
{
|
{
|
||||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public bool IsEffectEnabled { get; }
|
public bool IsEffectEnabled { get; }
|
||||||
|
|
||||||
public AuxiliaryBufferCommand(uint bufferOffset, byte inputBufferOffset, byte outputBufferOffset,
|
public AuxiliaryBufferCommand(
|
||||||
ref AuxiliaryBufferAddresses sendBufferInfo, bool isEnabled, uint countMax,
|
uint bufferOffset,
|
||||||
CpuAddress outputBuffer, CpuAddress inputBuffer, uint updateCount, uint writeOffset, int nodeId)
|
byte inputBufferOffset,
|
||||||
|
byte outputBufferOffset,
|
||||||
|
ref AuxiliaryBufferAddresses sendBufferInfo,
|
||||||
|
bool isEnabled,
|
||||||
|
uint countMax,
|
||||||
|
CpuAddress outputBuffer,
|
||||||
|
CpuAddress inputBuffer,
|
||||||
|
uint updateCount,
|
||||||
|
uint writeOffset,
|
||||||
|
int nodeId)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
|
|||||||
@@ -21,7 +21,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private BiquadFilterParameter _parameter;
|
private BiquadFilterParameter _parameter;
|
||||||
|
|
||||||
public BiquadFilterCommand(int baseIndex, ref BiquadFilterParameter filter, Memory<BiquadFilterState> biquadFilterStateMemory, int inputBufferOffset, int outputBufferOffset, bool needInitialization, int nodeId)
|
public BiquadFilterCommand(
|
||||||
|
int baseIndex,
|
||||||
|
ref BiquadFilterParameter filter,
|
||||||
|
Memory<BiquadFilterState> biquadFilterStateMemory,
|
||||||
|
int inputBufferOffset,
|
||||||
|
int outputBufferOffset,
|
||||||
|
bool needInitialization,
|
||||||
|
int nodeId)
|
||||||
{
|
{
|
||||||
_parameter = filter;
|
_parameter = filter;
|
||||||
BiquadFilterState = biquadFilterStateMemory;
|
BiquadFilterState = biquadFilterStateMemory;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public unsafe void ClearBuffer(int index)
|
public unsafe void ClearBuffer(int index)
|
||||||
{
|
{
|
||||||
Unsafe.InitBlock((void*)GetBufferPointer(index), 0, SampleCount);
|
Unsafe.InitBlock((void*)GetBufferPointer(index), 0, SampleCount * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -89,7 +89,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public unsafe void CopyBuffer(int outputBufferIndex, int inputBufferIndex)
|
public unsafe void CopyBuffer(int outputBufferIndex, int inputBufferIndex)
|
||||||
{
|
{
|
||||||
Unsafe.CopyBlock((void*)GetBufferPointer(outputBufferIndex), (void*)GetBufferPointer(inputBufferIndex), SampleCount);
|
Unsafe.CopyBlock((void*)GetBufferPointer(outputBufferIndex), (void*)GetBufferPointer(inputBufferIndex), SampleCount * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|||||||
@@ -94,18 +94,18 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
||||||
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||||
float z = 0.0f;
|
float z = 1.0f;
|
||||||
|
|
||||||
bool unknown10OutOfRange = false;
|
bool unknown10OutOfRange = y >= state.Unknown10;
|
||||||
|
|
||||||
if (newMean < 1.0e-10f)
|
if (newMean < 1.0e-10f)
|
||||||
{
|
{
|
||||||
z = 1.0f;
|
y = -100.0f;
|
||||||
|
|
||||||
unknown10OutOfRange = state.Unknown10 < -100.0f;
|
unknown10OutOfRange = state.Unknown10 <= -100.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y >= state.Unknown10 || unknown10OutOfRange)
|
if (unknown10OutOfRange)
|
||||||
{
|
{
|
||||||
float tmpGain;
|
float tmpGain;
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
|
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
|
||||||
}
|
}
|
||||||
|
|
||||||
z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
|
z = FloatingPointHelper.DecibelToLinear(tmpGain);
|
||||||
}
|
}
|
||||||
|
|
||||||
float unknown4New = z;
|
float unknown4New = z;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
Matrix2x2 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain,
|
Matrix2x2 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain,
|
||||||
delayFeedbackCrossGain, delayFeedbackBaseGain);
|
delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -125,9 +125,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
Matrix4x4 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
|
Matrix4x4 delayFeedback = new(delayFeedbackBaseGain, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
|
||||||
delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain,
|
delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain,
|
||||||
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
||||||
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
@@ -172,11 +172,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
Matrix6x6 delayFeedback = new(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
|
Matrix6x6 delayFeedback = new(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
|
||||||
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
|
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
|
||||||
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
|
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
|
||||||
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
|
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
|
||||||
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
||||||
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,7 +28,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private LimiterParameter _parameter;
|
private LimiterParameter _parameter;
|
||||||
|
|
||||||
public LimiterCommandVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> resultState, bool isEnabled, ulong workBuffer, int nodeId)
|
public LimiterCommandVersion2(
|
||||||
|
uint bufferOffset,
|
||||||
|
LimiterParameter parameter,
|
||||||
|
Memory<LimiterState> state,
|
||||||
|
Memory<EffectResultState> resultState,
|
||||||
|
bool isEnabled,
|
||||||
|
ulong workBuffer,
|
||||||
|
int nodeId)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
|
|||||||
@@ -79,53 +79,57 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ProcessReverbMono(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private void ProcessReverbMono(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
ProcessReverbGeneric(ref state,
|
ProcessReverbGeneric(
|
||||||
outputBuffers,
|
ref state,
|
||||||
inputBuffers,
|
outputBuffers,
|
||||||
sampleCount,
|
inputBuffers,
|
||||||
_outputEarlyIndicesTableMono,
|
sampleCount,
|
||||||
_targetEarlyDelayLineIndicesTableMono,
|
_outputEarlyIndicesTableMono,
|
||||||
_targetOutputFeedbackIndicesTableMono,
|
_targetEarlyDelayLineIndicesTableMono,
|
||||||
_outputIndicesTableMono);
|
_targetOutputFeedbackIndicesTableMono,
|
||||||
|
_outputIndicesTableMono);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ProcessReverbStereo(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private void ProcessReverbStereo(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
ProcessReverbGeneric(ref state,
|
ProcessReverbGeneric(
|
||||||
outputBuffers,
|
ref state,
|
||||||
inputBuffers,
|
outputBuffers,
|
||||||
sampleCount,
|
inputBuffers,
|
||||||
_outputEarlyIndicesTableStereo,
|
sampleCount,
|
||||||
_targetEarlyDelayLineIndicesTableStereo,
|
_outputEarlyIndicesTableStereo,
|
||||||
_targetOutputFeedbackIndicesTableStereo,
|
_targetEarlyDelayLineIndicesTableStereo,
|
||||||
_outputIndicesTableStereo);
|
_targetOutputFeedbackIndicesTableStereo,
|
||||||
|
_outputIndicesTableStereo);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ProcessReverbQuadraphonic(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private void ProcessReverbQuadraphonic(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
ProcessReverbGeneric(ref state,
|
ProcessReverbGeneric(
|
||||||
outputBuffers,
|
ref state,
|
||||||
inputBuffers,
|
outputBuffers,
|
||||||
sampleCount,
|
inputBuffers,
|
||||||
_outputEarlyIndicesTableQuadraphonic,
|
sampleCount,
|
||||||
_targetEarlyDelayLineIndicesTableQuadraphonic,
|
_outputEarlyIndicesTableQuadraphonic,
|
||||||
_targetOutputFeedbackIndicesTableQuadraphonic,
|
_targetEarlyDelayLineIndicesTableQuadraphonic,
|
||||||
_outputIndicesTableQuadraphonic);
|
_targetOutputFeedbackIndicesTableQuadraphonic,
|
||||||
|
_outputIndicesTableQuadraphonic);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ProcessReverbSurround(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private void ProcessReverbSurround(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
ProcessReverbGeneric(ref state,
|
ProcessReverbGeneric(
|
||||||
outputBuffers,
|
ref state,
|
||||||
inputBuffers,
|
outputBuffers,
|
||||||
sampleCount,
|
inputBuffers,
|
||||||
_outputEarlyIndicesTableSurround,
|
sampleCount,
|
||||||
_targetEarlyDelayLineIndicesTableSurround,
|
_outputEarlyIndicesTableSurround,
|
||||||
_targetOutputFeedbackIndicesTableSurround,
|
_targetEarlyDelayLineIndicesTableSurround,
|
||||||
_outputIndicesTableSurround);
|
_targetOutputFeedbackIndicesTableSurround,
|
||||||
|
_outputIndicesTableSurround);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable, ReadOnlySpan<int> outputIndicesTable)
|
private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount, ReadOnlySpan<int> outputEarlyIndicesTable, ReadOnlySpan<int> targetEarlyDelayLineIndicesTable, ReadOnlySpan<int> targetOutputFeedbackIndicesTable, ReadOnlySpan<int> outputIndicesTable)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
{
|
{
|
||||||
// NOTE: Nintendo uses an approximation of log10, we don't.
|
// NOTE: Nintendo uses an approximation of log10, we don't.
|
||||||
// As such, we support the same ranges as Nintendo to avoid unexpected behaviours.
|
// As such, we support the same ranges as Nintendo to avoid unexpected behaviours.
|
||||||
return MathF.Pow(10, MathF.Max(x, 1.0e-10f));
|
return MathF.Log10(MathF.Max(x, 1.0e-10f));
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -62,7 +62,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
|
|
||||||
foreach (float input in inputs)
|
foreach (float input in inputs)
|
||||||
{
|
{
|
||||||
res += (input * input);
|
float normInput = input * (1f / 32768f);
|
||||||
|
res += normInput * normInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
res /= inputs.Length;
|
res /= inputs.Length;
|
||||||
@@ -81,19 +82,6 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
return MathF.Pow(10.0f, db / 20.0f);
|
return MathF.Pow(10.0f, db / 20.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Map decibel to linear in [0, 2] range.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="db">The decibel value to convert</param>
|
|
||||||
/// <returns>Converted linear value in [0, 2] range</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static float DecibelToLinearExtended(float db)
|
|
||||||
{
|
|
||||||
float tmp = MathF.Log2(DecibelToLinear(db));
|
|
||||||
|
|
||||||
return MathF.Truncate(tmp) + MathF.Pow(2.0f, tmp - MathF.Truncate(tmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static float DegreesToRadians(float degrees)
|
public static float DegreesToRadians(float degrees)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Ryujinx.Audio.Renderer.Parameter.Effect;
|
|||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
public class CompressorState
|
public struct CompressorState
|
||||||
{
|
{
|
||||||
public ExponentialMovingAverage InputMovingAverage;
|
public ExponentialMovingAverage InputMovingAverage;
|
||||||
public float Unknown4;
|
public float Unknown4;
|
||||||
@@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
CompressorGainReduction = (1.0f - ratio) / Constants.ChannelCountMax;
|
CompressorGainReduction = (1.0f - ratio) / Constants.ChannelCountMax;
|
||||||
Unknown10 = threshold - 1.5f;
|
Unknown10 = threshold - 1.5f;
|
||||||
Unknown14 = threshold + 1.5f;
|
Unknown14 = threshold + 1.5f;
|
||||||
OutputGain = FloatingPointHelper.DecibelToLinearExtended(parameter.OutputGain + makeupGain);
|
OutputGain = FloatingPointHelper.DecibelToLinear(parameter.OutputGain + makeupGain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
public class DelayState
|
public struct DelayState
|
||||||
{
|
{
|
||||||
public DelayLine[] DelayLines { get; }
|
public DelayLine[] DelayLines { get; }
|
||||||
public float[] LowPassZ { get; set; }
|
public float[] LowPassZ { get; set; }
|
||||||
@@ -53,7 +53,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
LowPassBaseGain = 1.0f - LowPassFeedbackGain;
|
LowPassBaseGain = 1.0f - LowPassFeedbackGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateLowPassFilter(ref float tempRawRef, uint channelCount)
|
public readonly void UpdateLowPassFilter(ref float tempRawRef, uint channelCount)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < channelCount; i++)
|
for (int i = 0; i < channelCount; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System;
|
|||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
public class LimiterState
|
public struct LimiterState
|
||||||
{
|
{
|
||||||
public ExponentialMovingAverage[] DetectorAverage;
|
public ExponentialMovingAverage[] DetectorAverage;
|
||||||
public ExponentialMovingAverage[] CompressionGainAverage;
|
public ExponentialMovingAverage[] CompressionGainAverage;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System;
|
|||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
public class Reverb3dState
|
public struct Reverb3dState
|
||||||
{
|
{
|
||||||
private readonly float[] _fdnDelayMinTimes = new float[4] { 5.0f, 6.0f, 13.0f, 14.0f };
|
private readonly float[] _fdnDelayMinTimes = new float[4] { 5.0f, 6.0f, 13.0f, 14.0f };
|
||||||
private readonly float[] _fdnDelayMaxTimes = new float[4] { 45.704f, 82.782f, 149.94f, 271.58f };
|
private readonly float[] _fdnDelayMaxTimes = new float[4] { 45.704f, 82.782f, 149.94f, 271.58f };
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System;
|
|||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
public class ReverbState
|
public struct ReverbState
|
||||||
{
|
{
|
||||||
private static readonly float[] _fdnDelayTimes = new float[20]
|
private static readonly float[] _fdnDelayTimes = new float[20]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
"Light" => ThemeVariant.Light,
|
"Light" => ThemeVariant.Light,
|
||||||
"Dark" => ThemeVariant.Dark,
|
"Dark" => ThemeVariant.Dark,
|
||||||
_ => ThemeVariant.Default
|
_ => ThemeVariant.Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (enableCustomTheme)
|
if (enableCustomTheme)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Rendering;
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Audio.Backends.Dummy;
|
using Ryujinx.Audio.Backends.Dummy;
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count == 0)
|
if (result.Count == 0)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
|
|||||||
@@ -82,12 +82,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!");
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
|
||||||
});
|
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
@@ -114,10 +111,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -134,10 +130,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -149,10 +144,8 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, exception.Message);
|
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
|
||||||
});
|
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
@@ -167,12 +160,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
|
||||||
});
|
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
@@ -183,10 +173,9 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], "");
|
"");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -212,7 +201,7 @@ namespace Ryujinx.Modules
|
|||||||
_buildSize = -1;
|
_buildSize = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
// Show a message asking the user if they want to update
|
// Show a message asking the user if they want to update
|
||||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
||||||
@@ -222,7 +211,7 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
if (shouldUpdate)
|
if (shouldUpdate)
|
||||||
{
|
{
|
||||||
UpdateRyujinx(mainWindow, _buildUrl);
|
await UpdateRyujinx(mainWindow, _buildUrl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -241,7 +230,7 @@ namespace Ryujinx.Modules
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void UpdateRyujinx(Window parent, string downloadUrl)
|
private static async Task UpdateRyujinx(Window parent, string downloadUrl)
|
||||||
{
|
{
|
||||||
_updateSuccessful = false;
|
_updateSuccessful = false;
|
||||||
|
|
||||||
@@ -579,27 +568,24 @@ namespace Ryujinx.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||||
{
|
{
|
||||||
// Extract Update
|
// Extract Update
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
await Task.Run(() =>
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
||||||
{
|
}
|
||||||
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
else if (OperatingSystem.IsWindows())
|
||||||
}
|
{
|
||||||
else if (OperatingSystem.IsWindows())
|
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
||||||
{
|
}
|
||||||
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
else
|
||||||
}
|
{
|
||||||
else
|
throw new NotSupportedException();
|
||||||
{
|
}
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete downloaded zip
|
// Delete downloaded zip
|
||||||
File.Delete(updateFile);
|
File.Delete(updateFile);
|
||||||
@@ -613,36 +599,33 @@ namespace Ryujinx.Modules
|
|||||||
if (!OperatingSystem.IsMacOS())
|
if (!OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
// Replace old files
|
// Replace old files
|
||||||
await Task.Run(() =>
|
double count = 0;
|
||||||
|
foreach (string file in allFiles)
|
||||||
{
|
{
|
||||||
double count = 0;
|
count++;
|
||||||
foreach (string file in allFiles)
|
try
|
||||||
{
|
{
|
||||||
count++;
|
File.Move(file, file + ".ryuold");
|
||||||
try
|
|
||||||
{
|
|
||||||
File.Move(file, file + ".ryuold");
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
taskDialog.SetProgressBarState(GetPercentage(count, allFiles.Count), TaskDialogProgressState.Normal);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
{
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterAddingFiles];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
|
||||||
|
|
||||||
Directory.Delete(_updateDir, true);
|
Directory.Delete(_updateDir, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,12 +641,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -673,12 +655,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterNoInternetSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -688,12 +669,11 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -705,21 +685,19 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
if (ReleaseInformation.IsFlatHubBuild())
|
if (ReleaseInformation.IsFlatHubBuild())
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateWarningDialog(
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
|
||||||
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
bool error = false;
|
bool error = false;
|
||||||
string inputText = args.InitialText ?? "";
|
string inputText = args.InitialText ?? "";
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -149,7 +149,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool showDetails = false;
|
bool showDetails = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
|
if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
|
||||||
{
|
{
|
||||||
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
|
OpenSaveDirectory(viewModel, SaveDataType.Account, new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,14 +62,14 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default);
|
OpenSaveDirectory(viewModel, SaveDataType.Device, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
|
public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default);
|
OpenSaveDirectory(viewModel, SaveDataType.Bcat, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
|
private static void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId)
|
||||||
@@ -158,11 +158,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
@@ -205,11 +206,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
@@ -335,13 +337,13 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunApplication_Click(object sender, RoutedEventArgs args)
|
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
if (viewModel?.SelectedApplication != null)
|
if (viewModel?.SelectedApplication != null)
|
||||||
{
|
{
|
||||||
viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
await viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,9 +212,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.nsp" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var file in result)
|
foreach (var file in result)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ using Ryujinx.HLE.FileSystem;
|
|||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.Ui;
|
using Ryujinx.HLE.Ui;
|
||||||
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
@@ -39,7 +40,6 @@ using System.IO;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
|
||||||
using Key = Ryujinx.Input.Key;
|
using Key = Ryujinx.Input.Key;
|
||||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||||
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
||||||
@@ -1068,9 +1068,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
||||||
|
|
||||||
static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1163,16 +1161,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AppHost?.DisposeContext();
|
AppHost?.DisposeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleRelaunch()
|
private async Task HandleRelaunch()
|
||||||
{
|
{
|
||||||
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
|
if (UserChannelPersistence.PreviousIndex != -1 && UserChannelPersistence.ShouldRestart)
|
||||||
{
|
{
|
||||||
UserChannelPersistence.ShouldRestart = false;
|
UserChannelPersistence.ShouldRestart = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
await LoadApplication(_currentEmulatedGamePath);
|
||||||
{
|
|
||||||
LoadApplication(_currentEmulatedGamePath);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1191,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Application.Current.Styles.TryGetResource(args.VSyncEnabled
|
Application.Current.Styles.TryGetResource(args.VSyncEnabled
|
||||||
? "VsyncEnabled"
|
? "VsyncEnabled"
|
||||||
: "VsyncDisabled",
|
: "VsyncDisabled",
|
||||||
Avalonia.Application.Current.ActualThemeVariant,
|
Application.Current.ActualThemeVariant,
|
||||||
out object color);
|
out object color);
|
||||||
|
|
||||||
if (color is not null)
|
if (color is not null)
|
||||||
@@ -1283,7 +1278,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Glyph = Glyph.Grid;
|
Glyph = Glyph.Grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void InstallFirmwareFromFile()
|
public async Task InstallFirmwareFromFile()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -1294,21 +1289,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci", "*.zip" },
|
Patterns = new[] { "*.xci", "*.zip" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci", "public.zip-archive" },
|
||||||
MimeTypes = new[] { "application/x-nx-xci", "application/zip" }
|
MimeTypes = new[] { "application/x-nx-xci", "application/zip" },
|
||||||
},
|
},
|
||||||
new("XCI")
|
new("XCI")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci" },
|
Patterns = new[] { "*.xci" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||||
MimeTypes = new[] { "application/x-nx-xci" }
|
MimeTypes = new[] { "application/x-nx-xci" },
|
||||||
},
|
},
|
||||||
new("ZIP")
|
new("ZIP")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.zip" },
|
Patterns = new[] { "*.zip" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
AppleUniformTypeIdentifiers = new[] { "public.zip-archive" },
|
||||||
MimeTypes = new[] { "application/zip" }
|
MimeTypes = new[] { "application/zip" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
@@ -1317,11 +1312,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void InstallFirmwareFromFolder()
|
public async Task InstallFirmwareFromFolder()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
@@ -1352,7 +1347,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ExitCurrentState()
|
public async Task ExitCurrentState()
|
||||||
{
|
{
|
||||||
if (WindowState == WindowState.FullScreen)
|
if (WindowState == WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
@@ -1377,7 +1372,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ManageProfiles()
|
public async Task ManageProfiles()
|
||||||
{
|
{
|
||||||
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
||||||
}
|
}
|
||||||
@@ -1387,7 +1382,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AppHost.Device.System.SimulateWakeUpMessage();
|
AppHost.Device.System.SimulateWakeUpMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenFile()
|
public async Task OpenFile()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -1404,7 +1399,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
"com.ryujinx.xci",
|
"com.ryujinx.xci",
|
||||||
"com.ryujinx.nca",
|
"com.ryujinx.nca",
|
||||||
"com.ryujinx.nro",
|
"com.ryujinx.nro",
|
||||||
"com.ryujinx.nso"
|
"com.ryujinx.nso",
|
||||||
},
|
},
|
||||||
MimeTypes = new[]
|
MimeTypes = new[]
|
||||||
{
|
{
|
||||||
@@ -1412,63 +1407,63 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
"application/x-nx-xci",
|
"application/x-nx-xci",
|
||||||
"application/x-nx-nca",
|
"application/x-nx-nca",
|
||||||
"application/x-nx-nro",
|
"application/x-nx-nro",
|
||||||
"application/x-nx-nso"
|
"application/x-nx-nso",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
new("NSP")
|
new("NSP")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nsp" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
},
|
},
|
||||||
new("XCI")
|
new("XCI")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.xci" },
|
Patterns = new[] { "*.xci" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xci" },
|
||||||
MimeTypes = new[] { "application/x-nx-xci" }
|
MimeTypes = new[] { "application/x-nx-xci" },
|
||||||
},
|
},
|
||||||
new("NCA")
|
new("NCA")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nca" },
|
Patterns = new[] { "*.nca" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nca" },
|
||||||
MimeTypes = new[] { "application/x-nx-nca" }
|
MimeTypes = new[] { "application/x-nx-nca" },
|
||||||
},
|
},
|
||||||
new("NRO")
|
new("NRO")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nro" },
|
Patterns = new[] { "*.nro" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nro" },
|
||||||
MimeTypes = new[] { "application/x-nx-nro" }
|
MimeTypes = new[] { "application/x-nx-nro" },
|
||||||
},
|
},
|
||||||
new("NSO")
|
new("NSO")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.nso" },
|
Patterns = new[] { "*.nso" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nso" },
|
||||||
MimeTypes = new[] { "application/x-nx-nso" }
|
MimeTypes = new[] { "application/x-nx-nso" },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
{
|
{
|
||||||
LoadApplication(result[0].Path.LocalPath);
|
await LoadApplication(result[0].Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenFolder()
|
public async Task OpenFolder()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
|
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
{
|
{
|
||||||
LoadApplication(result[0].Path.LocalPath);
|
await LoadApplication(result[0].Path.LocalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
|
||||||
{
|
{
|
||||||
if (AppHost != null)
|
if (AppHost != null)
|
||||||
{
|
{
|
||||||
@@ -1505,35 +1500,30 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
this,
|
this,
|
||||||
TopLevel);
|
TopLevel);
|
||||||
|
|
||||||
async void Action()
|
if (!await AppHost.LoadGuestApplication())
|
||||||
{
|
{
|
||||||
if (!await AppHost.LoadGuestApplication())
|
AppHost.DisposeContext();
|
||||||
{
|
AppHost = null;
|
||||||
AppHost.DisposeContext();
|
|
||||||
AppHost = null;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
CanUpdate = false;
|
|
||||||
|
|
||||||
LoadHeading = TitleName = titleName;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
|
||||||
{
|
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
|
||||||
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchToRenderer(startFullscreen);
|
|
||||||
|
|
||||||
_currentEmulatedGamePath = path;
|
|
||||||
|
|
||||||
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
|
||||||
gameThread.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(Action);
|
CanUpdate = false;
|
||||||
|
|
||||||
|
LoadHeading = TitleName = titleName;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
|
{
|
||||||
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||||
|
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchToRenderer(startFullscreen);
|
||||||
|
|
||||||
|
_currentEmulatedGamePath = path;
|
||||||
|
|
||||||
|
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
|
||||||
|
gameThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchToRenderer(bool startFullscreen)
|
public void SwitchToRenderer(bool startFullscreen)
|
||||||
@@ -1596,7 +1586,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
IsGameRunning = false;
|
IsGameRunning = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = true;
|
ShowMenuAndStatusBar = true;
|
||||||
ShowContent = true;
|
ShowContent = true;
|
||||||
@@ -1609,7 +1599,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
AppHost = null;
|
AppHost = null;
|
||||||
|
|
||||||
HandleRelaunch();
|
await HandleRelaunch();
|
||||||
});
|
});
|
||||||
|
|
||||||
RendererHostControl.WindowCreated -= RendererHost_Created;
|
RendererHostControl.WindowCreated -= RendererHost_Created;
|
||||||
|
|||||||
@@ -78,14 +78,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
|
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
|
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
@@ -148,6 +147,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool EnableTextureRecompression { get; set; }
|
public bool EnableTextureRecompression { get; set; }
|
||||||
public bool EnableMacroHLE { get; set; }
|
public bool EnableMacroHLE { get; set; }
|
||||||
public bool EnableColorSpacePassthrough { get; set; }
|
public bool EnableColorSpacePassthrough { get; set; }
|
||||||
|
public bool ColorSpacePassthroughAvailable => IsMacOS;
|
||||||
public bool EnableFileLog { get; set; }
|
public bool EnableFileLog { get; set; }
|
||||||
public bool EnableStub { get; set; }
|
public bool EnableStub { get; set; }
|
||||||
public bool EnableInfo { get; set; }
|
public bool EnableInfo { get; set; }
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
@@ -184,18 +185,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path)));
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +202,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
SortUpdates();
|
SortUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Add()
|
public async Task Add()
|
||||||
{
|
{
|
||||||
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
{
|
{
|
||||||
@@ -218,9 +213,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.nsp" },
|
Patterns = new[] { "*.nsp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.nsp" },
|
||||||
MimeTypes = new[] { "application/x-nx-nsp" }
|
MimeTypes = new[] { "application/x-nx-nsp" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var file in result)
|
foreach (var file in result)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Views.Main
|
namespace Ryujinx.Ava.UI.Views.Main
|
||||||
{
|
{
|
||||||
@@ -107,20 +106,14 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
await Window.ViewModel.AppHost?.ShowExitPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
private void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
Window.ViewModel.AppHost?.Pause();
|
||||||
{
|
|
||||||
Window.ViewModel.AppHost?.Pause();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
private void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
Window.ViewModel.AppHost?.Resume();
|
||||||
{
|
|
||||||
Window.ViewModel.AppHost?.Resume();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenSettings(object sender, RoutedEventArgs e)
|
public async void OpenSettings(object sender, RoutedEventArgs e)
|
||||||
@@ -132,13 +125,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
ViewModel.LoadConfigurableHotKeys();
|
ViewModel.LoadConfigurableHotKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenMiiApplet(object sender, RoutedEventArgs e)
|
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(contentPath))
|
if (!string.IsNullOrEmpty(contentPath))
|
||||||
{
|
{
|
||||||
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
await ViewModel.LoadApplication(contentPath, false, "Mii Applet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +189,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
if (FileAssociationHelper.Install())
|
if (FileAssociationHelper.Install())
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -209,8 +201,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
if (FileAssociationHelper.Uninstall())
|
if (FileAssociationHelper.Uninstall())
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
||||||
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
|
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
|
||||||
|
IsVisible="{Binding ColorSpacePassthroughAvailable}"
|
||||||
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
|
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
|
||||||
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
|
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
{
|
{
|
||||||
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.xaml" },
|
Patterns = new[] { "*.xaml" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
|
AppleUniformTypeIdentifiers = new[] { "com.ryujinx.xaml" },
|
||||||
MimeTypes = new[] { "application/xaml+xml" }
|
MimeTypes = new[] { "application/xaml+xml" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ namespace Ryujinx.Ava.UI.Views.User
|
|||||||
{
|
{
|
||||||
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
Patterns = new[] { "*.jpg", "*.jpeg", "*.png", "*.bmp" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
|
AppleUniformTypeIdentifiers = new[] { "public.jpeg", "public.png", "com.microsoft.bmp" },
|
||||||
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" }
|
MimeTypes = new[] { "image/jpeg", "image/png", "image/bmp" },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Count > 0)
|
if (result.Count > 0)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ using Ryujinx.Ui.Common.Helper;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
@@ -79,35 +80,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
Initialize();
|
|
||||||
|
|
||||||
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
|
||||||
|
|
||||||
ViewModel.Initialize(
|
|
||||||
ContentManager,
|
|
||||||
StorageProvider,
|
|
||||||
ApplicationLibrary,
|
|
||||||
VirtualFileSystem,
|
|
||||||
AccountManager,
|
|
||||||
InputManager,
|
|
||||||
_userChannelPersistence,
|
|
||||||
LibHacHorizonManager,
|
|
||||||
UiHandler,
|
|
||||||
ShowLoading,
|
|
||||||
SwitchToGameControl,
|
|
||||||
SetMainContent,
|
|
||||||
this);
|
|
||||||
|
|
||||||
ViewModel.RefreshFirmwareStatus();
|
|
||||||
|
|
||||||
LoadGameList();
|
|
||||||
|
|
||||||
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
|
||||||
this.ScalingChanged += OnScalingChanged;
|
this.ScalingChanged += OnScalingChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
|
||||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
@@ -122,36 +99,17 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.IsActive = obj;
|
ViewModel.IsActive = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadGameList()
|
|
||||||
{
|
|
||||||
if (_isLoading)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isLoading = true;
|
|
||||||
|
|
||||||
LoadApplications();
|
|
||||||
|
|
||||||
_isLoading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnScalingChanged(object sender, EventArgs e)
|
private void OnScalingChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Program.DesktopScaleFactor = this.RenderScaling;
|
Program.DesktopScaleFactor = this.RenderScaling;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddApplication(ApplicationData applicationData)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
ViewModel.Applications.Add(applicationData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
|
||||||
{
|
{
|
||||||
AddApplication(e.AppData);
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
ViewModel.Applications.Add(e.AppData);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
|
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
|
||||||
@@ -183,7 +141,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
string path = new FileInfo(args.Application.Path).FullName;
|
string path = new FileInfo(args.Application.Path).FullName;
|
||||||
|
|
||||||
ViewModel.LoadApplication(path);
|
ViewModel.LoadApplication(path).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -202,13 +160,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.ShowContent = true;
|
ViewModel.ShowContent = true;
|
||||||
ViewModel.IsLoadingIndeterminate = false;
|
ViewModel.IsLoadingIndeterminate = false;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
ViewModel.ToggleFullscreen();
|
||||||
{
|
}
|
||||||
ViewModel.ToggleFullscreen();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowLoading(bool startFullscreen = false)
|
public void ShowLoading(bool startFullscreen = false)
|
||||||
@@ -217,13 +172,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.ShowLoadProgress = true;
|
ViewModel.ShowLoadProgress = true;
|
||||||
ViewModel.IsLoadingIndeterminate = true;
|
ViewModel.IsLoadingIndeterminate = true;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
|
ViewModel.ToggleFullscreen();
|
||||||
{
|
}
|
||||||
ViewModel.ToggleFullscreen();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
@@ -255,7 +207,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static async void ShowVmMaxMapCountWarning()
|
private static async Task ShowVmMaxMapCountWarning()
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
||||||
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
||||||
@@ -267,7 +219,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
private static async void ShowVmMaxMapCountDialog()
|
private static async Task ShowVmMaxMapCountDialog()
|
||||||
{
|
{
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
||||||
LinuxHelper.RecommendedVmMaxMapCount);
|
LinuxHelper.RecommendedVmMaxMapCount);
|
||||||
@@ -313,33 +265,46 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
private void CheckLaunchState()
|
private void CheckLaunchState()
|
||||||
{
|
{
|
||||||
if (ShowKeyErrorOnLoad)
|
|
||||||
{
|
|
||||||
ShowKeyErrorOnLoad = false;
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(async () => await
|
|
||||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
|
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
|
||||||
|
|
||||||
if (LinuxHelper.PkExecPath is not null)
|
if (LinuxHelper.PkExecPath is not null)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
|
Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
await ShowVmMaxMapCountDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
|
Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
await ShowVmMaxMapCountWarning();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_deferLoad)
|
if (!ShowKeyErrorOnLoad)
|
||||||
{
|
{
|
||||||
_deferLoad = false;
|
if (_deferLoad)
|
||||||
|
{
|
||||||
|
_deferLoad = false;
|
||||||
|
|
||||||
ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowKeyErrorOnLoad = false;
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
|
||||||
@@ -372,7 +337,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
||||||
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
||||||
|
|
||||||
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
|
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
|
||||||
|
|
||||||
if (CheckScreenBounds(savedPoint))
|
if (CheckScreenBounds(savedPoint))
|
||||||
{
|
{
|
||||||
@@ -415,6 +380,30 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
base.OnOpened(e);
|
base.OnOpened(e);
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
ViewModel.Initialize(
|
||||||
|
ContentManager,
|
||||||
|
StorageProvider,
|
||||||
|
ApplicationLibrary,
|
||||||
|
VirtualFileSystem,
|
||||||
|
AccountManager,
|
||||||
|
InputManager,
|
||||||
|
_userChannelPersistence,
|
||||||
|
LibHacHorizonManager,
|
||||||
|
UiHandler,
|
||||||
|
ShowLoading,
|
||||||
|
SwitchToGameControl,
|
||||||
|
SetMainContent,
|
||||||
|
this);
|
||||||
|
|
||||||
|
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||||
|
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||||
|
|
||||||
|
ViewModel.RefreshFirmwareStatus();
|
||||||
|
|
||||||
|
LoadApplications();
|
||||||
|
|
||||||
CheckLaunchState();
|
CheckLaunchState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,18 +503,15 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void LoadApplications()
|
public void LoadApplications()
|
||||||
{
|
{
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
ViewModel.Applications.Clear();
|
||||||
{
|
|
||||||
ViewModel.Applications.Clear();
|
|
||||||
|
|
||||||
StatusBarView.LoadProgressBar.IsVisible = true;
|
StatusBarView.LoadProgressBar.IsVisible = true;
|
||||||
ViewModel.StatusBarProgressMaximum = 0;
|
ViewModel.StatusBarProgressMaximum = 0;
|
||||||
ViewModel.StatusBarProgressValue = 0;
|
ViewModel.StatusBarProgressValue = 0;
|
||||||
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||||
});
|
|
||||||
|
|
||||||
ReloadGameList();
|
ReloadGameList();
|
||||||
}
|
}
|
||||||
@@ -558,9 +544,17 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
|
||||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
|
Thread applicationLibraryThread = new(() =>
|
||||||
|
{
|
||||||
|
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
|
||||||
|
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Name = "GUI.ApplicationLibraryThread",
|
||||||
|
IsBackground = true,
|
||||||
|
};
|
||||||
|
applicationLibraryThread.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Avalonia.Controls.Primitives;
|
|||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -25,11 +24,6 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
IconImage = new Bitmap(stream);
|
IconImage = new Bitmap(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnOpened(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnOpened(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ namespace Ryujinx.Common.Collections
|
|||||||
{
|
{
|
||||||
if (start.CompareTo(overlap.End) < 0)
|
if (start.CompareTo(overlap.End) < 0)
|
||||||
{
|
{
|
||||||
if (overlaps.Length >= overlapCount)
|
if (overlaps.Length <= overlapCount)
|
||||||
{
|
{
|
||||||
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpace : IDisposable
|
class HvAddressSpace : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
private const ulong KernelRegionBase = unchecked((ulong)-(1L << 39));
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ using Ryujinx.Cpu.AppleHv.Arm;
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvAddressSpaceRange : IDisposable
|
class HvAddressSpaceRange : IDisposable
|
||||||
{
|
{
|
||||||
private const ulong AllocationGranule = 1UL << 14;
|
private const ulong AllocationGranule = 1UL << 14;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
@@ -12,10 +13,18 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HvExitReason : uint
|
||||||
|
{
|
||||||
|
Canceled,
|
||||||
|
Exception,
|
||||||
|
VTimerActivated,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
struct HvVcpuExit
|
struct HvVcpuExit
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649 // Field is never assigned to
|
#pragma warning disable CS0649 // Field is never assigned to
|
||||||
public uint Reason;
|
public HvExitReason Reason;
|
||||||
public HvVcpuExitException Exception;
|
public HvVcpuExitException Exception;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
@@ -255,6 +264,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static partial class HvApi
|
static partial class HvApi
|
||||||
{
|
{
|
||||||
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
public const string LibraryName = "/System/Library/Frameworks/Hypervisor.framework/Hypervisor";
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvCpuContext : ICpuContext
|
class HvCpuContext : ICpuContext
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvEngine : ICpuEngine
|
public class HvEngine : ICpuEngine
|
||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ using ARMeilleure.State;
|
|||||||
using Ryujinx.Cpu.AppleHv.Arm;
|
using Ryujinx.Cpu.AppleHv.Arm;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContext : IExecutionContext
|
class HvExecutionContext : IExecutionContext
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -67,6 +70,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||||
|
|
||||||
|
private int _interruptRequested;
|
||||||
|
|
||||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||||
{
|
{
|
||||||
_counter = counter;
|
_counter = counter;
|
||||||
@@ -111,7 +116,15 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
_impl.RequestInterrupt();
|
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
|
||||||
|
{
|
||||||
|
impl.RequestInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetAndClearInterruptRequested()
|
||||||
|
{
|
||||||
|
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -131,9 +144,9 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
||||||
|
|
||||||
uint reason = vcpu.ExitInfo->Reason;
|
HvExitReason reason = vcpu.ExitInfo->Reason;
|
||||||
|
|
||||||
if (reason == 1)
|
if (reason == HvExitReason.Exception)
|
||||||
{
|
{
|
||||||
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
|
uint hvEsr = (uint)vcpu.ExitInfo->Exception.Syndrome;
|
||||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||||
@@ -146,14 +159,22 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
address = SynchronousException(memoryManager, ref vcpu);
|
address = SynchronousException(memoryManager, ref vcpu);
|
||||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||||
}
|
}
|
||||||
else if (reason == 0)
|
else if (reason == HvExitReason.Canceled || reason == HvExitReason.VTimerActivated)
|
||||||
{
|
{
|
||||||
if (_impl.GetAndClearInterruptRequested())
|
if (GetAndClearInterruptRequested())
|
||||||
{
|
{
|
||||||
ReturnToPool(vcpu);
|
ReturnToPool(vcpu);
|
||||||
InterruptHandler();
|
InterruptHandler();
|
||||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reason == HvExitReason.VTimerActivated)
|
||||||
|
{
|
||||||
|
vcpu.EnableAndUpdateVTimer();
|
||||||
|
|
||||||
|
// Unmask VTimer interrupts.
|
||||||
|
HvApi.hv_vcpu_set_vtimer_mask(vcpu.Handle, false).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,14 +46,5 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
_v[index] = value;
|
_v[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestInterrupt()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ using ARMeilleure.State;
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvExecutionContextVcpu : IHvExecutionContext
|
class HvExecutionContextVcpu : IHvExecutionContext
|
||||||
{
|
{
|
||||||
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
private static readonly MemoryBlock _setSimdFpRegFuncMem;
|
||||||
@@ -135,7 +136,6 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly ulong _vcpu;
|
private readonly ulong _vcpu;
|
||||||
private int _interruptRequested;
|
|
||||||
|
|
||||||
public HvExecutionContextVcpu(ulong vcpu)
|
public HvExecutionContextVcpu(ulong vcpu)
|
||||||
{
|
{
|
||||||
@@ -181,16 +181,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
public void RequestInterrupt()
|
public void RequestInterrupt()
|
||||||
{
|
{
|
||||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
ulong vcpu = _vcpu;
|
||||||
{
|
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
||||||
ulong vcpu = _vcpu;
|
|
||||||
HvApi.hv_vcpus_exit(ref vcpu, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetAndClearInterruptRequested()
|
|
||||||
{
|
|
||||||
return Interlocked.Exchange(ref _interruptRequested, 0) != 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
readonly struct HvMemoryBlockAllocation : IDisposable
|
readonly struct HvMemoryBlockAllocation : IDisposable
|
||||||
{
|
{
|
||||||
private readonly HvMemoryBlockAllocator _owner;
|
private readonly HvMemoryBlockAllocator _owner;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
class HvMemoryBlockAllocator : PrivateMemoryAllocatorImpl<HvMemoryBlockAllocator.Block>
|
||||||
{
|
{
|
||||||
public class Block : PrivateMemoryAllocator.Block
|
public class Block : PrivateMemoryAllocator.Block
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
@@ -14,6 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
public class HvMemoryManager : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
|
||||||
{
|
{
|
||||||
public const int PageBits = 12;
|
public const int PageBits = 12;
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
unsafe class HvVcpu
|
unsafe class HvVcpu
|
||||||
{
|
{
|
||||||
|
private const ulong InterruptIntervalNs = 16 * 1000000; // 16 ms
|
||||||
|
|
||||||
|
private static ulong _interruptTimeDeltaTicks = 0;
|
||||||
|
|
||||||
public readonly ulong Handle;
|
public readonly ulong Handle;
|
||||||
public readonly HvVcpuExit* ExitInfo;
|
public readonly HvVcpuExit* ExitInfo;
|
||||||
public readonly IHvExecutionContext ShadowContext;
|
public readonly IHvExecutionContext ShadowContext;
|
||||||
@@ -21,5 +29,28 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
NativeContext = nativeContext;
|
NativeContext = nativeContext;
|
||||||
IsEphemeral = isEphemeral;
|
IsEphemeral = isEphemeral;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableAndUpdateVTimer()
|
||||||
|
{
|
||||||
|
// We need to ensure interrupts will be serviced,
|
||||||
|
// and for that we set up the VTime to trigger an interrupt at fixed intervals.
|
||||||
|
|
||||||
|
ulong deltaTicks = _interruptTimeDeltaTicks;
|
||||||
|
|
||||||
|
if (deltaTicks == 0)
|
||||||
|
{
|
||||||
|
// Calculate our time delta in ticks based on the current clock frequency.
|
||||||
|
|
||||||
|
int result = TimeApi.mach_timebase_info(out var timeBaseInfo);
|
||||||
|
|
||||||
|
Debug.Assert(result == 0);
|
||||||
|
|
||||||
|
deltaTicks = ((InterruptIntervalNs * timeBaseInfo.Denom) + (timeBaseInfo.Numer - 1)) / timeBaseInfo.Numer;
|
||||||
|
_interruptTimeDeltaTicks = deltaTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CTL_EL0, 1).ThrowOnError();
|
||||||
|
HvApi.hv_vcpu_set_sys_reg(Handle, HvSysReg.CNTV_CVAL_EL0, TimeApi.mach_absolute_time() + deltaTicks).ThrowOnError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
class HvVcpuPool
|
class HvVcpuPool
|
||||||
{
|
{
|
||||||
// Since there's a limit on the number of VCPUs we can create,
|
// Since there's a limit on the number of VCPUs we can create,
|
||||||
@@ -81,6 +83,8 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
|
|
||||||
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
|
HvVcpu vcpu = new(vcpuHandle, exitInfo, shadowContext, nativeContext, isEphemeral);
|
||||||
|
|
||||||
|
vcpu.EnableAndUpdateVTimer();
|
||||||
|
|
||||||
return vcpu;
|
return vcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
static class HvVm
|
static class HvVm
|
||||||
{
|
{
|
||||||
// This alignment allows us to use larger blocks on the page table.
|
// This alignment allows us to use larger blocks on the page table.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using ARMeilleure.State;
|
|||||||
|
|
||||||
namespace Ryujinx.Cpu.AppleHv
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
{
|
{
|
||||||
public interface IHvExecutionContext
|
interface IHvExecutionContext
|
||||||
{
|
{
|
||||||
ulong Pc { get; set; }
|
ulong Pc { get; set; }
|
||||||
ulong ElrEl1 { get; set; }
|
ulong ElrEl1 { get; set; }
|
||||||
@@ -39,8 +39,5 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
SetV(i, context.GetV(i));
|
SetV(i, context.GetV(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestInterrupt();
|
|
||||||
bool GetAndClearInterruptRequested();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
21
src/Ryujinx.Cpu/AppleHv/TimeApi.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Cpu.AppleHv
|
||||||
|
{
|
||||||
|
struct MachTimebaseInfo
|
||||||
|
{
|
||||||
|
public uint Numer;
|
||||||
|
public uint Denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
static partial class TimeApi
|
||||||
|
{
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
public static partial ulong mach_absolute_time();
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
public static partial int mach_timebase_info(out MachTimebaseInfo info);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -696,11 +696,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find view compatible matches.
|
// Find view compatible matches.
|
||||||
int overlapsCount;
|
int overlapsCount = 0;
|
||||||
|
|
||||||
lock (_textures)
|
if (info.Target != Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
|
lock (_textures)
|
||||||
|
{
|
||||||
|
overlapsCount = _textures.FindOverlaps(range.Value, ref _textureOverlaps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_overlapInfo.Length != _textureOverlaps.Length)
|
if (_overlapInfo.Length != _textureOverlaps.Length)
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private int[] _allOffsets;
|
private int[] _allOffsets;
|
||||||
private int[] _sliceSizes;
|
private int[] _sliceSizes;
|
||||||
private readonly bool _is3D;
|
private readonly bool _is3D;
|
||||||
|
private readonly bool _isBuffer;
|
||||||
private bool _hasMipViews;
|
private bool _hasMipViews;
|
||||||
private bool _hasLayerViews;
|
private bool _hasLayerViews;
|
||||||
private readonly int _layers;
|
private readonly int _layers;
|
||||||
@@ -118,6 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_physicalMemory = physicalMemory;
|
_physicalMemory = physicalMemory;
|
||||||
|
|
||||||
_is3D = storage.Info.Target == Target.Texture3D;
|
_is3D = storage.Info.Target == Target.Texture3D;
|
||||||
|
_isBuffer = storage.Info.Target == Target.TextureBuffer;
|
||||||
_layers = storage.Info.GetSlices();
|
_layers = storage.Info.GetSlices();
|
||||||
_levels = storage.Info.Levels;
|
_levels = storage.Info.Levels;
|
||||||
|
|
||||||
@@ -794,7 +796,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
int targetLayerHandles = _hasLayerViews ? slices : 1;
|
||||||
int targetLevelHandles = _hasMipViews ? levels : 1;
|
int targetLevelHandles = _hasMipViews ? levels : 1;
|
||||||
|
|
||||||
if (_is3D)
|
if (_isBuffer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_is3D)
|
||||||
{
|
{
|
||||||
// Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
|
// Future mip levels come after all layers of the last mip level. Each mipmap has less layers (depth) than the last.
|
||||||
|
|
||||||
@@ -1327,7 +1333,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
TextureGroupHandle[] handles;
|
TextureGroupHandle[] handles;
|
||||||
|
|
||||||
if (!(_hasMipViews || _hasLayerViews))
|
if (_isBuffer)
|
||||||
|
{
|
||||||
|
handles = Array.Empty<TextureGroupHandle>();
|
||||||
|
}
|
||||||
|
else if (!(_hasMipViews || _hasLayerViews))
|
||||||
{
|
{
|
||||||
// Single dirty region.
|
// Single dirty region.
|
||||||
var cpuRegionHandles = new RegionHandle[TextureRange.Count];
|
var cpuRegionHandles = new RegionHandle[TextureRange.Count];
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 5682;
|
private const uint CodeGenVersion = 5764;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|||||||
@@ -184,8 +184,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.TextureSample:
|
case Instruction.TextureSample:
|
||||||
return TextureSample(context, operation);
|
return TextureSample(context, operation);
|
||||||
|
|
||||||
case Instruction.TextureSize:
|
case Instruction.TextureQuerySamples:
|
||||||
return TextureSize(context, operation);
|
return TextureQuerySamples(context, operation);
|
||||||
|
|
||||||
|
case Instruction.TextureQuerySize:
|
||||||
|
return TextureQuerySize(context, operation);
|
||||||
|
|
||||||
case Instruction.UnpackDouble2x32:
|
case Instruction.UnpackDouble2x32:
|
||||||
return UnpackDouble2x32(context, operation);
|
return UnpackDouble2x32(context, operation);
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
||||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||||
Add(Instruction.TextureSample, InstType.Special);
|
Add(Instruction.TextureSample, InstType.Special);
|
||||||
Add(Instruction.TextureSize, InstType.Special);
|
Add(Instruction.TextureQuerySamples, InstType.Special);
|
||||||
|
Add(Instruction.TextureQuerySize, InstType.Special);
|
||||||
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
Add(Instruction.Truncate, InstType.CallUnary, "trunc");
|
||||||
Add(Instruction.UnpackDouble2x32, InstType.Special);
|
Add(Instruction.UnpackDouble2x32, InstType.Special);
|
||||||
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
Add(Instruction.UnpackHalf2x16, InstType.Special);
|
||||||
|
|||||||
@@ -517,7 +517,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return texCall;
|
return texCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string TextureSize(CodeGenContext context, AstOperation operation)
|
public static string TextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
|
// TODO: Bindless texture support. For now we just return 0.
|
||||||
|
if (isBindless)
|
||||||
|
{
|
||||||
|
return NumberFormatter.FormatInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
string indexExpr = null;
|
||||||
|
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
indexExpr = GetSoureExpr(context, texOp.GetSource(0), AggregateType.S32);
|
||||||
|
}
|
||||||
|
|
||||||
|
string samplerName = GetSamplerName(context.Properties, texOp, indexExpr);
|
||||||
|
|
||||||
|
return $"textureSamples({samplerName})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
public StructuredFunction CurrentFunction { get; set; }
|
public StructuredFunction CurrentFunction { get; set; }
|
||||||
private readonly Dictionary<AstOperand, Instruction> _locals = new();
|
private readonly Dictionary<AstOperand, Instruction> _locals = new();
|
||||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new();
|
|
||||||
private readonly Dictionary<int, Instruction> _funcArgs = new();
|
private readonly Dictionary<int, Instruction> _funcArgs = new();
|
||||||
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new();
|
private readonly Dictionary<int, (StructuredFunction, Instruction)> _functions = new();
|
||||||
|
|
||||||
@@ -112,7 +111,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
IsMainFunction = isMainFunction;
|
IsMainFunction = isMainFunction;
|
||||||
MayHaveReturned = false;
|
MayHaveReturned = false;
|
||||||
_locals.Clear();
|
_locals.Clear();
|
||||||
_localForArgs.Clear();
|
|
||||||
_funcArgs.Clear();
|
_funcArgs.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,11 +167,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
_locals.Add(local, spvLocal);
|
_locals.Add(local, spvLocal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeclareLocalForArgs(int funcIndex, Instruction[] spvLocals)
|
|
||||||
{
|
|
||||||
_localForArgs.Add(funcIndex, spvLocals);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
public void DeclareArgument(int argIndex, Instruction spvLocal)
|
||||||
{
|
{
|
||||||
_funcArgs.Add(argIndex, spvLocal);
|
_funcArgs.Add(argIndex, spvLocal);
|
||||||
@@ -278,11 +271,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return _locals[local];
|
return _locals[local];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction[] GetLocalForArgsPointers(int funcIndex)
|
|
||||||
{
|
|
||||||
return _localForArgs[funcIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction GetArgumentPointer(AstOperand funcArg)
|
public Instruction GetArgumentPointer(AstOperand funcArg)
|
||||||
{
|
{
|
||||||
return _funcArgs[funcArg.Value];
|
return _funcArgs[funcArg.Value];
|
||||||
|
|||||||
@@ -41,28 +41,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DeclareLocalForArgs(CodeGenContext context, List<StructuredFunction> functions)
|
|
||||||
{
|
|
||||||
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
|
||||||
{
|
|
||||||
StructuredFunction function = functions[funcIndex];
|
|
||||||
SpvInstruction[] locals = new SpvInstruction[function.InArguments.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < function.InArguments.Length; i++)
|
|
||||||
{
|
|
||||||
var type = function.GetArgumentType(i);
|
|
||||||
var localPointerType = context.TypePointer(StorageClass.Function, context.GetType(type));
|
|
||||||
var spvLocal = context.Variable(localPointerType, StorageClass.Function);
|
|
||||||
|
|
||||||
context.AddLocalVariable(spvLocal);
|
|
||||||
|
|
||||||
locals[i] = spvLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.DeclareLocalForArgs(funcIndex, locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
public static void DeclareAll(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values);
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.Subtract, GenerateSubtract);
|
Add(Instruction.Subtract, GenerateSubtract);
|
||||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||||
Add(Instruction.TextureSize, GenerateTextureSize);
|
Add(Instruction.TextureQuerySamples, GenerateTextureQuerySamples);
|
||||||
|
Add(Instruction.TextureQuerySize, GenerateTextureQuerySize);
|
||||||
Add(Instruction.Truncate, GenerateTruncate);
|
Add(Instruction.Truncate, GenerateTruncate);
|
||||||
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
|
Add(Instruction.UnpackDouble2x32, GenerateUnpackDouble2x32);
|
||||||
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
|
Add(Instruction.UnpackHalf2x16, GenerateUnpackHalf2x16);
|
||||||
@@ -310,26 +311,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var (function, spvFunc) = context.GetFunction(funcId.Value);
|
var (function, spvFunc) = context.GetFunction(funcId.Value);
|
||||||
|
|
||||||
var args = new SpvInstruction[operation.SourcesCount - 1];
|
var args = new SpvInstruction[operation.SourcesCount - 1];
|
||||||
var spvLocals = context.GetLocalForArgsPointers(funcId.Value);
|
|
||||||
|
|
||||||
for (int i = 0; i < args.Length; i++)
|
for (int i = 0; i < args.Length; i++)
|
||||||
{
|
{
|
||||||
var operand = operation.GetSource(i + 1);
|
var operand = operation.GetSource(i + 1);
|
||||||
|
|
||||||
if (i >= function.InArguments.Length)
|
AstOperand local = (AstOperand)operand;
|
||||||
{
|
Debug.Assert(local.Type == OperandType.LocalVariable);
|
||||||
args[i] = context.GetLocalPointer((AstOperand)operand);
|
args[i] = context.GetLocalPointer(local);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var type = function.GetArgumentType(i);
|
|
||||||
var value = context.Get(type, operand);
|
|
||||||
var spvLocal = spvLocals[i];
|
|
||||||
|
|
||||||
context.Store(spvLocal, value);
|
|
||||||
|
|
||||||
args[i] = spvLocal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var retType = function.ReturnType;
|
var retType = function.ReturnType;
|
||||||
@@ -1492,7 +1481,36 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return new OperationResult(swizzledResultType, result);
|
return new OperationResult(swizzledResultType, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateTextureSize(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateTextureQuerySamples(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
|
// TODO: Bindless texture support. For now we just return 0.
|
||||||
|
if (isBindless)
|
||||||
|
{
|
||||||
|
return new OperationResult(AggregateType.S32, context.Constant(context.TypeS32(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
context.GetS32(texOp.GetSource(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
(var imageType, var sampledImageType, var sampledImageVariable) = context.Samplers[texOp.Binding];
|
||||||
|
|
||||||
|
var image = context.Load(sampledImageType, sampledImageVariable);
|
||||||
|
image = context.Image(imageType, image);
|
||||||
|
|
||||||
|
SpvInstruction result = context.ImageQuerySamples(context.TypeS32(), image);
|
||||||
|
|
||||||
|
return new OperationResult(AggregateType.S32, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OperationResult GenerateTextureQuerySize(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.EnterBlock(function.MainBlock);
|
context.EnterBlock(function.MainBlock);
|
||||||
|
|
||||||
Declarations.DeclareLocals(context, function);
|
Declarations.DeclareLocals(context, function);
|
||||||
Declarations.DeclareLocalForArgs(context, info.Functions);
|
|
||||||
|
|
||||||
Generate(context, function.MainBlock);
|
Generate(context, function.MainBlock);
|
||||||
|
|
||||||
|
|||||||
@@ -1094,7 +1094,14 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (isBindless)
|
if (isBindless)
|
||||||
{
|
{
|
||||||
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
if (query == TexQuery.TexHeaderTextureType)
|
||||||
|
{
|
||||||
|
type = SamplerType.Texture2D | SamplerType.Multisample;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = (componentMask & 4) != 0 ? SamplerType.Texture3D : SamplerType.Texture2D;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1102,31 +1109,69 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
TextureFlags flags = isBindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||||
|
int binding;
|
||||||
|
|
||||||
int binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
switch (query)
|
||||||
Instruction.TextureSize,
|
|
||||||
type,
|
|
||||||
TextureFormat.Unknown,
|
|
||||||
flags,
|
|
||||||
TextureOperation.DefaultCbufSlot,
|
|
||||||
imm);
|
|
||||||
|
|
||||||
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
|
||||||
{
|
{
|
||||||
if ((compMask & 1) != 0)
|
case TexQuery.TexHeaderDimension:
|
||||||
{
|
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||||
Operand d = GetDest();
|
Instruction.TextureQuerySize,
|
||||||
|
type,
|
||||||
|
TextureFormat.Unknown,
|
||||||
|
flags,
|
||||||
|
TextureOperation.DefaultCbufSlot,
|
||||||
|
imm);
|
||||||
|
|
||||||
if (d == null)
|
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||||
{
|
{
|
||||||
break;
|
if ((compMask & 1) != 0)
|
||||||
|
{
|
||||||
|
Operand d = GetDest();
|
||||||
|
|
||||||
|
if (d == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(d, context.TextureQuerySize(type, flags, binding, compIndex, sources));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// TODO: Validate and use query parameter.
|
case TexQuery.TexHeaderTextureType:
|
||||||
Operand res = context.TextureSize(type, flags, binding, compIndex, sources);
|
binding = isBindless ? 0 : context.ResourceManager.GetTextureOrImageBinding(
|
||||||
|
Instruction.TextureQuerySamples,
|
||||||
|
type,
|
||||||
|
TextureFormat.Unknown,
|
||||||
|
flags,
|
||||||
|
TextureOperation.DefaultCbufSlot,
|
||||||
|
imm);
|
||||||
|
|
||||||
context.Copy(d, res);
|
if ((componentMask & 4) != 0)
|
||||||
}
|
{
|
||||||
|
// Skip first 2 components if necessary.
|
||||||
|
if ((componentMask & 1) != 0)
|
||||||
|
{
|
||||||
|
GetDest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((componentMask & 2) != 0)
|
||||||
|
{
|
||||||
|
GetDest();
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand d = GetDest();
|
||||||
|
|
||||||
|
if (d != null)
|
||||||
|
{
|
||||||
|
context.Copy(d, context.TextureQuerySamples(type, flags, binding, sources));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
context.TranslatorContext.GpuAccessor.Log($"Invalid or unsupported query type \"{query}\".");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
|
|
||||||
enum Instruction
|
enum Instruction
|
||||||
{
|
{
|
||||||
Absolute = 1,
|
Absolute = 1,
|
||||||
@@ -118,7 +116,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
Subtract,
|
Subtract,
|
||||||
SwizzleAdd,
|
SwizzleAdd,
|
||||||
TextureSample,
|
TextureSample,
|
||||||
TextureSize,
|
TextureQuerySamples,
|
||||||
|
TextureQuerySize,
|
||||||
Truncate,
|
Truncate,
|
||||||
UnpackDouble2x32,
|
UnpackDouble2x32,
|
||||||
UnpackHalf2x16,
|
UnpackHalf2x16,
|
||||||
@@ -160,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
public static bool IsTextureQuery(this Instruction inst)
|
public static bool IsTextureQuery(this Instruction inst)
|
||||||
{
|
{
|
||||||
inst &= Instruction.Mask;
|
inst &= Instruction.Mask;
|
||||||
return inst == Instruction.Lod || inst == Instruction.TextureSize;
|
return inst == Instruction.Lod || inst == Instruction.TextureQuerySamples || inst == Instruction.TextureQuerySize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
||||||
Add(Instruction.TextureSample, AggregateType.FP32);
|
Add(Instruction.TextureSample, AggregateType.FP32);
|
||||||
Add(Instruction.TextureSize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.TextureQuerySamples, AggregateType.S32, AggregateType.S32);
|
||||||
|
Add(Instruction.TextureQuerySize, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Truncate, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
|
Add(Instruction.UnpackDouble2x32, AggregateType.U32, AggregateType.FP64);
|
||||||
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
|
Add(Instruction.UnpackHalf2x16, AggregateType.FP32, AggregateType.U32);
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
static class StructuredProgram
|
static class StructuredProgram
|
||||||
{
|
{
|
||||||
|
// TODO: Eventually it should be possible to specify the parameter types for the function instead of using S32 for everything.
|
||||||
|
private const AggregateType FuncParameterType = AggregateType.S32;
|
||||||
|
|
||||||
public static StructuredProgramInfo MakeStructuredProgram(
|
public static StructuredProgramInfo MakeStructuredProgram(
|
||||||
IReadOnlyList<Function> functions,
|
IReadOnlyList<Function> functions,
|
||||||
AttributeUsage attributeUsage,
|
AttributeUsage attributeUsage,
|
||||||
ShaderDefinitions definitions,
|
ShaderDefinitions definitions,
|
||||||
ResourceManager resourceManager,
|
ResourceManager resourceManager,
|
||||||
|
TargetLanguage targetLanguage,
|
||||||
bool debugMode)
|
bool debugMode)
|
||||||
{
|
{
|
||||||
StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode);
|
StructuredProgramContext context = new(attributeUsage, definitions, resourceManager, debugMode);
|
||||||
@@ -23,19 +27,19 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
BasicBlock[] blocks = function.Blocks;
|
BasicBlock[] blocks = function.Blocks;
|
||||||
|
|
||||||
AggregateType returnType = function.ReturnsValue ? AggregateType.S32 : AggregateType.Void;
|
AggregateType returnType = function.ReturnsValue ? FuncParameterType : AggregateType.Void;
|
||||||
|
|
||||||
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
|
AggregateType[] inArguments = new AggregateType[function.InArgumentsCount];
|
||||||
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
|
AggregateType[] outArguments = new AggregateType[function.OutArgumentsCount];
|
||||||
|
|
||||||
for (int i = 0; i < inArguments.Length; i++)
|
for (int i = 0; i < inArguments.Length; i++)
|
||||||
{
|
{
|
||||||
inArguments[i] = AggregateType.S32;
|
inArguments[i] = FuncParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < outArguments.Length; i++)
|
for (int i = 0; i < outArguments.Length; i++)
|
||||||
{
|
{
|
||||||
outArguments[i] = AggregateType.S32;
|
outArguments[i] = FuncParameterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
|
context.EnterFunction(blocks.Length, function.Name, returnType, inArguments, outArguments);
|
||||||
@@ -58,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddOperation(context, operation);
|
AddOperation(context, operation, targetLanguage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +77,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
return context.Info;
|
return context.Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOperation(StructuredProgramContext context, Operation operation)
|
private static void AddOperation(StructuredProgramContext context, Operation operation, TargetLanguage targetLanguage)
|
||||||
{
|
{
|
||||||
Instruction inst = operation.Inst;
|
Instruction inst = operation.Inst;
|
||||||
StorageKind storageKind = operation.StorageKind;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
@@ -114,9 +118,28 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
if (inst == Instruction.Call && targetLanguage == TargetLanguage.Spirv)
|
||||||
{
|
{
|
||||||
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
// SPIR-V requires that all function parameters are copied to a local variable before the call
|
||||||
|
// (or at least that's what the Khronos compiler does).
|
||||||
|
|
||||||
|
// First one is the function index.
|
||||||
|
sources[0] = context.GetOperandOrCbLoad(operation.GetSource(0));
|
||||||
|
|
||||||
|
// Remaining ones are parameters, copy them to a temp local variable.
|
||||||
|
for (int index = 1; index < operation.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
AstOperand argTemp = context.NewTemp(FuncParameterType);
|
||||||
|
context.AddNode(new AstAssignment(argTemp, context.GetOperandOrCbLoad(operation.GetSource(index))));
|
||||||
|
sources[index] = argTemp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
sources[index] = context.GetOperandOrCbLoad(operation.GetSource(index));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < outDestsCount; index++)
|
for (int index = 0; index < outDestsCount; index++)
|
||||||
|
|||||||
@@ -897,7 +897,21 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
context.Add(new TextureOperation(Instruction.TextureSample, type, TextureFormat.Unknown, flags, binding, compMask, dests, sources));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand TextureSize(
|
public static Operand TextureQuerySamples(
|
||||||
|
this EmitterContext context,
|
||||||
|
SamplerType type,
|
||||||
|
TextureFlags flags,
|
||||||
|
int binding,
|
||||||
|
Operand[] sources)
|
||||||
|
{
|
||||||
|
Operand dest = Local();
|
||||||
|
|
||||||
|
context.Add(new TextureOperation(Instruction.TextureQuerySamples, type, TextureFormat.Unknown, flags, binding, 0, new[] { dest }, sources));
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Operand TextureQuerySize(
|
||||||
this EmitterContext context,
|
this EmitterContext context,
|
||||||
SamplerType type,
|
SamplerType type,
|
||||||
TextureFlags flags,
|
TextureFlags flags,
|
||||||
@@ -907,7 +921,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
Operand dest = Local();
|
Operand dest = Local();
|
||||||
|
|
||||||
context.Add(new TextureOperation(Instruction.TextureSize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
context.Add(new TextureOperation(Instruction.TextureQuerySize, type, TextureFormat.Unknown, flags, binding, compIndex, new[] { dest }, sources));
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.Lod ||
|
if (texOp.Inst == Instruction.TextureSample || texOp.Inst.IsTextureQuery())
|
||||||
texOp.Inst == Instruction.TextureSample ||
|
|
||||||
texOp.Inst == Instruction.TextureSize)
|
|
||||||
{
|
{
|
||||||
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block);
|
||||||
|
|
||||||
@@ -40,7 +38,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
// as long bindless elimination is successful and we know where the texture descriptor is located.
|
// as long bindless elimination is successful and we know where the texture descriptor is located.
|
||||||
bool rewriteSamplerType =
|
bool rewriteSamplerType =
|
||||||
texOp.Type == SamplerType.TextureBuffer ||
|
texOp.Type == SamplerType.TextureBuffer ||
|
||||||
texOp.Inst == Instruction.TextureSize;
|
texOp.Inst == Instruction.TextureQuerySamples ||
|
||||||
|
texOp.Inst == Instruction.TextureQuerySize;
|
||||||
|
|
||||||
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
if (bindlessHandle.Type == OperandType.ConstantBuffer)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -232,8 +232,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
inst &= Instruction.Mask;
|
inst &= Instruction.Mask;
|
||||||
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||||
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
bool isWrite = inst == Instruction.ImageStore || inst == Instruction.ImageAtomic;
|
||||||
bool accurateType = inst != Instruction.Lod && inst != Instruction.TextureSize;
|
bool accurateType = !inst.IsTextureQuery();
|
||||||
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
|
bool intCoords = isImage || flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureQuerySize;
|
||||||
bool coherent = flags.HasFlag(TextureFlags.Coherent);
|
bool coherent = flags.HasFlag(TextureFlags.Coherent);
|
||||||
|
|
||||||
if (!isImage)
|
if (!isImage)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
{
|
{
|
||||||
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
|
node = InsertCoordNormalization(context.Hfm, node, context.ResourceManager, context.GpuAccessor, context.Stage);
|
||||||
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
|
node = InsertCoordGatherBias(node, context.ResourceManager, context.GpuAccessor);
|
||||||
node = InsertConstOffsets(node, context.ResourceManager, context.GpuAccessor);
|
node = InsertConstOffsets(node, context.GpuAccessor, context.Stage);
|
||||||
|
|
||||||
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
|
||||||
{
|
{
|
||||||
@@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.TextureSize &&
|
if (texOp.Inst == Instruction.TextureQuerySize &&
|
||||||
texOp.Index < 2 &&
|
texOp.Index < 2 &&
|
||||||
!isBindless &&
|
!isBindless &&
|
||||||
!isIndexed &&
|
!isIndexed &&
|
||||||
@@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
|
LinkedListNode<INode> textureSizeNode = node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
@@ -259,7 +259,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
@@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor)
|
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, IGpuAccessor gpuAccessor, ShaderStage stage)
|
||||||
{
|
{
|
||||||
// Non-constant texture offsets are not allowed (according to the spec),
|
// Non-constant texture offsets are not allowed (according to the spec),
|
||||||
// however some GPUs does support that.
|
// however some GPUs does support that.
|
||||||
@@ -440,7 +440,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
|
|
||||||
sources.CopyTo(newSources, 0);
|
sources.CopyTo(newSources, 0);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
|
||||||
|
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
|
|
||||||
@@ -502,7 +502,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount, stage);
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
@@ -554,21 +554,31 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
TextureOperation texOp,
|
TextureOperation texOp,
|
||||||
Operand[] lodSources,
|
Operand[] lodSources,
|
||||||
Operand bindlessHandle,
|
Operand bindlessHandle,
|
||||||
int coordsCount)
|
int coordsCount,
|
||||||
|
ShaderStage stage)
|
||||||
{
|
{
|
||||||
Operand[] texSizes = new Operand[coordsCount];
|
Operand[] texSizes = new Operand[coordsCount];
|
||||||
|
|
||||||
Operand lod = Local();
|
Operand lod;
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
if (stage == ShaderStage.Fragment)
|
||||||
Instruction.Lod,
|
{
|
||||||
texOp.Type,
|
lod = Local();
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
texOp.Binding,
|
Instruction.Lod,
|
||||||
0,
|
texOp.Type,
|
||||||
new[] { lod },
|
texOp.Format,
|
||||||
lodSources));
|
texOp.Flags,
|
||||||
|
texOp.Binding,
|
||||||
|
0,
|
||||||
|
new[] { lod },
|
||||||
|
lodSources));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lod = Const(0);
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
@@ -586,7 +596,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
Instruction.TextureSize,
|
Instruction.TextureQuerySize,
|
||||||
texOp.Type,
|
texOp.Type,
|
||||||
texOp.Format,
|
texOp.Format,
|
||||||
texOp.Flags,
|
texOp.Flags,
|
||||||
|
|||||||
@@ -329,6 +329,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
attributeUsage,
|
attributeUsage,
|
||||||
definitions,
|
definitions,
|
||||||
resourceManager,
|
resourceManager,
|
||||||
|
Options.TargetLanguage,
|
||||||
Options.Flags.HasFlag(TranslationFlags.DebugMode));
|
Options.Flags.HasFlag(TranslationFlags.DebugMode));
|
||||||
|
|
||||||
int geometryVerticesPerPrimitive = Definitions.OutputTopology switch
|
int geometryVerticesPerPrimitive = Definitions.OutputTopology switch
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
public bool InUse;
|
public bool InUse;
|
||||||
public bool InConsumption;
|
public bool InConsumption;
|
||||||
|
public int SubmissionCount;
|
||||||
public CommandBuffer CommandBuffer;
|
public CommandBuffer CommandBuffer;
|
||||||
public FenceHolder Fence;
|
public FenceHolder Fence;
|
||||||
public SemaphoreHolder Semaphore;
|
public SemaphoreHolder Semaphore;
|
||||||
@@ -193,6 +194,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _commandBuffers[cbIndex].Fence;
|
return _commandBuffers[cbIndex].Fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetSubmissionCount(int cbIndex)
|
||||||
|
{
|
||||||
|
return _commandBuffers[cbIndex].SubmissionCount;
|
||||||
|
}
|
||||||
|
|
||||||
private int FreeConsumed(bool wait)
|
private int FreeConsumed(bool wait)
|
||||||
{
|
{
|
||||||
int freeEntry = 0;
|
int freeEntry = 0;
|
||||||
@@ -282,6 +288,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
|
Debug.Assert(entry.CommandBuffer.Handle == cbs.CommandBuffer.Handle);
|
||||||
entry.InUse = false;
|
entry.InUse = false;
|
||||||
entry.InConsumption = true;
|
entry.InConsumption = true;
|
||||||
|
entry.SubmissionCount++;
|
||||||
_inUseCount--;
|
_inUseCount--;
|
||||||
|
|
||||||
var commandBuffer = entry.CommandBuffer;
|
var commandBuffer = entry.CommandBuffer;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
class DescriptorSetManager : IDisposable
|
class DescriptorSetManager : IDisposable
|
||||||
{
|
{
|
||||||
private const uint DescriptorPoolMultiplier = 16;
|
public const uint MaxSets = 16;
|
||||||
|
|
||||||
public class DescriptorPoolHolder : IDisposable
|
public class DescriptorPoolHolder : IDisposable
|
||||||
{
|
{
|
||||||
@@ -14,36 +14,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public Device Device { get; }
|
public Device Device { get; }
|
||||||
|
|
||||||
private readonly DescriptorPool _pool;
|
private readonly DescriptorPool _pool;
|
||||||
private readonly uint _capacity;
|
private int _freeDescriptors;
|
||||||
private int _totalSets;
|
private int _totalSets;
|
||||||
private int _setsInUse;
|
private int _setsInUse;
|
||||||
private bool _done;
|
private bool _done;
|
||||||
|
|
||||||
public unsafe DescriptorPoolHolder(Vk api, Device device)
|
public unsafe DescriptorPoolHolder(Vk api, Device device, ReadOnlySpan<DescriptorPoolSize> poolSizes, bool updateAfterBind)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Device = device;
|
Device = device;
|
||||||
|
|
||||||
var poolSizes = new[]
|
foreach (var poolSize in poolSizes)
|
||||||
{
|
{
|
||||||
new DescriptorPoolSize(DescriptorType.UniformBuffer, (1 + Constants.MaxUniformBufferBindings) * DescriptorPoolMultiplier),
|
_freeDescriptors += (int)poolSize.DescriptorCount;
|
||||||
new DescriptorPoolSize(DescriptorType.StorageBuffer, Constants.MaxStorageBufferBindings * DescriptorPoolMultiplier),
|
}
|
||||||
new DescriptorPoolSize(DescriptorType.CombinedImageSampler, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
|
||||||
new DescriptorPoolSize(DescriptorType.StorageImage, Constants.MaxImageBindings * DescriptorPoolMultiplier),
|
|
||||||
new DescriptorPoolSize(DescriptorType.UniformTexelBuffer, Constants.MaxTextureBindings * DescriptorPoolMultiplier),
|
|
||||||
new DescriptorPoolSize(DescriptorType.StorageTexelBuffer, Constants.MaxImageBindings * DescriptorPoolMultiplier),
|
|
||||||
};
|
|
||||||
|
|
||||||
uint maxSets = (uint)poolSizes.Length * DescriptorPoolMultiplier;
|
|
||||||
|
|
||||||
_capacity = maxSets;
|
|
||||||
|
|
||||||
fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
|
fixed (DescriptorPoolSize* pPoolsSize = poolSizes)
|
||||||
{
|
{
|
||||||
var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo
|
var descriptorPoolCreateInfo = new DescriptorPoolCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.DescriptorPoolCreateInfo,
|
SType = StructureType.DescriptorPoolCreateInfo,
|
||||||
MaxSets = maxSets,
|
Flags = updateAfterBind ? DescriptorPoolCreateFlags.UpdateAfterBindBit : DescriptorPoolCreateFlags.None,
|
||||||
|
MaxSets = MaxSets,
|
||||||
PoolSizeCount = (uint)poolSizes.Length,
|
PoolSizeCount = (uint)poolSizes.Length,
|
||||||
PPoolSizes = pPoolsSize,
|
PPoolSizes = pPoolsSize,
|
||||||
};
|
};
|
||||||
@@ -52,18 +44,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts)
|
public unsafe DescriptorSetCollection AllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors)
|
||||||
{
|
{
|
||||||
TryAllocateDescriptorSets(layouts, isTry: false, out var dsc);
|
TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: false, out var dsc);
|
||||||
return dsc;
|
return dsc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, out DescriptorSetCollection dsc)
|
public bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, int consumedDescriptors, out DescriptorSetCollection dsc)
|
||||||
{
|
{
|
||||||
return TryAllocateDescriptorSets(layouts, isTry: true, out dsc);
|
return TryAllocateDescriptorSets(layouts, consumedDescriptors, isTry: true, out dsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool TryAllocateDescriptorSets(ReadOnlySpan<DescriptorSetLayout> layouts, bool isTry, out DescriptorSetCollection dsc)
|
private unsafe bool TryAllocateDescriptorSets(
|
||||||
|
ReadOnlySpan<DescriptorSetLayout> layouts,
|
||||||
|
int consumedDescriptors,
|
||||||
|
bool isTry,
|
||||||
|
out DescriptorSetCollection dsc)
|
||||||
{
|
{
|
||||||
Debug.Assert(!_done);
|
Debug.Assert(!_done);
|
||||||
|
|
||||||
@@ -84,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
|
var result = Api.AllocateDescriptorSets(Device, &descriptorSetAllocateInfo, pDescriptorSets);
|
||||||
if (isTry && result == Result.ErrorOutOfPoolMemory)
|
if (isTry && result == Result.ErrorOutOfPoolMemory)
|
||||||
{
|
{
|
||||||
_totalSets = (int)_capacity;
|
_totalSets = (int)MaxSets;
|
||||||
_done = true;
|
_done = true;
|
||||||
DestroyIfDone();
|
DestroyIfDone();
|
||||||
dsc = default;
|
dsc = default;
|
||||||
@@ -95,6 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_freeDescriptors -= consumedDescriptors;
|
||||||
_totalSets += layouts.Length;
|
_totalSets += layouts.Length;
|
||||||
_setsInUse += layouts.Length;
|
_setsInUse += layouts.Length;
|
||||||
|
|
||||||
@@ -109,9 +106,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
DestroyIfDone();
|
DestroyIfDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanFit(int count)
|
public bool CanFit(int setsCount, int descriptorsCount)
|
||||||
{
|
{
|
||||||
if (_totalSets + count <= _capacity)
|
// Try to determine if an allocation with the given parameters will succeed.
|
||||||
|
// An allocation may fail if the sets count or descriptors count exceeds the available counts
|
||||||
|
// of the pool.
|
||||||
|
// Not getting that right is not fatal, it will just create a new pool and try again,
|
||||||
|
// but it is less efficient.
|
||||||
|
|
||||||
|
if (_totalSets + setsCount <= MaxSets && _freeDescriptors >= descriptorsCount)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -148,46 +151,74 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private DescriptorPoolHolder _currentPool;
|
private readonly DescriptorPoolHolder[] _currentPools;
|
||||||
|
|
||||||
public DescriptorSetManager(Device device)
|
public DescriptorSetManager(Device device, int poolCount)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
|
_currentPools = new DescriptorPoolHolder[poolCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> AllocateDescriptorSet(Vk api, DescriptorSetLayout layout)
|
public Auto<DescriptorSetCollection> AllocateDescriptorSet(
|
||||||
|
Vk api,
|
||||||
|
DescriptorSetLayout layout,
|
||||||
|
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||||
|
int poolIndex,
|
||||||
|
int consumedDescriptors,
|
||||||
|
bool updateAfterBind)
|
||||||
{
|
{
|
||||||
Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
|
Span<DescriptorSetLayout> layouts = stackalloc DescriptorSetLayout[1];
|
||||||
layouts[0] = layout;
|
layouts[0] = layout;
|
||||||
return AllocateDescriptorSets(api, layouts);
|
return AllocateDescriptorSets(api, layouts, poolSizes, poolIndex, consumedDescriptors, updateAfterBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> AllocateDescriptorSets(Vk api, ReadOnlySpan<DescriptorSetLayout> layouts)
|
public Auto<DescriptorSetCollection> AllocateDescriptorSets(
|
||||||
|
Vk api,
|
||||||
|
ReadOnlySpan<DescriptorSetLayout> layouts,
|
||||||
|
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||||
|
int poolIndex,
|
||||||
|
int consumedDescriptors,
|
||||||
|
bool updateAfterBind)
|
||||||
{
|
{
|
||||||
// If we fail the first time, just create a new pool and try again.
|
// If we fail the first time, just create a new pool and try again.
|
||||||
if (!GetPool(api, layouts.Length).TryAllocateDescriptorSets(layouts, out var dsc))
|
|
||||||
|
var pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
|
||||||
|
if (!pool.TryAllocateDescriptorSets(layouts, consumedDescriptors, out var dsc))
|
||||||
{
|
{
|
||||||
dsc = GetPool(api, layouts.Length).AllocateDescriptorSets(layouts);
|
pool = GetPool(api, poolSizes, poolIndex, layouts.Length, consumedDescriptors, updateAfterBind);
|
||||||
|
dsc = pool.AllocateDescriptorSets(layouts, consumedDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Auto<DescriptorSetCollection>(dsc);
|
return new Auto<DescriptorSetCollection>(dsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DescriptorPoolHolder GetPool(Vk api, int requiredCount)
|
private DescriptorPoolHolder GetPool(
|
||||||
|
Vk api,
|
||||||
|
ReadOnlySpan<DescriptorPoolSize> poolSizes,
|
||||||
|
int poolIndex,
|
||||||
|
int setsCount,
|
||||||
|
int descriptorsCount,
|
||||||
|
bool updateAfterBind)
|
||||||
{
|
{
|
||||||
if (_currentPool == null || !_currentPool.CanFit(requiredCount))
|
ref DescriptorPoolHolder currentPool = ref _currentPools[poolIndex];
|
||||||
|
|
||||||
|
if (currentPool == null || !currentPool.CanFit(setsCount, descriptorsCount))
|
||||||
{
|
{
|
||||||
_currentPool = new DescriptorPoolHolder(api, _device);
|
currentPool = new DescriptorPoolHolder(api, _device, poolSizes, updateAfterBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _currentPool;
|
return currentPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
_currentPool?.Dispose();
|
for (int index = 0; index < _currentPools.Length; index++)
|
||||||
|
{
|
||||||
|
_currentPools[index]?.Dispose();
|
||||||
|
_currentPools[index] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private BitMapStruct<Array2<long>> _uniformMirrored;
|
private BitMapStruct<Array2<long>> _uniformMirrored;
|
||||||
private BitMapStruct<Array2<long>> _storageMirrored;
|
private BitMapStruct<Array2<long>> _storageMirrored;
|
||||||
|
|
||||||
|
private bool _updateDescriptorCacheCbIndex;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
private enum DirtyFlags
|
private enum DirtyFlags
|
||||||
{
|
{
|
||||||
@@ -218,6 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public void SetProgram(ShaderCollection program)
|
public void SetProgram(ShaderCollection program)
|
||||||
{
|
{
|
||||||
_program = program;
|
_program = program;
|
||||||
|
_updateDescriptorCacheCbIndex = true;
|
||||||
_dirty = DirtyFlags.All;
|
_dirty = DirtyFlags.All;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +493,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||||
|
|
||||||
var dsc = program.GetNewDescriptorSetCollection(_gd, cbs.CommandBufferIndex, setIndex, out var isNew).Get(cbs);
|
if (_updateDescriptorCacheCbIndex)
|
||||||
|
{
|
||||||
|
_updateDescriptorCacheCbIndex = false;
|
||||||
|
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dsc = program.GetNewDescriptorSetCollection(setIndex, out var isNew).Get(cbs);
|
||||||
|
|
||||||
if (!program.HasMinimalLayout)
|
if (!program.HasMinimalLayout)
|
||||||
{
|
{
|
||||||
@@ -697,6 +706,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void SignalCommandBufferChange()
|
public void SignalCommandBufferChange()
|
||||||
{
|
{
|
||||||
|
_updateDescriptorCacheCbIndex = true;
|
||||||
_dirty = DirtyFlags.All;
|
_dirty = DirtyFlags.All;
|
||||||
|
|
||||||
_uniformSet.Clear();
|
_uniformSet.Clear();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
private readonly List<MemoryAllocatorBlockList> _blockLists;
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
|
||||||
{
|
{
|
||||||
@@ -21,6 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_device = device;
|
_device = device;
|
||||||
_blockLists = new List<MemoryAllocatorBlockList>();
|
_blockLists = new List<MemoryAllocatorBlockList>();
|
||||||
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / _physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryAllocation AllocateDeviceMemory(
|
public MemoryAllocation AllocateDeviceMemory(
|
||||||
@@ -40,21 +43,37 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
private MemoryAllocation Allocate(int memoryTypeIndex, ulong size, ulong alignment, bool map, bool isBuffer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blockLists.Count; i++)
|
_lock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var bl = _blockLists[i];
|
for (int i = 0; i < _blockLists.Count; i++)
|
||||||
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
|
||||||
{
|
{
|
||||||
lock (bl)
|
var bl = _blockLists[i];
|
||||||
|
if (bl.MemoryTypeIndex == memoryTypeIndex && bl.ForBuffer == isBuffer)
|
||||||
{
|
{
|
||||||
return bl.Allocate(size, alignment, map);
|
return bl.Allocate(size, alignment, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
_lock.EnterWriteLock();
|
||||||
_blockLists.Add(newBl);
|
|
||||||
return newBl.Allocate(size, alignment, map);
|
try
|
||||||
|
{
|
||||||
|
var newBl = new MemoryAllocatorBlockList(_api, _device, memoryTypeIndex, _blockAlignment, isBuffer);
|
||||||
|
_blockLists.Add(newBl);
|
||||||
|
|
||||||
|
return newBl.Allocate(size, alignment, map);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int FindSuitableMemoryTypeIndex(
|
internal int FindSuitableMemoryTypeIndex(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -166,6 +167,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private readonly int _blockAlignment;
|
private readonly int _blockAlignment;
|
||||||
|
|
||||||
|
private readonly ReaderWriterLockSlim _lock;
|
||||||
|
|
||||||
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
public MemoryAllocatorBlockList(Vk api, Device device, int memoryTypeIndex, int blockAlignment, bool forBuffer)
|
||||||
{
|
{
|
||||||
_blocks = new List<Block>();
|
_blocks = new List<Block>();
|
||||||
@@ -174,6 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
MemoryTypeIndex = memoryTypeIndex;
|
MemoryTypeIndex = memoryTypeIndex;
|
||||||
ForBuffer = forBuffer;
|
ForBuffer = forBuffer;
|
||||||
_blockAlignment = blockAlignment;
|
_blockAlignment = blockAlignment;
|
||||||
|
_lock = new(LockRecursionPolicy.NoRecursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
public unsafe MemoryAllocation Allocate(ulong size, ulong alignment, bool map)
|
||||||
@@ -184,19 +188,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
throw new ArgumentOutOfRangeException(nameof(alignment), $"Invalid alignment 0x{alignment:X}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _blocks.Count; i++)
|
_lock.EnterReadLock();
|
||||||
{
|
|
||||||
var block = _blocks[i];
|
|
||||||
|
|
||||||
if (block.Mapped == map && block.Size >= size)
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _blocks.Count; i++)
|
||||||
{
|
{
|
||||||
ulong offset = block.Allocate(size, alignment);
|
var block = _blocks[i];
|
||||||
if (offset != InvalidOffset)
|
|
||||||
|
if (block.Mapped == map && block.Size >= size)
|
||||||
{
|
{
|
||||||
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
ulong offset = block.Allocate(size, alignment);
|
||||||
|
if (offset != InvalidOffset)
|
||||||
|
{
|
||||||
|
return new MemoryAllocation(this, block, block.Memory, GetHostPointer(block, offset), offset, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
ulong blockAlignedSize = BitUtils.AlignUp(size, (ulong)_blockAlignment);
|
||||||
|
|
||||||
@@ -244,14 +257,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (block.IsTotallyFree())
|
if (block.IsTotallyFree())
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _blocks.Count; i++)
|
_lock.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_blocks[i] == block)
|
for (int i = 0; i < _blocks.Count; i++)
|
||||||
{
|
{
|
||||||
_blocks.RemoveAt(i);
|
if (_blocks[i] == block)
|
||||||
break;
|
{
|
||||||
|
_blocks.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
block.Destroy(_api, _device);
|
block.Destroy(_api, _device);
|
||||||
}
|
}
|
||||||
@@ -259,13 +281,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private void InsertBlock(Block block)
|
private void InsertBlock(Block block)
|
||||||
{
|
{
|
||||||
int index = _blocks.BinarySearch(block);
|
_lock.EnterWriteLock();
|
||||||
if (index < 0)
|
|
||||||
{
|
|
||||||
index = ~index;
|
|
||||||
}
|
|
||||||
|
|
||||||
_blocks.Insert(index, block);
|
try
|
||||||
|
{
|
||||||
|
int index = _blocks.BinarySearch(block);
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index = ~index;
|
||||||
|
}
|
||||||
|
|
||||||
|
_blocks.Insert(index, block);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.ExitWriteLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
@@ -7,15 +8,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
class PipelineLayoutCacheEntry
|
class PipelineLayoutCacheEntry
|
||||||
{
|
{
|
||||||
|
// Those were adjusted based on current descriptor usage and the descriptor counts usually used on pipeline layouts.
|
||||||
|
// It might be a good idea to tweak them again if those change, or maybe find a way to calculate an optimal value dynamically.
|
||||||
|
private const uint DefaultUniformBufferPoolCapacity = 19 * DescriptorSetManager.MaxSets;
|
||||||
|
private const uint DefaultStorageBufferPoolCapacity = 16 * DescriptorSetManager.MaxSets;
|
||||||
|
private const uint DefaultTexturePoolCapacity = 128 * DescriptorSetManager.MaxSets;
|
||||||
|
private const uint DefaultImagePoolCapacity = 8 * DescriptorSetManager.MaxSets;
|
||||||
|
|
||||||
|
private const int MaxPoolSizesPerSet = 2;
|
||||||
|
|
||||||
private readonly VulkanRenderer _gd;
|
private readonly VulkanRenderer _gd;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
|
|
||||||
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
public DescriptorSetLayout[] DescriptorSetLayouts { get; }
|
||||||
public PipelineLayout PipelineLayout { get; }
|
public PipelineLayout PipelineLayout { get; }
|
||||||
|
|
||||||
|
private readonly int[] _consumedDescriptorsPerSet;
|
||||||
|
|
||||||
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
|
private readonly List<Auto<DescriptorSetCollection>>[][] _dsCache;
|
||||||
|
private List<Auto<DescriptorSetCollection>>[] _currentDsCache;
|
||||||
private readonly int[] _dsCacheCursor;
|
private readonly int[] _dsCacheCursor;
|
||||||
private int _dsLastCbIndex;
|
private int _dsLastCbIndex;
|
||||||
|
private int _dsLastSubmissionCount;
|
||||||
|
|
||||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||||
{
|
{
|
||||||
@@ -44,29 +58,55 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
bool usePushDescriptors) : this(gd, device, setDescriptors.Count)
|
||||||
{
|
{
|
||||||
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
(DescriptorSetLayouts, PipelineLayout) = PipelineLayoutFactory.Create(gd, device, setDescriptors, usePushDescriptors);
|
||||||
|
|
||||||
|
_consumedDescriptorsPerSet = new int[setDescriptors.Count];
|
||||||
|
|
||||||
|
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
foreach (var descriptor in setDescriptors[setIndex].Descriptors)
|
||||||
|
{
|
||||||
|
count += descriptor.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
_consumedDescriptorsPerSet[setIndex] = count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||||
VulkanRenderer gd,
|
|
||||||
int commandBufferIndex,
|
|
||||||
int setIndex,
|
|
||||||
out bool isNew)
|
|
||||||
{
|
{
|
||||||
if (_dsLastCbIndex != commandBufferIndex)
|
int submissionCount = _gd.CommandBufferPool.GetSubmissionCount(commandBufferIndex);
|
||||||
|
|
||||||
|
if (_dsLastCbIndex != commandBufferIndex || _dsLastSubmissionCount != submissionCount)
|
||||||
{
|
{
|
||||||
_dsLastCbIndex = commandBufferIndex;
|
_dsLastCbIndex = commandBufferIndex;
|
||||||
|
_dsLastSubmissionCount = submissionCount;
|
||||||
for (int i = 0; i < _dsCacheCursor.Length; i++)
|
Array.Clear(_dsCacheCursor);
|
||||||
{
|
|
||||||
_dsCacheCursor[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = _dsCache[commandBufferIndex][setIndex];
|
_currentDsCache = _dsCache[commandBufferIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
|
||||||
|
{
|
||||||
|
var list = _currentDsCache[setIndex];
|
||||||
int index = _dsCacheCursor[setIndex]++;
|
int index = _dsCacheCursor[setIndex]++;
|
||||||
if (index == list.Count)
|
if (index == list.Count)
|
||||||
{
|
{
|
||||||
var dsc = gd.DescriptorSetManager.AllocateDescriptorSet(gd.Api, DescriptorSetLayouts[setIndex]);
|
Span<DescriptorPoolSize> poolSizes = stackalloc DescriptorPoolSize[MaxPoolSizesPerSet];
|
||||||
|
poolSizes = GetDescriptorPoolSizes(poolSizes, setIndex);
|
||||||
|
|
||||||
|
int consumedDescriptors = _consumedDescriptorsPerSet[setIndex];
|
||||||
|
|
||||||
|
var dsc = _gd.DescriptorSetManager.AllocateDescriptorSet(
|
||||||
|
_gd.Api,
|
||||||
|
DescriptorSetLayouts[setIndex],
|
||||||
|
poolSizes,
|
||||||
|
setIndex,
|
||||||
|
consumedDescriptors,
|
||||||
|
false);
|
||||||
|
|
||||||
list.Add(dsc);
|
list.Add(dsc);
|
||||||
isNew = true;
|
isNew = true;
|
||||||
return dsc;
|
return dsc;
|
||||||
@@ -76,6 +116,33 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return list[index];
|
return list[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Span<DescriptorPoolSize> GetDescriptorPoolSizes(Span<DescriptorPoolSize> output, int setIndex)
|
||||||
|
{
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
switch (setIndex)
|
||||||
|
{
|
||||||
|
case PipelineBase.UniformSetIndex:
|
||||||
|
output[0] = new(DescriptorType.UniformBuffer, DefaultUniformBufferPoolCapacity);
|
||||||
|
break;
|
||||||
|
case PipelineBase.StorageSetIndex:
|
||||||
|
output[0] = new(DescriptorType.StorageBuffer, DefaultStorageBufferPoolCapacity);
|
||||||
|
break;
|
||||||
|
case PipelineBase.TextureSetIndex:
|
||||||
|
output[0] = new(DescriptorType.CombinedImageSampler, DefaultTexturePoolCapacity);
|
||||||
|
output[1] = new(DescriptorType.UniformTexelBuffer, DefaultTexturePoolCapacity);
|
||||||
|
count = 2;
|
||||||
|
break;
|
||||||
|
case PipelineBase.ImageSetIndex:
|
||||||
|
output[0] = new(DescriptorType.StorageImage, DefaultImagePoolCapacity);
|
||||||
|
output[1] = new(DescriptorType.StorageTexelBuffer, DefaultImagePoolCapacity);
|
||||||
|
count = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output[..count];
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual unsafe void Dispose(bool disposing)
|
protected virtual unsafe void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
|
|||||||
@@ -464,13 +464,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
|
public void UpdateDescriptorCacheCommandBufferIndex(int commandBufferIndex)
|
||||||
VulkanRenderer gd,
|
|
||||||
int commandBufferIndex,
|
|
||||||
int setIndex,
|
|
||||||
out bool isNew)
|
|
||||||
{
|
{
|
||||||
return _plce.GetNewDescriptorSetCollection(gd, commandBufferIndex, setIndex, out isNew);
|
_plce.UpdateCommandBufferIndex(commandBufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(int setIndex, out bool isNew)
|
||||||
|
{
|
||||||
|
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||||
|
|
||||||
DescriptorSetManager = new DescriptorSetManager(_device);
|
DescriptorSetManager = new DescriptorSetManager(_device, PipelineBase.DescriptorSetLayouts);
|
||||||
|
|
||||||
PipelineLayoutCache = new PipelineLayoutCache();
|
PipelineLayoutCache = new PipelineLayoutCache();
|
||||||
|
|
||||||
|
|||||||
@@ -327,8 +327,10 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
private void StartNewServices()
|
private void StartNewServices()
|
||||||
{
|
{
|
||||||
|
HorizonFsClient fsClient = new(this);
|
||||||
|
|
||||||
ServiceTable = new ServiceTable();
|
ServiceTable = new ServiceTable();
|
||||||
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient));
|
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient));
|
||||||
|
|
||||||
foreach (var service in services)
|
foreach (var service in services)
|
||||||
{
|
{
|
||||||
|
|||||||
119
src/Ryujinx.HLE/HOS/HorizonFsClient.cs
Normal file
119
src/Ryujinx.HLE/HOS/HorizonFsClient.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.Horizon;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Fs;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS
|
||||||
|
{
|
||||||
|
class HorizonFsClient : IFsClient
|
||||||
|
{
|
||||||
|
private readonly Horizon _system;
|
||||||
|
private readonly LibHac.Fs.FileSystemClient _fsClient;
|
||||||
|
private readonly ConcurrentDictionary<string, LocalStorage> _mountedStorages;
|
||||||
|
|
||||||
|
public HorizonFsClient(Horizon system)
|
||||||
|
{
|
||||||
|
_system = system;
|
||||||
|
_fsClient = _system.LibHacHorizonManager.FsClient.Fs;
|
||||||
|
_mountedStorages = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseFile(FileHandle handle)
|
||||||
|
{
|
||||||
|
_fsClient.CloseFile((LibHac.Fs.FileHandle)handle.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetFileSize(out long size, FileHandle handle)
|
||||||
|
{
|
||||||
|
return _fsClient.GetFileSize(out size, (LibHac.Fs.FileHandle)handle.Value).ToHorizonResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result MountSystemData(string mountName, ulong dataId)
|
||||||
|
{
|
||||||
|
string contentPath = _system.ContentManager.GetInstalledContentPath(dataId, StorageId.BuiltInSystem, NcaContentType.PublicData);
|
||||||
|
string installPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(installPath))
|
||||||
|
{
|
||||||
|
string ncaPath = installPath;
|
||||||
|
|
||||||
|
if (File.Exists(ncaPath))
|
||||||
|
{
|
||||||
|
LocalStorage ncaStorage = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ncaStorage = new LocalStorage(ncaPath, FileAccess.Read, FileMode.Open);
|
||||||
|
|
||||||
|
Nca nca = new(_system.KeySet, ncaStorage);
|
||||||
|
|
||||||
|
using var ncaFileSystem = nca.OpenFileSystem(NcaSectionType.Data, _system.FsIntegrityCheckLevel);
|
||||||
|
using var ncaFsRef = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||||
|
|
||||||
|
Result result = _fsClient.Register(mountName.ToU8Span(), ref ncaFsRef.Ref).ToHorizonResult();
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
ncaStorage.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mountedStorages.TryAdd(mountName, ncaStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (HorizonResultException ex)
|
||||||
|
{
|
||||||
|
ncaStorage?.Dispose();
|
||||||
|
|
||||||
|
return ex.ResultValue.ToHorizonResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Return correct result here, this is likely wrong.
|
||||||
|
|
||||||
|
return LibHac.Fs.ResultFs.TargetNotFound.Handle().ToHorizonResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenFile(out FileHandle handle, string path, OpenMode openMode)
|
||||||
|
{
|
||||||
|
var result = _fsClient.OpenFile(out var libhacHandle, path.ToU8Span(), (LibHac.Fs.OpenMode)openMode);
|
||||||
|
handle = new(libhacHandle);
|
||||||
|
|
||||||
|
return result.ToHorizonResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result QueryMountSystemDataCacheSize(out long size, ulong dataId)
|
||||||
|
{
|
||||||
|
// TODO.
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadFile(FileHandle handle, long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
return _fsClient.ReadFile((LibHac.Fs.FileHandle)handle.Value, offset, destination).ToHorizonResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmount(string mountName)
|
||||||
|
{
|
||||||
|
if (_mountedStorages.TryRemove(mountName, out LocalStorage ncaStorage))
|
||||||
|
{
|
||||||
|
ncaStorage.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_fsClient.Unmount(mountName.ToU8Span());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
throw new InvalidOperationException("Out of handles!");
|
throw new InvalidOperationException("Out of handles!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_completionEvent.WritableEvent.Signal();
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
@@ -187,6 +189,20 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10420)]
|
||||||
|
// nn::friends::CheckBlockedUserListAvailability(nn::account::Uid userId) -> bool
|
||||||
|
public ResultCode CheckBlockedUserListAvailability(ServiceCtx context)
|
||||||
|
{
|
||||||
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
|
|
||||||
|
// Yes, it is available.
|
||||||
|
context.ResponseData.Write(true);
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(10600)]
|
[CommandCmif(10600)]
|
||||||
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
|
||||||
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
|
||||||
|
|||||||
@@ -669,6 +669,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
lock (Core.Lock)
|
lock (Core.Lock)
|
||||||
{
|
{
|
||||||
|
// If we are replacing a buffer that has already been queued, make sure we release the references.
|
||||||
|
if (Core.Slots[slot].BufferState == BufferState.Queued)
|
||||||
|
{
|
||||||
|
Core.Slots[slot].GraphicBuffer.Object.DecrementNvMapHandleRefCount(Core.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
Core.Slots[slot].BufferState = BufferState.Free;
|
Core.Slots[slot].BufferState = BufferState.Free;
|
||||||
Core.Slots[slot].Fence = AndroidFence.NoFence;
|
Core.Slots[slot].Fence = AndroidFence.NoFence;
|
||||||
Core.Slots[slot].RequestBufferCalled = false;
|
Core.Slots[slot].RequestBufferCalled = false;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using LibHac;
|
using LibHac;
|
||||||
|
using Ryujinx.Horizon.Sdk.Fs;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon
|
namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
@@ -8,12 +9,14 @@ namespace Ryujinx.Horizon
|
|||||||
public bool ThrowOnInvalidCommandIds { get; }
|
public bool ThrowOnInvalidCommandIds { get; }
|
||||||
|
|
||||||
public HorizonClient BcatClient { get; }
|
public HorizonClient BcatClient { get; }
|
||||||
|
public IFsClient FsClient { get; }
|
||||||
|
|
||||||
public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient)
|
public HorizonOptions(bool ignoreMissingServices, HorizonClient bcatClient, IFsClient fsClient)
|
||||||
{
|
{
|
||||||
IgnoreMissingServices = ignoreMissingServices;
|
IgnoreMissingServices = ignoreMissingServices;
|
||||||
ThrowOnInvalidCommandIds = true;
|
ThrowOnInvalidCommandIds = true;
|
||||||
BcatClient = bcatClient;
|
BcatClient = bcatClient;
|
||||||
|
FsClient = fsClient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ryujinx.Horizon
|
namespace Ryujinx.Horizon
|
||||||
{
|
{
|
||||||
internal static class LibHacResultExtensions
|
public static class LibHacResultExtensions
|
||||||
{
|
{
|
||||||
public static Result ToHorizonResult(this LibHac.Result result)
|
public static Result ToHorizonResult(this LibHac.Result result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Horizon.LogManager.Ipc
|
|||||||
[CmifCommand(0)]
|
[CmifCommand(0)]
|
||||||
public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid)
|
public Result OpenLogger(out LmLogger logger, [ClientProcessId] ulong pid)
|
||||||
{
|
{
|
||||||
// NOTE: Internal name is Logger, but we rename it LmLogger to avoid name clash with Ryujinx.Common.Logging logger.
|
// NOTE: Internal name is Logger, but we rename it to LmLogger to avoid name clash with Ryujinx.Common.Logging logger.
|
||||||
logger = new LmLogger(this, pid);
|
logger = new LmLogger(this, pid);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|||||||
64
src/Ryujinx.Horizon/Ngc/Ipc/Service.cs
Normal file
64
src/Ryujinx.Horizon/Ngc/Ipc/Service.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Ngc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Ngc.Detail;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Ngc.Ipc
|
||||||
|
{
|
||||||
|
partial class Service : INgcService
|
||||||
|
{
|
||||||
|
private readonly ProfanityFilter _profanityFilter;
|
||||||
|
|
||||||
|
public Service(ProfanityFilter profanityFilter)
|
||||||
|
{
|
||||||
|
_profanityFilter = profanityFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(0)]
|
||||||
|
public Result GetContentVersion(out uint version)
|
||||||
|
{
|
||||||
|
lock (_profanityFilter)
|
||||||
|
{
|
||||||
|
return _profanityFilter.GetContentVersion(out version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(1)]
|
||||||
|
public Result Check(out uint checkMask, ReadOnlySpan<byte> text, uint regionMask, ProfanityFilterOption option)
|
||||||
|
{
|
||||||
|
lock (_profanityFilter)
|
||||||
|
{
|
||||||
|
return _profanityFilter.CheckProfanityWords(out checkMask, text, regionMask, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(2)]
|
||||||
|
public Result Mask(
|
||||||
|
out int maskedWordsCount,
|
||||||
|
[Buffer(HipcBufferFlags.Out | HipcBufferFlags.MapAlias)] Span<byte> filteredText,
|
||||||
|
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> text,
|
||||||
|
uint regionMask,
|
||||||
|
ProfanityFilterOption option)
|
||||||
|
{
|
||||||
|
lock (_profanityFilter)
|
||||||
|
{
|
||||||
|
int length = Math.Min(filteredText.Length, text.Length);
|
||||||
|
|
||||||
|
text[..length].CopyTo(filteredText[..length]);
|
||||||
|
|
||||||
|
return _profanityFilter.MaskProfanityWordsInText(out maskedWordsCount, filteredText, regionMask, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(3)]
|
||||||
|
public Result Reload()
|
||||||
|
{
|
||||||
|
lock (_profanityFilter)
|
||||||
|
{
|
||||||
|
return _profanityFilter.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs
Normal file
51
src/Ryujinx.Horizon/Ngc/NgcIpcServer.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Ryujinx.Horizon.Ngc.Ipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Fs;
|
||||||
|
using Ryujinx.Horizon.Sdk.Ngc.Detail;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Ngc
|
||||||
|
{
|
||||||
|
class NgcIpcServer
|
||||||
|
{
|
||||||
|
private const int MaxSessionsCount = 4;
|
||||||
|
|
||||||
|
private const int PointerBufferSize = 0;
|
||||||
|
private const int MaxDomains = 0;
|
||||||
|
private const int MaxDomainObjects = 0;
|
||||||
|
private const int MaxPortsCount = 1;
|
||||||
|
|
||||||
|
private static readonly ManagerOptions _options = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
|
||||||
|
|
||||||
|
private SmApi _sm;
|
||||||
|
private ServerManager _serverManager;
|
||||||
|
private ProfanityFilter _profanityFilter;
|
||||||
|
|
||||||
|
public void Initialize(IFsClient fsClient)
|
||||||
|
{
|
||||||
|
HeapAllocator allocator = new();
|
||||||
|
|
||||||
|
_sm = new SmApi();
|
||||||
|
_sm.Initialize().AbortOnFailure();
|
||||||
|
|
||||||
|
_profanityFilter = new(fsClient);
|
||||||
|
_profanityFilter.Initialize().AbortOnFailure();
|
||||||
|
|
||||||
|
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _options, MaxSessionsCount);
|
||||||
|
|
||||||
|
_serverManager.RegisterObjectForServer(new Service(_profanityFilter), ServiceName.Encode("ngc:u"), MaxSessionsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServiceRequests()
|
||||||
|
{
|
||||||
|
_serverManager.ServiceRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
_serverManager.Dispose();
|
||||||
|
_profanityFilter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/Ryujinx.Horizon/Ngc/NgcMain.cs
Normal file
21
src/Ryujinx.Horizon/Ngc/NgcMain.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace Ryujinx.Horizon.Ngc
|
||||||
|
{
|
||||||
|
class NgcMain : IService
|
||||||
|
{
|
||||||
|
public static void Main(ServiceTable serviceTable)
|
||||||
|
{
|
||||||
|
NgcIpcServer ipcServer = new();
|
||||||
|
|
||||||
|
ipcServer.Initialize(HorizonStatic.Options.FsClient);
|
||||||
|
|
||||||
|
// TODO: Notification thread, requires implementing OpenSystemDataUpdateEventNotifier on FS.
|
||||||
|
// The notification thread seems to wait until the event returned by OpenSystemDataUpdateEventNotifier is signalled
|
||||||
|
// in a loop. When it receives the signal, it calls ContentsReader.Reload and then waits for the next signal.
|
||||||
|
|
||||||
|
serviceTable.SignalServiceReady();
|
||||||
|
|
||||||
|
ipcServer.ServiceRequests();
|
||||||
|
ipcServer.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Ryujinx.Horizon/Sdk/Fs/FileHandle.cs
Normal file
13
src/Ryujinx.Horizon/Sdk/Fs/FileHandle.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Fs
|
||||||
|
{
|
||||||
|
public readonly struct FileHandle
|
||||||
|
{
|
||||||
|
public object Value { get; }
|
||||||
|
|
||||||
|
public FileHandle(object value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Ryujinx.Horizon/Sdk/Fs/FsResult.cs
Normal file
13
src/Ryujinx.Horizon/Sdk/Fs/FsResult.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Fs
|
||||||
|
{
|
||||||
|
static class FsResult
|
||||||
|
{
|
||||||
|
private const int ModuleId = 2;
|
||||||
|
|
||||||
|
public static Result PathNotFound => new(ModuleId, 1);
|
||||||
|
public static Result PathAlreadyExists => new(ModuleId, 2);
|
||||||
|
public static Result TargetNotFound => new(ModuleId, 1002);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Ryujinx.Horizon/Sdk/Fs/IFsClient.cs
Normal file
16
src/Ryujinx.Horizon/Sdk/Fs/IFsClient.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Fs
|
||||||
|
{
|
||||||
|
public interface IFsClient
|
||||||
|
{
|
||||||
|
Result QueryMountSystemDataCacheSize(out long size, ulong dataId);
|
||||||
|
Result MountSystemData(string mountName, ulong dataId);
|
||||||
|
Result OpenFile(out FileHandle handle, string path, OpenMode openMode);
|
||||||
|
Result ReadFile(FileHandle handle, long offset, Span<byte> destination);
|
||||||
|
Result GetFileSize(out long size, FileHandle handle);
|
||||||
|
void CloseFile(FileHandle handle);
|
||||||
|
void Unmount(string mountName);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/Ryujinx.Horizon/Sdk/Fs/OpenMode.cs
Normal file
14
src/Ryujinx.Horizon/Sdk/Fs/OpenMode.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Fs
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum OpenMode
|
||||||
|
{
|
||||||
|
Read = 1,
|
||||||
|
Write = 2,
|
||||||
|
AllowAppend = 4,
|
||||||
|
ReadWrite = 3,
|
||||||
|
All = 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
251
src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs
Normal file
251
src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class AhoCorasick
|
||||||
|
{
|
||||||
|
public delegate bool MatchCallback(ReadOnlySpan<byte> text, int matchStartOffset, int matchEndOffset, int nodeId, ref MatchState state);
|
||||||
|
public delegate bool MatchCallback<T>(ReadOnlySpan<byte> text, int matchStartOffset, int matchEndOffset, int nodeId, ref T state);
|
||||||
|
|
||||||
|
private readonly SparseSet _wordMap = new();
|
||||||
|
private readonly CompressedArray _wordLengths = new();
|
||||||
|
private readonly SparseSet _multiWordMap = new();
|
||||||
|
private readonly CompressedArray _multiWordIndices = new();
|
||||||
|
private readonly SparseSet _nodeMap = new();
|
||||||
|
private uint _nodesPerCharacter;
|
||||||
|
private readonly Bp _bp = new();
|
||||||
|
|
||||||
|
public bool Import(ref BinaryReader reader)
|
||||||
|
{
|
||||||
|
if (!_wordLengths.Import(ref reader) ||
|
||||||
|
!_wordMap.Import(ref reader) ||
|
||||||
|
!_multiWordIndices.Import(ref reader) ||
|
||||||
|
!_multiWordMap.Import(ref reader))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reader.Read(out _nodesPerCharacter))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _nodeMap.Import(ref reader) && _bp.Import(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Match(ReadOnlySpan<byte> utf8Text, MatchCallback callback, ref MatchState state)
|
||||||
|
{
|
||||||
|
int nodeId = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < utf8Text.Length; index++)
|
||||||
|
{
|
||||||
|
long c = utf8Text[index];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long nodeSparseIndex = _nodesPerCharacter * c + (uint)nodeId;
|
||||||
|
int nodePlainIndex = _nodeMap.Rank1(nodeSparseIndex);
|
||||||
|
|
||||||
|
if (nodePlainIndex != 0)
|
||||||
|
{
|
||||||
|
long foundNodeSparseIndex = _nodeMap.Select1Ex(nodePlainIndex - 1);
|
||||||
|
|
||||||
|
if (foundNodeSparseIndex > 0 && foundNodeSparseIndex == nodeSparseIndex)
|
||||||
|
{
|
||||||
|
nodeId = nodePlainIndex;
|
||||||
|
|
||||||
|
if (callback != null)
|
||||||
|
{
|
||||||
|
// Match full word.
|
||||||
|
if (_wordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
int wordLength = _wordLengths[_wordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
int startIndex = index + 1 - wordLength;
|
||||||
|
|
||||||
|
if (!callback(utf8Text, startIndex, index + 1, nodeId, ref state))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a phrase composed of multiple words, also match each sub-word.
|
||||||
|
while (_multiWordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
nodePlainIndex = _multiWordIndices[_multiWordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
|
||||||
|
int wordLength = _wordMap.Has(nodePlainIndex) ? _wordLengths[_wordMap.Rank1(nodePlainIndex) - 1] : 0;
|
||||||
|
int startIndex = index + 1 - wordLength;
|
||||||
|
|
||||||
|
if (!callback(utf8Text, startIndex, index + 1, nodePlainIndex, ref state))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeId == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodePos = _bp.ToPos(nodeId);
|
||||||
|
nodePos = _bp.Enclose(nodePos);
|
||||||
|
if (nodePos < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeId = _bp.ToNodeId(nodePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Match<T>(ReadOnlySpan<byte> utf8Text, MatchCallback<T> callback, ref T state)
|
||||||
|
{
|
||||||
|
int nodeId = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < utf8Text.Length; index++)
|
||||||
|
{
|
||||||
|
long c = utf8Text[index];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long nodeSparseIndex = _nodesPerCharacter * c + (uint)nodeId;
|
||||||
|
int nodePlainIndex = _nodeMap.Rank1(nodeSparseIndex);
|
||||||
|
|
||||||
|
if (nodePlainIndex != 0)
|
||||||
|
{
|
||||||
|
long foundNodeSparseIndex = _nodeMap.Select1Ex(nodePlainIndex - 1);
|
||||||
|
|
||||||
|
if (foundNodeSparseIndex > 0 && foundNodeSparseIndex == nodeSparseIndex)
|
||||||
|
{
|
||||||
|
nodeId = nodePlainIndex;
|
||||||
|
|
||||||
|
if (callback != null)
|
||||||
|
{
|
||||||
|
// Match full word.
|
||||||
|
if (_wordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
int wordLength = _wordLengths[_wordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
int startIndex = index + 1 - wordLength;
|
||||||
|
|
||||||
|
if (!callback(utf8Text, startIndex, index + 1, nodeId, ref state))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a phrase composed of multiple words, also match each sub-word.
|
||||||
|
while (_multiWordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
nodePlainIndex = _multiWordIndices[_multiWordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
|
||||||
|
int wordLength = _wordMap.Has(nodePlainIndex) ? _wordLengths[_wordMap.Rank1(nodePlainIndex) - 1] : 0;
|
||||||
|
int startIndex = index + 1 - wordLength;
|
||||||
|
|
||||||
|
if (!callback(utf8Text, startIndex, index + 1, nodePlainIndex, ref state))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeId == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodePos = _bp.ToPos(nodeId);
|
||||||
|
nodePos = _bp.Enclose(nodePos);
|
||||||
|
if (nodePos < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeId = _bp.ToNodeId(nodePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetWordList(bool includeMultiWord = true)
|
||||||
|
{
|
||||||
|
// Storage must be large enough to fit the largest word in the dictionary.
|
||||||
|
// Since this is only used for debugging, it's fine to increase the size manually if needed.
|
||||||
|
StringBuilder sb = new();
|
||||||
|
Span<byte> storage = new byte[1024];
|
||||||
|
|
||||||
|
// Traverse trie from the root.
|
||||||
|
GetWord(sb, storage, 0, 0, includeMultiWord);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetWord(StringBuilder sb, Span<byte> storage, int storageOffset, int nodeId, bool includeMultiWord)
|
||||||
|
{
|
||||||
|
int characters = (int)((_nodeMap.RangeEndValue + _nodesPerCharacter - 1) / _nodesPerCharacter);
|
||||||
|
|
||||||
|
for (int c = 0; c < characters; c++)
|
||||||
|
{
|
||||||
|
long nodeSparseIndex = _nodesPerCharacter * c + (uint)nodeId;
|
||||||
|
int nodePlainIndex = _nodeMap.Rank1(nodeSparseIndex);
|
||||||
|
|
||||||
|
if (nodePlainIndex != 0)
|
||||||
|
{
|
||||||
|
long foundNodeSparseIndex = _nodeMap.Select1Ex(nodePlainIndex - 1);
|
||||||
|
|
||||||
|
if (foundNodeSparseIndex > 0 && foundNodeSparseIndex == nodeSparseIndex)
|
||||||
|
{
|
||||||
|
storage[storageOffset] = (byte)c;
|
||||||
|
int nextNodeId = nodePlainIndex;
|
||||||
|
|
||||||
|
if (_wordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
sb.AppendLine(Encoding.UTF8.GetString(storage[..(storageOffset + 1)]));
|
||||||
|
|
||||||
|
// Some basic validation to ensure we imported the dictionary properly.
|
||||||
|
int wordLength = _wordLengths[_wordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
|
||||||
|
Debug.Assert(storageOffset + 1 == wordLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeMultiWord)
|
||||||
|
{
|
||||||
|
int lastMultiWordIndex = 0;
|
||||||
|
string multiWord = "";
|
||||||
|
|
||||||
|
while (_multiWordMap.Has(nodePlainIndex))
|
||||||
|
{
|
||||||
|
nodePlainIndex = _multiWordIndices[_multiWordMap.Rank1((uint)nodePlainIndex) - 1];
|
||||||
|
|
||||||
|
int wordLength = _wordMap.Has(nodePlainIndex) ? _wordLengths[_wordMap.Rank1(nodePlainIndex) - 1] : 0;
|
||||||
|
int startIndex = storageOffset + 1 - wordLength;
|
||||||
|
|
||||||
|
multiWord += Encoding.UTF8.GetString(storage[lastMultiWordIndex..startIndex]) + " ";
|
||||||
|
lastMultiWordIndex = startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastMultiWordIndex != 0)
|
||||||
|
{
|
||||||
|
multiWord += Encoding.UTF8.GetString(storage[lastMultiWordIndex..(storageOffset + 1)]);
|
||||||
|
|
||||||
|
sb.AppendLine(multiWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWord(sb, storage, storageOffset + 1, nextNodeId, includeMultiWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BinaryReader.cs
Normal file
63
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BinaryReader.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
ref struct BinaryReader
|
||||||
|
{
|
||||||
|
private readonly ReadOnlySpan<byte> _data;
|
||||||
|
private int _offset;
|
||||||
|
|
||||||
|
public BinaryReader(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Read<T>(out T value) where T : unmanaged
|
||||||
|
{
|
||||||
|
int byteLength = Unsafe.SizeOf<T>();
|
||||||
|
|
||||||
|
if ((uint)(_offset + byteLength) <= (uint)_data.Length)
|
||||||
|
{
|
||||||
|
value = MemoryMarshal.Cast<byte, T>(_data[_offset..])[0];
|
||||||
|
_offset += byteLength;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AllocateAndReadArray<T>(ref T[] array, int length, int maxLengthExclusive) where T : unmanaged
|
||||||
|
{
|
||||||
|
return AllocateAndReadArray(ref array, Math.Min(length, maxLengthExclusive));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AllocateAndReadArray<T>(ref T[] array, int length) where T : unmanaged
|
||||||
|
{
|
||||||
|
array = new T[length];
|
||||||
|
|
||||||
|
return ReadArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadArray<T>(T[] array) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (array != null)
|
||||||
|
{
|
||||||
|
int byteLength = array.Length * Unsafe.SizeOf<T>();
|
||||||
|
byteLength = Math.Min(byteLength, _data.Length - _offset);
|
||||||
|
|
||||||
|
MemoryMarshal.Cast<byte, T>(_data.Slice(_offset, byteLength)).CopyTo(array);
|
||||||
|
|
||||||
|
_offset += byteLength;
|
||||||
|
|
||||||
|
return byteLength / Unsafe.SizeOf<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BitVector32.cs
Normal file
78
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BitVector32.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class BitVector32
|
||||||
|
{
|
||||||
|
private const int BitsPerWord = Set.BitsPerWord;
|
||||||
|
|
||||||
|
private int _bitLength;
|
||||||
|
private uint[] _array;
|
||||||
|
|
||||||
|
public int BitLength => _bitLength;
|
||||||
|
public uint[] Array => _array;
|
||||||
|
|
||||||
|
public BitVector32()
|
||||||
|
{
|
||||||
|
_bitLength = 0;
|
||||||
|
_array = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitVector32(int length)
|
||||||
|
{
|
||||||
|
_bitLength = length;
|
||||||
|
_array = new uint[(length + BitsPerWord - 1) / BitsPerWord];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Has(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index < (uint)_bitLength)
|
||||||
|
{
|
||||||
|
int wordIndex = index / BitsPerWord;
|
||||||
|
int wordBitOffset = index % BitsPerWord;
|
||||||
|
|
||||||
|
return ((_array[wordIndex] >> wordBitOffset) & 1u) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TurnOn(int index, int count)
|
||||||
|
{
|
||||||
|
for (int bit = 0; bit < count; bit++)
|
||||||
|
{
|
||||||
|
if (!TurnOn(index + bit))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TurnOn(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index < (uint)_bitLength)
|
||||||
|
{
|
||||||
|
int wordIndex = index / BitsPerWord;
|
||||||
|
int wordBitOffset = index % BitsPerWord;
|
||||||
|
|
||||||
|
_array[wordIndex] |= 1u << wordBitOffset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Import(ref BinaryReader reader)
|
||||||
|
{
|
||||||
|
if (!reader.Read(out _bitLength))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arrayLength = (_bitLength + BitsPerWord - 1) / BitsPerWord;
|
||||||
|
|
||||||
|
return reader.AllocateAndReadArray(ref _array, arrayLength) == arrayLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Ryujinx.Horizon/Sdk/Ngc/Detail/Bp.cs
Normal file
54
src/Ryujinx.Horizon/Sdk/Ngc/Detail/Bp.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class Bp
|
||||||
|
{
|
||||||
|
private readonly BpNode _firstNode = new();
|
||||||
|
private readonly SbvSelect _sbvSelect = new();
|
||||||
|
|
||||||
|
public bool Import(ref BinaryReader reader)
|
||||||
|
{
|
||||||
|
return _firstNode.Import(ref reader) && _sbvSelect.Import(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ToPos(int index)
|
||||||
|
{
|
||||||
|
return _sbvSelect.Select(_firstNode.Set, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Enclose(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index < (uint)_firstNode.Set.BitVector.BitLength)
|
||||||
|
{
|
||||||
|
if (!_firstNode.Set.Has(index))
|
||||||
|
{
|
||||||
|
index = _firstNode.FindOpen(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
return _firstNode.Enclose(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ToNodeId(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index < (uint)_firstNode.Set.BitVector.BitLength)
|
||||||
|
{
|
||||||
|
if (!_firstNode.Set.Has(index))
|
||||||
|
{
|
||||||
|
index = _firstNode.FindOpen(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
return _firstNode.Set.Rank1(index) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
241
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BpNode.cs
Normal file
241
src/Ryujinx.Horizon/Sdk/Ngc/Detail/BpNode.cs
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class BpNode
|
||||||
|
{
|
||||||
|
private readonly Set _set = new();
|
||||||
|
private SparseSet _sparseSet;
|
||||||
|
private BpNode _nextNode;
|
||||||
|
|
||||||
|
public Set Set => _set;
|
||||||
|
|
||||||
|
public bool Import(ref BinaryReader reader)
|
||||||
|
{
|
||||||
|
if (!_set.Import(ref reader))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reader.Read(out byte hasNext))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNext == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sparseSet = new();
|
||||||
|
_nextNode = new();
|
||||||
|
|
||||||
|
return _sparseSet.Import(ref reader) && _nextNode.Import(ref reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FindOpen(int index)
|
||||||
|
{
|
||||||
|
uint membershipBits = _set.BitVector.Array[index / Set.BitsPerWord];
|
||||||
|
|
||||||
|
int wordBitOffset = index % Set.BitsPerWord;
|
||||||
|
int unsetBits = 1;
|
||||||
|
|
||||||
|
for (int bit = wordBitOffset - 1; bit >= 0; bit--)
|
||||||
|
{
|
||||||
|
if (((membershipBits >> bit) & 1) != 0)
|
||||||
|
{
|
||||||
|
if (--unsetBits == 0)
|
||||||
|
{
|
||||||
|
return (index & ~(Set.BitsPerWord - 1)) | bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsetBits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int plainIndex = _sparseSet.Rank1(index);
|
||||||
|
if (plainIndex == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newIndex = index;
|
||||||
|
|
||||||
|
if (!_sparseSet.Has(index))
|
||||||
|
{
|
||||||
|
if (plainIndex == 0 || _nextNode == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newIndex = _sparseSet.Select1(plainIndex);
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plainIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int openIndex = _nextNode.FindOpen(plainIndex);
|
||||||
|
if (openIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int openSparseIndex = _sparseSet.Select1(openIndex);
|
||||||
|
if (openSparseIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex != index)
|
||||||
|
{
|
||||||
|
unsetBits = 1;
|
||||||
|
|
||||||
|
for (int bit = newIndex % Set.BitsPerWord - 1; bit > wordBitOffset; bit--)
|
||||||
|
{
|
||||||
|
unsetBits += ((membershipBits >> bit) & 1) != 0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bestCandidate = -1;
|
||||||
|
|
||||||
|
membershipBits = _set.BitVector.Array[openSparseIndex / Set.BitsPerWord];
|
||||||
|
|
||||||
|
for (int bit = openSparseIndex % Set.BitsPerWord + 1; bit < Set.BitsPerWord; bit++)
|
||||||
|
{
|
||||||
|
if (unsetBits - 1 == 0)
|
||||||
|
{
|
||||||
|
bestCandidate = bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsetBits += ((membershipBits >> bit) & 1) != 0 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (openSparseIndex & ~(Set.BitsPerWord - 1)) | bestCandidate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return openSparseIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Enclose(int index)
|
||||||
|
{
|
||||||
|
uint membershipBits = _set.BitVector.Array[index / Set.BitsPerWord];
|
||||||
|
|
||||||
|
int unsetBits = 1;
|
||||||
|
|
||||||
|
for (int bit = index % Set.BitsPerWord - 1; bit >= 0; bit--)
|
||||||
|
{
|
||||||
|
if (((membershipBits >> bit) & 1) != 0)
|
||||||
|
{
|
||||||
|
if (--unsetBits == 0)
|
||||||
|
{
|
||||||
|
return (index & ~(Set.BitsPerWord - 1)) + bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsetBits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int setBits = 2;
|
||||||
|
|
||||||
|
for (int bit = index % Set.BitsPerWord + 1; bit < Set.BitsPerWord; bit++)
|
||||||
|
{
|
||||||
|
if (((membershipBits >> bit) & 1) != 0)
|
||||||
|
{
|
||||||
|
setBits++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (--setBits == 0)
|
||||||
|
{
|
||||||
|
return FindOpen((index & ~(Set.BitsPerWord - 1)) + bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int newIndex = index;
|
||||||
|
|
||||||
|
if (!_sparseSet.Has(index))
|
||||||
|
{
|
||||||
|
newIndex = _sparseSet.Select1(_sparseSet.Rank1(index));
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_set.Has(newIndex))
|
||||||
|
{
|
||||||
|
newIndex = FindOpen(newIndex);
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newIndex = _nextNode.Enclose(_sparseSet.Rank1(newIndex) - 1);
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newIndex = _sparseSet.Select1(newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nearestIndex = _sparseSet.Select1(_sparseSet.Rank1(newIndex));
|
||||||
|
if (nearestIndex < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBits = 0;
|
||||||
|
|
||||||
|
membershipBits = _set.BitVector.Array[newIndex / Set.BitsPerWord];
|
||||||
|
|
||||||
|
if ((newIndex / Set.BitsPerWord) == (nearestIndex / Set.BitsPerWord))
|
||||||
|
{
|
||||||
|
for (int bit = nearestIndex % Set.BitsPerWord - 1; bit >= newIndex % Set.BitsPerWord; bit--)
|
||||||
|
{
|
||||||
|
if (((membershipBits >> bit) & 1) != 0)
|
||||||
|
{
|
||||||
|
if (++setBits > 0)
|
||||||
|
{
|
||||||
|
return (newIndex & ~(Set.BitsPerWord - 1)) + bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBits--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int bit = Set.BitsPerWord - 1; bit >= newIndex % Set.BitsPerWord; bit--)
|
||||||
|
{
|
||||||
|
if (((membershipBits >> bit) & 1) != 0)
|
||||||
|
{
|
||||||
|
if (++setBits > 0)
|
||||||
|
{
|
||||||
|
return (newIndex & ~(Set.BitsPerWord - 1)) + bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBits--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
Normal file
100
src/Ryujinx.Horizon/Sdk/Ngc/Detail/CompressedArray.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class CompressedArray
|
||||||
|
{
|
||||||
|
private const int MaxUncompressedEntries = 64;
|
||||||
|
private const int CompressedEntriesPerBlock = 64;
|
||||||
|
private const int BitsPerWord = Set.BitsPerWord;
|
||||||
|
|
||||||
|
private readonly struct BitfieldRange
|
||||||
|
{
|
||||||
|
private readonly uint _range;
|
||||||
|
private readonly int _baseValue;
|
||||||
|
|
||||||
|
public int BitfieldIndex => (int)(_range & 0x7ffffff);
|
||||||
|
public int BitfieldLength => (int)(_range >> 27) + 1;
|
||||||
|
public int BaseValue => _baseValue;
|
||||||
|
|
||||||
|
public BitfieldRange(uint range, int baseValue)
|
||||||
|
{
|
||||||
|
_range = range;
|
||||||
|
_baseValue = baseValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint[] _bitfieldRanges;
|
||||||
|
private uint[] _bitfields;
|
||||||
|
private int[] _uncompressedArray;
|
||||||
|
|
||||||
|
public int Length => (_bitfieldRanges.Length / 2) * CompressedEntriesPerBlock + _uncompressedArray.Length;
|
||||||
|
|
||||||
|
public int this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var ranges = GetBitfieldRanges();
|
||||||
|
|
||||||
|
int rangeBlockIndex = index / CompressedEntriesPerBlock;
|
||||||
|
|
||||||
|
if (rangeBlockIndex < ranges.Length)
|
||||||
|
{
|
||||||
|
var range = ranges[rangeBlockIndex];
|
||||||
|
|
||||||
|
int bitfieldLength = range.BitfieldLength;
|
||||||
|
int bitfieldOffset = (index % CompressedEntriesPerBlock) * bitfieldLength;
|
||||||
|
int bitfieldIndex = range.BitfieldIndex + (bitfieldOffset / BitsPerWord);
|
||||||
|
int bitOffset = bitfieldOffset % BitsPerWord;
|
||||||
|
|
||||||
|
ulong bitfieldValue = _bitfields[bitfieldIndex];
|
||||||
|
|
||||||
|
// If the bit fields crosses the word boundary, let's load the next one to ensure we
|
||||||
|
// have access to the full value.
|
||||||
|
if (bitOffset + bitfieldLength > BitsPerWord)
|
||||||
|
{
|
||||||
|
bitfieldValue |= (ulong)_bitfields[bitfieldIndex + 1] << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value = (int)(bitfieldValue >> bitOffset) & ((1 << bitfieldLength) - 1);
|
||||||
|
|
||||||
|
// Sign-extend.
|
||||||
|
int remainderBits = BitsPerWord - bitfieldLength;
|
||||||
|
value <<= remainderBits;
|
||||||
|
value >>= remainderBits;
|
||||||
|
|
||||||
|
return value + range.BaseValue;
|
||||||
|
}
|
||||||
|
else if (rangeBlockIndex < _uncompressedArray.Length + _bitfieldRanges.Length * BitsPerWord)
|
||||||
|
{
|
||||||
|
return _uncompressedArray[index % MaxUncompressedEntries];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<BitfieldRange> GetBitfieldRanges()
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<uint, BitfieldRange>(_bitfieldRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Import(ref BinaryReader reader)
|
||||||
|
{
|
||||||
|
if (!reader.Read(out int bitfieldRangesCount) ||
|
||||||
|
reader.AllocateAndReadArray(ref _bitfieldRanges, bitfieldRangesCount) != bitfieldRangesCount)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reader.Read(out int bitfieldsCount) || reader.AllocateAndReadArray(ref _bitfields, bitfieldsCount) != bitfieldsCount)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader.Read(out byte uncompressedArrayLength) &&
|
||||||
|
reader.AllocateAndReadArray(ref _uncompressedArray, uncompressedArrayLength, MaxUncompressedEntries) == uncompressedArrayLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
404
src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs
Normal file
404
src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Fs;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
class ContentsReader : IDisposable
|
||||||
|
{
|
||||||
|
private const string MountName = "NgWord";
|
||||||
|
private const string VersionFilePath = $"{MountName}:/version.dat";
|
||||||
|
private const ulong DataId = 0x100000000000823UL;
|
||||||
|
|
||||||
|
private enum AcType
|
||||||
|
{
|
||||||
|
AcNotB,
|
||||||
|
AcB1,
|
||||||
|
AcB2,
|
||||||
|
AcSimilarForm,
|
||||||
|
TableSimilarForm,
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IFsClient _fsClient;
|
||||||
|
private readonly object _lock;
|
||||||
|
private bool _intialized;
|
||||||
|
private ulong _cacheSize;
|
||||||
|
|
||||||
|
public ContentsReader(IFsClient fsClient)
|
||||||
|
{
|
||||||
|
_lock = new();
|
||||||
|
_fsClient = fsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MakeMountPoint(out string path, AcType type, int regionIndex)
|
||||||
|
{
|
||||||
|
path = null;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case AcType.AcNotB:
|
||||||
|
if (regionIndex < 0)
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_common_not_b_nx";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_{regionIndex}_not_b_nx";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AcType.AcB1:
|
||||||
|
if (regionIndex < 0)
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_common_b1_nx";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_{regionIndex}_b1_nx";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AcType.AcB2:
|
||||||
|
if (regionIndex < 0)
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_common_b2_nx";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = $"{MountName}:/ac_{regionIndex}_b2_nx";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AcType.AcSimilarForm:
|
||||||
|
path = $"{MountName}:/ac_similar_form_nx";
|
||||||
|
break;
|
||||||
|
case AcType.TableSimilarForm:
|
||||||
|
path = $"{MountName}:/table_similar_form_nx";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Initialize(ulong cacheSize)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_intialized)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result result = _fsClient.QueryMountSystemDataCacheSize(out long dataCacheSize, DataId);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheSize < (ulong)dataCacheSize)
|
||||||
|
{
|
||||||
|
return NgcResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _fsClient.MountSystemData(MountName, DataId);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
// Official firmware would return the result here,
|
||||||
|
// we don't to support older firmware where the archive didn't exist yet.
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cacheSize = cacheSize;
|
||||||
|
_intialized = true;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Reload()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!_intialized)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fsClient.Unmount(MountName);
|
||||||
|
|
||||||
|
Result result = Result.Success;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = _fsClient.QueryMountSystemDataCacheSize(out long cacheSize, DataId);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cacheSize < (ulong)cacheSize)
|
||||||
|
{
|
||||||
|
result = NgcResult.InvalidSize;
|
||||||
|
return NgcResult.InvalidSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _fsClient.MountSystemData(MountName, DataId);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
_intialized = false;
|
||||||
|
_cacheSize = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result GetFileSize(out long size, string filePath)
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
Result result = _fsClient.OpenFile(out FileHandle handle, filePath, OpenMode.Read);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = _fsClient.GetFileSize(out size, handle);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_fsClient.CloseFile(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result GetFileContent(Span<byte> destination, string filePath)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
Result result = _fsClient.OpenFile(out FileHandle handle, filePath, OpenMode.Read);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = _fsClient.ReadFile(handle, 0, destination);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_fsClient.CloseFile(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetVersionDataSize(out long size)
|
||||||
|
{
|
||||||
|
return GetFileSize(out size, VersionFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetVersionData(Span<byte> destination)
|
||||||
|
{
|
||||||
|
return GetFileContent(destination, VersionFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadDictionaries(out AhoCorasick partialWordsTrie, out AhoCorasick completeWordsTrie, out AhoCorasick delimitedWordsTrie, int regionIndex)
|
||||||
|
{
|
||||||
|
completeWordsTrie = null;
|
||||||
|
delimitedWordsTrie = null;
|
||||||
|
|
||||||
|
MakeMountPoint(out string partialWordsTriePath, AcType.AcNotB, regionIndex);
|
||||||
|
MakeMountPoint(out string completeWordsTriePath, AcType.AcB1, regionIndex);
|
||||||
|
MakeMountPoint(out string delimitedWordsTriePath, AcType.AcB2, regionIndex);
|
||||||
|
|
||||||
|
Result result = ReadDictionary(out partialWordsTrie, partialWordsTriePath);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ReadDictionary(out completeWordsTrie, completeWordsTriePath);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadDictionary(out delimitedWordsTrie, delimitedWordsTriePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadSimilarFormDictionary(out AhoCorasick similarFormTrie)
|
||||||
|
{
|
||||||
|
MakeMountPoint(out string similarFormTriePath, AcType.AcSimilarForm, 0);
|
||||||
|
|
||||||
|
return ReadDictionary(out similarFormTrie, similarFormTriePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadSimilarFormTable(out SimilarFormTable similarFormTable)
|
||||||
|
{
|
||||||
|
similarFormTable = null;
|
||||||
|
|
||||||
|
MakeMountPoint(out string similarFormTablePath, AcType.TableSimilarForm, 0);
|
||||||
|
|
||||||
|
Result result = ReadGZipCompressedArchive(out byte[] data, similarFormTablePath);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryReader reader = new(data);
|
||||||
|
SimilarFormTable table = new();
|
||||||
|
|
||||||
|
if (!table.Import(ref reader))
|
||||||
|
{
|
||||||
|
// Official firmware doesn't return an error here and just assumes the import was successful.
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
similarFormTable = table;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result ReadNotSeparatorDictionary(out AhoCorasick notSeparatorTrie)
|
||||||
|
{
|
||||||
|
notSeparatorTrie = null;
|
||||||
|
|
||||||
|
BinaryReader reader = new(EmbeddedTries.NotSeparatorTrie);
|
||||||
|
AhoCorasick ac = new();
|
||||||
|
|
||||||
|
if (!ac.Import(ref reader))
|
||||||
|
{
|
||||||
|
// Official firmware doesn't return an error here and just assumes the import was successful.
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
notSeparatorTrie = ac;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result ReadDictionary(out AhoCorasick trie, string path)
|
||||||
|
{
|
||||||
|
trie = null;
|
||||||
|
|
||||||
|
Result result = ReadGZipCompressedArchive(out byte[] data, path);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryReader reader = new(data);
|
||||||
|
AhoCorasick ac = new();
|
||||||
|
|
||||||
|
if (!ac.Import(ref reader))
|
||||||
|
{
|
||||||
|
// Official firmware doesn't return an error here and just assumes the import was successful.
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
trie = ac;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result ReadGZipCompressedArchive(out byte[] data, string filePath)
|
||||||
|
{
|
||||||
|
data = null;
|
||||||
|
|
||||||
|
Result result = _fsClient.OpenFile(out FileHandle handle, filePath, OpenMode.Read);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = _fsClient.GetFileSize(out long fileSize, handle);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new byte[fileSize];
|
||||||
|
|
||||||
|
result = _fsClient.ReadFile(handle, 0, data.AsSpan());
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_fsClient.CloseFile(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = DecompressGZipCompressedStream(data);
|
||||||
|
}
|
||||||
|
catch (InvalidDataException)
|
||||||
|
{
|
||||||
|
// Official firmware returns a different error, but it is translated to this error on the caller.
|
||||||
|
return NgcResult.DataAccessError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] DecompressGZipCompressedStream(byte[] data)
|
||||||
|
{
|
||||||
|
using MemoryStream input = new(data);
|
||||||
|
using GZipStream gZipStream = new(input, CompressionMode.Decompress);
|
||||||
|
using MemoryStream output = new();
|
||||||
|
|
||||||
|
gZipStream.CopyTo(output);
|
||||||
|
|
||||||
|
return output.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!_intialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fsClient.Unmount(MountName);
|
||||||
|
_intialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
266
src/Ryujinx.Horizon/Sdk/Ngc/Detail/EmbeddedTries.cs
Normal file
266
src/Ryujinx.Horizon/Sdk/Ngc/Detail/EmbeddedTries.cs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
static class EmbeddedTries
|
||||||
|
{
|
||||||
|
public static ReadOnlySpan<byte> NotSeparatorTrie => new byte[]
|
||||||
|
{
|
||||||
|
0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
|
||||||
|
0xE9, 0xFF, 0xE9, 0xFF, 0xF4, 0xFF, 0xFA, 0xBF, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x5F, 0xFF, 0xAF,
|
||||||
|
0xFF, 0xEB, 0xFF, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xBF, 0xFF, 0xFB, 0x7F, 0xFF, 0xEF, 0xFF, 0xFD,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xBF, 0xFF, 0xF7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFC, 0x3F, 0xFF, 0xCF, 0xFF, 0xF3, 0xFF, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xE7, 0xFF,
|
||||||
|
0xFC, 0x9F, 0xFF, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x9F, 0xFF, 0xE7, 0xFF, 0xF9, 0x7F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFE, 0x5F, 0xFF, 0xCF, 0xFF, 0xF3, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x3F, 0xFF, 0xCF, 0xFF, 0xF3, 0xFF, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xCF, 0xFF, 0xF3,
|
||||||
|
0xFF, 0xFC, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xFF, 0xFB, 0x7F, 0xFE, 0x9F, 0xFF, 0xF3,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x9F, 0xFF, 0xF3, 0xFF, 0xFC, 0x9F, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xFF, 0xF3, 0x7F, 0xFE, 0xCF, 0xFF, 0xF5, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x8A, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
|
||||||
|
0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0x55, 0xA9, 0x52, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0xAA, 0x54, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0xAA, 0x54, 0x55, 0xA5, 0x4A, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0x52, 0x55, 0x55,
|
||||||
|
0x95, 0xAA, 0xAA, 0xAA, 0x54, 0x55, 0x55, 0xA5, 0xAA, 0xAA, 0x2A, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xA5, 0xAA, 0xAA, 0xAA, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||||
|
0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x4A,
|
||||||
|
0x55, 0x55, 0x55, 0xA9, 0xAA, 0xAA, 0x52, 0x55, 0x55, 0xA5, 0xAA, 0xAA, 0x4A, 0x55, 0x55, 0x05,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00,
|
||||||
|
0x00, 0xF7, 0x01, 0x00, 0x00, 0x77, 0x02, 0x00, 0x00, 0xF7, 0x02, 0x00, 0x00, 0x6E, 0x03, 0x00,
|
||||||
|
0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00,
|
||||||
|
0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x4E, 0x5E, 0x6D, 0x00, 0x0F, 0x1E,
|
||||||
|
0x2E, 0x3D, 0x4C, 0x5C, 0x6B, 0x00, 0x10, 0x20, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x00, 0x10, 0x20,
|
||||||
|
0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20,
|
||||||
|
0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20, 0x30, 0x3F, 0x4F, 0x5E, 0x6D, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x03,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20,
|
||||||
|
0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01,
|
||||||
|
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||||
|
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||||
|
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||||
|
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||||
|
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
|
||||||
|
0x21, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
|
||||||
|
0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
|
||||||
|
0x00, 0x02, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x8A, 0x03, 0x00, 0x00, 0x00, 0x8A, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0xC5, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x51, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x03, 0x00,
|
||||||
|
0x00, 0x07, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xCF, 0xED, 0x81, 0x61, 0xD9, 0xDC, 0x8A,
|
||||||
|
0xD3, 0xF0, 0xBB, 0x05, 0x6E, 0xEB, 0x0D, 0x88, 0x6C, 0x39, 0x62, 0x01, 0x95, 0x82, 0xCF, 0xEE,
|
||||||
|
0x3A, 0x7F, 0x53, 0xDF, 0x09, 0x90, 0xF7, 0x06, 0xA4, 0x7A, 0x2D, 0xB3, 0xE7, 0xFA, 0x20, 0x48,
|
||||||
|
0x0F, 0x38, 0x34, 0xED, 0xBC, 0x8A, 0x96, 0xAB, 0x8E, 0xE3, 0xFF, 0xC6, 0xD2, 0xBF, 0xC0, 0x90,
|
||||||
|
0x06, 0x34, 0xDF, 0xF0, 0xDB, 0xDE, 0x27, 0x2E, 0xD5, 0x3C, 0xA2, 0x22, 0x72, 0xBD, 0x02, 0x0D,
|
||||||
|
0x1F, 0xB2, 0x99, 0xBE, 0x17, 0x26, 0xA1, 0xEF, 0x40, 0xF2, 0x61, 0xE1, 0x16, 0x17, 0xA4, 0xF4,
|
||||||
|
0x3A, 0x0F, 0x3C, 0x3A, 0xAB, 0x74, 0x83, 0x93, 0xB2, 0x09, 0x43, 0x52, 0x6E, 0xB8, 0xBF, 0xC8,
|
||||||
|
0x9C, 0x6A, 0x73, 0xD3, 0x0C, 0xC8, 0x5C, 0x71, 0xCD, 0x87, 0xCA, 0x28, 0xF6, 0xEB, 0x87, 0x60,
|
||||||
|
0x3D, 0xA5, 0x15, 0x9B, 0xAA, 0x99, 0x23, 0x9F, 0xD6, 0x2E, 0x79, 0x58, 0xE9, 0x8E, 0x54, 0xB0,
|
||||||
|
0xF8, 0x07, 0x6F, 0x6C, 0x52, 0xB7, 0xE2, 0x34, 0x42, 0x8C, 0x7A, 0xD5, 0xEC, 0xA4, 0xFE, 0x52,
|
||||||
|
0x9A, 0x05, 0x9F, 0xDD, 0x8D, 0x73, 0x8B, 0xA6, 0xDB, 0xA7, 0x84, 0xD0, 0xAB, 0xB7, 0xCC, 0x9E,
|
||||||
|
0x4B, 0xD8, 0xB2, 0xDC, 0x0F, 0xE8, 0x3A, 0x56, 0xB9, 0x63, 0x75, 0x1C, 0x7F, 0x89, 0xDF, 0x7C,
|
||||||
|
0x84, 0xE2, 0x8C, 0xA9, 0x0D, 0xA3, 0xDF, 0xF6, 0x3E, 0xC7, 0xCE, 0x1B, 0x24, 0x94, 0xB8, 0xE8,
|
||||||
|
0xD7, 0xDC, 0xA6, 0xEF, 0x85, 0xA1, 0x7D, 0x00, 0xE1, 0x78, 0xD4, 0x8B, 0x13, 0xCB, 0xB6, 0x4B,
|
||||||
|
0x5E, 0xCB, 0xF3, 0xC0, 0xA3, 0x09, 0x68, 0x68, 0x4C, 0xF4, 0x98, 0x0D, 0x38, 0x0D, 0xBF, 0xFB,
|
||||||
|
0x8B, 0xCC, 0x55, 0x71, 0x21, 0xC1, 0xFC, 0x3B, 0x60, 0x77, 0x9D, 0x3F, 0x54, 0x46, 0x61, 0x4A,
|
||||||
|
0xC8, 0xA5, 0xDB, 0x21, 0x8A, 0xCA, 0x73, 0x7D, 0x10, 0xF9, 0xB4, 0xD6, 0x9E, 0x15, 0x8E, 0x58,
|
||||||
|
0x94, 0x3C, 0xA9, 0xF1, 0x7F, 0x63, 0x93, 0xBA, 0xD5, 0x51, 0x35, 0xA1, 0x93, 0x93, 0xF5, 0xEE,
|
||||||
|
0x13, 0x97, 0xD2, 0x2C, 0xF8, 0x97, 0xFD, 0x98, 0x58, 0xD3, 0x6A, 0x8C, 0x2E, 0x4C, 0x42, 0xAF,
|
||||||
|
0xDE, 0x32, 0xC1, 0x4B, 0x5A, 0x61, 0x6D, 0xF9, 0xA3, 0xB3, 0xCA, 0x1D, 0xAB, 0x13, 0xE3, 0x14,
|
||||||
|
0xAC, 0xBB, 0xF3, 0x33, 0xA7, 0xDA, 0x30, 0xFA, 0xED, 0x40, 0xBB, 0x6A, 0x62, 0xC0, 0x30, 0x8A,
|
||||||
|
0xFD, 0x9A, 0xDB, 0xF4, 0x49, 0x7B, 0xA6, 0x3B, 0x17, 0x90, 0xD6, 0x2E, 0x79, 0x2D, 0xCF, 0x63,
|
||||||
|
0xE4, 0xB8, 0x1F, 0x5B, 0xD1, 0xDC, 0x8A, 0xD3, 0xF0, 0xBB, 0xBF, 0x73, 0xEF, 0x11, 0xE2, 0x0F,
|
||||||
|
0x29, 0xF8, 0xEC, 0xAE, 0xF3, 0x07, 0x5B, 0x11, 0x5F, 0x90, 0xB0, 0x53, 0xAE, 0x65, 0xF6, 0x5C,
|
||||||
|
0x1F, 0x44, 0x80, 0x4F, 0xC1, 0x83, 0x63, 0x9F, 0xE1, 0xAA, 0xE3, 0xF8, 0xBF, 0xB1, 0x51, 0x66,
|
||||||
|
0x19, 0x19, 0x13, 0xA0, 0xF7, 0x6D, 0xEF, 0x13, 0x97, 0x12, 0x75, 0xAC, 0xB7, 0x8C, 0x60, 0x3F,
|
||||||
|
0xC5, 0x71, 0x9B, 0xBE, 0x17, 0x26, 0xA1, 0x97, 0xB7, 0x0D, 0x6A, 0xE9, 0x28, 0x99, 0x68, 0x79,
|
||||||
|
0x1E, 0x78, 0x74, 0x56, 0x39, 0xF4, 0x5D, 0x75, 0x23, 0x7A, 0xB6, 0xEF, 0xFE, 0x22, 0x73, 0xAA,
|
||||||
|
0x0D, 0xE5, 0x01, 0x5A, 0xD0, 0x89, 0x2A, 0xE7, 0x0F, 0x95, 0x51, 0xEC, 0xD7, 0xE4, 0x2F, 0x7C,
|
||||||
|
0x4B, 0xAC, 0xEC, 0x3D, 0x88, 0x7C, 0x5A, 0xBB, 0xE4, 0xD5, 0x50, 0x41, 0x56, 0xC5, 0xBC, 0x7C,
|
||||||
|
0x63, 0x93, 0xBA, 0x15, 0xA7, 0x61, 0xC8, 0x47, 0xFA, 0x65, 0x1B, 0x07, 0x97, 0xD2, 0x2C, 0xF8,
|
||||||
|
0xEC, 0xAE, 0x35, 0x29, 0x6E, 0xDA, 0x0E, 0x6D, 0x84, 0x5E, 0xBD, 0x65, 0xF6, 0x5C, 0x27, 0xCD,
|
||||||
|
0xCC, 0x73, 0x80, 0xF6, 0xB2, 0xCA, 0x1D, 0xAB, 0xE3, 0xF8, 0xDF, 0xD5, 0x83, 0xF7, 0x15, 0xE4,
|
||||||
|
0x50, 0x6D, 0x18, 0xFD, 0xB6, 0xF7, 0x09, 0xDC, 0x51, 0x7F, 0xA0, 0xB8, 0x57, 0xB0, 0x5F, 0x73,
|
||||||
|
0x9B, 0xBE, 0x17, 0x26, 0x42, 0x42, 0xC4, 0x83, 0xAF, 0xE9, 0x92, 0xD7, 0xF2, 0x3C, 0xF0, 0xE8,
|
||||||
|
0x30, 0x1D, 0x1B, 0x94, 0xE0, 0x47, 0x9C, 0x86, 0xDF, 0xFD, 0x45, 0xE6, 0x64, 0xC5, 0x94, 0x64,
|
||||||
|
0x8C, 0xA4, 0xB3, 0xBB, 0xCE, 0x1F, 0x2A, 0xA3, 0x18, 0x58, 0xF4, 0xE2, 0x59, 0xA6, 0xD8, 0x73,
|
||||||
|
0x7D, 0x10, 0xF9, 0xB4, 0x76, 0x6A, 0x56, 0xCE, 0xD8, 0x15, 0xC7, 0xFF, 0x8D, 0x4D, 0xEA, 0x56,
|
||||||
|
0xA4, 0xDB, 0x86, 0x50, 0xD5, 0x99, 0xBD, 0x4F, 0x5C, 0x4A, 0xB3, 0xE0, 0xD3, 0x0F, 0x6C, 0x6A,
|
||||||
|
0x69, 0x71, 0x7B, 0x21, 0xF4, 0xEA, 0x2D, 0xB3, 0x08, 0xE5, 0x95, 0xEC, 0xDB, 0x03, 0x1E, 0xAB,
|
||||||
|
0xDC, 0xB1, 0x3A, 0x96, 0x50, 0xC3, 0x6E, 0x64, 0x41, 0x91, 0xA9, 0x0D, 0xA3, 0xDF, 0x36, 0x27,
|
||||||
|
0xEA, 0x5D, 0xE3, 0xA5, 0x0F, 0xCA, 0xE8, 0xD7, 0xDC, 0xA6, 0xEF, 0x26, 0x74, 0x5D, 0xC0, 0xCD,
|
||||||
|
0x78, 0x5A, 0xC9, 0x6B, 0x79, 0x1E, 0x80, 0xC9, 0xFF, 0x8C, 0x96, 0x79, 0x84, 0xBA, 0x4D, 0xC3,
|
||||||
|
0xEF, 0xFE, 0x42, 0xC7, 0x4F, 0x58, 0xE0, 0x2D, 0x59, 0xB0, 0xBB, 0xCE, 0x1F, 0x2A, 0x44, 0xC3,
|
||||||
|
0x04, 0xA4, 0xBF, 0xF1, 0x96, 0xE7, 0xFA, 0x20, 0xF2, 0x71, 0x42, 0x3A, 0x2A, 0x42, 0xD0, 0x58,
|
||||||
|
0x8D, 0xFF, 0x1B, 0x9B, 0x14, 0x56, 0x73, 0xA2, 0x39, 0x96, 0xD0, 0xEF, 0x3E, 0x71, 0x29, 0xCD,
|
||||||
|
0xC4, 0xA4, 0x98, 0x6F, 0x89, 0xE9, 0x54, 0xB5, 0xE9, 0xC2, 0x24, 0xF4, 0xEA, 0xB1, 0x5D, 0x3B,
|
||||||
|
0x64, 0x55, 0x44, 0x9E, 0x3F, 0x3A, 0xAB, 0xDC, 0xD1, 0x8E, 0x2B, 0x4A, 0xBF, 0x2C, 0x77, 0x3F,
|
||||||
|
0x73, 0xAA, 0x0D, 0xA3, 0x00, 0xE1, 0x93, 0x9B, 0xB6, 0xE1, 0x0F, 0xA3, 0xD8, 0xAF, 0xB9, 0x55,
|
||||||
|
0x30, 0xB3, 0xE6, 0x39, 0x50, 0xD0, 0xDA, 0x25, 0xAF, 0x65, 0x8A, 0x75, 0x0C, 0xEF, 0x53, 0xBD,
|
||||||
|
0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xBF, 0x70, 0xEF, 0xBF, 0xB0, 0xFB, 0x37, 0xF4, 0xFD,
|
||||||
|
0x0D, 0xDD, 0xDF, 0x85, 0xEF, 0xEF, 0x89, 0xF7, 0xFB, 0xC4, 0xFB, 0x3E, 0x78, 0xF7, 0x13, 0xDF,
|
||||||
|
0x7D, 0xC5, 0xB7, 0x5F, 0xF8, 0xF6, 0x0B, 0x5F, 0x7F, 0xE1, 0xED, 0x2F, 0xDC, 0xFD, 0x85, 0xBD,
|
||||||
|
0xDF, 0xD0, 0xF7, 0xDF, 0xC1, 0xF7, 0x77, 0xF0, 0x7D, 0x0F, 0xBE, 0xEF, 0x83, 0xEF, 0xFB, 0xE0,
|
||||||
|
0xBD, 0x1F, 0xBC, 0xF7, 0x0B, 0x77, 0xBF, 0x70, 0xF7, 0x0B, 0xD7, 0xBF, 0x70, 0xFD, 0x0B, 0xD7,
|
||||||
|
0xBF, 0xB0, 0xFD, 0x1D, 0xBA, 0xDF, 0x83, 0xF7, 0x7B, 0x70, 0xDF, 0x87, 0xDE, 0xF7, 0x83, 0xFB,
|
||||||
|
0xFE, 0xE0, 0xDE, 0x2F, 0xDC, 0xFD, 0x85, 0xDB, 0xDF, 0x70, 0xFB, 0x1B, 0xAE, 0x7F, 0xC3, 0xF5,
|
||||||
|
0x6F, 0xD8, 0xFE, 0x0D, 0xDB, 0xDF, 0xA1, 0xFB, 0x3B, 0x78, 0xBF, 0x07, 0xF7, 0xF7, 0xE0, 0x7E,
|
||||||
|
0x1F, 0xDC, 0xF7, 0x83, 0x7B, 0x3F, 0xB8, 0xF7, 0x07, 0x77, 0xBF, 0x70, 0xFB, 0x0B, 0xD7, 0xBF,
|
||||||
|
0xF0, 0xFA, 0x17, 0xB6, 0xBF, 0x61, 0xF7, 0x37, 0x74, 0xBF, 0x83, 0xF7, 0x3D, 0xB8, 0xDF, 0x83,
|
||||||
|
0xFB, 0x3E, 0x78, 0xDF, 0x0F, 0xDE, 0xFD, 0xE0, 0xDD, 0x17, 0xDE, 0x7E, 0xE1, 0xF5, 0x0B, 0x0F,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xA8, 0x01, 0x00,
|
||||||
|
0x00, 0x53, 0x02, 0x00, 0x00, 0xFD, 0x02, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0x88, 0x03, 0x00,
|
||||||
|
0x00, 0x89, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00,
|
||||||
|
0x00, 0x89, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x27, 0x3B, 0x00, 0x18, 0x2B, 0x42, 0x57, 0x6D, 0x81,
|
||||||
|
0x98, 0x00, 0x17, 0x2B, 0x42, 0x56, 0x6D, 0x80, 0x97, 0x00, 0x17, 0x2B, 0x43, 0x56, 0x6D, 0x80,
|
||||||
|
0x97, 0x00, 0x16, 0x2B, 0x40, 0x55, 0x69, 0x80, 0x94, 0x00, 0x13, 0x29, 0x3E, 0x52, 0x68, 0x7C,
|
||||||
|
0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1C, 0x00,
|
||||||
|
0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x01, 0x80,
|
||||||
|
0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x02, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00,
|
||||||
|
0x20, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x40, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x20, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x40, 0x00, 0x00, 0x08, 0x00, 0x80, 0x00, 0x00, 0x20, 0x00, 0x00, 0x02, 0x40, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00,
|
||||||
|
0x19, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x05, 0x07, 0x08, 0x0A, 0x0B,
|
||||||
|
0x00, 0x01, 0x02, 0x04, 0x06, 0x07, 0x09, 0x0A, 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0A,
|
||||||
|
0x00, 0x01, 0x03, 0x04, 0x06, 0x14, 0x07, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||||
|
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x81, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00,
|
||||||
|
0x00, 0x81, 0x02, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x81, 0x03, 0x00, 0x00, 0x00, 0x11, 0x21,
|
||||||
|
0x31, 0x41, 0x51, 0x61, 0x71, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20,
|
||||||
|
0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20,
|
||||||
|
0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x10, 0x20,
|
||||||
|
0x30, 0x40, 0x50, 0x60, 0x70, 0x00, 0x01, 0x14, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x38, 0x8E, 0xE3, 0x38, 0x8E,
|
||||||
|
0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3,
|
||||||
|
0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x38,
|
||||||
|
0x8E, 0xE3, 0x38, 0x8E, 0xE3, 0x18, 0x00, 0x00, 0x02, 0x00, 0x00, 0x51, 0x14, 0x45, 0x51, 0x14,
|
||||||
|
0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45,
|
||||||
|
0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51, 0x14, 0x45, 0x51,
|
||||||
|
0x14, 0x45, 0x51, 0x14, 0x45, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x15, 0x20, 0x2B, 0x35, 0x40, 0x4B, 0x00,
|
||||||
|
0x0B, 0x16, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x08, 0x20, 0x00, 0x01, 0x08, 0x20, 0x00, 0x01, 0x08, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x09, 0x72, 0x00, 0x00,
|
||||||
|
0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x21, 0x31, 0x01, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x8E,
|
||||||
|
0x23, 0x00, 0x20, 0x00, 0x00, 0x00, 0x51, 0x14, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x8A, 0x03, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||||
|
0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A,
|
||||||
|
0x0C, 0x0E, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x00, 0x02, 0x04, 0x06, 0x08,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/Ryujinx.Horizon/Sdk/Ngc/Detail/MatchCheckState.cs
Normal file
16
src/Ryujinx.Horizon/Sdk/Ngc/Detail/MatchCheckState.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
struct MatchCheckState
|
||||||
|
{
|
||||||
|
public uint CheckMask;
|
||||||
|
public readonly uint RegionMask;
|
||||||
|
public readonly ProfanityFilterOption Option;
|
||||||
|
|
||||||
|
public MatchCheckState(uint checkMask, uint regionMask, ProfanityFilterOption option)
|
||||||
|
{
|
||||||
|
CheckMask = checkMask;
|
||||||
|
RegionMask = regionMask;
|
||||||
|
Option = option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Ryujinx.Horizon/Sdk/Ngc/Detail/MatchDelimitedState.cs
Normal file
24
src/Ryujinx.Horizon/Sdk/Ngc/Detail/MatchDelimitedState.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
||||||
|
{
|
||||||
|
struct MatchDelimitedState
|
||||||
|
{
|
||||||
|
public bool Matched;
|
||||||
|
public readonly bool PrevCharIsWordSeparator;
|
||||||
|
public readonly bool NextCharIsWordSeparator;
|
||||||
|
public readonly Sbv NoSeparatorMap;
|
||||||
|
public readonly AhoCorasick DelimitedWordsTrie;
|
||||||
|
|
||||||
|
public MatchDelimitedState(
|
||||||
|
bool prevCharIsWordSeparator,
|
||||||
|
bool nextCharIsWordSeparator,
|
||||||
|
Sbv noSeparatorMap,
|
||||||
|
AhoCorasick delimitedWordsTrie)
|
||||||
|
{
|
||||||
|
Matched = false;
|
||||||
|
PrevCharIsWordSeparator = prevCharIsWordSeparator;
|
||||||
|
NextCharIsWordSeparator = nextCharIsWordSeparator;
|
||||||
|
NoSeparatorMap = noSeparatorMap;
|
||||||
|
DelimitedWordsTrie = delimitedWordsTrie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user