Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
9e50dd99d7 | |||
0dec91bb42 | |||
d9b63353b0 | |||
eabd0ec93f | |||
138d5dc64a | |||
3e68a87d63 | |||
69b6ef7a4a | |||
40e87c634e | |||
79d1c190db | |||
2bc88467eb | |||
baf8752e74 | |||
d5e4378aea | |||
6dbcdfea47 | |||
c5258cf082 | |||
5c89e22bb9 | |||
11ecff2ff0 | |||
4c3f09644a | |||
e187a8870a | |||
a64fee29dc | |||
9ef94c8292 | |||
915d6d044c | |||
a4780ab33b | |||
a947a45d81 | |||
9db73f74cf | |||
a1efd87c45 | |||
49be977588 | |||
c95be55091 | |||
63dedbda86 | |||
c532118d94 | |||
52d6f2e656 | |||
c9bc4eaf58 | |||
3249f8ff41 | |||
1b41b285ac | |||
f5a6f45b27 | |||
210557951b | |||
4c2d9ff3ff | |||
8198b99935 | |||
460f96967d | |||
7ca779a26d | |||
b5032b3c91 | |||
f0a3dff136 | |||
f659dcb9d8 | |||
a34fb0e939 | |||
21ce8a9b80 | |||
9ecbee8032 | |||
80519af67d | |||
26e30faff3 | |||
0992310b76 | |||
009c1101d2 | |||
ba95ee54ab | |||
4ce4299ca2 | |||
17620d18db | |||
9f1cf6458c |
@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
|
|||||||
|
|
||||||
#### C# Coding Conventions ####
|
#### C# Coding Conventions ####
|
||||||
|
|
||||||
|
# Namespace preferences
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
resharper_csharp_namespace_body = block_scoped
|
||||||
|
|
||||||
# var preferences
|
# var preferences
|
||||||
csharp_style_var_elsewhere = false:silent
|
csharp_style_var_elsewhere = false:silent
|
||||||
csharp_style_var_for_built_in_types = false:silent
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@ -112,6 +112,17 @@ jobs:
|
|||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
token: ${{ secrets.RELEASE_TOKEN }}
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create tag
|
||||||
|
uses: actions/github-script@v5
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
|
sha: context.sha
|
||||||
|
})
|
||||||
|
|
||||||
flatpak_release:
|
flatpak_release:
|
||||||
uses: ./.github/workflows/flatpak.yml
|
uses: ./.github/workflows/flatpak.yml
|
||||||
needs: release
|
needs: release
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -226,6 +226,8 @@ namespace ARMeilleure.CodeGen.Arm64
|
|||||||
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
|
||||||
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
|
||||||
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
|
||||||
|
Add(Intrinsic.Arm64MrsFpcr, new IntrinsicInfo(0xd53b4400u, IntrinsicType.GetRegister));
|
||||||
|
Add(Intrinsic.Arm64MsrFpcr, new IntrinsicInfo(0xd51b4400u, IntrinsicType.SetRegister));
|
||||||
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
|
||||||
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
|
||||||
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));
|
||||||
|
@ -1034,7 +1034,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(opCode != BadOp, "Invalid opcode value.");
|
Debug.Assert(opCode != BadOp, "Invalid opcode value.");
|
||||||
|
|
||||||
if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
|
if ((flags & InstructionFlags.Evex) != 0 && HardwareCapabilities.SupportsEvexEncoding)
|
||||||
|
{
|
||||||
|
WriteEvexInst(dest, src1, src2, type, flags, opCode);
|
||||||
|
|
||||||
|
opCode &= 0xff;
|
||||||
|
}
|
||||||
|
else if ((flags & InstructionFlags.Vex) != 0 && HardwareCapabilities.SupportsVexEncoding)
|
||||||
{
|
{
|
||||||
// In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits.
|
// In a vex encoding, only one prefix can be active at a time. The active prefix is encoded in the second byte using two bits.
|
||||||
|
|
||||||
@ -1153,6 +1159,103 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WriteEvexInst(
|
||||||
|
Operand dest,
|
||||||
|
Operand src1,
|
||||||
|
Operand src2,
|
||||||
|
OperandType type,
|
||||||
|
InstructionFlags flags,
|
||||||
|
int opCode,
|
||||||
|
bool broadcast = false,
|
||||||
|
int registerWidth = 128,
|
||||||
|
int maskRegisterIdx = 0,
|
||||||
|
bool zeroElements = false)
|
||||||
|
{
|
||||||
|
int op1Idx = dest.GetRegister().Index;
|
||||||
|
int op2Idx = src1.GetRegister().Index;
|
||||||
|
int op3Idx = src2.GetRegister().Index;
|
||||||
|
|
||||||
|
WriteByte(0x62);
|
||||||
|
|
||||||
|
// P0
|
||||||
|
// Extend operand 1 register
|
||||||
|
bool r = (op1Idx & 8) == 0;
|
||||||
|
// Extend operand 3 register
|
||||||
|
bool x = (op3Idx & 16) == 0;
|
||||||
|
// Extend operand 3 register
|
||||||
|
bool b = (op3Idx & 8) == 0;
|
||||||
|
// Extend operand 1 register
|
||||||
|
bool rp = (op1Idx & 16) == 0;
|
||||||
|
// Escape code index
|
||||||
|
byte mm = 0b00;
|
||||||
|
|
||||||
|
switch ((ushort)(opCode >> 8))
|
||||||
|
{
|
||||||
|
case 0xf00: mm = 0b01; break;
|
||||||
|
case 0xf38: mm = 0b10; break;
|
||||||
|
case 0xf3a: mm = 0b11; break;
|
||||||
|
|
||||||
|
default: Debug.Fail($"Failed to EVEX encode opcode 0x{opCode:X}."); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(r ? 0x80 : 0) |
|
||||||
|
(x ? 0x40 : 0) |
|
||||||
|
(b ? 0x20 : 0) |
|
||||||
|
(rp ? 0x10 : 0) |
|
||||||
|
mm));
|
||||||
|
|
||||||
|
// P1
|
||||||
|
// Specify 64-bit lane mode
|
||||||
|
bool w = Is64Bits(type);
|
||||||
|
// Operand 2 register index
|
||||||
|
byte vvvv = (byte)(~op2Idx & 0b1111);
|
||||||
|
// Opcode prefix
|
||||||
|
byte pp = (flags & InstructionFlags.PrefixMask) switch
|
||||||
|
{
|
||||||
|
InstructionFlags.Prefix66 => 0b01,
|
||||||
|
InstructionFlags.PrefixF3 => 0b10,
|
||||||
|
InstructionFlags.PrefixF2 => 0b11,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(w ? 0x80 : 0) |
|
||||||
|
(vvvv << 3) |
|
||||||
|
0b100 |
|
||||||
|
pp));
|
||||||
|
|
||||||
|
// P2
|
||||||
|
// Mask register determines what elements to zero, rather than what elements to merge
|
||||||
|
bool z = zeroElements;
|
||||||
|
// Specifies register-width
|
||||||
|
byte ll = 0b00;
|
||||||
|
switch (registerWidth)
|
||||||
|
{
|
||||||
|
case 128: ll = 0b00; break;
|
||||||
|
case 256: ll = 0b01; break;
|
||||||
|
case 512: ll = 0b10; break;
|
||||||
|
|
||||||
|
default: Debug.Fail($"Invalid EVEX vector register width {registerWidth}."); break;
|
||||||
|
}
|
||||||
|
// Embedded broadcast in the case of a memory operand
|
||||||
|
bool bcast = broadcast;
|
||||||
|
// Extend operand 2 register
|
||||||
|
bool vp = (op2Idx & 16) == 0;
|
||||||
|
// Mask register index
|
||||||
|
Debug.Assert(maskRegisterIdx < 8, $"Invalid mask register index {maskRegisterIdx}.");
|
||||||
|
byte aaa = (byte)(maskRegisterIdx & 0b111);
|
||||||
|
|
||||||
|
WriteByte(
|
||||||
|
(byte)(
|
||||||
|
(z ? 0x80 : 0) |
|
||||||
|
(ll << 5) |
|
||||||
|
(bcast ? 0x10 : 0) |
|
||||||
|
(vp ? 8 : 0) |
|
||||||
|
aaa));
|
||||||
|
}
|
||||||
|
|
||||||
private void WriteCompactInst(Operand operand, int opCode)
|
private void WriteCompactInst(Operand operand, int opCode)
|
||||||
{
|
{
|
||||||
int regIndex = operand.GetRegister().Index;
|
int regIndex = operand.GetRegister().Index;
|
||||||
|
@ -20,6 +20,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Reg8Dest = 1 << 2,
|
Reg8Dest = 1 << 2,
|
||||||
RexW = 1 << 3,
|
RexW = 1 << 3,
|
||||||
Vex = 1 << 4,
|
Vex = 1 << 4,
|
||||||
|
Evex = 1 << 5,
|
||||||
|
|
||||||
PrefixBit = 16,
|
PrefixBit = 16,
|
||||||
PrefixMask = 7 << PrefixBit,
|
PrefixMask = 7 << PrefixBit,
|
||||||
@ -267,17 +268,20 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vfmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vfnmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
Add(X86Instruction.Vfnmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
|
||||||
Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vfnmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bf, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Vpblendvb, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4c, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
|
Add(X86Instruction.Vpternlogd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a25, InstructionFlags.Evex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
|
Add(X86Instruction.Xor, new InstructionInfo(0x00000031, 0x06000083, 0x06000081, BadOp, 0x00000033, InstructionFlags.None));
|
||||||
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
Add(X86Instruction.Xorpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||||
Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex));
|
Add(X86Instruction.Xorps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f57, InstructionFlags.Vex));
|
||||||
|
@ -249,10 +249,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
case IntrinsicType.Mxcsr:
|
case IntrinsicType.Mxcsr:
|
||||||
{
|
{
|
||||||
Operand offset = operation.GetSource(0);
|
Operand offset = operation.GetSource(0);
|
||||||
Operand bits = operation.GetSource(1);
|
|
||||||
|
|
||||||
Debug.Assert(offset.Kind == OperandKind.Constant && bits.Kind == OperandKind.Constant);
|
Debug.Assert(offset.Kind == OperandKind.Constant);
|
||||||
Debug.Assert(offset.Type == OperandType.I32 && bits.Type == OperandType.I32);
|
Debug.Assert(offset.Type == OperandType.I32);
|
||||||
|
|
||||||
int offs = offset.AsInt32() + context.CallArgsRegionSize;
|
int offs = offset.AsInt32() + context.CallArgsRegionSize;
|
||||||
|
|
||||||
@ -261,21 +260,23 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding);
|
Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding);
|
||||||
|
|
||||||
context.Assembler.Stmxcsr(memOp);
|
if (operation.Intrinsic == Intrinsic.X86Ldmxcsr)
|
||||||
|
|
||||||
if (operation.Intrinsic == Intrinsic.X86Mxcsrmb)
|
|
||||||
{
|
{
|
||||||
context.Assembler.Or(memOp, bits, OperandType.I32);
|
Operand bits = operation.GetSource(1);
|
||||||
|
Debug.Assert(bits.Type == OperandType.I32);
|
||||||
|
|
||||||
|
context.Assembler.Mov(memOp, bits, OperandType.I32);
|
||||||
|
context.Assembler.Ldmxcsr(memOp);
|
||||||
}
|
}
|
||||||
else /* if (intrinOp.Intrinsic == Intrinsic.X86Mxcsrub) */
|
else if (operation.Intrinsic == Intrinsic.X86Stmxcsr)
|
||||||
{
|
{
|
||||||
Operand notBits = Const(~bits.AsInt32());
|
Operand dest = operation.Destination;
|
||||||
|
Debug.Assert(dest.Type == OperandType.I32);
|
||||||
|
|
||||||
context.Assembler.And(memOp, notBits, OperandType.I32);
|
context.Assembler.Stmxcsr(memOp);
|
||||||
|
context.Assembler.Mov(dest, memOp, OperandType.I32);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Assembler.Ldmxcsr(memOp);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
namespace ARMeilleure.CodeGen.X86
|
namespace ARMeilleure.CodeGen.X86
|
||||||
{
|
{
|
||||||
static class HardwareCapabilities
|
static class HardwareCapabilities
|
||||||
{
|
{
|
||||||
|
private delegate uint GetXcr0();
|
||||||
|
|
||||||
static HardwareCapabilities()
|
static HardwareCapabilities()
|
||||||
{
|
{
|
||||||
if (!X86Base.IsSupported)
|
if (!X86Base.IsSupported)
|
||||||
@ -24,6 +28,34 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
FeatureInfo7Ebx = (FeatureFlags7Ebx)ebx7;
|
||||||
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
|
FeatureInfo7Ecx = (FeatureFlags7Ecx)ecx7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Xcr0InfoEax = (Xcr0FlagsEax)GetXcr0Eax();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GetXcr0Eax()
|
||||||
|
{
|
||||||
|
if (!FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave))
|
||||||
|
{
|
||||||
|
// XSAVE feature required for xgetbv
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> asmGetXcr0 = new byte[]
|
||||||
|
{
|
||||||
|
0x31, 0xc9, // xor ecx, ecx
|
||||||
|
0xf, 0x01, 0xd0, // xgetbv
|
||||||
|
0xc3, // ret
|
||||||
|
};
|
||||||
|
|
||||||
|
using MemoryBlock memGetXcr0 = new MemoryBlock((ulong)asmGetXcr0.Length);
|
||||||
|
|
||||||
|
memGetXcr0.Write(0, asmGetXcr0);
|
||||||
|
|
||||||
|
memGetXcr0.Reprotect(0, (ulong)asmGetXcr0.Length, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
|
var fGetXcr0 = Marshal.GetDelegateForFunctionPointer<GetXcr0>(memGetXcr0.Pointer);
|
||||||
|
|
||||||
|
return fGetXcr0();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
@ -44,6 +76,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Sse42 = 1 << 20,
|
Sse42 = 1 << 20,
|
||||||
Popcnt = 1 << 23,
|
Popcnt = 1 << 23,
|
||||||
Aes = 1 << 25,
|
Aes = 1 << 25,
|
||||||
|
Xsave = 1 << 26,
|
||||||
|
Osxsave = 1 << 27,
|
||||||
Avx = 1 << 28,
|
Avx = 1 << 28,
|
||||||
F16c = 1 << 29
|
F16c = 1 << 29
|
||||||
}
|
}
|
||||||
@ -52,7 +86,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public enum FeatureFlags7Ebx
|
public enum FeatureFlags7Ebx
|
||||||
{
|
{
|
||||||
Avx2 = 1 << 5,
|
Avx2 = 1 << 5,
|
||||||
Sha = 1 << 29
|
Avx512f = 1 << 16,
|
||||||
|
Avx512dq = 1 << 17,
|
||||||
|
Sha = 1 << 29,
|
||||||
|
Avx512bw = 1 << 30,
|
||||||
|
Avx512vl = 1 << 31
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
@ -61,10 +99,21 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Gfni = 1 << 8,
|
Gfni = 1 << 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Xcr0FlagsEax
|
||||||
|
{
|
||||||
|
Sse = 1 << 1,
|
||||||
|
YmmHi128 = 1 << 2,
|
||||||
|
Opmask = 1 << 5,
|
||||||
|
ZmmHi256 = 1 << 6,
|
||||||
|
Hi16Zmm = 1 << 7
|
||||||
|
}
|
||||||
|
|
||||||
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
public static FeatureFlags1Edx FeatureInfo1Edx { get; }
|
||||||
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
public static FeatureFlags1Ecx FeatureInfo1Ecx { get; }
|
||||||
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
|
public static FeatureFlags7Ebx FeatureInfo7Ebx { get; } = 0;
|
||||||
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
|
public static FeatureFlags7Ecx FeatureInfo7Ecx { get; } = 0;
|
||||||
|
public static Xcr0FlagsEax Xcr0InfoEax { get; } = 0;
|
||||||
|
|
||||||
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
|
public static bool SupportsSse => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse);
|
||||||
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
|
public static bool SupportsSse2 => FeatureInfo1Edx.HasFlag(FeatureFlags1Edx.Sse2);
|
||||||
@ -76,8 +125,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
public static bool SupportsSse42 => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Sse42);
|
||||||
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
public static bool SupportsPopcnt => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Popcnt);
|
||||||
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
public static bool SupportsAesni => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Aes);
|
||||||
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx);
|
public static bool SupportsAvx => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Avx | FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave) && Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128);
|
||||||
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
public static bool SupportsAvx2 => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx2) && SupportsAvx;
|
||||||
|
public static bool SupportsAvx512F => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512f) && FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.Xsave | FeatureFlags1Ecx.Osxsave)
|
||||||
|
&& Xcr0InfoEax.HasFlag(Xcr0FlagsEax.Sse | Xcr0FlagsEax.YmmHi128 | Xcr0FlagsEax.Opmask | Xcr0FlagsEax.ZmmHi256 | Xcr0FlagsEax.Hi16Zmm);
|
||||||
|
public static bool SupportsAvx512Vl => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512vl) && SupportsAvx512F;
|
||||||
|
public static bool SupportsAvx512Bw => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512bw) && SupportsAvx512F;
|
||||||
|
public static bool SupportsAvx512Dq => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Avx512dq) && SupportsAvx512F;
|
||||||
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
|
public static bool SupportsF16c => FeatureInfo1Ecx.HasFlag(FeatureFlags1Ecx.F16c);
|
||||||
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
|
public static bool SupportsSha => FeatureInfo7Ebx.HasFlag(FeatureFlags7Ebx.Sha);
|
||||||
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
|
public static bool SupportsGfni => FeatureInfo7Ecx.HasFlag(FeatureFlags7Ecx.Gfni);
|
||||||
@ -85,5 +139,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
public static bool ForceLegacySse { get; set; }
|
public static bool ForceLegacySse { get; set; }
|
||||||
|
|
||||||
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse;
|
||||||
|
public static bool SupportsEvexEncoding => SupportsAvx512F && !ForceLegacySse;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -60,6 +60,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
|
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
|
||||||
|
Add(Intrinsic.X86Ldmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
|
||||||
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
|
||||||
@ -75,8 +76,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
|
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits.
|
|
||||||
Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits.
|
|
||||||
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
|
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
|
||||||
@ -160,6 +159,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
|
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
|
||||||
|
Add(Intrinsic.X86Stmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
|
||||||
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
|
||||||
@ -170,16 +170,19 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
|
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
|
||||||
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
|
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
|
||||||
|
Add(Intrinsic.X86Vfmadd231pd, new IntrinsicInfo(X86Instruction.Vfmadd231pd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
|
||||||
|
Add(Intrinsic.X86Vfnmadd231pd, new IntrinsicInfo(X86Instruction.Vfnmadd231pd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmsub231sd, new IntrinsicInfo(X86Instruction.Vfnmsub231sd, IntrinsicType.Fma));
|
||||||
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
|
Add(Intrinsic.X86Vfnmsub231ss, new IntrinsicInfo(X86Instruction.Vfnmsub231ss, IntrinsicType.Fma));
|
||||||
|
Add(Intrinsic.X86Vpternlogd, new IntrinsicInfo(X86Instruction.Vpternlogd, IntrinsicType.TernaryImm));
|
||||||
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
|
Add(Intrinsic.X86Xorpd, new IntrinsicInfo(X86Instruction.Xorpd, IntrinsicType.Binary));
|
||||||
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
|
Add(Intrinsic.X86Xorps, new IntrinsicInfo(X86Instruction.Xorps, IntrinsicType.Binary));
|
||||||
}
|
}
|
||||||
|
15
ARMeilleure/CodeGen/X86/Mxcsr.cs
Normal file
15
ARMeilleure/CodeGen/X86/Mxcsr.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ARMeilleure.CodeGen.X86
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum Mxcsr
|
||||||
|
{
|
||||||
|
Ftz = 1 << 15, // Flush To Zero.
|
||||||
|
Rhi = 1 << 14, // Round Mode high bit.
|
||||||
|
Rlo = 1 << 13, // Round Mode low bit.
|
||||||
|
Um = 1 << 11, // Underflow Mask.
|
||||||
|
Dm = 1 << 8, // Denormal Mask.
|
||||||
|
Daz = 1 << 6 // Denormals Are Zero.
|
||||||
|
}
|
||||||
|
}
|
@ -120,12 +120,18 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Instruction.Extended:
|
case Instruction.Extended:
|
||||||
if (node.Intrinsic == Intrinsic.X86Mxcsrmb || node.Intrinsic == Intrinsic.X86Mxcsrub)
|
if (node.Intrinsic == Intrinsic.X86Ldmxcsr)
|
||||||
{
|
{
|
||||||
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
||||||
|
|
||||||
node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) });
|
node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) });
|
||||||
}
|
}
|
||||||
|
else if (node.Intrinsic == Intrinsic.X86Stmxcsr)
|
||||||
|
{
|
||||||
|
int stackOffset = stackAlloc.Allocate(OperandType.I32);
|
||||||
|
|
||||||
|
node.SetSources(new Operand[] { Const(stackOffset) });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,17 +208,20 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
Vblendvps,
|
Vblendvps,
|
||||||
Vcvtph2ps,
|
Vcvtph2ps,
|
||||||
Vcvtps2ph,
|
Vcvtps2ph,
|
||||||
|
Vfmadd231pd,
|
||||||
Vfmadd231ps,
|
Vfmadd231ps,
|
||||||
Vfmadd231sd,
|
Vfmadd231sd,
|
||||||
Vfmadd231ss,
|
Vfmadd231ss,
|
||||||
Vfmsub231sd,
|
Vfmsub231sd,
|
||||||
Vfmsub231ss,
|
Vfmsub231ss,
|
||||||
|
Vfnmadd231pd,
|
||||||
Vfnmadd231ps,
|
Vfnmadd231ps,
|
||||||
Vfnmadd231sd,
|
Vfnmadd231sd,
|
||||||
Vfnmadd231ss,
|
Vfnmadd231ss,
|
||||||
Vfnmsub231sd,
|
Vfnmsub231sd,
|
||||||
Vfnmsub231ss,
|
Vfnmsub231ss,
|
||||||
Vpblendvb,
|
Vpblendvb,
|
||||||
|
Vpternlogd,
|
||||||
Xor,
|
Xor,
|
||||||
Xorpd,
|
Xorpd,
|
||||||
Xorps,
|
Xorps,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
namespace ARMeilleure.Decoders;
|
namespace ARMeilleure.Decoders
|
||||||
|
|
||||||
interface IOpCode32Exception
|
|
||||||
{
|
{
|
||||||
int Id { get; }
|
interface IOpCode32Exception
|
||||||
|
{
|
||||||
|
int Id { get; }
|
||||||
|
}
|
||||||
}
|
}
|
@ -108,6 +108,13 @@ namespace ARMeilleure.Decoders
|
|||||||
SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create);
|
SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create);
|
||||||
SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
||||||
SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
|
||||||
|
SetA64("11010101000000110010000011011111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("11010101000000110010000011111111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("110101010000001100100001xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("1101010100000011001000100xx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("1101010100000011001000101>>11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("110101010000001100100011xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
|
SetA64("11010101000000110010>>xxxxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
|
||||||
SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create);
|
SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create);
|
||||||
SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create);
|
SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create);
|
||||||
SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create);
|
SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create);
|
||||||
|
@ -615,14 +615,11 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||||
{
|
|
||||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
|
||||||
|
|
||||||
Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd;
|
Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd;
|
||||||
|
|
||||||
return context.AddIntrinsic(addInst, op1, op2);
|
return context.AddIntrinsic(addInst, op1, op2);
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -696,17 +693,33 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@ -730,10 +743,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
|
||||||
}, scalar: true, op1, op2);
|
|
||||||
}, scalar: true);
|
}, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -755,10 +765,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false);
|
}, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -886,10 +893,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -914,10 +918,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -940,10 +941,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
|
||||||
}, scalar: true, op1, op2);
|
|
||||||
}, scalar: true);
|
}, scalar: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -965,10 +963,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false);
|
}, scalar: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1096,10 +1091,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1124,10 +1116,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
|
|
||||||
}, scalar: false, op1, op2);
|
|
||||||
}, scalar: false, op1, op2);
|
}, scalar: false, op1, op2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1146,6 +1135,37 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe);
|
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||||
|
|
||||||
|
Operand d = GetVec(op.Rd);
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
if (sizeF == 0)
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
|
}
|
||||||
|
else /* if (sizeF == 1) */
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 1;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
||||||
@ -1171,11 +1191,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@ -1186,9 +1214,15 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
else /* if (sizeF == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@ -1224,8 +1258,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@ -1240,8 +1281,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@ -1261,6 +1309,37 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe);
|
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
|
||||||
|
|
||||||
|
Operand d = GetVec(op.Rd);
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
if (sizeF == 0)
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
|
}
|
||||||
|
else /* if (sizeF == 1) */
|
||||||
|
{
|
||||||
|
int shuffleMask = op.Index | op.Index << 1;
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, d, n, res);
|
||||||
|
|
||||||
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
|
||||||
@ -1286,11 +1365,19 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@ -1301,9 +1388,15 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
else /* if (sizeF == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@ -1339,8 +1432,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@ -1355,8 +1455,15 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, res);
|
context.Copy(d, res);
|
||||||
}
|
}
|
||||||
@ -1385,17 +1492,33 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
|
||||||
|
}
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@ -1669,25 +1792,39 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
}
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
|
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
}
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
|
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@ -1716,25 +1853,39 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand n = GetVec(op.Rn);
|
Operand n = GetVec(op.Rn);
|
||||||
Operand m = GetVec(op.Rm);
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (op.Size == 0)
|
if (op.Size == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0f);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231ss, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0f);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
}
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
|
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper96(res));
|
context.Copy(d, context.VectorZeroUpper96(res));
|
||||||
}
|
}
|
||||||
else /* if (op.Size == 1) */
|
else /* if (op.Size == 1) */
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, -0d);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231sd, a, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand mask = X86GetScalar(context, -0d);
|
||||||
|
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
||||||
|
|
||||||
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
}
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
|
|
||||||
|
|
||||||
context.Copy(d, context.VectorZeroUpper64(res));
|
context.Copy(d, context.VectorZeroUpper64(res));
|
||||||
}
|
}
|
||||||
@ -1830,13 +1981,22 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, 2f);
|
Operand mask = X86GetScalar(context, 2f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||||
@ -1845,9 +2005,16 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand mask = X86GetScalar(context, 2d);
|
Operand mask = X86GetScalar(context, 2d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
||||||
@ -1877,14 +2044,23 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand mask = X86GetAllElements(context, 2f);
|
Operand mask = X86GetAllElements(context, 2f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
||||||
|
|
||||||
if (op.RegisterSize == RegisterSize.Simd64)
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
{
|
{
|
||||||
@ -1897,10 +2073,17 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Operand mask = X86GetAllElements(context, 2d);
|
Operand mask = X86GetAllElements(context, 2d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, mask, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), res);
|
context.Copy(GetVec(op.Rd), res);
|
||||||
}
|
}
|
||||||
@ -2113,20 +2296,32 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Frintx_S(ArmEmitterContext context)
|
public static void Frintx_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
// TODO Arm64: Fast path. Should we set host FPCR?
|
if (Optimizations.UseAdvSimd)
|
||||||
EmitScalarUnaryOpF(context, (op1) =>
|
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintxS);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF(context, (op1) =>
|
||||||
|
{
|
||||||
|
return EmitRoundByRMode(context, op1);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Frintx_V(ArmEmitterContext context)
|
public static void Frintx_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
// TODO Arm64: Fast path. Should we set host FPCR?
|
if (Optimizations.UseAdvSimd)
|
||||||
EmitVectorUnaryOpF(context, (op1) =>
|
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintxV);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpF(context, (op1) =>
|
||||||
|
{
|
||||||
|
return EmitRoundByRMode(context, op1);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Frintz_S(ArmEmitterContext context)
|
public static void Frintz_S(ArmEmitterContext context)
|
||||||
@ -2237,16 +2432,25 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand maskHalf = X86GetScalar(context, 0.5f);
|
Operand maskHalf = X86GetScalar(context, 0.5f);
|
||||||
Operand maskThree = X86GetScalar(context, 3f);
|
Operand maskThree = X86GetScalar(context, 3f);
|
||||||
Operand maskOneHalf = X86GetScalar(context, 1.5f);
|
Operand maskOneHalf = X86GetScalar(context, 1.5f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
|
||||||
@ -2257,10 +2461,17 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand maskThree = X86GetScalar(context, 3d);
|
Operand maskThree = X86GetScalar(context, 3d);
|
||||||
Operand maskOneHalf = X86GetScalar(context, 1.5d);
|
Operand maskOneHalf = X86GetScalar(context, 1.5d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
|
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
|
||||||
|
|
||||||
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
|
||||||
@ -2290,15 +2501,24 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
int sizeF = op.Size & 1;
|
int sizeF = op.Size & 1;
|
||||||
|
|
||||||
|
Operand res;
|
||||||
|
|
||||||
if (sizeF == 0)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
Operand maskHalf = X86GetAllElements(context, 0.5f);
|
Operand maskHalf = X86GetAllElements(context, 0.5f);
|
||||||
Operand maskThree = X86GetAllElements(context, 3f);
|
Operand maskThree = X86GetAllElements(context, 3f);
|
||||||
Operand maskOneHalf = X86GetAllElements(context, 1.5f);
|
Operand maskOneHalf = X86GetAllElements(context, 1.5f);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
||||||
|
|
||||||
@ -2315,9 +2535,16 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand maskThree = X86GetAllElements(context, 3d);
|
Operand maskThree = X86GetAllElements(context, 3d);
|
||||||
Operand maskOneHalf = X86GetAllElements(context, 1.5d);
|
Operand maskOneHalf = X86GetAllElements(context, 1.5d);
|
||||||
|
|
||||||
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
if (Optimizations.UseFma)
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, maskThree, n, m);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
|
||||||
|
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
|
||||||
|
}
|
||||||
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
|
|
||||||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res);
|
res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res);
|
||||||
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
|
||||||
|
|
||||||
@ -4728,53 +4955,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand EmitSseOrAvxHandleFzModeOpF(
|
|
||||||
ArmEmitterContext context,
|
|
||||||
Func2I emit,
|
|
||||||
bool scalar,
|
|
||||||
Operand n = default,
|
|
||||||
Operand m = default)
|
|
||||||
{
|
|
||||||
Operand nCopy = n == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rn)) : n;
|
|
||||||
Operand mCopy = m == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rm)) : m;
|
|
||||||
|
|
||||||
EmitSseOrAvxEnterFtzAndDazModesOpF(context, out Operand isTrue);
|
|
||||||
|
|
||||||
Operand res = emit(nCopy, mCopy);
|
|
||||||
|
|
||||||
EmitSseOrAvxExitFtzAndDazModesOpF(context, isTrue);
|
|
||||||
|
|
||||||
if (n != default || m != default)
|
|
||||||
{
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sizeF = ((IOpCodeSimd)context.CurrOp).Size & 1;
|
|
||||||
|
|
||||||
if (sizeF == 0)
|
|
||||||
{
|
|
||||||
if (scalar)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper96(res);
|
|
||||||
}
|
|
||||||
else if (((OpCodeSimdReg)context.CurrOp).RegisterSize == RegisterSize.Simd64)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper64(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* if (sizeF == 1) */
|
|
||||||
{
|
|
||||||
if (scalar)
|
|
||||||
{
|
|
||||||
res = context.VectorZeroUpper64(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rd), res);
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax)
|
private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax)
|
||||||
{
|
{
|
||||||
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
|
||||||
@ -4834,10 +5014,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
|
||||||
}, scalar: scalar, op1, op2);
|
|
||||||
}, scalar: scalar, nCopy, mCopy);
|
}, scalar: scalar, nCopy, mCopy);
|
||||||
|
|
||||||
if (n != default || m != default)
|
if (n != default || m != default)
|
||||||
@ -4872,10 +5049,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
|
||||||
{
|
{
|
||||||
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) =>
|
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
||||||
{
|
|
||||||
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
|
|
||||||
}, scalar: scalar, op1, op2);
|
|
||||||
}, scalar: scalar, nCopy, mCopy);
|
}, scalar: scalar, nCopy, mCopy);
|
||||||
|
|
||||||
if (n != default || m != default)
|
if (n != default || m != default)
|
||||||
|
@ -356,9 +356,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
||||||
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(method, src);
|
Operand res = context.Call(method, src);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
InsertScalar16(context, op.Vd, op.T, res);
|
InsertScalar16(context, op.Vd, op.T, res);
|
||||||
}
|
}
|
||||||
@ -372,9 +374,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
||||||
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(method, src);
|
Operand res = context.Call(method, src);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
InsertScalar(context, op.Vd, res);
|
InsertScalar(context, op.Vd, res);
|
||||||
}
|
}
|
||||||
@ -542,10 +546,17 @@ namespace ARMeilleure.Instructions
|
|||||||
// VRINTX (floating-point).
|
// VRINTX (floating-point).
|
||||||
public static void Vrintx_S(ArmEmitterContext context)
|
public static void Vrintx_S(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF32(context, (op1) =>
|
if (Optimizations.UseAdvSimd)
|
||||||
{
|
{
|
||||||
return EmitRoundByRMode(context, op1);
|
InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS);
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitScalarUnaryOpF32(context, (op1) =>
|
||||||
|
{
|
||||||
|
return EmitRoundByRMode(context, op1);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.CodeGen.X86;
|
||||||
using ARMeilleure.Decoders;
|
using ARMeilleure.Decoders;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
@ -158,6 +159,75 @@ namespace ARMeilleure.Instructions
|
|||||||
};
|
};
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public static void EnterArmFpMode(EmitterContext context, Func<FPState, Operand> getFpFlag)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
Operand fzTrue = getFpFlag(FPState.FzFlag);
|
||||||
|
Operand r0True = getFpFlag(FPState.RMode0Flag);
|
||||||
|
Operand r1True = getFpFlag(FPState.RMode1Flag);
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(fzTrue, Const((int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Um | Mxcsr.Dm)), Const(0)));
|
||||||
|
|
||||||
|
// X86 round modes in order: nearest, negative, positive, zero
|
||||||
|
// ARM round modes in order: nearest, positive, negative, zero
|
||||||
|
// Read the bits backwards to correct this.
|
||||||
|
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r0True, Const((int)Mxcsr.Rhi), Const(0)));
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r1True, Const((int)Mxcsr.Rlo), Const(0)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
Operand fzTrue = getFpFlag(FPState.FzFlag);
|
||||||
|
Operand r0True = getFpFlag(FPState.RMode0Flag);
|
||||||
|
Operand r1True = getFpFlag(FPState.RMode1Flag);
|
||||||
|
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
|
||||||
|
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(fzTrue, Const((int)FPCR.Fz), Const(0)));
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r0True, Const((int)FPCR.RMode0), Const(0)));
|
||||||
|
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r1True, Const((int)FPCR.RMode1), Const(0)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
// TODO: Restore FPSR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ExitArmFpMode(EmitterContext context, Action<FPState, Operand> setFpFlag)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
// Unset round mode (to nearest) and ftz.
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
|
||||||
|
// Status flags would be stored here if they were used.
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
// Unset round mode (to nearest) and fz.
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
// TODO: Store FPSR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetImmShl(OpCodeSimdShImm op)
|
public static int GetImmShl(OpCodeSimdShImm op)
|
||||||
{
|
{
|
||||||
return op.Imm - (8 << op.Size);
|
return op.Imm - (8 << op.Size);
|
||||||
@ -465,9 +535,11 @@ namespace ARMeilleure.Instructions
|
|||||||
? typeof(SoftFloat32).GetMethod(name)
|
? typeof(SoftFloat32).GetMethod(name)
|
||||||
: typeof(SoftFloat64).GetMethod(name);
|
: typeof(SoftFloat64).GetMethod(name);
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(info, callArgs);
|
Operand res = context.Call(info, callArgs);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -1358,39 +1430,6 @@ namespace ARMeilleure.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum Mxcsr
|
|
||||||
{
|
|
||||||
Ftz = 1 << 15, // Flush To Zero.
|
|
||||||
Um = 1 << 11, // Underflow Mask.
|
|
||||||
Dm = 1 << 8, // Denormal Mask.
|
|
||||||
Daz = 1 << 6 // Denormals Are Zero.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue)
|
|
||||||
{
|
|
||||||
isTrue = GetFpFlag(FPState.FzFlag);
|
|
||||||
|
|
||||||
Operand lblTrue = Label();
|
|
||||||
context.BranchIfFalse(lblTrue, isTrue);
|
|
||||||
|
|
||||||
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrmb, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm | Mxcsr.Daz)));
|
|
||||||
|
|
||||||
context.MarkLabel(lblTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
|
|
||||||
{
|
|
||||||
isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue;
|
|
||||||
|
|
||||||
Operand lblTrue = Label();
|
|
||||||
context.BranchIfFalse(lblTrue, isTrue);
|
|
||||||
|
|
||||||
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrub, Const((int)(Mxcsr.Ftz | Mxcsr.Daz)));
|
|
||||||
|
|
||||||
context.MarkLabel(lblTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CmpCondition
|
public enum CmpCondition
|
||||||
{
|
{
|
||||||
// Legacy Sse.
|
// Legacy Sse.
|
||||||
|
@ -1197,9 +1197,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Array.Resize(ref callArgs, callArgs.Length + 1);
|
Array.Resize(ref callArgs, callArgs.Length + 1);
|
||||||
callArgs[callArgs.Length - 1] = Const(1);
|
callArgs[callArgs.Length - 1] = Const(1);
|
||||||
|
|
||||||
|
context.ExitArmFpMode();
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
Operand res = context.Call(info, callArgs);
|
Operand res = context.Call(info, callArgs);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
context.EnterArmFpMode();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -254,7 +254,22 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Not_V(ArmEmitterContext context)
|
public static void Not_V(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse2)
|
if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, n, Const(~0b10101010));
|
||||||
|
|
||||||
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
|
{
|
||||||
|
res = context.VectorZeroUpper64(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVec(op.Rd), res);
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
@ -283,6 +298,22 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV);
|
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64OrnV);
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetVec(op.Rn);
|
||||||
|
Operand m = GetVec(op.Rm);
|
||||||
|
|
||||||
|
Operand res = context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||||
|
|
||||||
|
if (op.RegisterSize == RegisterSize.Simd64)
|
||||||
|
{
|
||||||
|
res = context.VectorZeroUpper64(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Copy(GetVec(op.Rd), res);
|
||||||
|
}
|
||||||
else if (Optimizations.UseSse2)
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||||
|
@ -151,6 +151,13 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m));
|
InstEmitSimdHelper32Arm64.EmitVectorBinaryOpSimd32(context, (n, m) => context.AddIntrinsic(Intrinsic.Arm64OrnV | Intrinsic.Arm64V128, n, m));
|
||||||
}
|
}
|
||||||
|
else if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpSimd32(context, (n, m) =>
|
||||||
|
{
|
||||||
|
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, n, m, Const(0b11001100 | ~0b10101010));
|
||||||
|
});
|
||||||
|
}
|
||||||
else if (Optimizations.UseSse2)
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
Operand mask = context.VectorOne();
|
Operand mask = context.VectorOne();
|
||||||
|
@ -34,7 +34,14 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
public static void Vmvn_I(ArmEmitterContext context)
|
public static void Vmvn_I(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse2)
|
if (Optimizations.UseAvx512Ortho)
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||||
|
{
|
||||||
|
return context.AddIntrinsic(Intrinsic.X86Vpternlogd, op1, op1, Const(0b01010101));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseSse2)
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpSimd32(context, (op1) =>
|
EmitVectorUnaryOpSimd32(context, (op1) =>
|
||||||
{
|
{
|
||||||
|
@ -33,8 +33,8 @@ namespace ARMeilleure.Instructions
|
|||||||
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
|
||||||
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
|
||||||
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break;
|
case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return;
|
||||||
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break;
|
case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return;
|
||||||
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
|
||||||
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
|
||||||
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
|
||||||
@ -49,19 +49,15 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
switch (GetPackedId(op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
|
||||||
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
|
||||||
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
|
||||||
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break;
|
case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Call(info, GetIntOrZR(context, op.Rt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Nop(ArmEmitterContext context)
|
public static void Nop(ArmEmitterContext context)
|
||||||
@ -165,6 +161,28 @@ namespace ARMeilleure.Instructions
|
|||||||
SetIntOrZR(context, op.Rt, fpsr);
|
SetIntOrZR(context, op.Rt, fpsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitGetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||||
|
|
||||||
|
SetIntOrZR(context, op.Rt, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||||
|
|
||||||
|
SetIntOrZR(context, op.Rt, result);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitSetNzcv(ArmEmitterContext context)
|
private static void EmitSetNzcv(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
@ -192,6 +210,8 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitSetFpsr(ArmEmitterContext context)
|
private static void EmitSetFpsr(ArmEmitterContext context)
|
||||||
@ -210,6 +230,19 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
|
||||||
|
|
||||||
|
Operand value = GetIntOrZR(context, op.Rt);
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@ namespace ARMeilleure.Instructions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodInfo info;
|
|
||||||
|
|
||||||
switch (op.CRn)
|
switch (op.CRn)
|
||||||
{
|
{
|
||||||
case 13: // Process and Thread Info.
|
case 13: // Process and Thread Info.
|
||||||
@ -36,14 +34,12 @@ namespace ARMeilleure.Instructions
|
|||||||
switch (op.Opc2)
|
switch (op.Opc2)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break;
|
EmitSetTpidrEl0(context); return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
switch (op.CRm) // Cache and Memory barrier.
|
switch (op.CRm) // Cache and Memory barrier.
|
||||||
{
|
{
|
||||||
@ -64,8 +60,6 @@ namespace ARMeilleure.Instructions
|
|||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Call(info, GetIntA32(context, op.Rt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Mrc(ArmEmitterContext context)
|
public static void Mrc(ArmEmitterContext context)
|
||||||
@ -79,7 +73,7 @@ namespace ARMeilleure.Instructions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodInfo info;
|
Operand result;
|
||||||
|
|
||||||
switch (op.CRn)
|
switch (op.CRn)
|
||||||
{
|
{
|
||||||
@ -92,10 +86,10 @@ namespace ARMeilleure.Instructions
|
|||||||
switch (op.Opc2)
|
switch (op.Opc2)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break;
|
result = EmitGetTpidrEl0(context); break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break;
|
result = EmitGetTpidrroEl0(context); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
|
||||||
@ -110,13 +104,13 @@ namespace ARMeilleure.Instructions
|
|||||||
if (op.Rt == RegisterAlias.Aarch32Pc)
|
if (op.Rt == RegisterAlias.Aarch32Pc)
|
||||||
{
|
{
|
||||||
// Special behavior: copy NZCV flags into APSR.
|
// Special behavior: copy NZCV flags into APSR.
|
||||||
EmitSetNzcv(context, context.Call(info));
|
EmitSetNzcv(context, result);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetIntA32(context, op.Rt, context.Call(info));
|
SetIntA32(context, op.Rt, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,6 +315,37 @@ namespace ARMeilleure.Instructions
|
|||||||
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.UpdateArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitSetTpidrEl0(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
OpCode32System op = (OpCode32System)context.CurrOp;
|
||||||
|
|
||||||
|
Operand value = GetIntA32(context, op.Rt);
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
|
||||||
|
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,26 +72,6 @@ namespace ARMeilleure.Instructions
|
|||||||
return (ulong)GetContext().DczidEl0;
|
return (ulong)GetContext().DczidEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetTpidrEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidrEl032()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().TpidrEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetTpidrroEl0()
|
|
||||||
{
|
|
||||||
return (ulong)GetContext().TpidrroEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static uint GetTpidr32()
|
|
||||||
{
|
|
||||||
return (uint)GetContext().TpidrroEl0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong GetCntfrqEl0()
|
public static ulong GetCntfrqEl0()
|
||||||
{
|
{
|
||||||
return GetContext().CntfrqEl0;
|
return GetContext().CntfrqEl0;
|
||||||
@ -106,16 +86,6 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
return GetContext().CntvctEl0;
|
return GetContext().CntvctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetTpidrEl0(ulong value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetTpidrEl032(uint value)
|
|
||||||
{
|
|
||||||
GetContext().TpidrEl0 = (long)value;
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "Read"
|
#region "Read"
|
||||||
|
@ -53,6 +53,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Haddpd,
|
X86Haddpd,
|
||||||
X86Haddps,
|
X86Haddps,
|
||||||
X86Insertps,
|
X86Insertps,
|
||||||
|
X86Ldmxcsr,
|
||||||
X86Maxpd,
|
X86Maxpd,
|
||||||
X86Maxps,
|
X86Maxps,
|
||||||
X86Maxsd,
|
X86Maxsd,
|
||||||
@ -68,8 +69,6 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Mulps,
|
X86Mulps,
|
||||||
X86Mulsd,
|
X86Mulsd,
|
||||||
X86Mulss,
|
X86Mulss,
|
||||||
X86Mxcsrmb,
|
|
||||||
X86Mxcsrub,
|
|
||||||
X86Paddb,
|
X86Paddb,
|
||||||
X86Paddd,
|
X86Paddd,
|
||||||
X86Paddq,
|
X86Paddq,
|
||||||
@ -153,6 +152,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Sqrtps,
|
X86Sqrtps,
|
||||||
X86Sqrtsd,
|
X86Sqrtsd,
|
||||||
X86Sqrtss,
|
X86Sqrtss,
|
||||||
|
X86Stmxcsr,
|
||||||
X86Subpd,
|
X86Subpd,
|
||||||
X86Subps,
|
X86Subps,
|
||||||
X86Subsd,
|
X86Subsd,
|
||||||
@ -163,16 +163,19 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
X86Unpcklps,
|
X86Unpcklps,
|
||||||
X86Vcvtph2ps,
|
X86Vcvtph2ps,
|
||||||
X86Vcvtps2ph,
|
X86Vcvtps2ph,
|
||||||
|
X86Vfmadd231pd,
|
||||||
X86Vfmadd231ps,
|
X86Vfmadd231ps,
|
||||||
X86Vfmadd231sd,
|
X86Vfmadd231sd,
|
||||||
X86Vfmadd231ss,
|
X86Vfmadd231ss,
|
||||||
X86Vfmsub231sd,
|
X86Vfmsub231sd,
|
||||||
X86Vfmsub231ss,
|
X86Vfmsub231ss,
|
||||||
|
X86Vfnmadd231pd,
|
||||||
X86Vfnmadd231ps,
|
X86Vfnmadd231ps,
|
||||||
X86Vfnmadd231sd,
|
X86Vfnmadd231sd,
|
||||||
X86Vfnmadd231ss,
|
X86Vfnmadd231ss,
|
||||||
X86Vfnmsub231sd,
|
X86Vfnmsub231sd,
|
||||||
X86Vfnmsub231ss,
|
X86Vfnmsub231ss,
|
||||||
|
X86Vpternlogd,
|
||||||
X86Xorpd,
|
X86Xorpd,
|
||||||
X86Xorps,
|
X86Xorps,
|
||||||
|
|
||||||
@ -393,6 +396,8 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
Arm64MlsVe,
|
Arm64MlsVe,
|
||||||
Arm64MlsV,
|
Arm64MlsV,
|
||||||
Arm64MoviV,
|
Arm64MoviV,
|
||||||
|
Arm64MrsFpcr,
|
||||||
|
Arm64MsrFpcr,
|
||||||
Arm64MrsFpsr,
|
Arm64MrsFpsr,
|
||||||
Arm64MsrFpsr,
|
Arm64MsrFpsr,
|
||||||
Arm64MulVe,
|
Arm64MulVe,
|
||||||
|
@ -23,6 +23,10 @@ namespace ARMeilleure
|
|||||||
public static bool UseSse42IfAvailable { get; set; } = true;
|
public static bool UseSse42IfAvailable { get; set; } = true;
|
||||||
public static bool UsePopCntIfAvailable { get; set; } = true;
|
public static bool UsePopCntIfAvailable { get; set; } = true;
|
||||||
public static bool UseAvxIfAvailable { get; set; } = true;
|
public static bool UseAvxIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512FIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512VlIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512BwIfAvailable { get; set; } = true;
|
||||||
|
public static bool UseAvx512DqIfAvailable { get; set; } = true;
|
||||||
public static bool UseF16cIfAvailable { get; set; } = true;
|
public static bool UseF16cIfAvailable { get; set; } = true;
|
||||||
public static bool UseFmaIfAvailable { get; set; } = true;
|
public static bool UseFmaIfAvailable { get; set; } = true;
|
||||||
public static bool UseAesniIfAvailable { get; set; } = true;
|
public static bool UseAesniIfAvailable { get; set; } = true;
|
||||||
@ -47,11 +51,18 @@ namespace ARMeilleure
|
|||||||
internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
|
internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
|
||||||
internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
|
internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
|
||||||
internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512F => UseAvx512FIfAvailable && X86HardwareCapabilities.SupportsAvx512F && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Vl => UseAvx512VlIfAvailable && X86HardwareCapabilities.SupportsAvx512Vl && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Bw => UseAvx512BwIfAvailable && X86HardwareCapabilities.SupportsAvx512Bw && !ForceLegacySse;
|
||||||
|
internal static bool UseAvx512Dq => UseAvx512DqIfAvailable && X86HardwareCapabilities.SupportsAvx512Dq && !ForceLegacySse;
|
||||||
internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
|
internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
|
||||||
internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
|
internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
|
||||||
internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
|
internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
|
||||||
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
|
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
|
||||||
internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
|
internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
|
||||||
internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
|
internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
|
||||||
|
|
||||||
|
internal static bool UseAvx512Ortho => UseAvx512F && UseAvx512Vl;
|
||||||
|
internal static bool UseAvx512OrthoFloat => UseAvx512Ortho && UseAvx512Dq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,17 @@ namespace ARMeilleure.State
|
|||||||
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0
|
||||||
public ulong CntvctEl0 => CntpctEl0;
|
public ulong CntvctEl0 => CntpctEl0;
|
||||||
|
|
||||||
public long TpidrEl0 { get; set; }
|
public long TpidrEl0
|
||||||
public long TpidrroEl0 { get; set; }
|
{
|
||||||
|
get => _nativeContext.GetTpidrEl0();
|
||||||
|
set => _nativeContext.SetTpidrEl0(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long TpidrroEl0
|
||||||
|
{
|
||||||
|
get => _nativeContext.GetTpidrroEl0();
|
||||||
|
set => _nativeContext.SetTpidrroEl0(value);
|
||||||
|
}
|
||||||
|
|
||||||
public uint Pstate
|
public uint Pstate
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,8 @@ namespace ARMeilleure.State
|
|||||||
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
|
public fixed ulong V[RegisterConsts.VecRegsCount * 2];
|
||||||
public fixed uint Flags[RegisterConsts.FlagsCount];
|
public fixed uint Flags[RegisterConsts.FlagsCount];
|
||||||
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
|
public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
|
||||||
|
public long TpidrEl0;
|
||||||
|
public long TpidrroEl0;
|
||||||
public int Counter;
|
public int Counter;
|
||||||
public ulong DispatchAddress;
|
public ulong DispatchAddress;
|
||||||
public ulong ExclusiveAddress;
|
public ulong ExclusiveAddress;
|
||||||
@ -168,6 +170,12 @@ namespace ARMeilleure.State
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetTpidrEl0() => GetStorage().TpidrEl0;
|
||||||
|
public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
|
||||||
|
|
||||||
|
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
|
||||||
|
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
|
||||||
|
|
||||||
public int GetCounter() => GetStorage().Counter;
|
public int GetCounter() => GetStorage().Counter;
|
||||||
public void SetCounter(int value) => GetStorage().Counter = value;
|
public void SetCounter(int value) => GetStorage().Counter = value;
|
||||||
|
|
||||||
@ -214,6 +222,16 @@ namespace ARMeilleure.State
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetTpidrEl0Offset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetTpidrroEl0Offset()
|
||||||
|
{
|
||||||
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
|
||||||
|
}
|
||||||
|
|
||||||
public static int GetCounterOffset()
|
public static int GetCounterOffset()
|
||||||
{
|
{
|
||||||
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
|
||||||
|
@ -188,6 +188,21 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnterArmFpMode()
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.EnterArmFpMode(this, InstEmitHelper.GetFpFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateArmFpMode()
|
||||||
|
{
|
||||||
|
EnterArmFpMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitArmFpMode()
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.ExitArmFpMode(this, (flag, value) => InstEmitHelper.SetFpFlag(this, flag, value));
|
||||||
|
}
|
||||||
|
|
||||||
public Operand TryGetComparisonResult(Condition condition)
|
public Operand TryGetComparisonResult(Condition condition)
|
||||||
{
|
{
|
||||||
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
|
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)
|
||||||
|
@ -105,17 +105,11 @@ namespace ARMeilleure.Translation
|
|||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
|
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
|
||||||
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
|
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 4484; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
@ -969,6 +969,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap,
|
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap,
|
||||||
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2,
|
(ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2,
|
||||||
(ulong)Arm64HardwareCapabilities.MacOsFeatureInfo,
|
(ulong)Arm64HardwareCapabilities.MacOsFeatureInfo,
|
||||||
|
0,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
@ -977,11 +978,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
(ulong)X86HardwareCapabilities.FeatureInfo1Ecx,
|
(ulong)X86HardwareCapabilities.FeatureInfo1Ecx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo1Edx,
|
(ulong)X86HardwareCapabilities.FeatureInfo1Edx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo7Ebx,
|
(ulong)X86HardwareCapabilities.FeatureInfo7Ebx,
|
||||||
(ulong)X86HardwareCapabilities.FeatureInfo7Ecx);
|
(ulong)X86HardwareCapabilities.FeatureInfo7Ecx,
|
||||||
|
(ulong)X86HardwareCapabilities.Xcr0InfoEax);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return new FeatureInfo(0, 0, 0, 0);
|
return new FeatureInfo(0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1002,7 +1004,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return osPlatform;
|
return osPlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 78*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
|
||||||
private struct OuterHeader
|
private struct OuterHeader
|
||||||
{
|
{
|
||||||
public ulong Magic;
|
public ulong Magic;
|
||||||
@ -1034,8 +1036,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 32*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 40*/)]
|
||||||
private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3);
|
private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3, ulong FeatureInfo4);
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
|
||||||
private struct InnerHeader
|
private struct InnerHeader
|
||||||
|
@ -25,5 +25,10 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
return _func(context.NativeContextPtr);
|
return _func(context.NativeContextPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong Execute(WrapperFunction dispatcher, State.ExecutionContext context)
|
||||||
|
{
|
||||||
|
return dispatcher(context.NativeContextPtr, (ulong)FuncPointer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -183,7 +183,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
Statistics.StartTimer();
|
Statistics.StartTimer();
|
||||||
|
|
||||||
ulong nextAddr = func.Execute(context);
|
ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
Statistics.StopTimer(address);
|
Statistics.StopTimer(address);
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
|
||||||
|
|
||||||
address = func.Execute(context);
|
address = func.Execute(Stubs.ContextWrapper, context);
|
||||||
|
|
||||||
EnqueueForDeletion(address, func);
|
EnqueueForDeletion(address, func);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ namespace ARMeilleure.Translation
|
|||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
private readonly Lazy<IntPtr> _dispatchStub;
|
private readonly Lazy<IntPtr> _dispatchStub;
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||||
|
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the dispatch stub.
|
/// Gets the dispatch stub.
|
||||||
@ -64,6 +65,20 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the context wrapper function.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
|
||||||
|
public WrapperFunction ContextWrapper
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
return _contextWrapper.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
|
||||||
/// <see cref="Translator"/> instance.
|
/// <see cref="Translator"/> instance.
|
||||||
@ -77,6 +92,7 @@ namespace ARMeilleure.Translation
|
|||||||
_translator = translator;
|
_translator = translator;
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||||
|
_contextWrapper = new(GenerateContextWrapper, isThreadSafe: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -202,6 +218,32 @@ namespace ARMeilleure.Translation
|
|||||||
return Marshal.GetFunctionPointerForDelegate(func);
|
return Marshal.GetFunctionPointerForDelegate(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emits code that syncs FP state before executing guest code, or returns it to normal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Emitter context for the method</param>
|
||||||
|
/// <param name="nativeContext">Pointer to the native context</param>
|
||||||
|
/// <param name="enter">True if entering guest code, false otherwise</param>
|
||||||
|
private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter)
|
||||||
|
{
|
||||||
|
if (enter)
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.EnterArmFpMode(context, (flag) =>
|
||||||
|
{
|
||||||
|
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
|
||||||
|
return context.Load(OperandType.I32, flagAddress);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstEmitSimdHelper.ExitArmFpMode(context, (flag, value) =>
|
||||||
|
{
|
||||||
|
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
|
||||||
|
context.Store(flagAddress, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a <see cref="DispatchLoop"/> function.
|
/// Generates a <see cref="DispatchLoop"/> function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -221,6 +263,8 @@ namespace ARMeilleure.Translation
|
|||||||
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
|
||||||
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
|
||||||
context.MarkLabel(beginLbl);
|
context.MarkLabel(beginLbl);
|
||||||
context.Store(dispatchAddress, guestAddress);
|
context.Store(dispatchAddress, guestAddress);
|
||||||
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
|
||||||
@ -229,6 +273,9 @@ namespace ARMeilleure.Translation
|
|||||||
context.Branch(beginLbl);
|
context.Branch(beginLbl);
|
||||||
|
|
||||||
context.MarkLabel(endLbl);
|
context.MarkLabel(endLbl);
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, false);
|
||||||
|
|
||||||
context.Return();
|
context.Return();
|
||||||
|
|
||||||
var cfg = context.GetControlFlowGraph();
|
var cfg = context.GetControlFlowGraph();
|
||||||
@ -237,5 +284,29 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a <see cref="ContextWrapper"/> function.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see cref="ContextWrapper"/> function</returns>
|
||||||
|
private WrapperFunction GenerateContextWrapper()
|
||||||
|
{
|
||||||
|
var context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
|
||||||
|
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
|
||||||
|
|
||||||
|
EmitSyncFpContext(context, nativeContext, true);
|
||||||
|
Operand returnValue = context.Call(guestMethod, OperandType.I64, nativeContext);
|
||||||
|
EmitSyncFpContext(context, nativeContext, false);
|
||||||
|
|
||||||
|
context.Return(returnValue);
|
||||||
|
|
||||||
|
var cfg = context.GetControlFlowGraph();
|
||||||
|
var retType = OperandType.I64;
|
||||||
|
var argTypes = new[] { OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
|
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
148
ARMeilleure/Translation/TranslatorTestMethods.cs
Normal file
148
ARMeilleure/Translation/TranslatorTestMethods.cs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
using ARMeilleure.CodeGen.X86;
|
||||||
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
|
using ARMeilleure.State;
|
||||||
|
using ARMeilleure.Translation;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Translation
|
||||||
|
{
|
||||||
|
public static class TranslatorTestMethods
|
||||||
|
{
|
||||||
|
public delegate int FpFlagsPInvokeTest(IntPtr managedMethod);
|
||||||
|
|
||||||
|
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
|
||||||
|
{
|
||||||
|
if (Optimizations.UseSse2)
|
||||||
|
{
|
||||||
|
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
|
||||||
|
|
||||||
|
if (ftz)
|
||||||
|
{
|
||||||
|
mxcsr = context.BitwiseOr(mxcsr, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)Mxcsr.Ftz));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (Optimizations.UseAdvSimd)
|
||||||
|
{
|
||||||
|
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
|
||||||
|
|
||||||
|
if (ftz)
|
||||||
|
{
|
||||||
|
fpcr = context.BitwiseOr(fpcr, Const((int)FPCR.Fz));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fpcr = context.BitwiseAnd(fpcr, Const(~(int)FPCR.Fz));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand FpBitsToInt(EmitterContext context, Operand fp)
|
||||||
|
{
|
||||||
|
Operand vec = context.VectorInsert(context.VectorZero(), fp, 0);
|
||||||
|
return context.VectorExtract(OperandType.I32, vec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0));
|
||||||
|
|
||||||
|
// Verify that default dotnet fp state does not flush to zero.
|
||||||
|
// This is required for SoftFloat to function.
|
||||||
|
|
||||||
|
// Denormal + zero != 0
|
||||||
|
|
||||||
|
Operand denormal = ConstF(BitConverter.Int32BitsToSingle(1)); // 1.40129846432e-45
|
||||||
|
Operand zeroF = ConstF(0f);
|
||||||
|
Operand zero = Const(0);
|
||||||
|
|
||||||
|
Operand result = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must not be zero.
|
||||||
|
|
||||||
|
Operand correct1Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(correct1Label, context.ICompareEqual(FpBitsToInt(context, result), zero));
|
||||||
|
|
||||||
|
context.Return(Const(1));
|
||||||
|
|
||||||
|
context.MarkLabel(correct1Label);
|
||||||
|
|
||||||
|
// Set flush to zero flag. If unsupported by the backend, just return true.
|
||||||
|
|
||||||
|
if (!SetPlatformFtz(context, true))
|
||||||
|
{
|
||||||
|
context.Return(Const(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Denormal + zero == 0
|
||||||
|
|
||||||
|
Operand resultFz = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must equal zero.
|
||||||
|
|
||||||
|
Operand correct2Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfTrue(correct2Label, context.ICompareEqual(FpBitsToInt(context, resultFz), zero));
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(2));
|
||||||
|
|
||||||
|
context.MarkLabel(correct2Label);
|
||||||
|
|
||||||
|
// Call a managed method. This method should not change Fz state.
|
||||||
|
|
||||||
|
context.Call(methodAddress, OperandType.None);
|
||||||
|
|
||||||
|
// Denormal + zero == 0
|
||||||
|
|
||||||
|
Operand resultFz2 = context.Add(zeroF, denormal);
|
||||||
|
|
||||||
|
// Must equal zero.
|
||||||
|
|
||||||
|
Operand correct3Label = Label();
|
||||||
|
|
||||||
|
context.BranchIfTrue(correct3Label, context.ICompareEqual(FpBitsToInt(context, resultFz2), zero));
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(3));
|
||||||
|
|
||||||
|
context.MarkLabel(correct3Label);
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
|
||||||
|
SetPlatformFtz(context, false);
|
||||||
|
|
||||||
|
context.Return(Const(0));
|
||||||
|
|
||||||
|
// Compile and return the function.
|
||||||
|
|
||||||
|
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||||
|
|
||||||
|
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,17 +3,17 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.12.11" />
|
<PackageVersion Include="DynamicData" Version="7.13.5" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||||
@ -44,10 +44,10 @@
|
|||||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.29.0" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
<PackageVersion Include="System.Management" Version="7.0.1" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
As of April 2023, Ryujinx has been tested on approximately 4,050 titles; over 4,000 boot past menus and into gameplay, with roughly 3,400 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -18,6 +18,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
private ManualResetEvent _updateRequiredEvent;
|
||||||
private uint _outputStream;
|
private uint _outputStream;
|
||||||
|
private bool _hasSetupError;
|
||||||
private SDL_AudioCallback _callbackDelegate;
|
private SDL_AudioCallback _callbackDelegate;
|
||||||
private int _bytesPerFrame;
|
private int _bytesPerFrame;
|
||||||
private uint _sampleCount;
|
private uint _sampleCount;
|
||||||
@ -42,7 +43,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||||
{
|
{
|
||||||
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
uint bufferSampleCount = (uint)GetSampleCount(buffer);
|
||||||
bool needAudioSetup = _outputStream == 0 ||
|
bool needAudioSetup = (_outputStream == 0 && !_hasSetupError) ||
|
||||||
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
(bufferSampleCount >= Constants.TargetSampleCount && bufferSampleCount < _sampleCount);
|
||||||
|
|
||||||
if (needAudioSetup)
|
if (needAudioSetup)
|
||||||
@ -51,12 +52,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
|
|
||||||
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate);
|
||||||
|
|
||||||
if (newOutputStream == 0)
|
_hasSetupError = newOutputStream == 0;
|
||||||
{
|
|
||||||
// No stream in place, this is unexpected.
|
if (!_hasSetupError)
|
||||||
throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\"");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (_outputStream != 0)
|
if (_outputStream != 0)
|
||||||
{
|
{
|
||||||
@ -151,11 +149,20 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
{
|
{
|
||||||
EnsureAudioStreamSetup(buffer);
|
EnsureAudioStreamSetup(buffer);
|
||||||
|
|
||||||
SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
|
if (_outputStream != 0)
|
||||||
|
{
|
||||||
|
SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer));
|
||||||
|
|
||||||
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
|
||||||
|
|
||||||
_queuedBuffers.Enqueue(driverBuffer);
|
_queuedBuffers.Enqueue(driverBuffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Interlocked.Add(ref _playedSampleCount, GetSampleCount(buffer));
|
||||||
|
|
||||||
|
_updateRequiredEvent.Set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetVolume(float volume)
|
public override void SetVolume(float volume)
|
||||||
|
@ -177,6 +177,8 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
|
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,14 +322,16 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
_viewModel.IsGameRunning = true;
|
_viewModel.IsGameRunning = true;
|
||||||
|
|
||||||
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty : $" - {Device.Application.TitleName}";
|
var activeProcess = Device.Processes.ActiveApplication;
|
||||||
string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty : $" v{Device.Application.DisplayVersion}";
|
|
||||||
string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty : $" ({Device.Application.TitleIdText.ToUpper()})";
|
string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}";
|
||||||
string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)";
|
string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}";
|
||||||
|
string titleIdSection = $" ({activeProcess.ProgramIdText.ToUpper()})";
|
||||||
|
string titleArchSection = activeProcess.Is64Bit ? " (64-bit)" : " (32-bit)";
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
_viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
_viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
||||||
});
|
});
|
||||||
|
|
||||||
_viewModel.SetUIProgressHandlers(Device);
|
_viewModel.SetUIProgressHandlers(Device);
|
||||||
@ -381,6 +385,11 @@ namespace Ryujinx.Ava
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLanInterfaceIdState(object sender, ReactiveEventArgs<string> e)
|
||||||
|
{
|
||||||
|
Device.Configuration.MultiplayerLanInterfaceId = e.NewValue;
|
||||||
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
@ -423,9 +432,9 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
private void Dispose()
|
private void Dispose()
|
||||||
{
|
{
|
||||||
if (Device.Application != null)
|
if (Device.Processes != null)
|
||||||
{
|
{
|
||||||
_viewModel.UpdateGameMetadata(Device.Application.TitleIdText);
|
_viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
||||||
@ -539,7 +548,12 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
|
Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
|
||||||
|
|
||||||
Device.LoadNca(ApplicationPath);
|
if (!Device.LoadNca(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (Directory.Exists(ApplicationPath))
|
else if (Directory.Exists(ApplicationPath))
|
||||||
{
|
{
|
||||||
@ -554,13 +568,23 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS.");
|
Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS.");
|
||||||
|
|
||||||
Device.LoadCart(ApplicationPath, romFsFiles[0]);
|
if (!Device.LoadCart(ApplicationPath, romFsFiles[0]))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
||||||
|
|
||||||
Device.LoadCart(ApplicationPath);
|
if (!Device.LoadCart(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (File.Exists(ApplicationPath))
|
else if (File.Exists(ApplicationPath))
|
||||||
@ -571,7 +595,12 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
|
Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
|
||||||
|
|
||||||
Device.LoadXci(ApplicationPath);
|
if (!Device.LoadXci(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -579,7 +608,12 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
|
Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
|
||||||
|
|
||||||
Device.LoadNca(ApplicationPath);
|
if (!Device.LoadNca(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -588,7 +622,12 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
|
Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
|
||||||
|
|
||||||
Device.LoadNsp(ApplicationPath);
|
if (!Device.LoadNsp(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -598,13 +637,18 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Device.LoadProgram(ApplicationPath);
|
if (!Device.LoadProgram(ApplicationPath))
|
||||||
|
{
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentOutOfRangeException)
|
catch (ArgumentOutOfRangeException)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx.");
|
Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx.");
|
||||||
|
|
||||||
Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -617,14 +661,14 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||||
|
|
||||||
Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName);
|
DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name);
|
||||||
|
|
||||||
_viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata =>
|
_viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata =>
|
||||||
{
|
{
|
||||||
appMetadata.LastPlayed = DateTime.UtcNow.ToString();
|
appMetadata.LastPlayed = DateTime.UtcNow.ToString();
|
||||||
});
|
});
|
||||||
@ -702,7 +746,8 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||||
ConfigurationState.Instance.System.AudioVolume,
|
ConfigurationState.Instance.System.AudioVolume,
|
||||||
ConfigurationState.Instance.System.UseHypervisor);
|
ConfigurationState.Instance.System.UseHypervisor,
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
|
||||||
|
|
||||||
Device = new Switch(configuration);
|
Device = new Switch(configuration);
|
||||||
}
|
}
|
||||||
@ -950,7 +995,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen)
|
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
Device.Application.DiskCacheLoadState?.Cancel();
|
Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1088,4 +1133,4 @@ namespace Ryujinx.Ava
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,6 +429,7 @@
|
|||||||
"DlcManagerEnableAllButton": "Enable All",
|
"DlcManagerEnableAllButton": "Enable All",
|
||||||
"DlcManagerDisableAllButton": "Disable All",
|
"DlcManagerDisableAllButton": "Disable All",
|
||||||
"MenuBarOptionsChangeLanguage": "Change Language",
|
"MenuBarOptionsChangeLanguage": "Change Language",
|
||||||
|
"MenuBarShowFileTypes": "Show File Types",
|
||||||
"CommonSort": "Sort",
|
"CommonSort": "Sort",
|
||||||
"CommonShowNames": "Show Names",
|
"CommonShowNames": "Show Names",
|
||||||
"CommonFavorite": "Favorite",
|
"CommonFavorite": "Favorite",
|
||||||
@ -637,5 +638,8 @@
|
|||||||
"SmaaHigh": "SMAA High",
|
"SmaaHigh": "SMAA High",
|
||||||
"SmaaUltra": "SMAA Ultra",
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle" : "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User"
|
"UserEditorTitleCreate" : "Create User",
|
||||||
|
"SettingsTabNetworkInterface": "Network Interface:",
|
||||||
|
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||||
|
"NetworkInterfaceDefault": "Default"
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,14 @@ using LibHac.Tools.Fs;
|
|||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
@ -151,25 +153,17 @@ namespace Ryujinx.Ava.Common
|
|||||||
string destination = await folderDialog.ShowAsync(_owner);
|
string destination = await folderDialog.ShowAsync(_owner);
|
||||||
var cancellationToken = new CancellationTokenSource();
|
var cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
UpdateWaitWindow waitingDialog = new(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(destination))
|
if (!string.IsNullOrWhiteSpace(destination))
|
||||||
{
|
{
|
||||||
Thread extractorThread = new(() =>
|
Thread extractorThread = new(() =>
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(waitingDialog.Show);
|
||||||
{
|
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
|
||||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
|
|
||||||
|
|
||||||
if (result == UserResult.Cancel)
|
|
||||||
{
|
|
||||||
cancellationToken.Cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
Nca mainNca = null;
|
Nca mainNca = null;
|
||||||
@ -221,13 +215,15 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
{
|
{
|
||||||
patchNca = updatePatchNca;
|
patchNca = updatePatchNca;
|
||||||
@ -262,11 +258,15 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (resultCode.Value.IsSuccess())
|
else if (resultCode.Value.IsSuccess())
|
||||||
{
|
{
|
||||||
|
Dispatcher.UIThread.Post(waitingDialog.Close);
|
||||||
|
|
||||||
NotificationHelper.Show(
|
NotificationHelper.Show(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||||
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
||||||
@ -283,6 +283,8 @@ namespace Ryujinx.Ava.Common
|
|||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
waitingDialog.Close();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
{
|
{
|
||||||
var localeStrings = new Dictionary<LocaleKeys, string>();
|
var localeStrings = new Dictionary<LocaleKeys, string>();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
||||||
{
|
{
|
||||||
|
@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using ICSharpCode.SharpZipLib.GZip;
|
using ICSharpCode.SharpZipLib.GZip;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Ryujinx.Ava;
|
using Ryujinx.Ava;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
using Ryujinx.Ui.Common.Models.Github;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -31,6 +32,7 @@ namespace Ryujinx.Modules
|
|||||||
internal static class Updater
|
internal static class Updater
|
||||||
{
|
{
|
||||||
private const string GitHubApiURL = "https://api.github.com";
|
private const string GitHubApiURL = "https://api.github.com";
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||||
@ -99,22 +101,16 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
|
||||||
JObject jsonRoot = JObject.Parse(fetchedJson);
|
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
|
||||||
JToken assets = jsonRoot["assets"];
|
_buildVer = fetched.Name;
|
||||||
|
|
||||||
_buildVer = (string)jsonRoot["name"];
|
foreach (var asset in fetched.Assets)
|
||||||
|
|
||||||
foreach (JToken asset in assets)
|
|
||||||
{
|
{
|
||||||
string assetName = (string)asset["name"];
|
if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
string assetState = (string)asset["state"];
|
|
||||||
string downloadURL = (string)asset["browser_download_url"];
|
|
||||||
|
|
||||||
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
|
|
||||||
{
|
{
|
||||||
_buildUrl = downloadURL;
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
if (assetState != "uploaded")
|
if (asset.State != "uploaded")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
@ -734,7 +730,7 @@ namespace Ryujinx.Modules
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
private static void MoveAllFilesOver(string root, string dest, TaskDialog taskDialog)
|
||||||
|
@ -53,6 +53,8 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool opened = false;
|
bool opened = false;
|
||||||
|
|
||||||
|
_parent.Activate();
|
||||||
|
|
||||||
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Focusable="True"
|
||||||
KeyUp="Message_KeyUp"
|
KeyUp="Message_KeyUp"
|
||||||
Text="{Binding Message}"
|
Text="{Binding Message}"
|
||||||
TextInput="Message_TextInput"
|
TextInput="Message_TextInput"
|
||||||
@ -61,4 +62,4 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -23,10 +23,11 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
private ContentDialog _host;
|
private ContentDialog _host;
|
||||||
|
|
||||||
public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder)
|
public SwkbdAppletDialog(string mainText, string secondaryText, string placeholder, string message)
|
||||||
{
|
{
|
||||||
MainText = mainText;
|
MainText = mainText;
|
||||||
SecondaryText = secondaryText;
|
SecondaryText = secondaryText;
|
||||||
|
Message = message ?? "";
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
_placeholder = placeholder;
|
_placeholder = placeholder;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -44,6 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||||
|
{
|
||||||
|
// FIXME: This does not work. Might be a bug in Avalonia with DialogHost
|
||||||
|
// Currently focus will be redirected to the overlay window instead.
|
||||||
|
Input.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
public string Message { get; set; } = "";
|
public string Message { get; set; } = "";
|
||||||
public string MainText { get; set; } = "";
|
public string MainText { get; set; } = "";
|
||||||
public string SecondaryText { get; set; } = "";
|
public string SecondaryText { get; set; } = "";
|
||||||
@ -54,31 +62,10 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
UserResult result = UserResult.Cancel;
|
UserResult result = UserResult.Cancel;
|
||||||
|
|
||||||
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText)
|
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
|
||||||
{
|
|
||||||
Message = args.InitialText ?? ""
|
|
||||||
};
|
|
||||||
|
|
||||||
string input = string.Empty;
|
string input = string.Empty;
|
||||||
|
|
||||||
var overlay = new ContentDialogOverlayWindow()
|
|
||||||
{
|
|
||||||
Height = window.Bounds.Height,
|
|
||||||
Width = window.Bounds.Width,
|
|
||||||
Position = window.PointToScreen(new Point())
|
|
||||||
};
|
|
||||||
|
|
||||||
window.PositionChanged += OverlayOnPositionChanged;
|
|
||||||
|
|
||||||
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
|
|
||||||
{
|
|
||||||
overlay.Position = window.PointToScreen(new Point());
|
|
||||||
}
|
|
||||||
|
|
||||||
contentDialog = overlay.ContentDialog;
|
|
||||||
|
|
||||||
bool opened = false;
|
|
||||||
|
|
||||||
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||||
|
|
||||||
content._host = contentDialog;
|
content._host = contentDialog;
|
||||||
@ -99,25 +86,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
};
|
};
|
||||||
contentDialog.Closed += handler;
|
contentDialog.Closed += handler;
|
||||||
|
|
||||||
overlay.Opened += OverlayOnActivated;
|
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||||
|
|
||||||
async void OverlayOnActivated(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (opened)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
opened = true;
|
|
||||||
|
|
||||||
overlay.Position = window.PointToScreen(new Point());
|
|
||||||
|
|
||||||
await contentDialog.ShowAsync(overlay);
|
|
||||||
contentDialog.Closed -= handler;
|
|
||||||
overlay.Close();
|
|
||||||
};
|
|
||||||
|
|
||||||
await overlay.ShowDialog(window);
|
|
||||||
|
|
||||||
return (result, input);
|
return (result, input);
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
<UserControl
|
|
||||||
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
|
|
||||||
xmlns="https://github.com/avaloniaui"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Focusable="True">
|
|
||||||
<Grid
|
|
||||||
Margin="5,10,5,5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
|
|
||||||
<TextBox
|
|
||||||
Grid.Row="1"
|
|
||||||
Width="300"
|
|
||||||
Margin="10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
MaxLength="{Binding MaxLength}"
|
|
||||||
Text="{Binding Input, Mode=TwoWay}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="5,5,5,10"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Text="{Binding SubMessage}" />
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
@ -1,57 +0,0 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using FluentAvalonia.UI.Controls;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
|
||||||
{
|
|
||||||
public partial class InputDialog : UserControl
|
|
||||||
{
|
|
||||||
public string Message { get; set; }
|
|
||||||
public string Input { get; set; }
|
|
||||||
public string SubMessage { get; set; }
|
|
||||||
|
|
||||||
public uint MaxLength { get; }
|
|
||||||
|
|
||||||
public InputDialog(string message, string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
Input = input;
|
|
||||||
SubMessage = subMessage;
|
|
||||||
MaxLength = maxLength;
|
|
||||||
|
|
||||||
DataContext = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputDialog()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, string message,
|
|
||||||
string input = "", string subMessage = "", uint maxLength = int.MaxValue)
|
|
||||||
{
|
|
||||||
UserResult result = UserResult.Cancel;
|
|
||||||
|
|
||||||
InputDialog content = new InputDialog(message, input, subMessage, maxLength);
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
|
||||||
SecondaryButtonText = "",
|
|
||||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel],
|
|
||||||
Content = content,
|
|
||||||
PrimaryButtonCommand = MiniCommand.Create(() =>
|
|
||||||
{
|
|
||||||
result = UserResult.Ok;
|
|
||||||
input = content.Input;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
await contentDialog.ShowAsync();
|
|
||||||
|
|
||||||
return (result, input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +1,26 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
{
|
{
|
||||||
public partial class UpdateWaitWindow : StyleableWindow
|
public partial class UpdateWaitWindow : StyleableWindow
|
||||||
{
|
{
|
||||||
|
public UpdateWaitWindow(string primaryText, string secondaryText, CancellationTokenSource cancellationToken) : this(primaryText, secondaryText)
|
||||||
|
{
|
||||||
|
SystemDecorations = SystemDecorations.Full;
|
||||||
|
ShowInTaskbar = true;
|
||||||
|
|
||||||
|
Closing += (_, _) => cancellationToken.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
public UpdateWaitWindow(string primaryText, string secondaryText) : this()
|
public UpdateWaitWindow(string primaryText, string secondaryText) : this()
|
||||||
{
|
{
|
||||||
PrimaryText.Text = primaryText;
|
PrimaryText.Text = primaryText;
|
||||||
SecondaryText.Text = secondaryText;
|
SecondaryText.Text = secondaryText;
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
SystemDecorations = SystemDecorations.BorderOnly;
|
||||||
|
ShowInTaskbar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateWaitWindow()
|
public UpdateWaitWindow()
|
||||||
|
@ -27,7 +27,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
string closeButton,
|
string closeButton,
|
||||||
UserResult primaryButtonResult = UserResult.Ok,
|
UserResult primaryButtonResult = UserResult.Ok,
|
||||||
ManualResetEvent deferResetEvent = null,
|
ManualResetEvent deferResetEvent = null,
|
||||||
Func<Window, Task> doWhileDeferred = null,
|
|
||||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||||
{
|
{
|
||||||
UserResult result = UserResult.None;
|
UserResult result = UserResult.None;
|
||||||
@ -78,12 +77,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
int iconSymbol,
|
int iconSymbol,
|
||||||
UserResult primaryButtonResult = UserResult.Ok,
|
UserResult primaryButtonResult = UserResult.Ok,
|
||||||
ManualResetEvent deferResetEvent = null,
|
ManualResetEvent deferResetEvent = null,
|
||||||
Func<Window, Task> doWhileDeferred = null,
|
|
||||||
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
|
||||||
{
|
{
|
||||||
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
|
||||||
|
|
||||||
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction);
|
return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, deferCloseAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async static Task<UserResult> ShowDeferredContentDialog(
|
public async static Task<UserResult> ShowDeferredContentDialog(
|
||||||
@ -111,7 +109,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
iconSymbol,
|
iconSymbol,
|
||||||
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
|
primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok,
|
||||||
deferResetEvent,
|
deferResetEvent,
|
||||||
doWhileDeferred,
|
|
||||||
DeferClose);
|
DeferClose);
|
||||||
|
|
||||||
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
async void DeferClose(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||||
@ -236,11 +233,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
primaryButtonResult);
|
primaryButtonResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static UpdateWaitWindow CreateWaitingDialog(string mainText, string secondaryText)
|
|
||||||
{
|
|
||||||
return new(mainText, secondaryText);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
|
||||||
{
|
{
|
||||||
await ShowTextDialog(
|
await ShowTextDialog(
|
||||||
@ -319,28 +311,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogExitSubMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static async Task<string> CreateInputDialog(
|
|
||||||
string title,
|
|
||||||
string mainText,
|
|
||||||
string subText,
|
|
||||||
uint maxLength = int.MaxValue,
|
|
||||||
string input = "")
|
|
||||||
{
|
|
||||||
var result = await InputDialog.ShowInputDialog(
|
|
||||||
title,
|
|
||||||
mainText,
|
|
||||||
input,
|
|
||||||
subText,
|
|
||||||
maxLength);
|
|
||||||
|
|
||||||
if (result.Result == UserResult.Ok)
|
|
||||||
{
|
|
||||||
return result.Input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
|
||||||
{
|
{
|
||||||
ContentDialogResult result;
|
ContentDialogResult result;
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models
|
|
||||||
{
|
|
||||||
public class Amiibo
|
|
||||||
{
|
|
||||||
public struct AmiiboJson
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
|
|
||||||
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AmiiboApi
|
|
||||||
{
|
|
||||||
[JsonPropertyName("name")] public string Name { get; set; }
|
|
||||||
[JsonPropertyName("head")] public string Head { get; set; }
|
|
||||||
[JsonPropertyName("tail")] public string Tail { get; set; }
|
|
||||||
[JsonPropertyName("image")] public string Image { get; set; }
|
|
||||||
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
|
|
||||||
[JsonPropertyName("character")] public string Character { get; set; }
|
|
||||||
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
|
|
||||||
[JsonPropertyName("type")] public string Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetId()
|
|
||||||
{
|
|
||||||
return Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is AmiiboApi amiibo)
|
|
||||||
{
|
|
||||||
return amiibo.Head + amiibo.Tail == Head + Tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiGamesSwitch
|
|
||||||
{
|
|
||||||
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("gameName")] public string GameName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AmiiboApiUsage
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Usage")] public string Usage { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("write")] public bool Write { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
|
||||||
|
|
||||||
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n";
|
Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n";
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -4,11 +4,11 @@ using Avalonia.Media.Imaging;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@ -17,6 +17,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private readonly StyleableWindow _owner;
|
private readonly StyleableWindow _owner;
|
||||||
|
|
||||||
private Bitmap _amiiboImage;
|
private Bitmap _amiiboImage;
|
||||||
private List<Amiibo.AmiiboApi> _amiiboList;
|
private List<AmiiboApi> _amiiboList;
|
||||||
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;
|
private AvaloniaList<AmiiboApi> _amiibos;
|
||||||
private ObservableCollection<string> _amiiboSeries;
|
private ObservableCollection<string> _amiiboSeries;
|
||||||
|
|
||||||
private int _amiiboSelectedIndex;
|
private int _amiiboSelectedIndex;
|
||||||
@ -41,6 +42,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _showAllAmiibo;
|
private bool _showAllAmiibo;
|
||||||
private bool _useRandomUuid;
|
private bool _useRandomUuid;
|
||||||
private string _usage;
|
private string _usage;
|
||||||
|
|
||||||
|
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
|
||||||
{
|
{
|
||||||
@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
|
||||||
|
|
||||||
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
|
||||||
_amiiboList = new List<Amiibo.AmiiboApi>();
|
_amiiboList = new List<AmiiboApi>();
|
||||||
_amiiboSeries = new ObservableCollection<string>();
|
_amiiboSeries = new ObservableCollection<string>();
|
||||||
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>();
|
_amiibos = new AvaloniaList<AmiiboApi>();
|
||||||
|
|
||||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
||||||
|
|
||||||
@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList
|
public AvaloniaList<AmiiboApi> AmiiboList
|
||||||
{
|
{
|
||||||
get => _amiibos;
|
get => _amiibos;
|
||||||
set
|
set
|
||||||
@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (File.Exists(_amiiboJsonPath))
|
if (File.Exists(_amiiboJsonPath))
|
||||||
{
|
{
|
||||||
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!ShowAllAmiibo)
|
if (!ShowAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private void SelectLastScannedAmiibo()
|
private void SelectLastScannedAmiibo()
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
|
||||||
|
|
||||||
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
|
||||||
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
|
||||||
@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList
|
List<AmiiboApi> amiiboSortedList = _amiiboList
|
||||||
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
|
||||||
.OrderBy(amiibo => amiibo.Name).ToList();
|
.OrderBy(amiibo => amiibo.Name).ToList();
|
||||||
|
|
||||||
@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
if (!_showAllAmiibo)
|
if (!_showAllAmiibo)
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
|
||||||
|
|
||||||
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
|
||||||
|
|
||||||
@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
bool writable = false;
|
bool writable = false;
|
||||||
|
|
||||||
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
|
||||||
{
|
{
|
||||||
if (item.GameId.Contains(TitleId))
|
if (item.GameId.Contains(TitleId))
|
||||||
{
|
{
|
||||||
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage)
|
foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
|
||||||
{
|
{
|
||||||
usageString += Environment.NewLine +
|
usageString += Environment.NewLine +
|
||||||
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";
|
||||||
|
@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
private readonly UserControl _owner;
|
private readonly UserControl _owner;
|
||||||
|
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
public IGamepadDriver AvaloniaKeyboardDriver { get; }
|
||||||
public IGamepad SelectedGamepad { get; private set; }
|
public IGamepad SelectedGamepad { get; private set; }
|
||||||
|
|
||||||
@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Stream stream = File.OpenRead(path))
|
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = JsonHelper.Deserialize<InputConfig>(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException) { }
|
catch (JsonException) { }
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, true);
|
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private ulong _titleId;
|
private ulong _titleId;
|
||||||
private string _titleName;
|
private string _titleName;
|
||||||
|
|
||||||
|
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
||||||
{
|
{
|
||||||
get => _downloadableContents;
|
get => _downloadableContents;
|
||||||
@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
|
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
_downloadableContentContainerList.Add(container);
|
_downloadableContentContainerList.Add(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
|
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
|
||||||
{
|
|
||||||
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -972,7 +972,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||||
|
|
||||||
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
|
UpdateWaitWindow waitingDialog = new(dialogTitle, LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallWaitMessage]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
@ -1208,10 +1208,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public void SetUIProgressHandlers(Switch emulationContext)
|
public void SetUIProgressHandlers(Switch emulationContext)
|
||||||
{
|
{
|
||||||
if (emulationContext.Application.DiskCacheLoadState != null)
|
if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null)
|
||||||
{
|
{
|
||||||
emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler;
|
emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler;
|
||||||
emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler;
|
emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler;
|
emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler;
|
||||||
@ -1336,6 +1336,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleFileType(string fileType)
|
||||||
|
{
|
||||||
|
_ = fileType switch
|
||||||
|
{
|
||||||
|
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
|
||||||
|
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
|
||||||
|
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
|
||||||
|
"NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
|
||||||
|
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
|
||||||
|
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(fileType),
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
LoadApplications();
|
||||||
|
}
|
||||||
|
|
||||||
public async void ManageProfiles()
|
public async void ManageProfiles()
|
||||||
{
|
{
|
||||||
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient);
|
||||||
@ -1705,8 +1722,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
{
|
{
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Application.TitleName);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
|
||||||
TitleName = AppHost.Device.Application.TitleName;
|
TitleName = AppHost.Device.Processes.ActiveApplication.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchToRenderer(startFullscreen);
|
SwitchToRenderer(startFullscreen);
|
||||||
|
@ -23,6 +23,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
@ -35,6 +36,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
private readonly List<string> _validTzRegions;
|
private readonly List<string> _validTzRegions;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, string> _networkInterfaces;
|
||||||
|
|
||||||
private float _customResolutionScale;
|
private float _customResolutionScale;
|
||||||
private int _resolutionScale;
|
private int _resolutionScale;
|
||||||
private int _graphicsBackendMultithreadingIndex;
|
private int _graphicsBackendMultithreadingIndex;
|
||||||
@ -50,6 +53,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
|
private int _networkInterfaceIndex;
|
||||||
|
|
||||||
public int ResolutionScale
|
public int ResolutionScale
|
||||||
{
|
{
|
||||||
@ -240,6 +244,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public AvaloniaList<string> GameDirectories { get; set; }
|
public AvaloniaList<string> GameDirectories { get; set; }
|
||||||
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
|
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
|
||||||
|
|
||||||
|
public AvaloniaList<string> NetworkInterfaceList
|
||||||
|
{
|
||||||
|
get => new AvaloniaList<string>(_networkInterfaces.Keys);
|
||||||
|
}
|
||||||
|
|
||||||
public KeyboardHotkeys KeyboardHotkeys
|
public KeyboardHotkeys KeyboardHotkeys
|
||||||
{
|
{
|
||||||
get => _keyboardHotkeys;
|
get => _keyboardHotkeys;
|
||||||
@ -251,6 +260,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int NetworkInterfaceIndex
|
||||||
|
{
|
||||||
|
get => _networkInterfaceIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_networkInterfaceIndex = value != -1 ? value : 0;
|
||||||
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
@ -267,8 +286,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
TimeZones = new AvaloniaList<TimeZone>();
|
TimeZones = new AvaloniaList<TimeZone>();
|
||||||
AvailableGpus = new ObservableCollection<ComboBoxItem>();
|
AvailableGpus = new ObservableCollection<ComboBoxItem>();
|
||||||
_validTzRegions = new List<string>();
|
_validTzRegions = new List<string>();
|
||||||
|
_networkInterfaces = new Dictionary<string, string>();
|
||||||
|
|
||||||
CheckSoundBackends();
|
CheckSoundBackends();
|
||||||
|
PopulateNetworkInterfaces();
|
||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
@ -327,6 +348,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PopulateNetworkInterfaces()
|
||||||
|
{
|
||||||
|
_networkInterfaces.Clear();
|
||||||
|
_networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");
|
||||||
|
|
||||||
|
foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
|
||||||
|
{
|
||||||
|
_networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ValidateAndSetTimeZone(string location)
|
public void ValidateAndSetTimeZone(string location)
|
||||||
{
|
{
|
||||||
if (_validTzRegions.Contains(location))
|
if (_validTzRegions.Contains(location))
|
||||||
@ -414,6 +446,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
||||||
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
||||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||||
|
|
||||||
|
NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(config.Multiplayer.LanInterfaceId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveSettings()
|
public void SaveSettings()
|
||||||
@ -515,6 +549,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
||||||
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
||||||
|
|
||||||
|
config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
|
||||||
|
|
||||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
@ -17,234 +17,236 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
|
using Ryujinx.Ui.App.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels;
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public class TitleUpdateViewModel : BaseModel
|
|
||||||
{
|
{
|
||||||
public TitleUpdateMetadata _titleUpdateWindowData;
|
public class TitleUpdateViewModel : BaseModel
|
||||||
public readonly string _titleUpdateJsonPath;
|
|
||||||
private VirtualFileSystem _virtualFileSystem { get; }
|
|
||||||
private ulong _titleId { get; }
|
|
||||||
private string _titleName { get; }
|
|
||||||
|
|
||||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
|
||||||
private AvaloniaList<object> _views = new();
|
|
||||||
private object _selectedUpdate;
|
|
||||||
|
|
||||||
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
|
||||||
{
|
{
|
||||||
get => _titleUpdates;
|
public TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
set
|
public readonly string _titleUpdateJsonPath;
|
||||||
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
|
private ulong _titleId { get; }
|
||||||
|
private string _titleName { get; }
|
||||||
|
|
||||||
|
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||||
|
private AvaloniaList<object> _views = new();
|
||||||
|
private object _selectedUpdate;
|
||||||
|
|
||||||
|
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
|
public AvaloniaList<TitleUpdateModel> TitleUpdates
|
||||||
{
|
{
|
||||||
_titleUpdates = value;
|
get => _titleUpdates;
|
||||||
OnPropertyChanged();
|
set
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AvaloniaList<object> Views
|
|
||||||
{
|
|
||||||
get => _views;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_views = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public object SelectedUpdate
|
|
||||||
{
|
|
||||||
get => _selectedUpdate;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_selectedUpdate = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
|
||||||
{
|
|
||||||
_virtualFileSystem = virtualFileSystem;
|
|
||||||
|
|
||||||
_titleId = titleId;
|
|
||||||
_titleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
|
||||||
|
|
||||||
_titleUpdateWindowData = new TitleUpdateMetadata
|
|
||||||
{
|
{
|
||||||
Selected = "",
|
_titleUpdates = value;
|
||||||
Paths = new List<string>()
|
OnPropertyChanged();
|
||||||
};
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadUpdates()
|
|
||||||
{
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
|
||||||
{
|
|
||||||
AddUpdate(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
|
||||||
|
|
||||||
SelectedUpdate = selected;
|
|
||||||
|
|
||||||
// NOTE: Save the list again to remove leftovers.
|
|
||||||
Save();
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortUpdates()
|
|
||||||
{
|
|
||||||
var list = TitleUpdates.ToList();
|
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
Views.Clear();
|
|
||||||
Views.Add(new BaseModel());
|
|
||||||
Views.AddRange(list);
|
|
||||||
|
|
||||||
if (SelectedUpdate == null)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
|
||||||
else if (!TitleUpdates.Contains(SelectedUpdate))
|
|
||||||
{
|
|
||||||
if (Views.Count > 1)
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedUpdate = Views[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void AddUpdate(string path)
|
public AvaloniaList<object> Views
|
||||||
{
|
|
||||||
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
|
||||||
{
|
{
|
||||||
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
get => _views;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_views = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object SelectedUpdate
|
||||||
|
{
|
||||||
|
get => _selectedUpdate;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedUpdate = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
|
{
|
||||||
|
_virtualFileSystem = virtualFileSystem;
|
||||||
|
|
||||||
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
|
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
_titleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
ApplicationControlProperty controlData = new();
|
Selected = "",
|
||||||
|
Paths = new List<string>()
|
||||||
|
};
|
||||||
|
|
||||||
using UniqueRef<IFile> nacpFile = new();
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
LoadUpdates();
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
}
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
private void LoadUpdates()
|
||||||
|
{
|
||||||
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
|
{
|
||||||
|
AddUpdate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
|
||||||
|
|
||||||
|
SelectedUpdate = selected;
|
||||||
|
|
||||||
|
// NOTE: Save the list again to remove leftovers.
|
||||||
|
Save();
|
||||||
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortUpdates()
|
||||||
|
{
|
||||||
|
var list = TitleUpdates.ToList();
|
||||||
|
|
||||||
|
list.Sort((first, second) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
Views.Clear();
|
||||||
|
Views.Add(new BaseModel());
|
||||||
|
Views.AddRange(list);
|
||||||
|
|
||||||
|
if (SelectedUpdate == null)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
|
}
|
||||||
|
else if (!TitleUpdates.Contains(SelectedUpdate))
|
||||||
|
{
|
||||||
|
if (Views.Count > 1)
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
SelectedUpdate = Views[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddUpdate(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
|
||||||
|
{
|
||||||
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||||
|
|
||||||
|
if (controlNca != null && patchNca != null)
|
||||||
|
{
|
||||||
|
ApplicationControlProperty controlData = new();
|
||||||
|
|
||||||
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
|
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
|
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Post(async () =>
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveUpdate(TitleUpdateModel update)
|
public void RemoveUpdate(TitleUpdateModel update)
|
||||||
{
|
|
||||||
TitleUpdates.Remove(update);
|
|
||||||
|
|
||||||
SortUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Add()
|
|
||||||
{
|
|
||||||
OpenFileDialog dialog = new()
|
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
TitleUpdates.Remove(update);
|
||||||
AllowMultiple = true
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.Filters.Add(new FileDialogFilter
|
SortUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Add()
|
||||||
{
|
{
|
||||||
Name = "NSP",
|
OpenFileDialog dialog = new()
|
||||||
Extensions = { "nsp" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
|
||||||
|
|
||||||
if (files != null)
|
|
||||||
{
|
{
|
||||||
foreach (string file in files)
|
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
|
||||||
|
AllowMultiple = true
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog.Filters.Add(new FileDialogFilter
|
||||||
|
{
|
||||||
|
Name = "NSP",
|
||||||
|
Extensions = { "nsp" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
string[] files = await dialog.ShowAsync(desktop.MainWindow);
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
{
|
{
|
||||||
AddUpdate(file);
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
AddUpdate(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SortUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
SortUpdates();
|
public void Save()
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
_titleUpdateWindowData.Paths.Clear();
|
|
||||||
_titleUpdateWindowData.Selected = "";
|
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in TitleUpdates)
|
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
_titleUpdateWindowData.Paths.Clear();
|
||||||
|
_titleUpdateWindowData.Selected = "";
|
||||||
|
|
||||||
if (update == SelectedUpdate)
|
foreach (TitleUpdateModel update in TitleUpdates)
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData.Selected = update.Path;
|
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
|
if (update == SelectedUpdate)
|
||||||
|
{
|
||||||
|
_titleUpdateWindowData.Selected = update.Path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -78,6 +78,7 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
|
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
|
||||||
|
<MenuItem Name="ToggleFileTypesMenuItem" Header="{locale:Locale MenuBarShowFileTypes}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenSettings"
|
Click="OpenSettings"
|
||||||
|
@ -11,6 +11,8 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
|
using Ryujinx.Ui.Common;
|
||||||
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -29,6 +31,30 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
ToggleFileTypesMenuItem.Items = GenerateToggleFileTypeItems();
|
||||||
|
ChangeLanguageMenuItem.Items = GenerateLanguageMenuItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CheckBox[] GenerateToggleFileTypeItems()
|
||||||
|
{
|
||||||
|
List<CheckBox> checkBoxes = new();
|
||||||
|
|
||||||
|
foreach (var item in Enum.GetValues(typeof (FileTypes)))
|
||||||
|
{
|
||||||
|
string fileName = Enum.GetName(typeof (FileTypes), item);
|
||||||
|
checkBoxes.Add(new CheckBox()
|
||||||
|
{
|
||||||
|
Content = $".{fileName}",
|
||||||
|
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
|
||||||
|
Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkBoxes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MenuItem[] GenerateLanguageMenuItems()
|
||||||
|
{
|
||||||
List<MenuItem> menuItems = new();
|
List<MenuItem> menuItems = new();
|
||||||
|
|
||||||
string localePath = "Ryujinx.Ava/Assets/Locales";
|
string localePath = "Ryujinx.Ava/Assets/Locales";
|
||||||
@ -42,7 +68,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
{
|
{
|
||||||
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
|
||||||
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
|
||||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
|
||||||
|
|
||||||
if (!strings.TryGetValue("Language", out string languageName))
|
if (!strings.TryGetValue("Language", out string languageName))
|
||||||
{
|
{
|
||||||
@ -61,7 +87,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
menuItems.Add(menuItem);
|
menuItems.Add(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeLanguageMenuItem.Items = menuItems.ToArray();
|
return menuItems.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
@ -126,7 +152,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
|
|
||||||
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
||||||
{
|
{
|
||||||
string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper();
|
string titleId = ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper();
|
||||||
AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
|
AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
|
||||||
|
|
||||||
await window.ShowDialog(Window);
|
await window.ShowDialog(Window);
|
||||||
@ -148,13 +174,11 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLoader application = ViewModel.AppHost.Device.Application;
|
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||||
if (application != null)
|
|
||||||
{
|
|
||||||
await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window);
|
|
||||||
|
|
||||||
ViewModel.AppHost.Device.EnableCheats();
|
await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window);
|
||||||
}
|
|
||||||
|
ViewModel.AppHost.Device.EnableCheats();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
|
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@ -29,7 +29,18 @@
|
|||||||
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
|
||||||
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale SettingsTabNetworkInterface}"
|
||||||
|
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
|
||||||
|
Width="200" />
|
||||||
|
<ComboBox SelectedIndex="{Binding NetworkInterfaceIndex}"
|
||||||
|
ToolTip.Tip="{locale:Locale NetworkInterfaceTooltip}"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Items="{Binding NetworkInterfaceList}"
|
||||||
|
Width="250" />
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Models;
|
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
{
|
{
|
||||||
@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool IsScanned { get; set; }
|
public bool IsScanned { get; set; }
|
||||||
public Amiibo.AmiiboApi ScannedAmiibo { get; set; }
|
public AmiiboApi ScannedAmiibo { get; set; }
|
||||||
public AmiiboWindowViewModel ViewModel { get; set; }
|
public AmiiboWindowViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
private void ScanButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel.AmiiboSelectedIndex > -1)
|
if (ViewModel.AmiiboSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
|
||||||
ScannedAmiibo = amiibo;
|
ScannedAmiibo = amiibo;
|
||||||
IsScanned = true;
|
IsScanned = true;
|
||||||
Close();
|
Close();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<window:StyleableWindow
|
<window:StyleableWindow
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
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"
|
||||||
@ -10,20 +10,16 @@
|
|||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow"
|
x:Class="Ryujinx.Ava.UI.Windows.ContentDialogOverlayWindow"
|
||||||
Title="ContentDialogOverlayWindow"
|
Title="ContentDialogOverlayWindow"
|
||||||
Focusable="True">
|
Focusable="False">
|
||||||
<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"
|
||||||
Value="Transparent" />
|
Value="Transparent" />
|
||||||
</Style>
|
</Style>
|
||||||
</window:StyleableWindow.Styles>
|
</window:StyleableWindow.Styles>
|
||||||
<ContentControl
|
<ui:ContentDialog Name="ContentDialog"
|
||||||
Focusable="False"
|
IsPrimaryButtonEnabled="True"
|
||||||
IsVisible="False"
|
IsSecondaryButtonEnabled="True"
|
||||||
KeyboardNavigation.IsTabStop="False">
|
IsVisible="False"
|
||||||
<ui:ContentDialog Name="ContentDialog"
|
Focusable="True"/>
|
||||||
IsPrimaryButtonEnabled="True"
|
|
||||||
IsSecondaryButtonEnabled="True"
|
|
||||||
IsVisible="False" />
|
|
||||||
</ContentControl>
|
|
||||||
</window:StyleableWindow>
|
</window:StyleableWindow>
|
||||||
|
@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Utilities;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Button = Avalonia.Controls.Button;
|
using Button = Avalonia.Controls.Button;
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
|
||||||
public enum AntiAliasing
|
public enum AntiAliasing
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
@ -9,4 +13,4 @@
|
|||||||
SmaaHigh,
|
SmaaHigh,
|
||||||
SmaaUltra
|
SmaaUltra
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
|
||||||
public enum AspectRatio
|
public enum AspectRatio
|
||||||
{
|
{
|
||||||
Fixed4x3,
|
Fixed4x3,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
|
||||||
public enum BackendThreading
|
public enum BackendThreading
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(List<DownloadableContentContainer>))]
|
||||||
|
public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
|
||||||
public enum GraphicsBackend
|
public enum GraphicsBackend
|
||||||
{
|
{
|
||||||
Vulkan,
|
Vulkan,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
|
||||||
public enum GraphicsDebugLevel
|
public enum GraphicsDebugLevel
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))]
|
||||||
public enum GamepadInputId : byte
|
public enum GamepadInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
@ -51,4 +55,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
{
|
{
|
||||||
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
|
||||||
{
|
{
|
||||||
|
private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
|
|
||||||
return motionBackendType switch
|
return motionBackendType switch
|
||||||
{
|
{
|
||||||
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options),
|
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
|
||||||
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options),
|
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
|||||||
switch (value.MotionBackend)
|
switch (value.MotionBackend)
|
||||||
{
|
{
|
||||||
case MotionInputBackendType.GamepadDriver:
|
case MotionInputBackendType.GamepadDriver:
|
||||||
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController);
|
||||||
break;
|
break;
|
||||||
case MotionInputBackendType.CemuHook:
|
case MotionInputBackendType.CemuHook:
|
||||||
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options);
|
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonMotionConfigControllerConverter))]
|
||||||
public class MotionConfigController
|
public class MotionConfigController
|
||||||
{
|
{
|
||||||
public MotionInputBackendType MotionBackend { get; set; }
|
public MotionInputBackendType MotionBackend { get; set; }
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(MotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(CemuHookMotionConfigController))]
|
||||||
|
[JsonSerializable(typeof(StandardMotionConfigController))]
|
||||||
|
public partial class MotionConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
|
||||||
public enum MotionInputBackendType : byte
|
public enum MotionInputBackendType : byte
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid.Controller
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid.Controller
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))]
|
||||||
public enum StickInputId : byte
|
public enum StickInputId : byte
|
||||||
{
|
{
|
||||||
Unbound,
|
Unbound,
|
||||||
@ -8,4 +12,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,12 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
[Flags]
|
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[Flags]
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
|
||||||
public enum ControllerType : int
|
public enum ControllerType : int
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
|
||||||
public enum InputBackendType
|
public enum InputBackendType
|
||||||
{
|
{
|
||||||
Invalid,
|
Invalid,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(JsonInputConfigConverter))]
|
||||||
public class InputConfig : INotifyPropertyChanged
|
public class InputConfig : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(InputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardKeyboardInputConfig))]
|
||||||
|
[JsonSerializable(typeof(StandardControllerInputConfig))]
|
||||||
|
public partial class InputConfigJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,16 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
class JsonInputConfigConverter : JsonConverter<InputConfig>
|
public class JsonInputConfigConverter : JsonConverter<InputConfig>
|
||||||
{
|
{
|
||||||
|
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
|
||||||
{
|
{
|
||||||
// Temporary reader to get the backend type
|
// Temporary reader to get the backend type
|
||||||
@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
|
|
||||||
return backendType switch
|
return backendType switch
|
||||||
{
|
{
|
||||||
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options),
|
InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
|
||||||
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options),
|
InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
|
||||||
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
switch (value.Backend)
|
switch (value.Backend)
|
||||||
{
|
{
|
||||||
case InputBackendType.WindowKeyboard:
|
case InputBackendType.WindowKeyboard:
|
||||||
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
|
||||||
break;
|
break;
|
||||||
case InputBackendType.GamepadSDL2:
|
case InputBackendType.GamepadSDL2:
|
||||||
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options);
|
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
throw new ArgumentException($"Unknown backend type {value.Backend}");
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
|
||||||
public enum Key
|
public enum Key
|
||||||
{
|
{
|
||||||
Unknown,
|
Unknown,
|
||||||
@ -136,4 +140,4 @@
|
|||||||
|
|
||||||
Count
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
|
// NOTE: Please don't change this to struct.
|
||||||
|
// This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys.
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Key ToggleVsync { get; set; }
|
||||||
@ -12,4 +14,4 @@
|
|||||||
public Key VolumeUp { get; set; }
|
public Key VolumeUp { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,10 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration.Hid
|
namespace Ryujinx.Common.Configuration.Hid
|
||||||
{
|
{
|
||||||
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
|
||||||
public enum PlayerIndex : int
|
public enum PlayerIndex : int
|
||||||
{
|
{
|
||||||
Player1 = 0,
|
Player1 = 0,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
namespace Ryujinx.Common.Configuration
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
|
||||||
public enum MemoryManagerMode : byte
|
public enum MemoryManagerMode : byte
|
||||||
{
|
{
|
||||||
SoftwarePageTable,
|
SoftwarePageTable,
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Configuration
|
namespace Ryujinx.Common.Configuration
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
|
||||||
public enum ScalingFilter
|
public enum ScalingFilter
|
||||||
{
|
{
|
||||||
Bilinear,
|
Bilinear,
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(TitleUpdateMetadata))]
|
||||||
|
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,20 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
internal class DefaultLogFormatter : ILogFormatter
|
internal class DefaultLogFormatter : ILogFormatter
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>();
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
public string Format(LogEventArgs args)
|
public string Format(LogEventArgs args)
|
||||||
{
|
{
|
||||||
StringBuilder sb = _stringBuilderPool.Allocate();
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
|
|
||||||
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time);
|
sb.Append($@"{args.Time:hh\:mm\:ss\.fff}");
|
||||||
sb.Append($" |{args.Level.ToString()[0]}| ");
|
sb.Append($" |{args.Level.ToString()[0]}| ");
|
||||||
|
|
||||||
if (args.ThreadName != null)
|
if (args.ThreadName != null)
|
||||||
@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
sb.Append(args.Message);
|
sb.Append(args.Message);
|
||||||
|
|
||||||
if (args.Data != null)
|
if (args.Data is not null)
|
||||||
{
|
{
|
||||||
PropertyInfo[] props = args.Data.GetType().GetProperties();
|
sb.Append(' ');
|
||||||
|
DynamicObjectFormatter.Format(sb, args.Data);
|
||||||
sb.Append(" {");
|
|
||||||
|
|
||||||
foreach (var prop in props)
|
|
||||||
{
|
|
||||||
sb.Append(prop.Name);
|
|
||||||
sb.Append(": ");
|
|
||||||
|
|
||||||
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
|
||||||
{
|
|
||||||
Array array = (Array)prop.GetValue(args.Data);
|
|
||||||
foreach (var item in array)
|
|
||||||
{
|
|
||||||
sb.Append(item.ToString());
|
|
||||||
sb.Append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 2, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.Append(prop.GetValue(args.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(" ; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We remove the final ';' from the string
|
|
||||||
if (props.Length > 0)
|
|
||||||
{
|
|
||||||
sb.Remove(sb.Length - 3, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append('}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_stringBuilderPool.Release(sb);
|
StringBuilderPool.Release(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
84
Ryujinx.Common/Logging/Formatters/DynamicObjectFormatter.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class DynamicObjectFormatter
|
||||||
|
{
|
||||||
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
|
public static string? Format(object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Format(sb, dynamicObject);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
StringBuilderPool.Release(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Format(StringBuilder sb, object? dynamicObject)
|
||||||
|
{
|
||||||
|
if (dynamicObject is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
|
||||||
|
|
||||||
|
sb.Append('{');
|
||||||
|
|
||||||
|
foreach (var prop in props)
|
||||||
|
{
|
||||||
|
sb.Append(prop.Name);
|
||||||
|
sb.Append(": ");
|
||||||
|
|
||||||
|
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
|
||||||
|
{
|
||||||
|
Array? array = (Array?) prop.GetValue(dynamicObject);
|
||||||
|
|
||||||
|
if (array is not null)
|
||||||
|
{
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
sb.Append(item);
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 2, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(prop.GetValue(dynamicObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(" ; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We remove the final ';' from the string
|
||||||
|
if (props.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Remove(sb.Length - 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
|
||||||
public enum LogClass
|
public enum LogClass
|
||||||
{
|
{
|
||||||
Application,
|
Application,
|
||||||
|
@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
public readonly string Message;
|
public readonly string Message;
|
||||||
public readonly object Data;
|
public readonly object Data;
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message)
|
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null)
|
||||||
{
|
|
||||||
Level = level;
|
|
||||||
Time = time;
|
|
||||||
ThreadName = threadName;
|
|
||||||
Message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data)
|
|
||||||
{
|
{
|
||||||
Level = level;
|
Level = level;
|
||||||
Time = time;
|
Time = time;
|
||||||
|
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
30
Ryujinx.Common/Logging/LogEventArgsJson.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
internal class LogEventArgsJson
|
||||||
|
{
|
||||||
|
public LogLevel Level { get; }
|
||||||
|
public TimeSpan Time { get; }
|
||||||
|
public string ThreadName { get; }
|
||||||
|
|
||||||
|
public string Message { get; }
|
||||||
|
public string Data { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
Time = time;
|
||||||
|
ThreadName = threadName;
|
||||||
|
Message = message;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogEventArgsJson FromLogEventArgs(LogEventArgs args)
|
||||||
|
{
|
||||||
|
return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
9
Ryujinx.Common/Logging/LogEventJsonSerializerContext.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(LogEventArgsJson))]
|
||||||
|
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
|
||||||
public enum LogLevel
|
public enum LogLevel
|
||||||
{
|
{
|
||||||
Debug,
|
Debug,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.IO;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.Text.Json;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
{
|
{
|
||||||
@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
public void Log(object sender, LogEventArgs e)
|
public void Log(object sender, LogEventArgs e)
|
||||||
{
|
{
|
||||||
string text = JsonSerializer.Serialize(e);
|
var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
|
||||||
|
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
|
||||||
using (BinaryWriter writer = new BinaryWriter(_stream))
|
|
||||||
{
|
|
||||||
writer.Write(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInterop
|
namespace Ryujinx.Common.SystemInterop
|
||||||
{
|
{
|
||||||
public partial class StdErrAdapter : IDisposable
|
public partial class StdErrAdapter : IDisposable
|
||||||
{
|
{
|
||||||
private bool _disposable = false;
|
private bool _disposable = false;
|
||||||
private UnixStream _pipeReader;
|
private Stream _pipeReader;
|
||||||
private UnixStream _pipeWriter;
|
private Stream _pipeWriter;
|
||||||
private Thread _worker;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private Task _worker;
|
||||||
|
|
||||||
public StdErrAdapter()
|
public StdErrAdapter()
|
||||||
{
|
{
|
||||||
@ -31,37 +34,39 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
(int readFd, int writeFd) = MakePipe();
|
(int readFd, int writeFd) = MakePipe();
|
||||||
dup2(writeFd, stdErrFileno);
|
dup2(writeFd, stdErrFileno);
|
||||||
|
|
||||||
_pipeReader = new UnixStream(readFd);
|
_pipeReader = CreateFileDescriptorStream(readFd);
|
||||||
_pipeWriter = new UnixStream(writeFd);
|
_pipeWriter = CreateFileDescriptorStream(writeFd);
|
||||||
|
|
||||||
_worker = new Thread(EventWorker);
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_worker = Task.Run(async () => await EventWorkerAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||||
_disposable = true;
|
_disposable = true;
|
||||||
_worker.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private void EventWorker()
|
private async Task EventWorkerAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
TextReader reader = new StreamReader(_pipeReader);
|
using TextReader reader = new StreamReader(_pipeReader, leaveOpen: true);
|
||||||
string line;
|
string line;
|
||||||
while ((line = reader.ReadLine()) != null)
|
while (cancellationToken.IsCancellationRequested == false && (line = await reader.ReadLineAsync(cancellationToken)) != null)
|
||||||
{
|
{
|
||||||
Logger.Error?.PrintRawMsg(line);
|
Logger.Error?.PrintRawMsg(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
private void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (_disposable)
|
if (_disposable)
|
||||||
{
|
{
|
||||||
|
_disposable = false;
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_worker.Wait(0);
|
||||||
_pipeReader?.Close();
|
_pipeReader?.Close();
|
||||||
_pipeWriter?.Close();
|
_pipeWriter?.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_disposable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,11 +79,11 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
private static partial int dup2(int fd, int fd2);
|
private static partial int dup2(int fd, int fd2);
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static unsafe partial int pipe(int* pipefd);
|
private static partial int pipe(Span<int> pipefd);
|
||||||
|
|
||||||
private static unsafe (int, int) MakePipe()
|
private static (int, int) MakePipe()
|
||||||
{
|
{
|
||||||
int *pipefd = stackalloc int[2];
|
Span<int> pipefd = stackalloc int[2];
|
||||||
|
|
||||||
if (pipe(pipefd) == 0)
|
if (pipe(pipefd) == 0)
|
||||||
{
|
{
|
||||||
@ -89,5 +94,16 @@ namespace Ryujinx.Common.SystemInterop
|
|||||||
throw new();
|
throw new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static Stream CreateFileDescriptorStream(int fd)
|
||||||
|
{
|
||||||
|
return new FileStream(
|
||||||
|
new SafeFileHandle((IntPtr)fd, ownsHandle: true),
|
||||||
|
FileAccess.ReadWrite
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,155 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Versioning;
|
|
||||||
|
|
||||||
namespace Ryujinx.Common.SystemInterop
|
|
||||||
{
|
|
||||||
[SupportedOSPlatform("linux")]
|
|
||||||
[SupportedOSPlatform("macos")]
|
|
||||||
public partial class UnixStream : Stream, IDisposable
|
|
||||||
{
|
|
||||||
private const int InvalidFd = -1;
|
|
||||||
|
|
||||||
private int _fd;
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial long read(int fd, IntPtr buf, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial long write(int fd, IntPtr buf, ulong count);
|
|
||||||
|
|
||||||
[LibraryImport("libc", SetLastError = true)]
|
|
||||||
private static partial int close(int fd);
|
|
||||||
|
|
||||||
public UnixStream(int fd)
|
|
||||||
{
|
|
||||||
if (InvalidFd == fd)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid file descriptor");
|
|
||||||
}
|
|
||||||
|
|
||||||
_fd = fd;
|
|
||||||
|
|
||||||
CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
|
||||||
CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
~UnixStream()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead { get; }
|
|
||||||
public override bool CanWrite { get; }
|
|
||||||
public override bool CanSeek => false;
|
|
||||||
|
|
||||||
public override long Length => throw new NotSupportedException();
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => throw new NotSupportedException();
|
|
||||||
set => throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Length == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long r = 0;
|
|
||||||
fixed (byte* buf = &buffer[offset])
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
r = read(_fd, (IntPtr)buf, (ulong)count);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override unsafe void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.Length == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (byte* buf = &buffer[offset])
|
|
||||||
{
|
|
||||||
long r = 0;
|
|
||||||
do {
|
|
||||||
r = write(_fd, (IntPtr)buf, (ulong)count);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
if (_fd == InvalidFd)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Flush();
|
|
||||||
|
|
||||||
int r;
|
|
||||||
do {
|
|
||||||
r = close(_fd);
|
|
||||||
} while (ShouldRetry(r));
|
|
||||||
|
|
||||||
_fd = InvalidFd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShouldRetry(long r)
|
|
||||||
{
|
|
||||||
if (r == -1)
|
|
||||||
{
|
|
||||||
const int eintr = 4;
|
|
||||||
|
|
||||||
int errno = Marshal.GetLastPInvokeError();
|
|
||||||
|
|
||||||
if (errno == eintr)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SystemException($"Operation failed with error 0x{errno:X}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
11
Ryujinx.Common/Utilities/CommonJsonContext.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")]
|
||||||
|
[JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")]
|
||||||
|
public partial class CommonJsonContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,62 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using System.IO;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Utilities
|
namespace Ryujinx.Common.Utilities
|
||||||
{
|
{
|
||||||
public class JsonHelper
|
public class JsonHelper
|
||||||
{
|
{
|
||||||
public static JsonNamingPolicy SnakeCase { get; }
|
private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
|
||||||
|
private const int DefaultFileWriteBufferSize = 4096;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates new serializer options with default settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
||||||
|
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
||||||
|
/// </remarks>
|
||||||
|
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
|
||||||
|
{
|
||||||
|
JsonSerializerOptions options = new()
|
||||||
|
{
|
||||||
|
DictionaryKeyPolicy = SnakeCasePolicy,
|
||||||
|
PropertyNamingPolicy = SnakeCasePolicy,
|
||||||
|
WriteIndented = indented,
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize(value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough);
|
||||||
|
JsonSerializer.Serialize(file, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
using FileStream file = File.OpenRead(filePath);
|
||||||
|
return JsonSerializer.Deserialize(file, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(stream, value, typeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||||
{
|
{
|
||||||
@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new();
|
||||||
|
|
||||||
for (int i = 0; i < name.Length; i++)
|
for (int i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.Append("_");
|
builder.Append('_');
|
||||||
builder.Append(char.ToLowerInvariant(c));
|
builder.Append(char.ToLowerInvariant(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonHelper()
|
|
||||||
{
|
|
||||||
SnakeCase = new SnakeCaseNamingPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
JsonSerializerOptions options = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DictionaryKeyPolicy = SnakeCase,
|
|
||||||
PropertyNamingPolicy = SnakeCase,
|
|
||||||
WriteIndented = prettyPrint,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip
|
|
||||||
};
|
|
||||||
|
|
||||||
options.Converters.Add(new JsonStringEnumConverter());
|
|
||||||
options.Converters.Add(new JsonInputConfigConverter());
|
|
||||||
options.Converters.Add(new JsonMotionConfigControllerConverter());
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(Stream stream)
|
|
||||||
{
|
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T DeserializeFromFile<T>(string path)
|
|
||||||
{
|
|
||||||
return Deserialize<T>(File.ReadAllText(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(string json)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
using (BinaryWriter writer = new BinaryWriter(stream))
|
|
||||||
{
|
|
||||||
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
|
|
||||||
{
|
|
||||||
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user