Compare commits

...

25 Commits

Author SHA1 Message Date
dependabot[bot]
d3709a753f nuget: bump XamlNameReferenceGenerator from 1.4.2 to 1.5.1 (#4026)
Bumps [XamlNameReferenceGenerator](https://github.com/avaloniaui/Avalonia.NameGenerator) from 1.4.2 to 1.5.1.
- [Release notes](https://github.com/avaloniaui/Avalonia.NameGenerator/releases)
- [Commits](https://github.com/avaloniaui/Avalonia.NameGenerator/compare/1.4.2...1.5.1)

---
updated-dependencies:
- dependency-name: XamlNameReferenceGenerator
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 19:00:08 +01:00
dependabot[bot]
ab676d58ea nuget: bump System.IdentityModel.Tokens.Jwt from 6.25.0 to 6.25.1 (#4043)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.0 to 6.25.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.25.0...6.25.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 18:01:21 +01:00
Ac_K
2372c194f1 ava: Cleanup Input classes (#4042)
* ava: Cleanup Input classes

This PR just cleanup all Input classes for consistencies.

* Addresses TSRBerry's feedback
2022-12-06 15:32:14 +00:00
Mary-nyan
40311310d1 amadeus: Add missing compressor effect from REV11 (#4010)
* amadeus: Add missing compressor effect from REV11

This was in my reversing notes but seems I completely forgot to
implement it

Also took the opportunity to simplify the Limiter effect a bit.

* Remove some outdated comment

* Address gdkchan's comments
2022-12-06 15:04:25 +01:00
gdkchan
dde9bb5c69 Fix storage buffer access when match fails (#4037)
* Fix storage buffer access when match fails

* Shader cache version bump
2022-12-06 03:36:54 +00:00
MetrosexualGarbodor
266338a7c9 Change default Vsync toggle hotkey to F1 instead of Tab (#3995) 2022-12-06 02:09:26 +00:00
dependabot[bot]
90156eea4c nuget: bump Microsoft.CodeAnalysis.CSharp from 4.2.0 to 4.4.0 (#4025)
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.2.0 to 4.4.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/compare/v4.2.0...Visual-Studio-2019-Version-16.0-Preview-4.4)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-06 01:51:33 +00:00
Isaac Marovitz
071c01c235 Fix Sorting Regression (#4032)
* Fix sorting regression + Remove unsued sort

* Fix GTK

* Attempt 2 to fix GTK

* Whoopsie

* Fix whitspace
2022-12-06 01:40:06 +00:00
gdkchan
de06ffb0f7 Fix shaders with global memory access from unknown locations (#4029)
* Fix shaders with global memory access from unknown locations

* Shader cache version bump
2022-12-06 01:09:24 +00:00
Andrew Glaze
8a7de35e3f Update 'OpenGL Log Level' to 'Graphics Backend Log Level' (#3996)
* Update 'OpenGL Log Level' to 'Graphics Backend Log Level'

* update other locals and change keys
2022-12-06 00:48:41 +00:00
Isaac Marovitz
121296834a Ava GUI: Several UI Fixes (#3991)
* Fix accessability violations in ListView

* Use accent colour for favourite star

* Hide progress bar when its done

* App Data Formating

- Added space before storage unit
- Changed so minutes have 0 decimals, and hours and days have 1

* Fix theming

* Fix mismatched corner radius

* Fix acceability violations in GridView

* More consistency between Grid and List View

* Fix margin

* Let whitespace defocus controls
2022-12-05 22:04:18 +00:00
gdkchan
bbb24d8c7e Restrict shader storage buffer search when match fails (#4011)
* Restrict storage buffer search when match fails

* Shader cache version bump
2022-12-05 19:11:32 +00:00
Andrey Sukharev
4da44e09cb Make structs readonly when applicable (#4002)
* Make all structs readonly when applicable. It should reduce amount of needless defensive copies

* Make structs with trivial boilerplate equality code record structs

* Remove unnecessary readonly modifiers from TextureCreateInfo

* Make BitMap structs readonly too
2022-12-05 14:47:39 +01:00
Mary-nyan
ae13f0ab4d misc: Fix obsolete warnings in Ryujinx.Graphics.Vulkan (#4020)
Was caused by some merges after the Silk.NET update
2022-12-05 12:57:11 +00:00
dependabot[bot]
a2a35f1be6 nuget: bump Microsoft.NET.Test.Sdk from 16.8.0 to 17.4.0 (#3900)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.8.0 to 17.4.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v16.8.0...v17.4.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-05 08:17:40 +01:00
Shane Slattery
aedfadaaf7 Add InfoType.MesosphereCurrentProcess (#3792)
* Add InfoType.MesosphereCurrentProcess

* Make outHandle inlined

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-04 19:46:02 +00:00
Ethan Page
5c0fb0cec3 ui: Disallow checking for updates while emulation active (#3886)
* Disallow updating while game is running

* Reflected change on Avalonia

* Git has gone wonky

* Fix accidental indent
2022-12-04 20:17:11 +01:00
gdkchan
17a1cab5d2 Allow SNorm buffer texture formats on Vulkan (#3957)
* Allow SNorm buffer texture formats on Vulkan

* Shader cache version bump
2022-12-04 15:36:03 -03:00
gdkchan
73aed239c3 Implement non-MS to MS copies with draws (#3958)
* Implement non-MS to MS copies with draws, simplify MS to non-MS copies and supports any host sample count

* Remove unused program
2022-12-04 15:07:11 -03:00
riperiperi
9ac66336a2 GPU: Use lazy checks for specialization state (#4004)
* GPU: Use lazy checks for specialization state

This PR adds a new class, the SpecializationStateUpdater, that allows elements of specialization state to be updated individually, and signal the state is checked when it changes between draws, instead of building and checking it on every draw. This also avoids building spec state when

Most state updates have been moved behind the shader state update, so that their specialization state updates make it in before shaders are fetched.

Downside: Fields in GpuChannelGraphicsState are no longer readonly. To counteract copies that might be caused this I pass it as `ref` when possible, though maybe `in` would be better? Not really sure about the quirks of `in` and the difference probably won't show on a benchmark.

The result is around 2 extra FPS on SMO in the usual spot. Not much right now, but it will remove costs when we're doing more expensive specialization checks, such as fragment output type specialization for macos. It may also help more on other games with more draws.

* Address Feedback

* Oops
2022-12-04 18:41:17 +01:00
riperiperi
4965681e06 GPU: Swap bindings array instead of copying (#4003)
* GPU: Swap bindings array instead of copying

Reduces work on UpdateShaderState. Now the cost is a few reference moves for arrays, rather than copying data.

Downside: bindings arrays are no longer readonly.

* Micro optimisation

* Add missing docs

* Address Feedback
2022-12-04 18:18:40 +01:00
Andrey Sukharev
3868a00206 Use source generated regular expressions (#4005) 2022-12-04 00:43:23 +00:00
mageven
933e5144a9 Query Available RAM on macOS (#4000) 2022-12-04 00:25:07 +00:00
Ac_K
73a42c85c4 Add crowdin badge and information in readme (#3990)
* Add crowdin bagde and informations in readme.

* Update README.md

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update README.md

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-02 23:57:38 +01:00
Ac_K
39ba11054b Update Crowdin configuration file 2022-12-02 15:22:21 +01:00
264 changed files with 2556 additions and 1884 deletions

View File

@@ -89,6 +89,7 @@ csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences # Modifier preferences
csharp_prefer_static_local_function = true:suggestion csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
csharp_style_prefer_readonly_struct = true
# Code-block preferences # Code-block preferences
csharp_prefer_braces = true:silent csharp_prefer_braces = true:silent

View File

@@ -1,6 +1,6 @@
namespace ARMeilleure.CodeGen.RegisterAllocators namespace ARMeilleure.CodeGen.RegisterAllocators
{ {
struct AllocationResult readonly struct AllocationResult
{ {
public int IntUsedRegisters { get; } public int IntUsedRegisters { get; }
public int VecUsedRegisters { get; } public int VecUsedRegisters { get; }

View File

@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{ {
private class ParallelCopy private class ParallelCopy
{ {
private struct Copy private readonly struct Copy
{ {
public Register Dest { get; } public Register Dest { get; }
public Register Source { get; } public Register Source { get; }

View File

@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{ {
class HybridAllocator : IRegisterAllocator class HybridAllocator : IRegisterAllocator
{ {
private struct BlockInfo private readonly struct BlockInfo
{ {
public bool HasCall { get; } public bool HasCall { get; }

View File

@@ -3,7 +3,7 @@ using System;
namespace ARMeilleure.CodeGen.RegisterAllocators namespace ARMeilleure.CodeGen.RegisterAllocators
{ {
struct RegisterMasks readonly struct RegisterMasks
{ {
public int IntAvailableRegisters { get; } public int IntAvailableRegisters { get; }
public int VecAvailableRegisters { get; } public int VecAvailableRegisters { get; }

View File

@@ -1,6 +1,6 @@
namespace ARMeilleure.CodeGen.X86 namespace ARMeilleure.CodeGen.X86
{ {
struct IntrinsicInfo readonly struct IntrinsicInfo
{ {
public X86Instruction Inst { get; } public X86Instruction Inst { get; }
public IntrinsicType Type { get; } public IntrinsicType Type { get; }

View File

@@ -2,7 +2,7 @@ using ARMeilleure.Instructions;
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
struct InstDescriptor readonly struct InstDescriptor
{ {
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und); public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);

View File

@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
private const int FastLookupSize = 0x1000; private const int FastLookupSize = 0x1000;
private struct InstInfo private readonly struct InstInfo
{ {
public int Mask { get; } public int Mask { get; }
public int Value { get; } public int Value { get; }

View File

@@ -6,7 +6,7 @@ namespace ARMeilleure.Diagnostics
{ {
static class Symbols static class Symbols
{ {
private struct RangedSymbol private readonly struct RangedSymbol
{ {
public readonly ulong Start; public readonly ulong Start;
public readonly ulong End; public readonly ulong End;

View File

@@ -3,7 +3,7 @@ using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.IntermediateRepresentation namespace ARMeilleure.IntermediateRepresentation
{ {
struct PhiOperation readonly struct PhiOperation
{ {
private readonly Operation _operation; private readonly Operation _operation;

View File

@@ -2,7 +2,7 @@ using System;
namespace ARMeilleure.IntermediateRepresentation namespace ARMeilleure.IntermediateRepresentation
{ {
struct Register : IEquatable<Register> readonly struct Register : IEquatable<Register>
{ {
public int Index { get; } public int Index { get; }

View File

@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
namespace ARMeilleure.Translation.Cache namespace ARMeilleure.Translation.Cache
{ {
struct CacheEntry : IComparable<CacheEntry> readonly struct CacheEntry : IComparable<CacheEntry>
{ {
public int Offset { get; } public int Offset { get; }
public int Size { get; } public int Size { get; }

View File

@@ -6,7 +6,7 @@ namespace ARMeilleure.Translation.Cache
{ {
class CacheMemoryAllocator class CacheMemoryAllocator
{ {
private struct MemoryBlock : IComparable<MemoryBlock> private readonly struct MemoryBlock : IComparable<MemoryBlock>
{ {
public int Offset { get; } public int Offset { get; }
public int Size { get; } public int Size { get; }

View File

@@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
struct CompilerContext readonly struct CompilerContext
{ {
public ControlFlowGraph Cfg { get; } public ControlFlowGraph Cfg { get; }

View File

@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
private const int RegsCount = 32; private const int RegsCount = 32;
private const int RegsMask = RegsCount - 1; private const int RegsMask = RegsCount - 1;
private struct RegisterMask : IEquatable<RegisterMask> private readonly struct RegisterMask : IEquatable<RegisterMask>
{ {
public long IntMask => Mask.GetElement(0); public long IntMask => Mask.GetElement(0);
public long VecMask => Mask.GetElement(1); public long VecMask => Mask.GetElement(1);

View File

@@ -293,7 +293,7 @@ namespace ARMeilleure.Translation
} }
} }
private struct Range private readonly struct Range
{ {
public ulong Start { get; } public ulong Start { get; }
public ulong End { get; } public ulong End { get; }

View File

@@ -21,6 +21,10 @@
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg" <img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
alt=""> alt="">
</a> </a>
<a href="https://crwd.in/ryujinx">
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
alt="">
</a>
<a href="https://discord.com/invite/VkQYXAZ"> <a href="https://discord.com/invite/VkQYXAZ">
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white" <img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
alt="Discord"> alt="Discord">
@@ -48,6 +52,8 @@ See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ry
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide). ](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
## Latest build ## Latest build
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.** These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**

View File

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace SoundIOSharp namespace SoundIOSharp
{ {
public struct SoundIOChannelLayout public readonly struct SoundIOChannelLayout
{ {
public static int BuiltInCount public static int BuiltInCount
{ {

View File

@@ -1,6 +1,6 @@
namespace SoundIOSharp namespace SoundIOSharp
{ {
public struct SoundIOSampleRateRange public readonly struct SoundIOSampleRateRange
{ {
internal SoundIOSampleRateRange(int min, int max) internal SoundIOSampleRateRange(int min, int max)
{ {

View File

@@ -48,6 +48,11 @@ namespace Ryujinx.Audio.Renderer.Common
/// <summary> /// <summary>
/// Effect to capture mixes (via auxiliary buffers). /// Effect to capture mixes (via auxiliary buffers).
/// </summary> /// </summary>
CaptureBuffer CaptureBuffer,
/// <summary>
/// Effect applying a compressor filter (DRC).
/// </summary>
Compressor,
} }
} }

View File

@@ -14,6 +14,7 @@ namespace Ryujinx.Audio.Renderer.Common
Reverb3d, Reverb3d,
PcmFloat, PcmFloat,
Limiter, Limiter,
CaptureBuffer CaptureBuffer,
Compressor
} }
} }

View File

@@ -31,6 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
LimiterVersion1, LimiterVersion1,
LimiterVersion2, LimiterVersion2,
GroupedBiquadFilter, GroupedBiquadFilter,
CaptureBuffer CaptureBuffer,
Compressor
} }
} }

View File

@@ -0,0 +1,173 @@
using System;
using System.Diagnostics;
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter.Effect;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{
public class CompressorCommand : ICommand
{
private const int FixedPointPrecision = 15;
public bool Enabled { get; set; }
public int NodeId { get; }
public CommandType CommandType => CommandType.Compressor;
public uint EstimatedProcessingTime { get; set; }
public CompressorParameter Parameter => _parameter;
public Memory<CompressorState> State { get; }
public ushort[] OutputBufferIndices { get; }
public ushort[] InputBufferIndices { get; }
public bool IsEffectEnabled { get; }
private CompressorParameter _parameter;
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
{
Enabled = true;
NodeId = nodeId;
_parameter = parameter;
State = state;
IsEffectEnabled = isEnabled;
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
for (int i = 0; i < _parameter.ChannelCount; i++)
{
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
}
}
public void Process(CommandList context)
{
ref CompressorState state = ref State.Span[0];
if (IsEffectEnabled)
{
if (_parameter.Status == Server.Effect.UsageState.Invalid)
{
state = new CompressorState(ref _parameter);
}
else if (_parameter.Status == Server.Effect.UsageState.New)
{
state.UpdateParameter(ref _parameter);
}
}
ProcessCompressor(context, ref state);
}
private unsafe void ProcessCompressor(CommandList context, ref CompressorState state)
{
Debug.Assert(_parameter.IsChannelCountValid());
if (IsEffectEnabled && _parameter.IsChannelCountValid())
{
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
float unknown4 = state.Unknown4;
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
float previousCompressionEmaAlpha = state.PreviousCompressionEmaAlpha;
for (int i = 0; i < _parameter.ChannelCount; i++)
{
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
}
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
{
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
{
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
}
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
float z = 0.0f;
bool unknown10OutOfRange = false;
if (newMean < 1.0e-10f)
{
z = 1.0f;
unknown10OutOfRange = state.Unknown10 < -100.0f;
}
if (y >= state.Unknown10 || unknown10OutOfRange)
{
float tmpGain;
if (y >= state.Unknown14)
{
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
}
else
{
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
}
z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
}
float unknown4New = z;
float compressionEmaAlpha;
if ((unknown4 - z) <= 0.08f)
{
compressionEmaAlpha = Parameter.ReleaseCoefficient;
if ((unknown4 - z) >= -0.08f)
{
if (MathF.Abs(compressionGainAverage.Read() - z) >= 0.001f)
{
unknown4New = unknown4;
}
compressionEmaAlpha = previousCompressionEmaAlpha;
}
}
else
{
compressionEmaAlpha = Parameter.AttackCoefficient;
}
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
{
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
}
unknown4 = unknown4New;
previousCompressionEmaAlpha = compressionEmaAlpha;
}
state.InputMovingAverage = inputMovingAverage;
state.Unknown4 = unknown4;
state.CompressionGainAverage = compressionGainAverage;
state.PreviousCompressionEmaAlpha = previousCompressionEmaAlpha;
}
else
{
for (int i = 0; i < Parameter.ChannelCount; i++)
{
if (InputBufferIndices[i] != OutputBufferIndices[i])
{
context.CopyBuffer(OutputBufferIndices[i], InputBufferIndices[i]);
}
}
}
}
}
}

View File

@@ -90,32 +90,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float inputCoefficient = Parameter.ReleaseCoefficient; float inputCoefficient = Parameter.ReleaseCoefficient;
if (sampleInputMax > state.DectectorAverage[channelIndex]) if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
{ {
inputCoefficient = Parameter.AttackCoefficient; inputCoefficient = Parameter.AttackCoefficient;
} }
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]); float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
float attenuation = 1.0f; float attenuation = 1.0f;
if (state.DectectorAverage[channelIndex] > Parameter.Threshold) if (detectorValue > Parameter.Threshold)
{ {
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex]; attenuation = Parameter.Threshold / detectorValue;
} }
float outputCoefficient = Parameter.ReleaseCoefficient; float outputCoefficient = Parameter.ReleaseCoefficient;
if (state.CompressionGain[channelIndex] > attenuation) if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
{ {
outputCoefficient = Parameter.AttackCoefficient; outputCoefficient = Parameter.AttackCoefficient;
} }
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]); float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
float outputSample = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain; float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;

View File

@@ -101,32 +101,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
float inputCoefficient = Parameter.ReleaseCoefficient; float inputCoefficient = Parameter.ReleaseCoefficient;
if (sampleInputMax > state.DectectorAverage[channelIndex]) if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
{ {
inputCoefficient = Parameter.AttackCoefficient; inputCoefficient = Parameter.AttackCoefficient;
} }
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]); float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
float attenuation = 1.0f; float attenuation = 1.0f;
if (state.DectectorAverage[channelIndex] > Parameter.Threshold) if (detectorValue > Parameter.Threshold)
{ {
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex]; attenuation = Parameter.Threshold / detectorValue;
} }
float outputCoefficient = Parameter.ReleaseCoefficient; float outputCoefficient = Parameter.ReleaseCoefficient;
if (state.CompressionGain[channelIndex] > attenuation) if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
{ {
outputCoefficient = Parameter.AttackCoefficient; outputCoefficient = Parameter.AttackCoefficient;
} }
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]); float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
float outputSample = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain; float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
@@ -144,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0]; ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax); statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], state.CompressionGain[channelIndex]); statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain);
} }
} }
} }

View File

@@ -0,0 +1,26 @@
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp.Effect
{
public struct ExponentialMovingAverage
{
private float _mean;
public ExponentialMovingAverage(float mean)
{
_mean = mean;
}
public float Read()
{
return _mean;
}
public float Update(float value, float alpha)
{
_mean += alpha * (value - _mean);
return _mean;
}
}
}

View File

@@ -16,6 +16,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
return (float)value / (1 << qBits); return (float)value / (1 << qBits);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ConvertFloat(float value, int qBits)
{
return value / (1 << qBits);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToFixed(float value, int qBits) public static int ToFixed(float value, int qBits)
{ {

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp namespace Ryujinx.Audio.Renderer.Dsp
@@ -46,6 +47,53 @@ namespace Ryujinx.Audio.Renderer.Dsp
return MathF.Pow(10, x); return MathF.Pow(10, x);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Log10(float x)
{
// NOTE: Nintendo uses an approximation of log10, we don't.
// As such, we support the same ranges as Nintendo to avoid unexpected behaviours.
return MathF.Pow(10, MathF.Max(x, 1.0e-10f));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float MeanSquare(ReadOnlySpan<float> inputs)
{
float res = 0.0f;
foreach (float input in inputs)
{
res += (input * input);
}
res /= inputs.Length;
return res;
}
/// <summary>
/// Map decibel to linear.
/// </summary>
/// <param name="db">The decibel value to convert</param>
/// <returns>Converted linear value/returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DecibelToLinear(float db)
{
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)
{ {

View File

@@ -0,0 +1,51 @@
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Parameter.Effect;
namespace Ryujinx.Audio.Renderer.Dsp.State
{
public class CompressorState
{
public ExponentialMovingAverage InputMovingAverage;
public float Unknown4;
public ExponentialMovingAverage CompressionGainAverage;
public float CompressorGainReduction;
public float Unknown10;
public float Unknown14;
public float PreviousCompressionEmaAlpha;
public float MakeupGain;
public float OutputGain;
public CompressorState(ref CompressorParameter parameter)
{
InputMovingAverage = new ExponentialMovingAverage(0.0f);
Unknown4 = 1.0f;
CompressionGainAverage = new ExponentialMovingAverage(1.0f);
UpdateParameter(ref parameter);
}
public void UpdateParameter(ref CompressorParameter parameter)
{
float threshold = parameter.Threshold;
float ratio = 1.0f / parameter.Ratio;
float attackCoefficient = parameter.AttackCoefficient;
float makeupGain;
if (parameter.MakeupGainEnabled)
{
makeupGain = (threshold * 0.5f * (ratio - 1.0f)) - 3.0f;
}
else
{
makeupGain = 0.0f;
}
PreviousCompressionEmaAlpha = attackCoefficient;
MakeupGain = makeupGain;
CompressorGainReduction = (1.0f - ratio) / Constants.ChannelCountMax;
Unknown10 = threshold - 1.5f;
Unknown14 = threshold + 1.5f;
OutputGain = FloatingPointHelper.DecibelToLinearExtended(parameter.OutputGain + makeupGain);
}
}
}

View File

@@ -1,3 +1,4 @@
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Parameter.Effect;
using System; using System;
@@ -5,20 +6,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
{ {
public class LimiterState public class LimiterState
{ {
public float[] DectectorAverage; public ExponentialMovingAverage[] DetectorAverage;
public float[] CompressionGain; public ExponentialMovingAverage[] CompressionGainAverage;
public float[] DelayedSampleBuffer; public float[] DelayedSampleBuffer;
public int[] DelayedSampleBufferPosition; public int[] DelayedSampleBufferPosition;
public LimiterState(ref LimiterParameter parameter, ulong workBuffer) public LimiterState(ref LimiterParameter parameter, ulong workBuffer)
{ {
DectectorAverage = new float[parameter.ChannelCount]; DetectorAverage = new ExponentialMovingAverage[parameter.ChannelCount];
CompressionGain = new float[parameter.ChannelCount]; CompressionGainAverage = new ExponentialMovingAverage[parameter.ChannelCount];
DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax]; DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax];
DelayedSampleBufferPosition = new int[parameter.ChannelCount]; DelayedSampleBufferPosition = new int[parameter.ChannelCount];
DectectorAverage.AsSpan().Fill(0.0f); DetectorAverage.AsSpan().Fill(new ExponentialMovingAverage(0.0f));
CompressionGain.AsSpan().Fill(1.0f); CompressionGainAverage.AsSpan().Fill(new ExponentialMovingAverage(1.0f));
DelayedSampleBufferPosition.AsSpan().Fill(0); DelayedSampleBufferPosition.AsSpan().Fill(0);
DelayedSampleBuffer.AsSpan().Fill(0.0f); DelayedSampleBuffer.AsSpan().Fill(0.0f);

View File

@@ -0,0 +1,115 @@
using Ryujinx.Audio.Renderer.Server.Effect;
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Parameter.Effect
{
/// <summary>
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Compressor"/>.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CompressorParameter
{
/// <summary>
/// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Input;
/// <summary>
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
/// </summary>
public Array6<byte> Output;
/// <summary>
/// The maximum number of channels supported.
/// </summary>
public ushort ChannelCountMax;
/// <summary>
/// The total channel count used.
/// </summary>
public ushort ChannelCount;
/// <summary>
/// The target sample rate.
/// </summary>
/// <remarks>This is in kHz.</remarks>
public int SampleRate;
/// <summary>
/// The threshold.
/// </summary>
public float Threshold;
/// <summary>
/// The compressor ratio.
/// </summary>
public float Ratio;
/// <summary>
/// The attack time.
/// <remarks>This is in microseconds.</remarks>
/// </summary>
public int AttackTime;
/// <summary>
/// The release time.
/// <remarks>This is in microseconds.</remarks>
/// </summary>
public int ReleaseTime;
/// <summary>
/// The input gain.
/// </summary>
public float InputGain;
/// <summary>
/// The attack coefficient.
/// </summary>
public float AttackCoefficient;
/// <summary>
/// The release coefficient.
/// </summary>
public float ReleaseCoefficient;
/// <summary>
/// The output gain.
/// </summary>
public float OutputGain;
/// <summary>
/// The current usage status of the effect on the client side.
/// </summary>
public UsageState Status;
/// <summary>
/// Indicate if the makeup gain should be used.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool MakeupGainEnabled;
/// <summary>
/// Reserved/padding.
/// </summary>
private Array2<byte> _reserved;
/// <summary>
/// Check if the <see cref="ChannelCount"/> is valid.
/// </summary>
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
public bool IsChannelCountValid()
{
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
}
/// <summary>
/// Check if the <see cref="ChannelCountMax"/> is valid.
/// </summary>
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
public bool IsChannelCountMaxValid()
{
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
}
}
}

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP. /// <see cref="Parameter.RendererInfoOutStatus"/> was added to supply the count of update done sent to the DSP.
/// A new version of the command estimator was added to address timing changes caused by the voice changes. /// A new version of the command estimator was added to address timing changes caused by the voice changes.
/// Additionally, the rendering limit percent was incremented to 80%. /// Additionally, the rendering limit percent was incremented to 80%.
/// ///
/// </summary> /// </summary>
/// <remarks>This was added in system update 6.0.0</remarks> /// <remarks>This was added in system update 6.0.0</remarks>
public const int Revision5 = 5 << 24; public const int Revision5 = 5 << 24;
@@ -93,6 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// REV11: /// REV11:
/// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer. /// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
/// A new effect was added: Compressor. This effect is effectively implemented with a DRC.
/// A new version of the command estimator was added to address timing changes caused by the legacy effects changes. /// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
/// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands. /// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands.
/// </summary> /// </summary>

View File

@@ -469,6 +469,18 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
{
if (parameter.IsChannelCountValid())
{
CompressorCommand command = new CompressorCommand(bufferOffset, parameter, state, isEnabled, nodeId);
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
AddCommand(command);
}
}
/// <summary> /// <summary>
/// Generate a new <see cref="VolumeCommand"/>. /// Generate a new <see cref="VolumeCommand"/>.
/// </summary> /// </summary>

View File

@@ -606,6 +606,17 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
{
Debug.Assert(effect.Type == EffectType.Compressor);
_commandBuffer.GenerateCompressorEffect(bufferOffset,
effect.Parameter,
effect.State,
effect.IsEnabled,
nodeId);
}
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect) private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
{ {
int nodeId = mix.NodeId; int nodeId = mix.NodeId;
@@ -650,6 +661,9 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.CaptureBuffer: case EffectType.CaptureBuffer:
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId); GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
break; break;
case EffectType.Compressor:
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
break;
default: default:
throw new NotImplementedException($"Unsupported effect type {effect.Type}"); throw new NotImplementedException($"Unsupported effect type {effect.Type}");
} }

View File

@@ -179,5 +179,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public uint Estimate(CompressorCommand command)
{
return 0;
}
} }
} }

View File

@@ -543,5 +543,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public uint Estimate(CompressorCommand command)
{
return 0;
}
} }
} }

View File

@@ -747,5 +747,10 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
return 0; return 0;
} }
public virtual uint Estimate(CompressorCommand command)
{
return 0;
}
} }
} }

View File

@@ -232,5 +232,79 @@ namespace Ryujinx.Audio.Renderer.Server
} }
} }
} }
public override uint Estimate(CompressorCommand command)
{
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
if (_sampleCount == 160)
{
if (command.Enabled)
{
switch (command.Parameter.ChannelCount)
{
case 1:
return 34431;
case 2:
return 44253;
case 4:
return 63827;
case 6:
return 83361;
default:
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
}
}
else
{
switch (command.Parameter.ChannelCount)
{
case 1:
return (uint)630.12f;
case 2:
return (uint)638.27f;
case 4:
return (uint)705.86f;
case 6:
return (uint)782.02f;
default:
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
}
}
}
if (command.Enabled)
{
switch (command.Parameter.ChannelCount)
{
case 1:
return 51095;
case 2:
return 65693;
case 4:
return 95383;
case 6:
return 124510;
default:
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
}
}
else
{
switch (command.Parameter.ChannelCount)
{
case 1:
return (uint)840.14f;
case 2:
return (uint)826.1f;
case 4:
return (uint)901.88f;
case 6:
return (uint)965.29f;
default:
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
}
}
}
} }
} }

View File

@@ -262,6 +262,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return PerformanceDetailType.Limiter; return PerformanceDetailType.Limiter;
case EffectType.CaptureBuffer: case EffectType.CaptureBuffer:
return PerformanceDetailType.CaptureBuffer; return PerformanceDetailType.CaptureBuffer;
case EffectType.Compressor:
return PerformanceDetailType.Compressor;
default: default:
throw new NotImplementedException($"{Type}"); throw new NotImplementedException($"{Type}");
} }

View File

@@ -0,0 +1,67 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Server.Effect
{
/// <summary>
/// Server state for a compressor effect.
/// </summary>
public class CompressorEffect : BaseEffect
{
/// <summary>
/// The compressor parameter.
/// </summary>
public CompressorParameter Parameter;
/// <summary>
/// The compressor state.
/// </summary>
public Memory<CompressorState> State { get; }
/// <summary>
/// Create a new <see cref="CompressorEffect"/>.
/// </summary>
public CompressorEffect()
{
State = new CompressorState[1];
}
public override EffectType TargetEffectType => EffectType.Compressor;
public override ulong GetWorkBuffer(int index)
{
return GetSingleBuffer();
}
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
{
// Nintendo doesn't do anything here but we still require updateErrorInfo to be initialised.
updateErrorInfo = new BehaviourParameter.ErrorInfo();
}
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
{
Debug.Assert(IsTypeValid(ref parameter));
UpdateParameterBase(ref parameter);
Parameter = MemoryMarshal.Cast<byte, CompressorParameter>(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
updateErrorInfo = new BehaviourParameter.ErrorInfo();
}
public override void UpdateForCommandGeneration()
{
UpdateUsageStateForCommandGeneration();
Parameter.Status = UsageState.Enabled;
}
}
}

View File

@@ -35,5 +35,6 @@ namespace Ryujinx.Audio.Renderer.Server
uint Estimate(LimiterCommandVersion2 command); uint Estimate(LimiterCommandVersion2 command);
uint Estimate(GroupedBiquadFilterCommand command); uint Estimate(GroupedBiquadFilterCommand command);
uint Estimate(CaptureBufferCommand command); uint Estimate(CaptureBufferCommand command);
uint Estimate(CompressorCommand command);
} }
} }

View File

@@ -240,6 +240,10 @@ namespace Ryujinx.Audio.Renderer.Server
case EffectType.CaptureBuffer: case EffectType.CaptureBuffer:
effect = new CaptureBufferEffect(); effect = new CaptureBufferEffect();
break; break;
case EffectType.Compressor:
effect = new CompressorEffect();
break;
default: default:
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!"); throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
} }

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Aktiviere Fs Zugriff-Logs", "SettingsTabLoggingEnableFsAccessLogs": "Aktiviere Fs Zugriff-Logs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Globaler Zugriff-Log-Modus:", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Globaler Zugriff-Log-Modus:",
"SettingsTabLoggingDeveloperOptions": "Entwickleroptionen (WARNUNG: Beeinträchtigt die Leistung)", "SettingsTabLoggingDeveloperOptions": "Entwickleroptionen (WARNUNG: Beeinträchtigt die Leistung)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL Logstufe:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Keine",
"SettingsTabLoggingOpenglLogLevelNone": "Keine", "SettingsTabLoggingGraphicsBackendLogLevelError": "Fehler",
"SettingsTabLoggingOpenglLogLevelError": "Fehler", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Verlangsamungen",
"SettingsTabLoggingOpenglLogLevelPerformance": "Verlangsamungen", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Alle",
"SettingsTabLoggingOpenglLogLevelAll": "Alle",
"SettingsTabLoggingEnableDebugLogs": "Aktiviere Debug-Log", "SettingsTabLoggingEnableDebugLogs": "Aktiviere Debug-Log",
"SettingsTabInput": "Eingabe", "SettingsTabInput": "Eingabe",
"SettingsTabInputEnableDockedMode": "Docked Modus", "SettingsTabInputEnableDockedMode": "Docked Modus",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Ενεργοποίηση Καταγραφής Πρόσβασης FS", "SettingsTabLoggingEnableFsAccessLogs": "Ενεργοποίηση Καταγραφής Πρόσβασης FS",
"SettingsTabLoggingFsGlobalAccessLogMode": "Λειτουργία Καταγραφής Καθολικής Πρόσβασης FS:", "SettingsTabLoggingFsGlobalAccessLogMode": "Λειτουργία Καταγραφής Καθολικής Πρόσβασης FS:",
"SettingsTabLoggingDeveloperOptions": "Επιλογές Προγραμματιστή (ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η απόδοση Θα μειωθεί)", "SettingsTabLoggingDeveloperOptions": "Επιλογές Προγραμματιστή (ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η απόδοση Θα μειωθεί)",
"SettingsTabLoggingOpenglLogLevel": "Επίπεδο Καταγραφής OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Κανένα",
"SettingsTabLoggingOpenglLogLevelNone": "Κανένα", "SettingsTabLoggingGraphicsBackendLogLevelError": "Σφάλμα",
"SettingsTabLoggingOpenglLogLevelError": "Σφάλμα", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Επιβραδύνσεις",
"SettingsTabLoggingOpenglLogLevelPerformance": "Επιβραδύνσεις", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Όλα",
"SettingsTabLoggingOpenglLogLevelAll": "Όλα",
"SettingsTabLoggingEnableDebugLogs": "Ενεργοποίηση Αρχείων Καταγραφής Εντοπισμού Σφαλμάτων", "SettingsTabLoggingEnableDebugLogs": "Ενεργοποίηση Αρχείων Καταγραφής Εντοπισμού Σφαλμάτων",
"SettingsTabInput": "Χειρισμός", "SettingsTabInput": "Χειρισμός",
"SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode", "SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode",

View File

@@ -157,11 +157,11 @@
"SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs", "SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:",
"SettingsTabLoggingDeveloperOptions": "Developer Options (WARNING: Will reduce performance)", "SettingsTabLoggingDeveloperOptions": "Developer Options (WARNING: Will reduce performance)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL Log Level:", "SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:",
"SettingsTabLoggingOpenglLogLevelNone": "None", "SettingsTabLoggingGraphicsBackendLogLevelNone": "None",
"SettingsTabLoggingOpenglLogLevelError": "Error", "SettingsTabLoggingGraphicsBackendLogLevelError": "Error",
"SettingsTabLoggingOpenglLogLevelPerformance": "Slowdowns", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Slowdowns",
"SettingsTabLoggingOpenglLogLevelAll": "All", "SettingsTabLoggingGraphicsBackendLogLevelAll": "All",
"SettingsTabLoggingEnableDebugLogs": "Enable Debug Logs", "SettingsTabLoggingEnableDebugLogs": "Enable Debug Logs",
"SettingsTabInput": "Input", "SettingsTabInput": "Input",
"SettingsTabInputEnableDockedMode": "Docked Mode", "SettingsTabInputEnableDockedMode": "Docked Mode",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Habilitar registros de Fs Access", "SettingsTabLoggingEnableFsAccessLogs": "Habilitar registros de Fs Access",
"SettingsTabLoggingFsGlobalAccessLogMode": "Modo de registros Fs Global Access:", "SettingsTabLoggingFsGlobalAccessLogMode": "Modo de registros Fs Global Access:",
"SettingsTabLoggingDeveloperOptions": "Opciones de desarrollador (ADVERTENCIA: empeorarán el rendimiento)", "SettingsTabLoggingDeveloperOptions": "Opciones de desarrollador (ADVERTENCIA: empeorarán el rendimiento)",
"SettingsTabLoggingOpenglLogLevel": "Nivel de registro de OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nada",
"SettingsTabLoggingOpenglLogLevelNone": "Nada", "SettingsTabLoggingGraphicsBackendLogLevelError": "Errores",
"SettingsTabLoggingOpenglLogLevelError": "Errores", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Ralentizaciones",
"SettingsTabLoggingOpenglLogLevelPerformance": "Ralentizaciones", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Todo",
"SettingsTabLoggingOpenglLogLevelAll": "Todo",
"SettingsTabLoggingEnableDebugLogs": "Habilitar registros de debug", "SettingsTabLoggingEnableDebugLogs": "Habilitar registros de debug",
"SettingsTabInput": "Entrada", "SettingsTabInput": "Entrada",
"SettingsTabInputEnableDockedMode": "Modo dock/TV", "SettingsTabInputEnableDockedMode": "Modo dock/TV",

View File

@@ -150,11 +150,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Activer les journaux des accès au système de fichiers", "SettingsTabLoggingEnableFsAccessLogs": "Activer les journaux des accès au système de fichiers",
"SettingsTabLoggingFsGlobalAccessLogMode": "Niveau des journaux des accès au système de fichiers:", "SettingsTabLoggingFsGlobalAccessLogMode": "Niveau des journaux des accès au système de fichiers:",
"SettingsTabLoggingDeveloperOptions": "Options développeur (ATTENTION: Cela peut réduire les performances)", "SettingsTabLoggingDeveloperOptions": "Options développeur (ATTENTION: Cela peut réduire les performances)",
"SettingsTabLoggingOpenglLogLevel": "Niveau des journaux OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Aucun",
"SettingsTabLoggingOpenglLogLevelNone": "Aucun", "SettingsTabLoggingGraphicsBackendLogLevelError": "Erreur",
"SettingsTabLoggingOpenglLogLevelError": "Erreur", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Ralentissements",
"SettingsTabLoggingOpenglLogLevelPerformance": "Ralentissements", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Tout",
"SettingsTabLoggingOpenglLogLevelAll": "Tout",
"SettingsTabLoggingEnableDebugLogs": "Activer les journaux de debug", "SettingsTabLoggingEnableDebugLogs": "Activer les journaux de debug",
"SettingsTabInput": "Contrôles", "SettingsTabInput": "Contrôles",
"SettingsTabInputEnableDockedMode": "Active le mode station d'accueil", "SettingsTabInputEnableDockedMode": "Active le mode station d'accueil",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Attiva Fs Access Logs", "SettingsTabLoggingEnableFsAccessLogs": "Attiva Fs Access Logs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Modalità log accesso globale Fs:", "SettingsTabLoggingFsGlobalAccessLogMode": "Modalità log accesso globale Fs:",
"SettingsTabLoggingDeveloperOptions": "Opzioni da sviluppatore (AVVISO: Ridurrà le prestazioni)", "SettingsTabLoggingDeveloperOptions": "Opzioni da sviluppatore (AVVISO: Ridurrà le prestazioni)",
"SettingsTabLoggingOpenglLogLevel": "Livello di log OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nessuno",
"SettingsTabLoggingOpenglLogLevelNone": "Nessuno", "SettingsTabLoggingGraphicsBackendLogLevelError": "Errore",
"SettingsTabLoggingOpenglLogLevelError": "Errore", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Rallentamenti",
"SettingsTabLoggingOpenglLogLevelPerformance": "Rallentamenti", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Tutto",
"SettingsTabLoggingOpenglLogLevelAll": "Tutto",
"SettingsTabLoggingEnableDebugLogs": "Attiva logs di debug", "SettingsTabLoggingEnableDebugLogs": "Attiva logs di debug",
"SettingsTabInput": "Input", "SettingsTabInput": "Input",
"SettingsTabInputEnableDockedMode": "Attiva modalità TV", "SettingsTabInputEnableDockedMode": "Attiva modalità TV",
@@ -558,8 +557,8 @@
"SettingsSelectThemeFileDialogTitle" : "Seleziona file del tema", "SettingsSelectThemeFileDialogTitle" : "Seleziona file del tema",
"SettingsXamlThemeFile" : "File del tema xaml", "SettingsXamlThemeFile" : "File del tema xaml",
"SettingsTabHotkeysResScaleUpHotkey": "Aumentare la risoluzione:", "SettingsTabHotkeysResScaleUpHotkey": "Aumentare la risoluzione:",
"SettingsTabHotkeysResScaleDownHotkey": "Diminuire la risoluzione:" "SettingsTabHotkeysResScaleDownHotkey": "Diminuire la risoluzione:",
"AvatarWindowTitle": "Gestisci account - Avatar" "AvatarWindowTitle": "Gestisci account - Avatar",
"Amiibo": "Amiibo", "Amiibo": "Amiibo",
"Unknown": "Sconosciuto", "Unknown": "Sconosciuto",
"Usage": "Utilizzo", "Usage": "Utilizzo",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Fs アクセスログを有効", "SettingsTabLoggingEnableFsAccessLogs": "Fs アクセスログを有効",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs グローバルアクセスログモード:", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs グローバルアクセスログモード:",
"SettingsTabLoggingDeveloperOptions": "開発者オプション (警告: パフォーマンスが低下します)", "SettingsTabLoggingDeveloperOptions": "開発者オプション (警告: パフォーマンスが低下します)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL ログレベル:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "なし",
"SettingsTabLoggingOpenglLogLevelNone": "なし", "SettingsTabLoggingGraphicsBackendLogLevelError": "エラー",
"SettingsTabLoggingOpenglLogLevelError": "エラー", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "パフォーマンス低下",
"SettingsTabLoggingOpenglLogLevelPerformance": "パフォーマンス低下", "SettingsTabLoggingGraphicsBackendLogLevelAll": "すべて",
"SettingsTabLoggingOpenglLogLevelAll": "すべて",
"SettingsTabLoggingEnableDebugLogs": "デバッグログを有効", "SettingsTabLoggingEnableDebugLogs": "デバッグログを有効",
"SettingsTabInput": "入力", "SettingsTabInput": "入力",
"SettingsTabInputEnableDockedMode": "ドッキングモード", "SettingsTabInputEnableDockedMode": "ドッキングモード",

View File

@@ -156,11 +156,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Fs 액세스 로그 켜기", "SettingsTabLoggingEnableFsAccessLogs": "Fs 액세스 로그 켜기",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs 전역 액세스 로그 모드 :", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs 전역 액세스 로그 모드 :",
"SettingsTabLoggingDeveloperOptions": "개발자 옵션 (경고 : 성능이 저하됩니다.)", "SettingsTabLoggingDeveloperOptions": "개발자 옵션 (경고 : 성능이 저하됩니다.)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL 로그 수준 :", "SettingsTabLoggingGraphicsBackendLogLevelNone": "없음",
"SettingsTabLoggingOpenglLogLevelNone": "없음", "SettingsTabLoggingGraphicsBackendLogLevelError": "오류",
"SettingsTabLoggingOpenglLogLevelError": "오류", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "감속",
"SettingsTabLoggingOpenglLogLevelPerformance": "감속", "SettingsTabLoggingGraphicsBackendLogLevelAll": "모두",
"SettingsTabLoggingOpenglLogLevelAll": "모두",
"SettingsTabLoggingEnableDebugLogs": "디버그 로그 사용", "SettingsTabLoggingEnableDebugLogs": "디버그 로그 사용",
"SettingsTabInput": "입력", "SettingsTabInput": "입력",
"SettingsTabInputEnableDockedMode": "도킹 모드 활성화", "SettingsTabInputEnableDockedMode": "도킹 모드 활성화",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Włącz Logi Dostępu do Systemu Plików", "SettingsTabLoggingEnableFsAccessLogs": "Włącz Logi Dostępu do Systemu Plików",
"SettingsTabLoggingFsGlobalAccessLogMode": "Tryb Globalnych Logów Systemu Plików:", "SettingsTabLoggingFsGlobalAccessLogMode": "Tryb Globalnych Logów Systemu Plików:",
"SettingsTabLoggingDeveloperOptions": "Opcje programistyczne (OSTRZEŻENIE: Zmniejszą wydajność)", "SettingsTabLoggingDeveloperOptions": "Opcje programistyczne (OSTRZEŻENIE: Zmniejszą wydajność)",
"SettingsTabLoggingOpenglLogLevel": "Poziom Logów OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Żadne",
"SettingsTabLoggingOpenglLogLevelNone": "Żadne", "SettingsTabLoggingGraphicsBackendLogLevelError": "Błędy",
"SettingsTabLoggingOpenglLogLevelError": "Błędy", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Spowolnienia",
"SettingsTabLoggingOpenglLogLevelPerformance": "Spowolnienia", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Wszystkie",
"SettingsTabLoggingOpenglLogLevelAll": "Wszystkie",
"SettingsTabLoggingEnableDebugLogs": "Włącz Logi Debugowania", "SettingsTabLoggingEnableDebugLogs": "Włącz Logi Debugowania",
"SettingsTabInput": "Sterowanie", "SettingsTabInput": "Sterowanie",
"SettingsTabInputEnableDockedMode": "Tryb Zadokowany", "SettingsTabInputEnableDockedMode": "Tryb Zadokowany",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Habilitar logs de acesso ao sistema de arquivos", "SettingsTabLoggingEnableFsAccessLogs": "Habilitar logs de acesso ao sistema de arquivos",
"SettingsTabLoggingFsGlobalAccessLogMode": "Modo global de logs do sistema de arquivos:", "SettingsTabLoggingFsGlobalAccessLogMode": "Modo global de logs do sistema de arquivos:",
"SettingsTabLoggingDeveloperOptions": "Opções do desenvolvedor (AVISO: Vai reduzir a performance)", "SettingsTabLoggingDeveloperOptions": "Opções do desenvolvedor (AVISO: Vai reduzir a performance)",
"SettingsTabLoggingOpenglLogLevel": "Nível de log do OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nenhum",
"SettingsTabLoggingOpenglLogLevelNone": "Nenhum", "SettingsTabLoggingGraphicsBackendLogLevelError": "Erro",
"SettingsTabLoggingOpenglLogLevelError": "Erro", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Lentidão",
"SettingsTabLoggingOpenglLogLevelPerformance": "Lentidão", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Todos",
"SettingsTabLoggingOpenglLogLevelAll": "Todos",
"SettingsTabLoggingEnableDebugLogs": "Habilitar logs de depuração", "SettingsTabLoggingEnableDebugLogs": "Habilitar logs de depuração",
"SettingsTabInput": "Controle", "SettingsTabInput": "Controle",
"SettingsTabInputEnableDockedMode": "Habilitar modo TV", "SettingsTabInputEnableDockedMode": "Habilitar modo TV",

View File

@@ -156,11 +156,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Включить журналы доступа Fs", "SettingsTabLoggingEnableFsAccessLogs": "Включить журналы доступа Fs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Режим журнала глобального доступа Fs:", "SettingsTabLoggingFsGlobalAccessLogMode": "Режим журнала глобального доступа Fs:",
"SettingsTabLoggingDeveloperOptions": "Параметры разработчика (ВНИМАНИЕ: снизит производительность)", "SettingsTabLoggingDeveloperOptions": "Параметры разработчика (ВНИМАНИЕ: снизит производительность)",
"SettingsTabLoggingOpenglLogLevel": "Уровень журнала OpenGL:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Ничего",
"SettingsTabLoggingOpenglLogLevelNone": "Ничего", "SettingsTabLoggingGraphicsBackendLogLevelError": "Ошибка",
"SettingsTabLoggingOpenglLogLevelError": "Ошибка", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Замедления",
"SettingsTabLoggingOpenglLogLevelPerformance": "Замедления", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Всё",
"SettingsTabLoggingOpenglLogLevelAll": "Всё",
"SettingsTabLoggingEnableDebugLogs": "Включить журналы отладки", "SettingsTabLoggingEnableDebugLogs": "Включить журналы отладки",
"SettingsTabInput": "Управление", "SettingsTabInput": "Управление",
"SettingsTabInputEnableDockedMode": "Включить режим закрепления", "SettingsTabInputEnableDockedMode": "Включить режим закрепления",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "Fs Erişim Loglarını Etkinleştir", "SettingsTabLoggingEnableFsAccessLogs": "Fs Erişim Loglarını Etkinleştir",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Evrensel Erişim Log Modu:", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Evrensel Erişim Log Modu:",
"SettingsTabLoggingDeveloperOptions": "Geliştirici Seçenekleri (UYARI: Performansı düşürecektir)", "SettingsTabLoggingDeveloperOptions": "Geliştirici Seçenekleri (UYARI: Performansı düşürecektir)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL Log Seviyesi:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "Hiç",
"SettingsTabLoggingOpenglLogLevelNone": "H", "SettingsTabLoggingGraphicsBackendLogLevelError": "Hata",
"SettingsTabLoggingOpenglLogLevelError": "Hata", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Yavaşlamalar",
"SettingsTabLoggingOpenglLogLevelPerformance": "Yavaşlamalar", "SettingsTabLoggingGraphicsBackendLogLevelAll": "Her Şey",
"SettingsTabLoggingOpenglLogLevelAll": "Her Şey",
"SettingsTabLoggingEnableDebugLogs": "Hata Ayıklama Loglarını Etkinleştir", "SettingsTabLoggingEnableDebugLogs": "Hata Ayıklama Loglarını Etkinleştir",
"SettingsTabInput": "Giriş Yöntemi", "SettingsTabInput": "Giriş Yöntemi",
"SettingsTabInputEnableDockedMode": "Docked Modunu Etkinleştir", "SettingsTabInputEnableDockedMode": "Docked Modunu Etkinleştir",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "记录文件访问", "SettingsTabLoggingEnableFsAccessLogs": "记录文件访问",
"SettingsTabLoggingFsGlobalAccessLogMode": "记录全局文件访问模式:", "SettingsTabLoggingFsGlobalAccessLogMode": "记录全局文件访问模式:",
"SettingsTabLoggingDeveloperOptions": "开发者选项 (警告: 会降低性能)", "SettingsTabLoggingDeveloperOptions": "开发者选项 (警告: 会降低性能)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL日志级别:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "",
"SettingsTabLoggingOpenglLogLevelNone": "", "SettingsTabLoggingGraphicsBackendLogLevelError": "错误",
"SettingsTabLoggingOpenglLogLevelError": "错误", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "减速",
"SettingsTabLoggingOpenglLogLevelPerformance": "减速", "SettingsTabLoggingGraphicsBackendLogLevelAll": "全部",
"SettingsTabLoggingOpenglLogLevelAll": "全部",
"SettingsTabLoggingEnableDebugLogs": "启用调试日志", "SettingsTabLoggingEnableDebugLogs": "启用调试日志",
"SettingsTabInput": "输入", "SettingsTabInput": "输入",
"SettingsTabInputEnableDockedMode": "主机模式", "SettingsTabInputEnableDockedMode": "主机模式",

View File

@@ -157,11 +157,10 @@
"SettingsTabLoggingEnableFsAccessLogs": "記錄檔案存取", "SettingsTabLoggingEnableFsAccessLogs": "記錄檔案存取",
"SettingsTabLoggingFsGlobalAccessLogMode": "記錄全域檔案存取模式:", "SettingsTabLoggingFsGlobalAccessLogMode": "記錄全域檔案存取模式:",
"SettingsTabLoggingDeveloperOptions": "開發者選項 (警告: 會降低效能)", "SettingsTabLoggingDeveloperOptions": "開發者選項 (警告: 會降低效能)",
"SettingsTabLoggingOpenglLogLevel": "OpenGL 日誌級別:", "SettingsTabLoggingGraphicsBackendLogLevelNone": "",
"SettingsTabLoggingOpenglLogLevelNone": "", "SettingsTabLoggingGraphicsBackendLogLevelError": "錯誤",
"SettingsTabLoggingOpenglLogLevelError": "錯誤", "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "減速",
"SettingsTabLoggingOpenglLogLevelPerformance": "減速", "SettingsTabLoggingGraphicsBackendLogLevelAll": "全部",
"SettingsTabLoggingOpenglLogLevelAll": "全部",
"SettingsTabLoggingEnableDebugLogs": "啟用除錯日誌", "SettingsTabLoggingEnableDebugLogs": "啟用除錯日誌",
"SettingsTabInput": "輸入", "SettingsTabInput": "輸入",
"SettingsTabInputEnableDockedMode": "Docked 模式", "SettingsTabInputEnableDockedMode": "Docked 模式",

View File

@@ -58,5 +58,7 @@
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color> <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
</Styles.Resources> </Styles.Resources>
</Styles> </Styles>

View File

@@ -50,5 +50,7 @@
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color> <Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color> <Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
</Styles.Resources> </Styles.Resources>
</Styles> </Styles>

View File

@@ -4,7 +4,6 @@ using Ryujinx.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using ConfigKey = Ryujinx.Common.Configuration.Hid.Key; using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
using Key = Ryujinx.Input.Key; using Key = Ryujinx.Input.Key;
@@ -13,30 +12,37 @@ namespace Ryujinx.Ava.Input
internal class AvaloniaKeyboard : IKeyboard internal class AvaloniaKeyboard : IKeyboard
{ {
private readonly List<ButtonMappingEntry> _buttonsUserMapping; private readonly List<ButtonMappingEntry> _buttonsUserMapping;
private readonly AvaloniaKeyboardDriver _driver; private readonly AvaloniaKeyboardDriver _driver;
private StandardKeyboardInputConfig _configuration;
private readonly object _userMappingLock = new(); private readonly object _userMappingLock = new();
private StandardKeyboardInputConfig _configuration; public string Id { get; }
private bool HasConfiguration => _configuration != null;
public string Id { get; }
public string Name { get; } public string Name { get; }
public bool IsConnected => true; public bool IsConnected => true;
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; private class ButtonMappingEntry
{
public readonly Key From;
public readonly GamepadButtonInputId To;
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
{
To = to;
From = from;
}
}
public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name) public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
{ {
_driver = driver;
Id = id;
Name = name;
_buttonsUserMapping = new List<ButtonMappingEntry>(); _buttonsUserMapping = new List<ButtonMappingEntry>();
}
public void Dispose() { } _driver = driver;
Id = id;
Name = name;
}
public KeyboardStateSnapshot GetKeyboardStateSnapshot() public KeyboardStateSnapshot GetKeyboardStateSnapshot()
{ {
@@ -46,11 +52,11 @@ namespace Ryujinx.Ava.Input
public GamepadStateSnapshot GetMappedStateSnapshot() public GamepadStateSnapshot GetMappedStateSnapshot()
{ {
KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot(); KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot();
GamepadStateSnapshot result = default; GamepadStateSnapshot result = default;
lock (_userMappingLock) lock (_userMappingLock)
{ {
if (!HasConfiguration) if (_configuration == null)
{ {
return result; return result;
} }
@@ -62,17 +68,17 @@ namespace Ryujinx.Ava.Input
continue; continue;
} }
// Do not touch state of the button already pressed // NOTE: Do not touch state of the button already pressed.
if (!result.IsPressed(entry.To)) if (!result.IsPressed(entry.To))
{ {
result.SetPressed(entry.To, rawState.IsPressed(entry.From)); result.SetPressed(entry.To, rawState.IsPressed(entry.From));
} }
} }
(short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick);
(short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick); (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick);
result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY));
result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY)); result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY));
} }
@@ -114,29 +120,29 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Clear(); _buttonsUserMapping.Clear();
// Left joycon // Left JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
// Finally right joycon // Right JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
} }
} }
@@ -190,16 +196,6 @@ namespace Ryujinx.Ava.Input
_driver?.ResetKeys(); _driver?.ResetKeys();
} }
private class ButtonMappingEntry public void Dispose() { }
{
public readonly Key From;
public readonly GamepadButtonInputId To;
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
{
To = to;
From = from;
}
}
} }
} }

View File

@@ -4,7 +4,6 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input; using Ryujinx.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using AvaKey = Avalonia.Input.Key; using AvaKey = Avalonia.Input.Key;
using Key = Ryujinx.Input.Key; using Key = Ryujinx.Input.Key;
@@ -13,24 +12,23 @@ namespace Ryujinx.Ava.Input
internal class AvaloniaKeyboardDriver : IGamepadDriver internal class AvaloniaKeyboardDriver : IGamepadDriver
{ {
private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
private readonly Control _control; private readonly Control _control;
private readonly HashSet<AvaKey> _pressedKeys; private readonly HashSet<AvaKey> _pressedKeys;
public event EventHandler<KeyEventArgs> KeyPressed; public event EventHandler<KeyEventArgs> KeyPressed;
public event EventHandler<KeyEventArgs> KeyRelease; public event EventHandler<KeyEventArgs> KeyRelease;
public event EventHandler<string> TextInput; public event EventHandler<string> TextInput;
public string DriverName => "Avalonia";
public string DriverName => "AvaloniaKeyboardDriver";
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers; public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
public AvaloniaKeyboardDriver(Control control) public AvaloniaKeyboardDriver(Control control)
{ {
_control = control; _control = control;
_pressedKeys = new HashSet<AvaKey>(); _pressedKeys = new HashSet<AvaKey>();
_control.KeyDown += OnKeyPress; _control.KeyDown += OnKeyPress;
_control.KeyUp += OnKeyRelease; _control.KeyUp += OnKeyRelease;
_control.TextInput += Control_TextInput; _control.TextInput += Control_TextInput;
} }
@@ -41,21 +39,16 @@ namespace Ryujinx.Ava.Input
public event Action<string> OnGamepadConnected public event Action<string> OnGamepadConnected
{ {
add { } add { }
remove { } remove { }
} }
public event Action<string> OnGamepadDisconnected public event Action<string> OnGamepadDisconnected
{ {
add { } add { }
remove { } remove { }
} }
public void Dispose()
{
Dispose(true);
}
public IGamepad GetGamepad(string id) public IGamepad GetGamepad(string id)
{ {
if (!_keyboardIdentifers[0].Equals(id)) if (!_keyboardIdentifers[0].Equals(id))
@@ -70,15 +63,13 @@ namespace Ryujinx.Ava.Input
{ {
if (disposing) if (disposing)
{ {
_control.KeyUp -= OnKeyPress; _control.KeyUp -= OnKeyPress;
_control.KeyDown -= OnKeyRelease; _control.KeyDown -= OnKeyRelease;
} }
} }
protected void OnKeyPress(object sender, KeyEventArgs args) protected void OnKeyPress(object sender, KeyEventArgs args)
{ {
AvaKey key = args.Key;
_pressedKeys.Add(args.Key); _pressedKeys.Add(args.Key);
KeyPressed?.Invoke(this, args); KeyPressed?.Invoke(this, args);
@@ -98,7 +89,7 @@ namespace Ryujinx.Ava.Input
return false; return false;
} }
AvaloniaMappingHelper.TryGetAvaKey(key, out var nativeKey); AvaloniaKeyboardMappingHelper.TryGetAvaKey(key, out var nativeKey);
return _pressedKeys.Contains(nativeKey); return _pressedKeys.Contains(nativeKey);
} }
@@ -107,5 +98,10 @@ namespace Ryujinx.Ava.Input
{ {
_pressedKeys.Clear(); _pressedKeys.Clear();
} }
public void Dispose()
{
Dispose(true);
}
} }
} }

View File

@@ -5,7 +5,7 @@ using AvaKey = Avalonia.Input.Key;
namespace Ryujinx.Ava.Input namespace Ryujinx.Ava.Input
{ {
internal static class AvaloniaMappingHelper internal static class AvaloniaKeyboardMappingHelper
{ {
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count] private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count]
{ {
@@ -149,11 +149,11 @@ namespace Ryujinx.Ava.Input
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping; private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;
static AvaloniaMappingHelper() static AvaloniaKeyboardMappingHelper()
{ {
var inputKeys = Enum.GetValues(typeof(Key)); var inputKeys = Enum.GetValues(typeof(Key));
// Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array. // NOTE: Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array.
_avaKeyMapping = new Dictionary<AvaKey, Key>(); _avaKeyMapping = new Dictionary<AvaKey, Key>();
foreach (var key in inputKeys) foreach (var key in inputKeys)
@@ -167,15 +167,13 @@ namespace Ryujinx.Ava.Input
public static bool TryGetAvaKey(Key key, out AvaKey avaKey) public static bool TryGetAvaKey(Key key, out AvaKey avaKey)
{ {
var keyExist = (int)key < _keyMapping.Length; avaKey = AvaKey.None;
bool keyExist = (int)key < _keyMapping.Length;
if (keyExist) if (keyExist)
{ {
avaKey = _keyMapping[(int)key]; avaKey = _keyMapping[(int)key];
} }
else
{
avaKey = AvaKey.None;
}
return keyExist; return keyExist;
} }

View File

@@ -10,15 +10,12 @@ namespace Ryujinx.Ava.Input
{ {
private AvaloniaMouseDriver _driver; private AvaloniaMouseDriver _driver;
public GamepadFeaturesFlag Features => throw new NotImplementedException(); public string Id => "0";
public string Id => "0";
public string Name => "AvaloniaMouse"; public string Name => "AvaloniaMouse";
public bool IsConnected => true; public bool IsConnected => true;
public GamepadFeaturesFlag Features => throw new NotImplementedException();
public bool[] Buttons => _driver.PressedButtons; public bool[] Buttons => _driver.PressedButtons;
public AvaloniaMouse(AvaloniaMouseDriver driver) public AvaloniaMouse(AvaloniaMouseDriver driver)
{ {

View File

@@ -11,35 +11,50 @@ namespace Ryujinx.Ava.Input
{ {
internal class AvaloniaMouseDriver : IGamepadDriver internal class AvaloniaMouseDriver : IGamepadDriver
{ {
private Control _widget; private Control _widget;
private bool _isDisposed; private bool _isDisposed;
private Size _size; private Size _size;
private readonly Window _window; private readonly Window _window;
public bool[] PressedButtons { get; } public bool[] PressedButtons { get; }
public Vector2 CurrentPosition { get; private set; } public Vector2 CurrentPosition { get; private set; }
public Vector2 Scroll { get; private set; } public Vector2 Scroll { get; private set; }
public string DriverName => "AvaloniaMouseDriver";
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public AvaloniaMouseDriver(Window window, Control parent) public AvaloniaMouseDriver(Window window, Control parent)
{ {
_widget = parent; _widget = parent;
_window = window; _window = window;
_widget.PointerMoved += Parent_PointerMovedEvent; _widget.PointerMoved += Parent_PointerMovedEvent;
_widget.PointerPressed += Parent_PointerPressEvent; _widget.PointerPressed += Parent_PointerPressEvent;
_widget.PointerReleased += Parent_PointerReleaseEvent; _widget.PointerReleased += Parent_PointerReleaseEvent;
_widget.PointerWheelChanged += Parent_ScrollEvent; _widget.PointerWheelChanged += Parent_ScrollEvent;
_window.PointerMoved += Parent_PointerMovedEvent; _window.PointerMoved += Parent_PointerMovedEvent;
_window.PointerPressed += Parent_PointerPressEvent; _window.PointerPressed += Parent_PointerPressEvent;
_window.PointerReleased += Parent_PointerReleaseEvent; _window.PointerReleased += Parent_PointerReleaseEvent;
_window.PointerWheelChanged += Parent_ScrollEvent; _window.PointerWheelChanged += Parent_ScrollEvent;
PressedButtons = new bool[(int)MouseButton.Count]; PressedButtons = new bool[(int)MouseButton.Count];
_size = new Size((int)parent.Bounds.Width, (int)parent.Bounds.Height); _size = new Size((int)parent.Bounds.Width, (int)parent.Bounds.Height);
parent.GetObservable(Control.BoundsProperty).Subscribe(Resized);
parent.GetObservable(Visual.BoundsProperty).Subscribe(Resized);
}
public event Action<string> OnGamepadConnected
{
add { }
remove { }
}
public event Action<string> OnGamepadDisconnected
{
add { }
remove { }
} }
private void Resized(Rect rect) private void Resized(Rect rect)
@@ -59,14 +74,12 @@ namespace Ryujinx.Ava.Input
private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args) private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args)
{ {
var pointerProperties = args.GetCurrentPoint(_widget).Properties; PressedButtons[(int)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind] = true;
PressedButtons[(int)pointerProperties.PointerUpdateKind] = true;
} }
private void Parent_PointerMovedEvent(object o, PointerEventArgs args) private void Parent_PointerMovedEvent(object o, PointerEventArgs args)
{ {
var position = args.GetPosition(_widget); Point position = args.GetPosition(_widget);
CurrentPosition = new Vector2((float)position.X, (float)position.Y); CurrentPosition = new Vector2((float)position.X, (float)position.Y);
} }
@@ -96,22 +109,6 @@ namespace Ryujinx.Ava.Input
return _size; return _size;
} }
public string DriverName => "Avalonia";
public event Action<string> OnGamepadConnected
{
add { }
remove { }
}
public event Action<string> OnGamepadDisconnected
{
add { }
remove { }
}
public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public IGamepad GetGamepad(string id) public IGamepad GetGamepad(string id)
{ {
return new AvaloniaMouse(this); return new AvaloniaMouse(this);
@@ -126,14 +123,14 @@ namespace Ryujinx.Ava.Input
_isDisposed = true; _isDisposed = true;
_widget.PointerMoved -= Parent_PointerMovedEvent; _widget.PointerMoved -= Parent_PointerMovedEvent;
_widget.PointerPressed -= Parent_PointerPressEvent; _widget.PointerPressed -= Parent_PointerPressEvent;
_widget.PointerReleased -= Parent_PointerReleaseEvent; _widget.PointerReleased -= Parent_PointerReleaseEvent;
_widget.PointerWheelChanged -= Parent_ScrollEvent; _widget.PointerWheelChanged -= Parent_ScrollEvent;
_window.PointerMoved -= Parent_PointerMovedEvent; _window.PointerMoved -= Parent_PointerMovedEvent;
_window.PointerPressed -= Parent_PointerPressEvent; _window.PointerPressed -= Parent_PointerPressEvent;
_window.PointerReleased -= Parent_PointerReleaseEvent; _window.PointerReleased -= Parent_PointerReleaseEvent;
_window.PointerWheelChanged -= Parent_ScrollEvent; _window.PointerWheelChanged -= Parent_ScrollEvent;
_widget = null; _widget = null;

View File

@@ -28,7 +28,7 @@
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" /> <PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageReference Include="DynamicData" Version="7.12.8" /> <PackageReference Include="DynamicData" Version="7.12.8" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageReference Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.4.2" /> <PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageReference Include="OpenTK.Core" Version="4.7.5" /> <PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />

View File

@@ -68,7 +68,7 @@ namespace Ryujinx.Ava.Ui.Applet
private void AvaloniaDynamicTextInputHandler_KeyRelease(object sender, Avalonia.Input.KeyEventArgs e) private void AvaloniaDynamicTextInputHandler_KeyRelease(object sender, Avalonia.Input.KeyEventArgs e)
{ {
var key = (HidKey)AvaloniaMappingHelper.ToInputKey(e.Key); var key = (HidKey)AvaloniaKeyboardMappingHelper.ToInputKey(e.Key);
if (!(KeyReleasedEvent?.Invoke(key)).GetValueOrDefault(true)) if (!(KeyReleasedEvent?.Invoke(key)).GetValueOrDefault(true))
{ {
@@ -88,7 +88,7 @@ namespace Ryujinx.Ava.Ui.Applet
private void AvaloniaDynamicTextInputHandler_KeyPressed(object sender, KeyEventArgs e) private void AvaloniaDynamicTextInputHandler_KeyPressed(object sender, KeyEventArgs e)
{ {
var key = (HidKey)AvaloniaMappingHelper.ToInputKey(e.Key); var key = (HidKey)AvaloniaKeyboardMappingHelper.ToInputKey(e.Key);
if (!(KeyPressedEvent?.Invoke(key)).GetValueOrDefault(true)) if (!(KeyPressedEvent?.Invoke(key)).GetValueOrDefault(true))
{ {

View File

@@ -11,7 +11,8 @@
Height="340" Height="340"
CanResize="False" CanResize="False"
SizeToContent="Height" SizeToContent="Height"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid <Grid
Margin="20" Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -6,7 +6,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Width="400" Width="400"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid <Grid
Margin="20" Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -10,7 +10,8 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened"> <MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
@@ -113,8 +114,8 @@
<Style Selector="ListBoxItem"> <Style Selector="ListBoxItem">
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="5" /> <Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="5" /> <Setter Property="CornerRadius" Value="4" />
<Setter Property="Background" Value="{DynamicResource SystemAccentColorDark3}" /> <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:0.7"> <Animation Duration="0:0:0.7">
<KeyFrame Cue="0%"> <KeyFrame Cue="0%">
@@ -132,27 +133,18 @@
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style>
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid> <Grid>
<Grid.Styles>
<Style Selector="ui|SymbolIcon.small.icon">
<Setter Property="FontSize" Value="15" />
</Style>
<Style Selector="ui|SymbolIcon.normal.icon">
<Setter Property="FontSize" Value="19" />
</Style>
<Style Selector="ui|SymbolIcon.large.icon">
<Setter Property="FontSize" Value="23" />
</Style>
<Style Selector="ui|SymbolIcon.huge.icon">
<Setter Property="FontSize" Value="26" />
</Style>
</Grid.Styles>
<Border <Border
Margin="0" Margin="10"
Padding="{Binding $parent[UserControl].DataContext.GridItemPadding}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
@@ -160,57 +152,41 @@
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}" Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}" Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="5"> CornerRadius="4">
<Grid Margin="0"> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image <Image
Grid.Row="0" Grid.Row="0"
Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" /> Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<StackPanel <Panel
Grid.Row="1" Grid.Row="1"
Height="50" Height="50"
Margin="5" Margin="0 10 0 0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}"> IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding TitleName}" Text="{Binding TitleName}"
TextAlignment="Center" TextAlignment="Center"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
</StackPanel> </Panel>
</Grid> </Grid>
</Border> </Border>
<ui:SymbolIcon <ui:SymbolIcon
Margin="5" Margin="5,5,0,0"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Top" VerticalAlignment="Top"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}" FontSize="16"
Classes.icon="true" Foreground="{DynamicResource SystemAccentColor}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Foreground="Yellow"
IsVisible="{Binding Favorite}" IsVisible="{Binding Favorite}"
Symbol="StarFilled" /> Symbol="StarFilled" />
<ui:SymbolIcon
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.icon="true"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Foreground="Black"
IsVisible="{Binding Favorite}"
Symbol="Star" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>

View File

@@ -10,7 +10,8 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened"> <MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
@@ -115,8 +116,8 @@
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" /> <Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="5" /> <Setter Property="CornerRadius" Value="5" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColorDark3}" /> <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
<Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderThickness" Value="2"/>
<Style.Animations> <Style.Animations>
<Animation Duration="0:0:0.7"> <Animation Duration="0:0:0.7">
<KeyFrame Cue="0%"> <KeyFrame Cue="0%">
@@ -134,6 +135,12 @@
</Animation> </Animation>
</Style.Animations> </Style.Animations>
</Style> </Style>
<Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style>
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
@@ -152,9 +159,6 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Image <Image
Grid.RowSpan="3" Grid.RowSpan="3"
Grid.Column="0" Grid.Column="0"
@@ -169,7 +173,7 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Top" VerticalAlignment="Top"
Orientation="Vertical" Orientation="Vertical"
Spacing="5"> Spacing="5" >
<TextBlock <TextBlock
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Text="{Binding TitleName}" Text="{Binding TitleName}"
@@ -214,20 +218,10 @@
Margin="-5,-5,0,0" Margin="-5,-5,0,0"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Top" VerticalAlignment="Top"
FontSize="20" FontSize="16"
Foreground="Yellow" Foreground="{DynamicResource SystemAccentColor}"
IsVisible="{Binding Favorite}" IsVisible="{Binding Favorite}"
Symbol="StarFilled" /> Symbol="StarFilled" />
<ui:SymbolIcon
Grid.Row="0"
Grid.Column="0"
Margin="-5,-5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="20"
Foreground="Black"
IsVisible="{Binding Favorite}"
Symbol="Star" />
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>

View File

@@ -4,7 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid <Grid
Margin="5,10,5,5" Margin="5,10,5,5"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -4,7 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.Ui.Controls.NavigationDialogHost"> x:Class="Ryujinx.Ava.Ui.Controls.NavigationDialogHost"
Focusable="True">
<ui:Frame HorizontalAlignment="Stretch" VerticalAlignment="Stretch" <ui:Frame HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
x:Name="ContentFrame" /> x:Name="ContentFrame" />
</UserControl> </UserControl>

View File

@@ -4,7 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
x:Class="Ryujinx.Ava.Ui.Controls.ProfileImageSelectionDialog"> x:Class="Ryujinx.Ava.Ui.Controls.ProfileImageSelectionDialog"
Focusable="True">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,10,5, 5"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,10,5, 5">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -3,5 +3,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.Ui.Controls.RendererHost"> x:Class="Ryujinx.Ava.Ui.Controls.RendererHost"
Focusable="True">
</UserControl> </UserControl>

View File

@@ -9,7 +9,8 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Height="400" Height="400"
Width="550" Width="550"
x:Class="Ryujinx.Ava.Ui.Controls.SaveManager"> x:Class="Ryujinx.Ava.Ui.Controls.SaveManager"
Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>

View File

@@ -8,7 +8,8 @@
Title="Ryujinx - Waiting" Title="Ryujinx - Waiting"
SizeToContent="WidthAndHeight" SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid <Grid
Margin="20" Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -12,7 +12,8 @@
Margin="0" Margin="0"
MinWidth="500" MinWidth="500"
Padding="0" Padding="0"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>

View File

@@ -10,7 +10,8 @@
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
x:Class="Ryujinx.Ava.Ui.Controls.UserRecoverer"> x:Class="Ryujinx.Ava.Ui.Controls.UserRecoverer"
Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>

View File

@@ -12,7 +12,8 @@
d:DesignHeight="450" d:DesignHeight="450"
MinWidth="500" MinWidth="500"
d:DesignWidth="800" d:DesignWidth="800"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>

View File

@@ -1,45 +0,0 @@
using Ryujinx.Ui.App.Common;
using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
internal class FileSizeSortComparer : IComparer
{
public int Compare(object x, object y)
{
string aValue = (x as ApplicationData).TimePlayed;
string bValue = (y as ApplicationData).TimePlayed;
if (aValue[^3..] == "GiB")
{
aValue = (float.Parse(aValue[0..^3]) * 1024).ToString();
}
else
{
aValue = aValue[0..^3];
}
if (bValue[^3..] == "GiB")
{
bValue = (float.Parse(bValue[0..^3]) * 1024).ToString();
}
else
{
bValue = bValue[0..^3];
}
if (float.Parse(aValue) > float.Parse(bValue))
{
return -1;
}
else if (float.Parse(bValue) > float.Parse(aValue))
{
return 1;
}
else
{
return 0;
}
}
}
}

View File

@@ -1,50 +0,0 @@
using Ryujinx.Ui.App.Common;
using System.Collections.Generic;
namespace Ryujinx.Ava.Ui.Models.Generic
{
internal class FileSizeSortComparer : IComparer<ApplicationData>
{
public FileSizeSortComparer() { }
public FileSizeSortComparer(bool isAscending) { _order = isAscending ? 1 : -1; }
private int _order;
public int Compare(ApplicationData x, ApplicationData y)
{
string aValue = x.FileSize;
string bValue = y.FileSize;
if (aValue[^3..] == "GiB")
{
aValue = (float.Parse(aValue[0..^3]) * 1024).ToString();
}
else
{
aValue = aValue[0..^3];
}
if (bValue[^3..] == "GiB")
{
bValue = (float.Parse(bValue[0..^3]) * 1024).ToString();
}
else
{
bValue = bValue[0..^3];
}
if (float.Parse(aValue) > float.Parse(bValue))
{
return -1 * _order;
}
else if (float.Parse(bValue) > float.Parse(aValue))
{
return 1 * _order;
}
else
{
return 0;
}
}
}
}

View File

@@ -1,66 +0,0 @@
using Ryujinx.Ui.App.Common;
using System.Collections.Generic;
namespace Ryujinx.Ava.Ui.Models.Generic
{
internal class TimePlayedSortComparer : IComparer<ApplicationData>
{
public TimePlayedSortComparer() { }
public TimePlayedSortComparer(bool isAscending) { _order = isAscending ? 1 : -1; }
private int _order;
public int Compare(ApplicationData x, ApplicationData y)
{
string aValue = x.TimePlayed;
string bValue = y.TimePlayed;
if (aValue.Length > 4 && aValue[^4..] == "mins")
{
aValue = (float.Parse(aValue[0..^5]) * 60).ToString();
}
else if (aValue.Length > 3 && aValue[^3..] == "hrs")
{
aValue = (float.Parse(aValue[0..^4]) * 3600).ToString();
}
else if (aValue.Length > 4 && aValue[^4..] == "days")
{
aValue = (float.Parse(aValue[0..^5]) * 86400).ToString();
}
else
{
aValue = aValue[0..^1];
}
if (bValue.Length > 4 && bValue[^4..] == "mins")
{
bValue = (float.Parse(bValue[0..^5]) * 60).ToString();
}
else if (bValue.Length > 3 && bValue[^3..] == "hrs")
{
bValue = (float.Parse(bValue[0..^4]) * 3600).ToString();
}
else if (bValue.Length > 4 && bValue[^4..] == "days")
{
bValue = (float.Parse(bValue[0..^5]) * 86400).ToString();
}
else
{
bValue = bValue[0..^1];
}
if (float.Parse(aValue) > float.Parse(bValue))
{
return -1 * _order;
}
else if (float.Parse(bValue) > float.Parse(aValue))
{
return 1 * _order;
}
else
{
return 0;
}
}
}
}

View File

@@ -1,27 +0,0 @@
using Ryujinx.Ui.App.Common;
using System;
using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
internal class LastPlayedSortComparer : IComparer
{
public int Compare(object x, object y)
{
string aValue = (x as ApplicationData).LastPlayed;
string bValue = (y as ApplicationData).LastPlayed;
if (aValue == "Never")
{
aValue = DateTime.UnixEpoch.ToString();
}
if (bValue == "Never")
{
bValue = DateTime.UnixEpoch.ToString();
}
return DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue));
}
}
}

View File

@@ -1,61 +0,0 @@
using Ryujinx.Ui.App.Common;
using System.Collections;
namespace Ryujinx.Ava.Ui.Models
{
internal class TimePlayedSortComparer : IComparer
{
public int Compare(object x, object y)
{
string aValue = (x as ApplicationData).TimePlayed;
string bValue = (y as ApplicationData).TimePlayed;
if (aValue.Length > 4 && aValue[^4..] == "mins")
{
aValue = (float.Parse(aValue[0..^5]) * 60).ToString();
}
else if (aValue.Length > 3 && aValue[^3..] == "hrs")
{
aValue = (float.Parse(aValue[0..^4]) * 3600).ToString();
}
else if (aValue.Length > 4 && aValue[^4..] == "days")
{
aValue = (float.Parse(aValue[0..^5]) * 86400).ToString();
}
else
{
aValue = aValue[0..^1];
}
if (bValue.Length > 4 && bValue[^4..] == "mins")
{
bValue = (float.Parse(bValue[0..^5]) * 60).ToString();
}
else if (bValue.Length > 3 && bValue[^3..] == "hrs")
{
bValue = (float.Parse(bValue[0..^4]) * 3600).ToString();
}
else if (bValue.Length > 4 && bValue[^4..] == "days")
{
bValue = (float.Parse(bValue[0..^5]) * 86400).ToString();
}
else
{
bValue = bValue[0..^1];
}
if (float.Parse(aValue) > float.Parse(bValue))
{
return -1;
}
else if (float.Parse(bValue) > float.Parse(aValue))
{
return 1;
}
else
{
return 0;
}
}
}
}

View File

@@ -435,9 +435,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
OnPropertyChanged(); OnPropertyChanged();
} }
} }
public Thickness GridItemPadding => ShowNames ? new Thickness() : new Thickness(5);
public bool ShowMenuAndStatusBar public bool ShowMenuAndStatusBar
{ {
get => _showMenuAndStatusBar; get => _showMenuAndStatusBar;
@@ -504,8 +502,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
return SortMode switch return SortMode switch
{ {
ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
ApplicationSort.FileSize => new Models.Generic.FileSizeSortComparer(IsAscending), ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes)
ApplicationSort.TotalTimePlayed => new Models.Generic.TimePlayedSortComparer(IsAscending), : SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes),
ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum)
: SortExpressionComparer<ApplicationData>.Descending(app => app.TimePlayedNum),
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName) ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName), : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite) ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
@@ -599,7 +599,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
ConfigurationState.Instance.Ui.ShowNames.Value = value; ConfigurationState.Instance.Ui.ShowNames.Value = value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(GridItemPadding));
OnPropertyChanged(nameof(GridSizeScale)); OnPropertyChanged(nameof(GridSizeScale));
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@@ -716,7 +715,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
OnPropertyChanged(nameof(IsGridLarge)); OnPropertyChanged(nameof(IsGridLarge));
OnPropertyChanged(nameof(IsGridHuge)); OnPropertyChanged(nameof(IsGridHuge));
OnPropertyChanged(nameof(ShowNames)); OnPropertyChanged(nameof(ShowNames));
OnPropertyChanged(nameof(GridItemPadding));
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
} }
@@ -780,6 +778,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
_owner.LoadProgressBar.IsVisible = false; _owner.LoadProgressBar.IsVisible = false;
} }
if (e.NumAppsLoaded == e.NumAppsFound)
{
_owner.LoadProgressBar.IsVisible = false;
}
}); });
} }
@@ -880,17 +883,17 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void LoadConfigurableHotKeys() public void LoadConfigurableHotKeys()
{ {
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
{ {
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None); ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
} }
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{ {
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None); ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
} }
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{ {
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None); PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
} }

View File

@@ -15,7 +15,8 @@
CanResize="False" CanResize="False"
SizeToContent="Width" SizeToContent="Width"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid <Grid
Margin="15" Margin="15"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -11,7 +11,8 @@
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
Width="800" MinHeight="650" Height="650" Width="800" MinHeight="650" Height="650"
SizeToContent="Manual" SizeToContent="Manual"
MinWidth="600"> MinWidth="600"
Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:AmiiboWindowViewModel /> <viewModels:AmiiboWindowViewModel />
</Design.DataContext> </Design.DataContext>

View File

@@ -11,7 +11,8 @@
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls" xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
x:CompileBindings="True" x:CompileBindings="True"
x:DataType="viewModels:AvatarProfileViewModel"> x:DataType="viewModels:AvatarProfileViewModel"
Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:AvatarProfileViewModel /> <viewModels:AvatarProfileViewModel />
</Design.DataContext> </Design.DataContext>

View File

@@ -12,7 +12,8 @@
MinWidth="500" MinWidth="500"
MinHeight="500" MinHeight="500"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Window.Styles> <Window.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="True" /> <Setter Property="IsExpanded" Value="True" />

View File

@@ -8,7 +8,8 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
x:Class="Ryujinx.Ava.Ui.Windows.ContentDialogOverlayWindow" x:Class="Ryujinx.Ava.Ui.Windows.ContentDialogOverlayWindow"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="ContentDialogOverlayWindow"> Title="ContentDialogOverlayWindow"
Focusable="True">
<window:StyleableWindow.Styles> <window:StyleableWindow.Styles>
<Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot"> <Style Selector="ui|ContentDialog /template/ Panel#LayoutRoot">
<Setter Property="Background" <Setter Property="Background"

View File

@@ -13,7 +13,8 @@
d:DesignHeight="800" d:DesignHeight="800"
d:DesignWidth="800" d:DesignWidth="800"
x:CompileBindings="False" x:CompileBindings="False"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:ControllerSettingsViewModel /> <viewModels:ControllerSettingsViewModel />
</Design.DataContext> </Design.DataContext>

View File

@@ -14,7 +14,8 @@
MaxHeight="500" MaxHeight="500"
SizeToContent="Height" SizeToContent="Height"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid Name="DownloadableContentGrid" Margin="15"> <Grid Name="DownloadableContentGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -20,7 +20,7 @@ namespace Ryujinx.Ava.Ui.Windows
private const int CutOffLuminosity = 64; private const int CutOffLuminosity = 64;
private struct PaletteColor private readonly struct PaletteColor
{ {
public int Qck { get; } public int Qck { get; }
public byte R { get; } public byte R { get; }

View File

@@ -20,7 +20,8 @@
x:CompileBindings="True" x:CompileBindings="True"
x:DataType="viewModels:MainWindowViewModel" x:DataType="viewModels:MainWindowViewModel"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Window.Styles> <Window.Styles>
<Style Selector="TitleBar:fullscreen"> <Style Selector="TitleBar:fullscreen">
<Setter Property="Background" Value="#000000" /> <Setter Property="Background" Value="#000000" />

View File

@@ -266,6 +266,7 @@ namespace Ryujinx.Ava.Ui.Windows
return; return;
} }
CanUpdate = false;
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName;
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
@@ -371,6 +372,7 @@ namespace Ryujinx.Ava.Ui.Windows
ViewModel.ShowContent = true; ViewModel.ShowContent = true;
ViewModel.ShowLoadProgress = false; ViewModel.ShowLoadProgress = false;
ViewModel.IsLoadingIndeterminate = false; ViewModel.IsLoadingIndeterminate = false;
CanUpdate = true;
Cursor = Cursor.Default; Cursor = Cursor.Default;
if (MainContent.Content != _mainViewContent) if (MainContent.Content != _mainViewContent)

View File

@@ -6,7 +6,8 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d" mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Windows.MotionSettingsWindow"> x:Class="Ryujinx.Ava.Ui.Windows.MotionSettingsWindow"
Focusable="True">
<Grid Margin="10"> <Grid Margin="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -6,7 +6,8 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d" mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Windows.RumbleSettingsWindow"> x:Class="Ryujinx.Ava.Ui.Windows.RumbleSettingsWindow"
Focusable="True">
<Grid Margin="10"> <Grid Margin="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -18,7 +18,8 @@
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
x:CompileBindings="True" x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel" x:DataType="viewModels:SettingsViewModel"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
@@ -875,7 +876,7 @@
</StackPanel> </StackPanel>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal"> <StackPanel Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabLoggingOpenglLogLevel}" Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}" ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
Width="285" /> Width="285" />
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}" <ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
@@ -883,17 +884,17 @@
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"> ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelNone}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelError}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock <TextBlock
Text="{locale:Locale SettingsTabLoggingOpenglLogLevelPerformance}" /> Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingOpenglLogLevelAll}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
</ComboBoxItem> </ComboBoxItem>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>

View File

@@ -14,7 +14,8 @@
MaxHeight="400" MaxHeight="400"
SizeToContent="Height" SizeToContent="Height"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
mc:Ignorable="d"> mc:Ignorable="d"
Focusable="True">
<Grid Margin="15"> <Grid Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -16,7 +16,7 @@ namespace Ryujinx.Common.Logging
public static event EventHandler<LogEventArgs> Updated; public static event EventHandler<LogEventArgs> Updated;
public struct Log public readonly struct Log
{ {
internal readonly LogLevel Level; internal readonly LogLevel Level;

View File

@@ -7,7 +7,7 @@ namespace Ryujinx.Common.Memory
/// This is useful to keep the Array representation when possible to avoid copies. /// This is useful to keep the Array representation when possible to avoid copies.
/// </summary> /// </summary>
/// <typeparam name="T">Element Type</typeparam> /// <typeparam name="T">Element Type</typeparam>
public ref struct SpanOrArray<T> where T : unmanaged public readonly ref struct SpanOrArray<T> where T : unmanaged
{ {
public readonly T[] Array; public readonly T[] Array;
public readonly ReadOnlySpan<T> Span; public readonly ReadOnlySpan<T> Span;

Some files were not shown because too many files have changed in this diff Show More