Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
79becc4b78 | ||
![]() |
223172ac0b | ||
![]() |
8c9633d72f | ||
![]() |
1f93fd52d9 | ||
![]() |
aac7bbd378 | ||
![]() |
bed516bfda | ||
![]() |
69b05f9918 | ||
![]() |
fb7c80e928 | ||
![]() |
bb2f9df0a1 | ||
![]() |
54bfaa125d | ||
![]() |
7af9fcbc06 | ||
![]() |
ee174be57c | ||
![]() |
0bcbe32367 | ||
![]() |
b97ff4da5e | ||
![]() |
747081d2c7 | ||
![]() |
497199bb50 | ||
![]() |
bd9ac0fdaa | ||
![]() |
ac21abbb9d | ||
![]() |
a3dd04deef | ||
![]() |
3705c20668 | ||
![]() |
7b35ebc64a | ||
![]() |
0a24aa6af2 |
@@ -121,7 +121,7 @@ namespace ARMeilleure.Decoders
|
|||||||
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsUnconditionalBranch(lastOp) || isCall)
|
if (isCall || !(IsUnconditionalBranch(lastOp) || IsTrap(lastOp)))
|
||||||
{
|
{
|
||||||
currBlock.Next = GetBlock(currBlock.EndAddress);
|
currBlock.Next = GetBlock(currBlock.EndAddress);
|
||||||
}
|
}
|
||||||
@@ -263,6 +263,11 @@ namespace ARMeilleure.Decoders
|
|||||||
// so we must consider such operations as a branch in potential aswell.
|
// so we must consider such operations as a branch in potential aswell.
|
||||||
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
||||||
{
|
{
|
||||||
|
if (opCode is OpCodeT32)
|
||||||
|
{
|
||||||
|
return opCode.Instruction.Name != InstName.Tst && opCode.Instruction.Name != InstName.Teq &&
|
||||||
|
opCode.Instruction.Name != InstName.Cmp && opCode.Instruction.Name != InstName.Cmn;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,9 +329,13 @@ namespace ARMeilleure.Decoders
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsException(OpCode opCode)
|
private static bool IsException(OpCode opCode)
|
||||||
|
{
|
||||||
|
return IsTrap(opCode) || opCode.Instruction.Name == InstName.Svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTrap(OpCode opCode)
|
||||||
{
|
{
|
||||||
return opCode.Instruction.Name == InstName.Brk ||
|
return opCode.Instruction.Name == InstName.Brk ||
|
||||||
opCode.Instruction.Name == InstName.Svc ||
|
|
||||||
opCode.Instruction.Name == InstName.Trap ||
|
opCode.Instruction.Name == InstName.Trap ||
|
||||||
opCode.Instruction.Name == InstName.Und;
|
opCode.Instruction.Name == InstName.Und;
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,25 @@ namespace ARMeilleure.Decoders
|
|||||||
Cond = (Condition)((uint)opCode >> 28);
|
Cond = (Condition)((uint)opCode >> 28);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsThumb()
|
||||||
|
{
|
||||||
|
return this is OpCodeT16 || this is OpCodeT32;
|
||||||
|
}
|
||||||
|
|
||||||
public uint GetPc()
|
public uint GetPc()
|
||||||
{
|
{
|
||||||
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
||||||
// the PC actually points 2 instructions ahead.
|
// the PC actually points 2 instructions ahead.
|
||||||
return (uint)Address + (uint)OpCodeSizeInBytes * 2;
|
if (IsThumb())
|
||||||
|
{
|
||||||
|
// PC is ahead by 4 in thumb mode whether or not the current instruction
|
||||||
|
// is 16 or 32 bit.
|
||||||
|
return (uint)Address + 4u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (uint)Address + 8u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders
|
||||||
{
|
{
|
||||||
class OpCodeT16BImm11 : OpCode32, IOpCode32BImm
|
class OpCodeT16BImm11 : OpCodeT16, IOpCode32BImm
|
||||||
{
|
{
|
||||||
public long Immediate { get; }
|
public long Immediate { get; }
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders
|
||||||
{
|
{
|
||||||
class OpCodeT16BImm8 : OpCode32, IOpCode32BImm
|
class OpCodeT16BImm8 : OpCodeT16, IOpCode32BImm
|
||||||
{
|
{
|
||||||
public long Immediate { get; }
|
public long Immediate { get; }
|
||||||
|
|
||||||
|
14
ARMeilleure/Decoders/OpCodeT32.cs
Normal file
14
ARMeilleure/Decoders/OpCodeT32.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32 : OpCode32
|
||||||
|
{
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Cond = Condition.Al;
|
||||||
|
|
||||||
|
OpCodeSizeInBytes = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
ARMeilleure/Decoders/OpCodeT32Alu.cs
Normal file
20
ARMeilleure/Decoders/OpCodeT32Alu.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32Alu : OpCodeT32, IOpCode32Alu
|
||||||
|
{
|
||||||
|
public int Rd { get; }
|
||||||
|
public int Rn { get; }
|
||||||
|
|
||||||
|
public bool? SetFlags { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32Alu(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32Alu(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rd = (opCode >> 8) & 0xf;
|
||||||
|
Rn = (opCode >> 16) & 0xf;
|
||||||
|
|
||||||
|
SetFlags = ((opCode >> 20) & 1) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
ARMeilleure/Decoders/OpCodeT32AluImm.cs
Normal file
38
ARMeilleure/Decoders/OpCodeT32AluImm.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32AluImm : OpCodeT32Alu, IOpCode32AluImm
|
||||||
|
{
|
||||||
|
public int Immediate { get; }
|
||||||
|
|
||||||
|
public bool IsRotated { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluImm(inst, address, opCode);
|
||||||
|
|
||||||
|
private static readonly Vector128<int> _factor = Vector128.Create(1, 0x00010001, 0x01000100, 0x01010101);
|
||||||
|
|
||||||
|
public OpCodeT32AluImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
int imm8 = (opCode >> 0) & 0xff;
|
||||||
|
int imm3 = (opCode >> 12) & 7;
|
||||||
|
int imm1 = (opCode >> 26) & 1;
|
||||||
|
|
||||||
|
int imm12 = imm8 | (imm3 << 8) | (imm1 << 11);
|
||||||
|
|
||||||
|
if ((imm12 >> 10) == 0)
|
||||||
|
{
|
||||||
|
Immediate = imm8 * _factor.GetElement((imm12 >> 8) & 3);
|
||||||
|
IsRotated = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int shift = imm12 >> 7;
|
||||||
|
|
||||||
|
Immediate = BitUtils.RotateRight(0x80 | (imm12 & 0x7f), shift, 32);
|
||||||
|
IsRotated = shift != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
ARMeilleure/Decoders/OpCodeT32AluRsImm.cs
Normal file
20
ARMeilleure/Decoders/OpCodeT32AluRsImm.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32AluRsImm : OpCodeT32Alu, IOpCode32AluRsImm
|
||||||
|
{
|
||||||
|
public int Rm { get; }
|
||||||
|
public int Immediate { get; }
|
||||||
|
|
||||||
|
public ShiftType ShiftType { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluRsImm(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32AluRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
Rm = (opCode >> 0) & 0xf;
|
||||||
|
Immediate = ((opCode >> 6) & 3) | ((opCode >> 10) & 0x1c);
|
||||||
|
|
||||||
|
ShiftType = (ShiftType)((opCode >> 4) & 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
ARMeilleure/Decoders/OpCodeT32BImm20.cs
Normal file
29
ARMeilleure/Decoders/OpCodeT32BImm20.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using ARMeilleure.Instructions;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32BImm20 : OpCodeT32, IOpCode32BImm
|
||||||
|
{
|
||||||
|
public long Immediate { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32BImm20(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32BImm20(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
uint pc = GetPc();
|
||||||
|
|
||||||
|
int imm11 = (opCode >> 0) & 0x7ff;
|
||||||
|
int j2 = (opCode >> 11) & 1;
|
||||||
|
int j1 = (opCode >> 13) & 1;
|
||||||
|
int imm6 = (opCode >> 16) & 0x3f;
|
||||||
|
int s = (opCode >> 26) & 1;
|
||||||
|
|
||||||
|
int imm32 = imm11 | (imm6 << 11) | (j1 << 17) | (j2 << 18) | (s << 19);
|
||||||
|
imm32 = (imm32 << 13) >> 12;
|
||||||
|
|
||||||
|
Immediate = pc + imm32;
|
||||||
|
|
||||||
|
Cond = (Condition)((opCode >> 22) & 0xf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
ARMeilleure/Decoders/OpCodeT32BImm24.cs
Normal file
35
ARMeilleure/Decoders/OpCodeT32BImm24.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using ARMeilleure.Instructions;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Decoders
|
||||||
|
{
|
||||||
|
class OpCodeT32BImm24 : OpCodeT32, IOpCode32BImm
|
||||||
|
{
|
||||||
|
public long Immediate { get; }
|
||||||
|
|
||||||
|
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32BImm24(inst, address, opCode);
|
||||||
|
|
||||||
|
public OpCodeT32BImm24(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||||
|
{
|
||||||
|
uint pc = GetPc();
|
||||||
|
|
||||||
|
if (inst.Name == InstName.Blx)
|
||||||
|
{
|
||||||
|
pc &= ~3u;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imm11 = (opCode >> 0) & 0x7ff;
|
||||||
|
int j2 = (opCode >> 11) & 1;
|
||||||
|
int j1 = (opCode >> 13) & 1;
|
||||||
|
int imm10 = (opCode >> 16) & 0x3ff;
|
||||||
|
int s = (opCode >> 26) & 1;
|
||||||
|
|
||||||
|
int i1 = j1 ^ s ^ 1;
|
||||||
|
int i2 = j2 ^ s ^ 1;
|
||||||
|
|
||||||
|
int imm32 = imm11 | (imm10 << 11) | (i2 << 21) | (i1 << 22) | (s << 23);
|
||||||
|
imm32 = (imm32 << 9) >> 8;
|
||||||
|
|
||||||
|
Immediate = pc + imm32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using ARMeilleure.Instructions;
|
using ARMeilleure.Instructions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders
|
||||||
{
|
{
|
||||||
@@ -972,8 +973,7 @@ namespace ARMeilleure.Decoders
|
|||||||
SetA32("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create);
|
SetA32("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, OpCode32SimdCmpZ.Create);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "OpCode Table (AArch32, T16/T32)"
|
#region "OpCode Table (AArch32, T16)"
|
||||||
// T16
|
|
||||||
SetT16("000<<xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftImm.Create);
|
SetT16("000<<xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT16ShiftImm.Create);
|
||||||
SetT16("0001100xxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubReg.Create);
|
SetT16("0001100xxxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT16AddSubReg.Create);
|
||||||
SetT16("0001101xxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubReg.Create);
|
SetT16("0001101xxxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT16AddSubReg.Create);
|
||||||
@@ -1045,6 +1045,46 @@ namespace ARMeilleure.Decoders
|
|||||||
SetT16("11100xxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm11.Create);
|
SetT16("11100xxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT16BImm11.Create);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region "OpCode Table (AArch32, T32)"
|
||||||
|
// Base
|
||||||
|
SetT32("11101011010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x01010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101011000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x01000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11110x<<<xxxxxxx10x0xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm20.Create);
|
||||||
|
SetT32("11110xxxxxxxxxxx10x1xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm24.Create);
|
||||||
|
SetT32("11101010001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11110xxxxxxxxxxx11x1xxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, OpCodeT32BImm24.Create);
|
||||||
|
SetT32("11110xxxxxxxxxxx11x0xxxxxxxxxxx0", InstName.Blx, InstEmit32.Blx, OpCodeT32BImm24.Create);
|
||||||
|
SetT32("111010110001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x010001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("111010111011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x011011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101010010x<<<<0xxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x00010x<<<<0xxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101011110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x01110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101011011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x01011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("11101011101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x01101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("111010101001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x001001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluImm.Create);
|
||||||
|
SetT32("111010100001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluRsImm.Create);
|
||||||
|
SetT32("11110x000001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluImm.Create);
|
||||||
|
#endregion
|
||||||
|
|
||||||
FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA);
|
FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA);
|
||||||
FillFastLookupTable(InstT32FastLookup, AllInstT32, ToFastLookupIndexT);
|
FillFastLookupTable(InstT32FastLookup, AllInstT32, ToFastLookupIndexT);
|
||||||
FillFastLookupTable(InstA64FastLookup, AllInstA64, ToFastLookupIndexA);
|
FillFastLookupTable(InstA64FastLookup, AllInstA64, ToFastLookupIndexA);
|
||||||
@@ -1092,8 +1132,11 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
|
private static void SetT32(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
|
||||||
{
|
{
|
||||||
encoding = encoding.Substring(16) + encoding.Substring(0, 16);
|
string reversedEncoding = encoding.Substring(16) + encoding.Substring(0, 16);
|
||||||
Set(encoding, AllInstT32, new InstDescriptor(name, emitter), makeOp);
|
MakeOp reversedMakeOp =
|
||||||
|
(InstDescriptor inst, ulong address, int opCode)
|
||||||
|
=> makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16));
|
||||||
|
Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
|
private static void SetA64(string encoding, InstName name, InstEmitter emitter, MakeOp makeOp)
|
||||||
|
@@ -244,6 +244,23 @@ namespace ARMeilleure.Instructions
|
|||||||
EmitAluStore(context, res);
|
EmitAluStore(context, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Orn(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||||
|
|
||||||
|
Operand n = GetAluN(context);
|
||||||
|
Operand m = GetAluM(context);
|
||||||
|
|
||||||
|
Operand res = context.BitwiseOr(n, context.BitwiseNot(m));
|
||||||
|
|
||||||
|
if (ShouldSetFlags(context))
|
||||||
|
{
|
||||||
|
EmitNZFlagsCheck(context, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitAluStore(context, res);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Pkh(ArmEmitterContext context)
|
public static void Pkh(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
|
OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
|
||||||
|
@@ -128,7 +128,7 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
Debug.Assert(value.Type == OperandType.I32);
|
Debug.Assert(value.Type == OperandType.I32);
|
||||||
|
|
||||||
if (IsThumb(context.CurrOp))
|
if (((OpCode32)context.CurrOp).IsThumb())
|
||||||
{
|
{
|
||||||
bool isReturn = IsA32Return(context);
|
bool isReturn = IsA32Return(context);
|
||||||
if (!isReturn)
|
if (!isReturn)
|
||||||
@@ -197,7 +197,7 @@ namespace ARMeilleure.Instructions
|
|||||||
// ARM32.
|
// ARM32.
|
||||||
case IOpCode32AluImm op:
|
case IOpCode32AluImm op:
|
||||||
{
|
{
|
||||||
if (ShouldSetFlags(context) && op.IsRotated)
|
if (ShouldSetFlags(context) && op.IsRotated && setCarry)
|
||||||
{
|
{
|
||||||
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31));
|
||||||
}
|
}
|
||||||
|
@@ -9,18 +9,25 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
public static void Brk(ArmEmitterContext context)
|
public static void Brk(ArmEmitterContext context)
|
||||||
{
|
{
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.Break));
|
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||||
|
|
||||||
|
string name = nameof(NativeInterface.Break);
|
||||||
|
|
||||||
|
context.StoreToContext();
|
||||||
|
|
||||||
|
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||||
|
|
||||||
|
context.LoadFromContext();
|
||||||
|
|
||||||
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Svc(ArmEmitterContext context)
|
public static void Svc(ArmEmitterContext context)
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitExceptionCall(ArmEmitterContext context, string name)
|
|
||||||
{
|
{
|
||||||
OpCodeException op = (OpCodeException)context.CurrOp;
|
OpCodeException op = (OpCodeException)context.CurrOp;
|
||||||
|
|
||||||
|
string name = nameof(NativeInterface.SupervisorCall);
|
||||||
|
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.Id));
|
||||||
@@ -41,6 +48,8 @@ namespace ARMeilleure.Instructions
|
|||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
|
context.Call(typeof(NativeInterface).GetMethod(name), Const(op.Address), Const(op.RawOpCode));
|
||||||
|
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
|
context.Return(Const(op.Address));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,19 +9,11 @@ namespace ARMeilleure.Instructions
|
|||||||
static partial class InstEmit32
|
static partial class InstEmit32
|
||||||
{
|
{
|
||||||
public static void Svc(ArmEmitterContext context)
|
public static void Svc(ArmEmitterContext context)
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.SupervisorCall));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Trap(ArmEmitterContext context)
|
|
||||||
{
|
|
||||||
EmitExceptionCall(context, nameof(NativeInterface.Break));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmitExceptionCall(ArmEmitterContext context, string name)
|
|
||||||
{
|
{
|
||||||
IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
|
IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
|
||||||
|
|
||||||
|
string name = nameof(NativeInterface.SupervisorCall);
|
||||||
|
|
||||||
context.StoreToContext();
|
context.StoreToContext();
|
||||||
|
|
||||||
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||||
@@ -30,5 +22,20 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Translator.EmitSynchronization(context);
|
Translator.EmitSynchronization(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Trap(ArmEmitterContext context)
|
||||||
|
{
|
||||||
|
IOpCode32Exception op = (IOpCode32Exception)context.CurrOp;
|
||||||
|
|
||||||
|
string name = nameof(NativeInterface.Break);
|
||||||
|
|
||||||
|
context.StoreToContext();
|
||||||
|
|
||||||
|
context.Call(typeof(NativeInterface).GetMethod(name), Const(((IOpCode)op).Address), Const(op.Id));
|
||||||
|
|
||||||
|
context.LoadFromContext();
|
||||||
|
|
||||||
|
context.Return(Const(context.CurrOp.Address));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
uint pc = op.GetPc();
|
uint pc = op.GetPc();
|
||||||
|
|
||||||
bool isThumb = IsThumb(context.CurrOp);
|
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||||
|
|
||||||
uint currentPc = isThumb
|
uint currentPc = isThumb
|
||||||
? pc | 1
|
? pc | 1
|
||||||
@@ -61,7 +61,7 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
Operand addr = context.Copy(GetIntA32(context, op.Rm));
|
||||||
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
Operand bitOne = context.BitwiseAnd(addr, Const(1));
|
||||||
|
|
||||||
bool isThumb = IsThumb(context.CurrOp);
|
bool isThumb = ((OpCode32)context.CurrOp).IsThumb();
|
||||||
|
|
||||||
uint currentPc = isThumb
|
uint currentPc = isThumb
|
||||||
? (pc - 2) | 1
|
? (pc - 2) | 1
|
||||||
|
@@ -10,11 +10,6 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
static class InstEmitHelper
|
static class InstEmitHelper
|
||||||
{
|
{
|
||||||
public static bool IsThumb(OpCode op)
|
|
||||||
{
|
|
||||||
return op is OpCodeT16;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Operand GetExtendedM(ArmEmitterContext context, int rm, IntType type)
|
public static Operand GetExtendedM(ArmEmitterContext context, int rm, IntType type)
|
||||||
{
|
{
|
||||||
Operand value = GetIntOrZR(context, rm);
|
Operand value = GetIntOrZR(context, rm);
|
||||||
|
@@ -43,6 +43,12 @@ namespace ARMeilleure.State
|
|||||||
public long TpidrEl0 { get; set; }
|
public long TpidrEl0 { get; set; }
|
||||||
public long Tpidr { get; set; }
|
public long Tpidr { get; set; }
|
||||||
|
|
||||||
|
public uint Pstate
|
||||||
|
{
|
||||||
|
get => _nativeContext.GetPstate();
|
||||||
|
set => _nativeContext.SetPstate(value);
|
||||||
|
}
|
||||||
|
|
||||||
public FPCR Fpcr { get; set; }
|
public FPCR Fpcr { get; set; }
|
||||||
public FPSR Fpsr { get; set; }
|
public FPSR Fpsr { get; set; }
|
||||||
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
|
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
|
||||||
|
@@ -95,6 +95,25 @@ namespace ARMeilleure.State
|
|||||||
GetStorage().Flags[(int)flag] = value ? 1u : 0u;
|
GetStorage().Flags[(int)flag] = value ? 1u : 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public unsafe uint GetPstate()
|
||||||
|
{
|
||||||
|
uint value = 0;
|
||||||
|
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
|
||||||
|
{
|
||||||
|
value |= GetStorage().Flags[flag] != 0 ? 1u << flag : 0u;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void SetPstate(uint value)
|
||||||
|
{
|
||||||
|
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
|
||||||
|
{
|
||||||
|
uint bit = 1u << flag;
|
||||||
|
GetStorage().Flags[flag] = (value & bit) == bit ? 1u : 0u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe bool GetFPStateFlag(FPState flag)
|
public unsafe bool GetFPStateFlag(FPState flag)
|
||||||
{
|
{
|
||||||
if ((uint)flag >= RegisterConsts.FpFlagsCount)
|
if ((uint)flag >= RegisterConsts.FpFlagsCount)
|
||||||
|
@@ -27,7 +27,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 = 3138; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 3179; //! 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";
|
||||||
|
@@ -149,11 +149,21 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
|||||||
|
|
||||||
Span<byte> targetSpan = performanceOutput.Slice(nextOffset);
|
Span<byte> targetSpan = performanceOutput.Slice(nextOffset);
|
||||||
|
|
||||||
|
// NOTE: We check for the space for two headers for the final blank header.
|
||||||
|
int requiredSpace = Unsafe.SizeOf<THeader>() + Unsafe.SizeOf<TEntry>() * inputHeader.GetEntryCount()
|
||||||
|
+ Unsafe.SizeOf<TEntryDetail>() * inputHeader.GetEntryDetailCount()
|
||||||
|
+ Unsafe.SizeOf<THeader>();
|
||||||
|
|
||||||
|
if (targetSpan.Length < requiredSpace)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(targetSpan)[0];
|
ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(targetSpan)[0];
|
||||||
|
|
||||||
nextOffset += Unsafe.SizeOf<THeader>();
|
nextOffset += Unsafe.SizeOf<THeader>();
|
||||||
|
|
||||||
Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(targetSpan.Slice(nextOffset));
|
Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(performanceOutput.Slice(nextOffset));
|
||||||
|
|
||||||
int totalProcessingTime = 0;
|
int totalProcessingTime = 0;
|
||||||
|
|
||||||
@@ -175,7 +185,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(targetSpan.Slice(nextOffset));
|
Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(performanceOutput.Slice(nextOffset));
|
||||||
|
|
||||||
int effectiveEntryDetailCount = 0;
|
int effectiveEntryDetailCount = 0;
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
private const string DefaultModsDir = "mods";
|
private const string DefaultModsDir = "mods";
|
||||||
|
|
||||||
public static string CustomModsPath { get; set; }
|
public static string CustomModsPath { get; set; }
|
||||||
|
public static string CustomSdModsPath {get; set; }
|
||||||
public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS
|
public static string CustomNandPath { get; set; } // TODO: Actually implement this into VFS
|
||||||
public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS
|
public static string CustomSdCardPath { get; set; } // TODO: Actually implement this into VFS
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
|
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
||||||
|
public static string GetSdModsPath() => CustomSdModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultSdcardDir, "atmosphere")).FullName;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,6 +5,7 @@
|
|||||||
public Stick Joystick { get; set; }
|
public Stick Joystick { get; set; }
|
||||||
public bool InvertStickX { get; set; }
|
public bool InvertStickX { get; set; }
|
||||||
public bool InvertStickY { get; set; }
|
public bool InvertStickY { get; set; }
|
||||||
|
public bool Rotate90CW { get; set; }
|
||||||
public Button StickButton { get; set; }
|
public Button StickButton { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
using System.Reflection;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
// DO NOT EDIT, filled by CI
|
// DO NOT EDIT, filled by CI
|
||||||
public static class ReleaseInformations
|
public static class ReleaseInformations
|
||||||
{
|
{
|
||||||
|
private const string FlatHubChannelOwner = "flathub";
|
||||||
|
|
||||||
public static string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
public static string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
||||||
public static string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
public static string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
||||||
public static string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
|
public static string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
|
||||||
@@ -19,6 +23,11 @@ namespace Ryujinx.Common
|
|||||||
!ReleaseChannelRepo.StartsWith("%%");
|
!ReleaseChannelRepo.StartsWith("%%");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsFlatHubBuild()
|
||||||
|
{
|
||||||
|
return IsValid() && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetVersion()
|
public static string GetVersion()
|
||||||
{
|
{
|
||||||
if (IsValid())
|
if (IsValid())
|
||||||
@@ -30,5 +39,15 @@ namespace Ryujinx.Common
|
|||||||
return Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
return Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetBaseApplicationDirectory()
|
||||||
|
{
|
||||||
|
if (IsFlatHubBuild())
|
||||||
|
{
|
||||||
|
return AppDataManager.BaseDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly PhysicalMemory _physicalMemory;
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
private readonly MultiRangeList<Texture> _textures;
|
private readonly MultiRangeList<Texture> _textures;
|
||||||
|
private readonly HashSet<Texture> _partiallyMappedTextures;
|
||||||
|
|
||||||
private Texture[] _textureOverlaps;
|
private Texture[] _textureOverlaps;
|
||||||
private OverlapInfo[] _overlapInfo;
|
private OverlapInfo[] _overlapInfo;
|
||||||
@@ -57,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_physicalMemory = physicalMemory;
|
_physicalMemory = physicalMemory;
|
||||||
|
|
||||||
_textures = new MultiRangeList<Texture>();
|
_textures = new MultiRangeList<Texture>();
|
||||||
|
_partiallyMappedTextures = new HashSet<Texture>();
|
||||||
|
|
||||||
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
|
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
|
||||||
_overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
|
_overlapInfo = new OverlapInfo[OverlapsBufferInitialCapacity];
|
||||||
@@ -74,17 +76,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Texture[] overlaps = new Texture[10];
|
Texture[] overlaps = new Texture[10];
|
||||||
int overlapCount;
|
int overlapCount;
|
||||||
|
|
||||||
MultiRange unmapped;
|
MultiRange unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
unmapped = ((MemoryManager)sender).GetPhysicalRegions(e.Address, e.Size);
|
|
||||||
}
|
|
||||||
catch (InvalidMemoryRegionException)
|
|
||||||
{
|
|
||||||
// This event fires on Map in case any mappings are overwritten. In that case, there may not be an existing mapping.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_textures)
|
lock (_textures)
|
||||||
{
|
{
|
||||||
@@ -95,6 +87,24 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
overlaps[i].Unmapped(unmapped);
|
overlaps[i].Unmapped(unmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If any range was previously unmapped, we also need to purge
|
||||||
|
// all partially mapped texture, as they might be fully mapped now.
|
||||||
|
for (int i = 0; i < unmapped.Count; i++)
|
||||||
|
{
|
||||||
|
if (unmapped.GetSubRange(i).Address == MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
lock (_partiallyMappedTextures)
|
||||||
|
{
|
||||||
|
foreach (var texture in _partiallyMappedTextures)
|
||||||
|
{
|
||||||
|
texture.Unmapped(unmapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -495,10 +505,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
|
SizeInfo sizeInfo = info.CalculateSizeInfo(layerSize);
|
||||||
|
|
||||||
ulong size = (ulong)sizeInfo.TotalSize;
|
ulong size = (ulong)sizeInfo.TotalSize;
|
||||||
|
bool partiallyMapped = false;
|
||||||
|
|
||||||
if (range == null)
|
if (range == null)
|
||||||
{
|
{
|
||||||
range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
|
range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
|
||||||
|
|
||||||
|
for (int i = 0; i < range.Value.Count; i++)
|
||||||
|
{
|
||||||
|
if (range.Value.GetSubRange(i).Address == MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
partiallyMapped = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find view compatible matches.
|
// Find view compatible matches.
|
||||||
@@ -668,7 +688,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);
|
bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);
|
||||||
|
|
||||||
if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
|
if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group))
|
||||||
{
|
{
|
||||||
incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
|
incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
|
||||||
@@ -784,6 +804,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_textures.Add(texture);
|
_textures.Add(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (partiallyMapped)
|
||||||
|
{
|
||||||
|
lock (_partiallyMappedTextures)
|
||||||
|
{
|
||||||
|
_partiallyMappedTextures.Add(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ShrinkOverlapsBufferIfNeeded();
|
ShrinkOverlapsBufferIfNeeded();
|
||||||
|
|
||||||
for (int i = 0; i < overlapsCount; i++)
|
for (int i = 0; i < overlapsCount; i++)
|
||||||
@@ -1079,6 +1107,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
_textures.Remove(texture);
|
_textures.Remove(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (_partiallyMappedTextures)
|
||||||
|
{
|
||||||
|
_partiallyMappedTextures.Remove(texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -236,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronize memory for a given texture.
|
/// Synchronize memory for a given texture.
|
||||||
/// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
|
/// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="texture">The texture being used</param>
|
/// <param name="texture">The texture being used</param>
|
||||||
@@ -280,7 +280,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
// Evaluate if any copy dependencies need to be fulfilled. A few rules:
|
// Evaluate if any copy dependencies need to be fulfilled. A few rules:
|
||||||
// If the copy handle needs to be synchronized, prefer our own state.
|
// If the copy handle needs to be synchronized, prefer our own state.
|
||||||
// If we need to be synchronized and there is a copy present, prefer the copy.
|
// If we need to be synchronized and there is a copy present, prefer the copy.
|
||||||
|
|
||||||
if (group.NeedsCopy && group.Copy(_context))
|
if (group.NeedsCopy && group.Copy(_context))
|
||||||
{
|
{
|
||||||
@@ -618,7 +618,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluate the range of tracking handles which a view texture overlaps with,
|
/// Evaluate the range of tracking handles which a view texture overlaps with,
|
||||||
/// using the view's position and slice/level counts.
|
/// using the view's position and slice/level counts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="firstLayer">The first layer of the texture</param>
|
/// <param name="firstLayer">The first layer of the texture</param>
|
||||||
@@ -879,7 +879,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int sliceStart = Math.Clamp(offset, 0, subRangeSize);
|
int sliceStart = Math.Clamp(offset, 0, subRangeSize);
|
||||||
int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
|
int sliceEnd = Math.Clamp(endOffset, 0, subRangeSize);
|
||||||
|
|
||||||
if (sliceStart != sliceEnd)
|
if (sliceStart != sliceEnd && item.Address != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
|
result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
|
||||||
}
|
}
|
||||||
@@ -1097,11 +1097,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
// Single dirty region.
|
// Single dirty region.
|
||||||
var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
|
var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < TextureRange.Count; i++)
|
for (int i = 0; i < TextureRange.Count; i++)
|
||||||
{
|
{
|
||||||
var currentRange = TextureRange.GetSubRange(i);
|
var currentRange = TextureRange.GetSubRange(i);
|
||||||
cpuRegionHandles[i] = GenerateHandle(currentRange.Address, currentRange.Size);
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
cpuRegionHandles[count++] = GenerateHandle(currentRange.Address, currentRange.Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != TextureRange.Count)
|
||||||
|
{
|
||||||
|
Array.Resize(ref cpuRegionHandles, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
|
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
|
||||||
@@ -1277,7 +1286,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
TextureInfo info = Storage.Info;
|
TextureInfo info = Storage.Info;
|
||||||
TextureInfo otherInfo = other.Storage.Info;
|
TextureInfo otherInfo = other.Storage.Info;
|
||||||
|
|
||||||
if (TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) &&
|
if (TextureCompatibility.ViewLayoutCompatible(info, otherInfo, level, otherLevel) &&
|
||||||
TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel))
|
TextureCompatibility.CopySizeMatches(info, otherInfo, level, otherLevel))
|
||||||
{
|
{
|
||||||
// These textures are copy compatible. Create the dependency.
|
// These textures are copy compatible. Create the dependency.
|
||||||
|
@@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private const ulong BufferAlignmentSize = 0x1000;
|
private const ulong BufferAlignmentSize = 0x1000;
|
||||||
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
||||||
|
|
||||||
|
private const ulong MaxDynamicGrowthSize = 0x100000;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly PhysicalMemory _physicalMemory;
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
@@ -166,10 +168,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
||||||
// that fits all the data we need. We also need to copy the contents from the
|
// that fits all the data we need. We also need to copy the contents from the
|
||||||
// old buffer(s) to the new buffer.
|
// old buffer(s) to the new buffer.
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
|
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
|
||||||
{
|
{
|
||||||
|
// Check if the following conditions are met:
|
||||||
|
// - We have a single overlap.
|
||||||
|
// - The overlap starts at or before the requested range. That is, the overlap happens at the end.
|
||||||
|
// - The size delta between the new, merged buffer and the old one is of at most 2 pages.
|
||||||
|
// In this case, we attempt to extend the buffer further than the requested range,
|
||||||
|
// this can potentially avoid future resizes if the application keeps using overlapping
|
||||||
|
// sequential memory.
|
||||||
|
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
|
||||||
|
// range crosses a page, and after alignment, ends having a size of 2 pages.
|
||||||
|
if (overlapsCount == 1 &&
|
||||||
|
address >= _bufferOverlaps[0].Address &&
|
||||||
|
endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2)
|
||||||
|
{
|
||||||
|
// Try to grow the buffer by 1.5x of its current size.
|
||||||
|
// This improves performance in the cases where the buffer is resized often by small amounts.
|
||||||
|
ulong existingSize = _bufferOverlaps[0].Size;
|
||||||
|
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
||||||
|
|
||||||
|
size = Math.Max(size, growthSize);
|
||||||
|
endAddress = address + size;
|
||||||
|
|
||||||
|
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < overlapsCount; index++)
|
for (int index = 0; index < overlapsCount; index++)
|
||||||
{
|
{
|
||||||
Buffer buffer = _bufferOverlaps[index];
|
Buffer buffer = _bufferOverlaps[index];
|
||||||
@@ -183,7 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
|
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount));
|
||||||
|
|
||||||
lock (_buffers)
|
lock (_buffers)
|
||||||
{
|
{
|
||||||
@@ -202,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
buffer.DisposeData();
|
buffer.DisposeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
newBuffer.SynchronizeMemory(address, endAddress - address);
|
newBuffer.SynchronizeMemory(address, newSize);
|
||||||
|
|
||||||
// Existing buffers were modified, we need to rebind everything.
|
// Existing buffers were modified, we need to rebind everything.
|
||||||
NotifyBuffersModified?.Invoke();
|
NotifyBuffersModified?.Invoke();
|
||||||
|
@@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private const int PtLvl1Bit = PtPageBits;
|
private const int PtLvl1Bit = PtPageBits;
|
||||||
private const int AddressSpaceBits = PtPageBits + PtLvl1Bits + PtLvl0Bits;
|
private const int AddressSpaceBits = PtPageBits + PtLvl1Bits + PtLvl0Bits;
|
||||||
|
|
||||||
public const ulong PteUnmapped = 0xffffffff_ffffffff;
|
public const ulong PteUnmapped = ulong.MaxValue;
|
||||||
|
|
||||||
private readonly ulong[][] _pageTable;
|
private readonly ulong[][] _pageTable;
|
||||||
|
|
||||||
@@ -340,7 +340,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="va">Virtual address of the range</param>
|
/// <param name="va">Virtual address of the range</param>
|
||||||
/// <param name="size">Size of the range</param>
|
/// <param name="size">Size of the range</param>
|
||||||
/// <returns>Multi-range with the physical regions</returns>
|
/// <returns>Multi-range with the physical regions</returns>
|
||||||
/// <exception cref="InvalidMemoryRegionException">The memory region specified by <paramref name="va"/> and <paramref name="size"/> is not fully mapped</exception>
|
|
||||||
public MultiRange GetPhysicalRegions(ulong va, ulong size)
|
public MultiRange GetPhysicalRegions(ulong va, ulong size)
|
||||||
{
|
{
|
||||||
if (IsContiguous(va, (int)size))
|
if (IsContiguous(va, (int)size))
|
||||||
@@ -348,11 +347,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return new MultiRange(Translate(va), size);
|
return new MultiRange(Translate(va), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsMapped(va))
|
|
||||||
{
|
|
||||||
throw new InvalidMemoryRegionException($"The specified GPU virtual address 0x{va:X} is not mapped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong regionStart = Translate(va);
|
ulong regionStart = Translate(va);
|
||||||
ulong regionSize = Math.Min(size, PageSize - (va & PageMask));
|
ulong regionSize = Math.Min(size, PageSize - (va & PageMask));
|
||||||
|
|
||||||
@@ -367,14 +361,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
{
|
{
|
||||||
if (!IsMapped(va + PageSize))
|
ulong currPa = Translate(va);
|
||||||
{
|
|
||||||
throw new InvalidMemoryRegionException($"The specified GPU virtual memory range 0x{va:X}..0x{(va + size):X} is not fully mapped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong newPa = Translate(va + PageSize);
|
ulong newPa = Translate(va + PageSize);
|
||||||
|
|
||||||
if (Translate(va) + PageSize != newPa)
|
if ((currPa != PteUnmapped || newPa != PteUnmapped) && currPa + PageSize != newPa)
|
||||||
{
|
{
|
||||||
regions.Add(new MemoryRange(regionStart, regionSize));
|
regions.Add(new MemoryRange(regionStart, regionSize));
|
||||||
regionStart = newPa;
|
regionStart = newPa;
|
||||||
@@ -405,18 +395,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
MemoryRange currentRange = range.GetSubRange(i);
|
MemoryRange currentRange = range.GetSubRange(i);
|
||||||
|
|
||||||
ulong address = currentRange.Address & ~PageMask;
|
if (currentRange.Address != PteUnmapped)
|
||||||
ulong endAddress = (currentRange.EndAddress + PageMask) & ~PageMask;
|
|
||||||
|
|
||||||
while (address < endAddress)
|
|
||||||
{
|
{
|
||||||
if (Translate(va) != address)
|
ulong address = currentRange.Address & ~PageMask;
|
||||||
{
|
ulong endAddress = (currentRange.EndAddress + PageMask) & ~PageMask;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
va += PageSize;
|
while (address < endAddress)
|
||||||
address += PageSize;
|
{
|
||||||
|
if (Translate(va) != address)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
va += PageSize;
|
||||||
|
address += PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ulong endVa = va + (((currentRange.Size) + PageMask) & ~PageMask);
|
||||||
|
|
||||||
|
while (va < endVa)
|
||||||
|
{
|
||||||
|
if (Translate(va) != PteUnmapped)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
va += PageSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,8 +7,6 @@ using Ryujinx.Memory.Range;
|
|||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
@@ -19,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class PhysicalMemory : IDisposable
|
class PhysicalMemory : IDisposable
|
||||||
{
|
{
|
||||||
public const int PageSize = 0x1000;
|
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private IVirtualMemoryManagerTracked _cpuMemory;
|
private IVirtualMemoryManagerTracked _cpuMemory;
|
||||||
private int _referenceCount;
|
private int _referenceCount;
|
||||||
@@ -103,24 +99,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (range.Count == 1)
|
if (range.Count == 1)
|
||||||
{
|
{
|
||||||
var singleRange = range.GetSubRange(0);
|
var singleRange = range.GetSubRange(0);
|
||||||
return _cpuMemory.GetSpan(singleRange.Address, (int)singleRange.Size, tracked);
|
if (singleRange.Address != MemoryManager.PteUnmapped)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Span<byte> data = new byte[range.GetSize()];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < range.Count; i++)
|
|
||||||
{
|
{
|
||||||
var currentRange = range.GetSubRange(i);
|
return _cpuMemory.GetSpan(singleRange.Address, (int)singleRange.Size, tracked);
|
||||||
int size = (int)currentRange.Size;
|
|
||||||
_cpuMemory.GetSpan(currentRange.Address, size, tracked).CopyTo(data.Slice(offset, size));
|
|
||||||
offset += size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Span<byte> data = new byte[range.GetSize()];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < range.Count; i++)
|
||||||
|
{
|
||||||
|
var currentRange = range.GetSubRange(i);
|
||||||
|
int size = (int)currentRange.Size;
|
||||||
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
_cpuMemory.GetSpan(currentRange.Address, size, tracked).CopyTo(data.Slice(offset, size));
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -156,11 +156,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
{
|
{
|
||||||
MemoryRange subrange = range.GetSubRange(i);
|
var currentRange = range.GetSubRange(i);
|
||||||
|
int size = (int)currentRange.Size;
|
||||||
GetSpan(subrange.Address, (int)subrange.Size).CopyTo(memory.Span.Slice(offset, (int)subrange.Size));
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
offset += (int)subrange.Size;
|
GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size));
|
||||||
|
}
|
||||||
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memory, tracked);
|
return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memory, tracked);
|
||||||
@@ -253,7 +255,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
if (range.Count == 1)
|
if (range.Count == 1)
|
||||||
{
|
{
|
||||||
var singleRange = range.GetSubRange(0);
|
var singleRange = range.GetSubRange(0);
|
||||||
writeCallback(singleRange.Address, data);
|
if (singleRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
writeCallback(singleRange.Address, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -263,7 +268,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
var currentRange = range.GetSubRange(i);
|
var currentRange = range.GetSubRange(i);
|
||||||
int size = (int)currentRange.Size;
|
int size = (int)currentRange.Size;
|
||||||
writeCallback(currentRange.Address, data.Slice(offset, size));
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
writeCallback(currentRange.Address, data.Slice(offset, size));
|
||||||
|
}
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,11 +296,20 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public GpuRegionHandle BeginTracking(MultiRange range)
|
public GpuRegionHandle BeginTracking(MultiRange range)
|
||||||
{
|
{
|
||||||
var cpuRegionHandles = new CpuRegionHandle[range.Count];
|
var cpuRegionHandles = new CpuRegionHandle[range.Count];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
{
|
{
|
||||||
var currentRange = range.GetSubRange(i);
|
var currentRange = range.GetSubRange(i);
|
||||||
cpuRegionHandles[i] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size);
|
if (currentRange.Address != MemoryManager.PteUnmapped)
|
||||||
|
{
|
||||||
|
cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != range.Count)
|
||||||
|
{
|
||||||
|
Array.Resize(ref cpuRegionHandles, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GpuRegionHandle(cpuRegionHandles);
|
return new GpuRegionHandle(cpuRegionHandles);
|
||||||
|
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const ulong ShaderCodeGenVersion = 3132;
|
private const ulong ShaderCodeGenVersion = 3054;
|
||||||
|
|
||||||
// Progress reporting helpers
|
// Progress reporting helpers
|
||||||
private volatile int _shaderCount;
|
private volatile int _shaderCount;
|
||||||
|
@@ -308,7 +308,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
int attr = offset + elemIndex * 4;
|
int attr = offset + elemIndex * 4;
|
||||||
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
|
||||||
{
|
{
|
||||||
int index = (attr - AttributeConsts.UserAttributeBase) / 16;
|
int userAttr = attr - AttributeConsts.UserAttributeBase;
|
||||||
|
int index = userAttr / 16;
|
||||||
|
|
||||||
if (isStore)
|
if (isStore)
|
||||||
{
|
{
|
||||||
@@ -316,7 +317,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
config.SetInputUserAttribute(index, perPatch);
|
config.SetInputUserAttribute(index, (userAttr >> 2) & 3, perPatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,6 +54,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
private int _nextUsedInputAttributes;
|
private int _nextUsedInputAttributes;
|
||||||
private int _thisUsedInputAttributes;
|
private int _thisUsedInputAttributes;
|
||||||
|
|
||||||
|
public UInt128 NextInputAttributesComponents { get; private set; }
|
||||||
|
public UInt128 ThisInputAttributesComponents { get; private set; }
|
||||||
|
public UInt128 NextInputAttributesPerPatchComponents { get; private set; }
|
||||||
|
public UInt128 ThisInputAttributesPerPatchComponents { get; private set; }
|
||||||
|
|
||||||
private int _usedConstantBuffers;
|
private int _usedConstantBuffers;
|
||||||
private int _usedStorageBuffers;
|
private int _usedStorageBuffers;
|
||||||
private int _usedStorageBuffersWrite;
|
private int _usedStorageBuffersWrite;
|
||||||
@@ -227,11 +232,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
UsedOutputAttributes |= 1 << index;
|
UsedOutputAttributes |= 1 << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInputUserAttribute(int index, bool perPatch)
|
public void SetInputUserAttribute(int index, int component, bool perPatch)
|
||||||
{
|
{
|
||||||
if (perPatch)
|
if (perPatch)
|
||||||
{
|
{
|
||||||
UsedInputAttributesPerPatch |= 1 << index;
|
UsedInputAttributesPerPatch |= 1 << index;
|
||||||
|
ThisInputAttributesPerPatchComponents |= UInt128.Pow2(index * 4 + component);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -239,6 +245,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
UsedInputAttributes |= mask;
|
UsedInputAttributes |= mask;
|
||||||
_thisUsedInputAttributes |= mask;
|
_thisUsedInputAttributes |= mask;
|
||||||
|
ThisInputAttributesComponents |= UInt128.Pow2(index * 4 + component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +263,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public void MergeFromtNextStage(ShaderConfig config)
|
public void MergeFromtNextStage(ShaderConfig config)
|
||||||
{
|
{
|
||||||
|
NextInputAttributesComponents = config.ThisInputAttributesComponents;
|
||||||
|
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
|
||||||
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
||||||
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
||||||
}
|
}
|
||||||
@@ -319,6 +328,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public void SetAllInputUserAttributes()
|
public void SetAllInputUserAttributes()
|
||||||
{
|
{
|
||||||
UsedInputAttributes |= Constants.AllAttributesMask;
|
UsedInputAttributes |= Constants.AllAttributesMask;
|
||||||
|
ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAllOutputUserAttributes()
|
public void SetAllOutputUserAttributes()
|
||||||
|
@@ -214,24 +214,31 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
|
InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usedAttributes = context.Config.UsedOutputAttributes;
|
UInt128 usedAttributes = context.Config.NextInputAttributesComponents;
|
||||||
while (usedAttributes != 0)
|
while (usedAttributes != UInt128.Zero)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
int index = usedAttributes.TrailingZeroCount();
|
||||||
|
int vecIndex = index / 4;
|
||||||
|
|
||||||
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
|
usedAttributes &= ~UInt128.Pow2(index);
|
||||||
|
|
||||||
usedAttributes &= ~(1 << index);
|
// We don't need to initialize passthrough attributes.
|
||||||
|
if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usedAttributesPerPatch = context.Config.UsedOutputAttributesPerPatch;
|
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;
|
||||||
while (usedAttributesPerPatch != 0)
|
while (usedAttributesPerPatch != UInt128.Zero)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributesPerPatch);
|
int index = usedAttributesPerPatch.TrailingZeroCount();
|
||||||
|
|
||||||
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: true);
|
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: true);
|
||||||
|
|
||||||
usedAttributesPerPatch &= ~(1 << index);
|
usedAttributesPerPatch &= ~UInt128.Pow2(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.NextUsesFixedFuncAttributes)
|
if (config.NextUsesFixedFuncAttributes)
|
||||||
@@ -260,6 +267,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void InitializeOutputComponent(EmitterContext context, int attrOffset, bool perPatch)
|
||||||
|
{
|
||||||
|
int c = (attrOffset >> 2) & 3;
|
||||||
|
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitOps(EmitterContext context, Block block)
|
private static void EmitOps(EmitterContext context, Block block)
|
||||||
{
|
{
|
||||||
for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++)
|
for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++)
|
||||||
|
112
Ryujinx.Graphics.Shader/Translation/UInt128.cs
Normal file
112
Ryujinx.Graphics.Shader/Translation/UInt128.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
struct UInt128 : IEquatable<UInt128>
|
||||||
|
{
|
||||||
|
public static UInt128 Zero => new UInt128() { _v0 = 0, _v1 = 0 };
|
||||||
|
|
||||||
|
private ulong _v0;
|
||||||
|
private ulong _v1;
|
||||||
|
|
||||||
|
public UInt128(ulong low, ulong high)
|
||||||
|
{
|
||||||
|
_v0 = low;
|
||||||
|
_v1 = high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int TrailingZeroCount()
|
||||||
|
{
|
||||||
|
int count = BitOperations.TrailingZeroCount(_v0);
|
||||||
|
if (count == 64)
|
||||||
|
{
|
||||||
|
count += BitOperations.TrailingZeroCount(_v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 Pow2(int x)
|
||||||
|
{
|
||||||
|
if (x >= 64)
|
||||||
|
{
|
||||||
|
return new UInt128(0, 1UL << (x - 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UInt128(1UL << x, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator ~(UInt128 x)
|
||||||
|
{
|
||||||
|
return new UInt128(~x._v0, ~x._v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator &(UInt128 x, UInt128 y)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0 & y._v0, x._v1 & y._v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator |(UInt128 x, UInt128 y)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0 | y._v0, x._v1 | y._v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator <<(UInt128 x, int shift)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0, x._v1);
|
||||||
|
}
|
||||||
|
else if (shift >= 64)
|
||||||
|
{
|
||||||
|
return new UInt128(0, x._v0 << (shift - 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong shiftOut = x._v0 >> (64 - shift);
|
||||||
|
|
||||||
|
return new UInt128(x._v0 << shift, (x._v1 << shift) | shiftOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator >>(UInt128 x, int shift)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0, x._v1);
|
||||||
|
}
|
||||||
|
else if (shift >= 64)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v1 >> (shift - 64), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong shiftOut = x._v1 & ((1UL << shift) - 1);
|
||||||
|
|
||||||
|
return new UInt128((x._v0 >> shift) | (shiftOut << (64 - shift)), x._v1 >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(UInt128 x, UInt128 y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(UInt128 x, UInt128 y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is UInt128 other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(UInt128 other)
|
||||||
|
{
|
||||||
|
return _v0 == other._v0 && _v1 == other._v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(_v0, _v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -84,7 +84,10 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
MetaLoader metaData = ReadNpdm(codeFs);
|
MetaLoader metaData = ReadNpdm(codeFs);
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { TitleId }, _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
|
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
||||||
|
new[] { TitleId },
|
||||||
|
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
||||||
|
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
||||||
|
|
||||||
if (TitleId != 0)
|
if (TitleId != 0)
|
||||||
{
|
{
|
||||||
@@ -388,7 +391,10 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
MetaLoader metaData = ReadNpdm(codeFs);
|
MetaLoader metaData = ReadNpdm(codeFs);
|
||||||
|
|
||||||
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath());
|
_device.Configuration.VirtualFileSystem.ModLoader.CollectMods(
|
||||||
|
_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId),
|
||||||
|
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
|
||||||
|
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
|
||||||
|
|
||||||
if (controlNca != null)
|
if (controlNca != null)
|
||||||
{
|
{
|
||||||
@@ -481,14 +487,14 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length)
|
if (result.IsSuccess() && bytesRead == controlData.ByteSpan.Length)
|
||||||
{
|
{
|
||||||
titleName = controlData.Value.Titles[(int)device.System.State.DesiredTitleLanguage].Name.ToString();
|
titleName = controlData.Value.Title[(int)device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
{
|
{
|
||||||
titleName = controlData.Value.Titles.ToArray().FirstOrDefault(x => x.Name[0] != 0).Name.ToString();
|
titleName = controlData.Value.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
displayVersion = controlData.Value.DisplayVersion.ToString();
|
displayVersion = controlData.Value.DisplayVersionString.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -615,20 +621,20 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
ref ApplicationControlProperty nacp = ref ControlData.Value;
|
ref ApplicationControlProperty nacp = ref ControlData.Value;
|
||||||
|
|
||||||
programInfo.Name = nacp.Titles[(int)_device.System.State.DesiredTitleLanguage].Name.ToString();
|
programInfo.Name = nacp.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(programInfo.Name))
|
if (string.IsNullOrWhiteSpace(programInfo.Name))
|
||||||
{
|
{
|
||||||
programInfo.Name = nacp.Titles.ToArray().FirstOrDefault(x => x.Name[0] != 0).Name.ToString();
|
programInfo.Name = nacp.Title.ItemsRo.ToArray().FirstOrDefault(x => x.Name[0] != 0).NameString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nacp.PresenceGroupId != 0)
|
if (nacp.PresenceGroupId != 0)
|
||||||
{
|
{
|
||||||
programInfo.ProgramId = nacp.PresenceGroupId;
|
programInfo.ProgramId = nacp.PresenceGroupId;
|
||||||
}
|
}
|
||||||
else if (nacp.SaveDataOwnerId.Value != 0)
|
else if (nacp.SaveDataOwnerId != 0)
|
||||||
{
|
{
|
||||||
programInfo.ProgramId = nacp.SaveDataOwnerId.Value;
|
programInfo.ProgramId = nacp.SaveDataOwnerId;
|
||||||
}
|
}
|
||||||
else if (nacp.AddOnContentBaseId != 0)
|
else if (nacp.AddOnContentBaseId != 0)
|
||||||
{
|
{
|
||||||
@@ -776,14 +782,14 @@ namespace Ryujinx.HLE.HOS
|
|||||||
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
|
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
|
||||||
control.UserAccountSaveDataSize = 0x4000;
|
control.UserAccountSaveDataSize = 0x4000;
|
||||||
control.UserAccountSaveDataJournalSize = 0x4000;
|
control.UserAccountSaveDataJournalSize = 0x4000;
|
||||||
control.SaveDataOwnerId = applicationId;
|
control.SaveDataOwnerId = applicationId.Value;
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Application,
|
Logger.Warning?.Print(LogClass.Application,
|
||||||
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
|
||||||
}
|
}
|
||||||
|
|
||||||
HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient;
|
HorizonClient hos = _device.System.LibHacHorizonManager.RyujinxClient;
|
||||||
Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, ref control);
|
Result resultCode = hos.Fs.EnsureApplicationCacheStorage(out _, out _, applicationId, in control);
|
||||||
|
|
||||||
if (resultCode.IsFailure())
|
if (resultCode.IsFailure())
|
||||||
{
|
{
|
||||||
@@ -792,7 +798,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
resultCode = EnsureApplicationSaveData(hos.Fs, out _, applicationId, ref control, ref user);
|
resultCode = hos.Fs.EnsureApplicationSaveData(out _, applicationId, in control, in user);
|
||||||
|
|
||||||
if (resultCode.IsFailure())
|
if (resultCode.IsFailure())
|
||||||
{
|
{
|
||||||
|
@@ -466,7 +466,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
AudioRendererManager.Dispose();
|
AudioRendererManager.Dispose();
|
||||||
|
|
||||||
LibHacHorizonManager.AmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value);
|
LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure();
|
||||||
|
|
||||||
KernelContext.Dispose();
|
KernelContext.Dispose();
|
||||||
}
|
}
|
||||||
|
@@ -751,7 +751,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
|
||||||
if (currentThread.Owner != null &&
|
if (currentThread.Context.Running &&
|
||||||
|
currentThread.Owner != null &&
|
||||||
currentThread.GetUserDisableCount() != 0 &&
|
currentThread.GetUserDisableCount() != 0 &&
|
||||||
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
|
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
|
||||||
{
|
{
|
||||||
|
@@ -658,10 +658,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
|
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
|
||||||
{
|
{
|
||||||
return (context.GetPstateFlag(ARMeilleure.State.PState.NFlag) ? (1U << (int)ARMeilleure.State.PState.NFlag) : 0U) |
|
return context.Pstate & 0xFF0FFE20;
|
||||||
(context.GetPstateFlag(ARMeilleure.State.PState.ZFlag) ? (1U << (int)ARMeilleure.State.PState.ZFlag) : 0U) |
|
|
||||||
(context.GetPstateFlag(ARMeilleure.State.PState.CFlag) ? (1U << (int)ARMeilleure.State.PState.CFlag) : 0U) |
|
|
||||||
(context.GetPstateFlag(ARMeilleure.State.PState.VFlag) ? (1U << (int)ARMeilleure.State.PState.VFlag) : 0U);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThreadContext GetCurrentContext()
|
private ThreadContext GetCurrentContext()
|
||||||
@@ -1371,7 +1368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
PreferredCore = _originalPreferredCore;
|
PreferredCore = _originalPreferredCore;
|
||||||
AffinityMask = _originalAffinityMask;
|
AffinityMask = _originalAffinityMask;
|
||||||
|
|
||||||
if (AffinityMask != affinityMask)
|
if (AffinityMask != affinityMask)
|
||||||
{
|
{
|
||||||
if ((AffinityMask & 1UL << ActiveCore) != 0)
|
if ((AffinityMask & 1UL << ActiveCore) != 0)
|
||||||
|
@@ -24,6 +24,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
public HorizonClient BcatClient { get; private set; }
|
public HorizonClient BcatClient { get; private set; }
|
||||||
public HorizonClient FsClient { get; private set; }
|
public HorizonClient FsClient { get; private set; }
|
||||||
public HorizonClient NsClient { get; private set; }
|
public HorizonClient NsClient { get; private set; }
|
||||||
|
public HorizonClient PmClient { get; private set; }
|
||||||
public HorizonClient SdbClient { get; private set; }
|
public HorizonClient SdbClient { get; private set; }
|
||||||
|
|
||||||
private SharedRef<LibHacIReader> _arpIReader;
|
private SharedRef<LibHacIReader> _arpIReader;
|
||||||
@@ -65,6 +66,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
public void InitializeSystemClients()
|
public void InitializeSystemClients()
|
||||||
{
|
{
|
||||||
|
PmClient = Server.CreatePrivilegedHorizonClient();
|
||||||
AccountClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Account, StorageId.BuiltInSystem), AccountFsPermissions);
|
AccountClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Account, StorageId.BuiltInSystem), AccountFsPermissions);
|
||||||
AmClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Am, StorageId.BuiltInSystem), AmFsPermissions);
|
AmClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Am, StorageId.BuiltInSystem), AmFsPermissions);
|
||||||
NsClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Ns, StorageId.BuiltInSystem), NsFsPermissions);
|
NsClient = Server.CreateHorizonClient(new ProgramLocation(SystemProgramId.Ns, StorageId.BuiltInSystem), NsFsPermissions);
|
||||||
|
@@ -136,7 +136,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);
|
private static bool StrEquals(string s1, string s2) => string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath());
|
public string GetModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetModsPath());
|
||||||
|
public string GetSdModsBasePath() => EnsureBaseDirStructure(AppDataManager.GetSdModsPath());
|
||||||
|
|
||||||
private string EnsureBaseDirStructure(string modsBasePath)
|
private string EnsureBaseDirStructure(string modsBasePath)
|
||||||
{
|
{
|
||||||
@@ -695,7 +696,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
var buildIds = programs.Select(p => p switch
|
var buildIds = programs.Select(p => p switch
|
||||||
{
|
{
|
||||||
NsoExecutable nso => BitConverter.ToString(nso.BuildId.Bytes.ToArray()).Replace("-", "").TrimEnd('0'),
|
NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()).Replace("-", "").TrimEnd('0'),
|
||||||
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId).Replace("-", "").TrimEnd('0'),
|
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId).Replace("-", "").TrimEnd('0'),
|
||||||
_ => string.Empty
|
_ => string.Empty
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
@@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
var buildIds = executables.Select(e => (e switch
|
var buildIds = executables.Select(e => (e switch
|
||||||
{
|
{
|
||||||
NsoExecutable nso => BitConverter.ToString(nso.BuildId.Bytes.ToArray()),
|
NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()),
|
||||||
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId),
|
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId),
|
||||||
_ => ""
|
_ => ""
|
||||||
}).Replace("-", "").ToUpper());
|
}).Replace("-", "").ToUpper());
|
||||||
|
@@ -3,6 +3,7 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -25,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
|
|
||||||
public UserProfile LastOpenedUser { get; private set; }
|
public UserProfile LastOpenedUser { get; private set; }
|
||||||
|
|
||||||
public AccountManager(HorizonClient horizonClient)
|
public AccountManager(HorizonClient horizonClient, string initialProfileName = null)
|
||||||
{
|
{
|
||||||
_horizonClient = horizonClient;
|
_horizonClient = horizonClient;
|
||||||
|
|
||||||
@@ -43,7 +44,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OpenUser(_accountSaveDataManager.LastOpened);
|
UserId commandLineUserProfileOverride = default;
|
||||||
|
if (!string.IsNullOrEmpty(initialProfileName))
|
||||||
|
{
|
||||||
|
commandLineUserProfileOverride = _profiles.Values.FirstOrDefault(x => x.Name == initialProfileName)?.UserId ?? default;
|
||||||
|
if (commandLineUserProfileOverride.IsNull)
|
||||||
|
Logger.Warning?.Print(LogClass.Application, $"The command line specified profile named '{initialProfileName}' was not found");
|
||||||
|
}
|
||||||
|
OpenUser(commandLineUserProfileOverride.IsNull ? _accountSaveDataManager.LastOpened : commandLineUserProfileOverride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +176,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
|
|
||||||
private void DeleteSaveData(UserId userId)
|
private void DeleteSaveData(UserId userId)
|
||||||
{
|
{
|
||||||
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
var saveDataFilter = SaveDataFilter.Make(programId: default, saveType: default,
|
||||||
saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)userId.High, (ulong)userId.Low));
|
new LibHac.Fs.UserId((ulong)userId.High, (ulong)userId.Low), saveDataId: default, index: default);
|
||||||
|
|
||||||
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
|
||||||
|
|
||||||
|
@@ -169,7 +169,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
// TODO: Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current Pid and store the result (NACP file) internally.
|
// TODO: Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current Pid and store the result (NACP file) internally.
|
||||||
// But since we use LibHac and we load one Application at a time, it's not necessary.
|
// But since we use LibHac and we load one Application at a time, it's not necessary.
|
||||||
|
|
||||||
context.ResponseData.Write(context.Device.Application.ControlData.Value.UserAccountSwitchLock);
|
context.ResponseData.Write((byte)context.Device.Application.ControlData.Value.UserAccountSwitchLock);
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||||
|
|
||||||
|
@@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient;
|
HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient;
|
||||||
Result result = EnsureApplicationSaveData(hos.Fs, out long requiredSize, applicationId, ref control, ref userId);
|
Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in control, in userId);
|
||||||
|
|
||||||
context.ResponseData.Write(requiredSize);
|
context.ResponseData.Write(requiredSize);
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
// TODO: When above calls are implemented, switch to using ns:am
|
// TODO: When above calls are implemented, switch to using ns:am
|
||||||
|
|
||||||
long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode;
|
long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode;
|
||||||
int supportedLanguages = (int)context.Device.Application.ControlData.Value.SupportedLanguages;
|
int supportedLanguages = (int)context.Device.Application.ControlData.Value.SupportedLanguageFlag;
|
||||||
int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages);
|
int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages);
|
||||||
|
|
||||||
if (firstSupported > (int)SystemState.TitleLanguage.BrazilianPortuguese)
|
if (firstSupported > (int)SystemState.TitleLanguage.BrazilianPortuguese)
|
||||||
@@ -190,7 +190,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
// GetDisplayVersion() -> nn::oe::DisplayVersion
|
// GetDisplayVersion() -> nn::oe::DisplayVersion
|
||||||
public ResultCode GetDisplayVersion(ServiceCtx context)
|
public ResultCode GetDisplayVersion(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// This should work as DisplayVersion U8Span always gives a 0x10 size byte array.
|
|
||||||
// If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation.
|
// If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation.
|
||||||
context.ResponseData.Write(context.Device.Application.ControlData.Value.DisplayVersion);
|
context.ResponseData.Write(context.Device.Application.ControlData.Value.DisplayVersion);
|
||||||
|
|
||||||
@@ -252,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||||||
BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData;
|
BlitStruct<ApplicationControlProperty> controlHolder = context.Device.Application.ControlData;
|
||||||
|
|
||||||
Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize,
|
Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize,
|
||||||
out CacheStorageTargetMedia storageTarget, applicationId, ref controlHolder.Value, index, saveSize,
|
out CacheStorageTargetMedia storageTarget, applicationId, in controlHolder.Value, index, saveSize,
|
||||||
journalSize);
|
journalSize);
|
||||||
|
|
||||||
if (result.IsFailure()) return (ResultCode)result.Value;
|
if (result.IsFailure()) return (ResultCode)result.Value;
|
||||||
|
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
|
|||||||
{
|
{
|
||||||
launchProperty = new LibHac.Arp.ApplicationLaunchProperty
|
launchProperty = new LibHac.Arp.ApplicationLaunchProperty
|
||||||
{
|
{
|
||||||
BaseStorageId = StorageId.BuiltInUser,
|
StorageId = StorageId.BuiltInUser,
|
||||||
ApplicationId = ApplicationId
|
ApplicationId = ApplicationId
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
|
|||||||
{
|
{
|
||||||
launchProperty = new LibHac.Arp.ApplicationLaunchProperty
|
launchProperty = new LibHac.Arp.ApplicationLaunchProperty
|
||||||
{
|
{
|
||||||
BaseStorageId = StorageId.BuiltInUser,
|
StorageId = StorageId.BuiltInUser,
|
||||||
ApplicationId = applicationId
|
ApplicationId = applicationId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -132,22 +132,10 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result ReadFsPath(out FsPath path, ServiceCtx context, int index = 0)
|
|
||||||
{
|
|
||||||
ulong position = context.Request.PtrBuff[index].Position;
|
|
||||||
ulong size = context.Request.PtrBuff[index].Size;
|
|
||||||
|
|
||||||
byte[] pathBytes = new byte[size];
|
|
||||||
|
|
||||||
context.Memory.Read(position, pathBytes);
|
|
||||||
|
|
||||||
return FsPath.FromSpan(out path, pathBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ref readonly FspPath GetFspPath(ServiceCtx context, int index = 0)
|
public static ref readonly FspPath GetFspPath(ServiceCtx context, int index = 0)
|
||||||
{
|
{
|
||||||
ulong position = (ulong)context.Request.PtrBuff[index].Position;
|
ulong position = context.Request.PtrBuff[index].Position;
|
||||||
ulong size = (ulong)context.Request.PtrBuff[index].Size;
|
ulong size = context.Request.PtrBuff[index].Size;
|
||||||
|
|
||||||
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
|
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
|
||||||
ReadOnlySpan<FspPath> fspBuffer = MemoryMarshal.Cast<byte, FspPath>(buffer);
|
ReadOnlySpan<FspPath> fspBuffer = MemoryMarshal.Cast<byte, FspPath>(buffer);
|
||||||
@@ -157,8 +145,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
|||||||
|
|
||||||
public static ref readonly LibHac.FsSrv.Sf.Path GetSfPath(ServiceCtx context, int index = 0)
|
public static ref readonly LibHac.FsSrv.Sf.Path GetSfPath(ServiceCtx context, int index = 0)
|
||||||
{
|
{
|
||||||
ulong position = (ulong)context.Request.PtrBuff[index].Position;
|
ulong position = context.Request.PtrBuff[index].Position;
|
||||||
ulong size = (ulong)context.Request.PtrBuff[index].Size;
|
ulong size = context.Request.PtrBuff[index].Size;
|
||||||
|
|
||||||
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
|
ReadOnlySpan<byte> buffer = context.Memory.GetSpan(position, (int)size);
|
||||||
ReadOnlySpan<LibHac.FsSrv.Sf.Path> pathBuffer = MemoryMarshal.Cast<byte, LibHac.FsSrv.Sf.Path>(buffer);
|
ReadOnlySpan<LibHac.FsSrv.Sf.Path> pathBuffer = MemoryMarshal.Cast<byte, LibHac.FsSrv.Sf.Path>(buffer);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.FsSrv;
|
using LibHac.Fs;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||||
{
|
{
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Olsc
|
namespace Ryujinx.HLE.HOS.Services.Olsc
|
||||||
{
|
{
|
||||||
[Service("olsc:u")] // 10.0.0+
|
[Service("olsc:u")] // 10.0.0+
|
||||||
class IOlscServiceForApplication : IpcService
|
class IOlscServiceForApplication : IpcService
|
||||||
{
|
{
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
private Dictionary<UserId, bool> _saveDataBackupSettingDatabase;
|
||||||
|
|
||||||
public IOlscServiceForApplication(ServiceCtx context) { }
|
public IOlscServiceForApplication(ServiceCtx context) { }
|
||||||
|
|
||||||
@@ -16,7 +18,9 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
public ResultCode Initialize(ServiceCtx context)
|
public ResultCode Initialize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// NOTE: Service call arp:r GetApplicationInstanceUnregistrationNotifier with the pid and initialize some internal struct.
|
// NOTE: Service call arp:r GetApplicationInstanceUnregistrationNotifier with the pid and initialize some internal struct.
|
||||||
// Since we will not support online savedata backup. It's fine to stub it for now.
|
// Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
|
_saveDataBackupSettingDatabase = new Dictionary<UserId, bool>();
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
@@ -25,12 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(14)]
|
[CommandHipc(13)]
|
||||||
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
|
// GetSaveDataBackupSetting(nn::account::Uid) -> u8
|
||||||
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
|
public ResultCode GetSaveDataBackupSetting(ServiceCtx context)
|
||||||
{
|
{
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
ulong saveDataBackupSettingEnabled = context.RequestData.ReadUInt64();
|
|
||||||
|
|
||||||
if (!_initialized)
|
if (!_initialized)
|
||||||
{
|
{
|
||||||
@@ -42,8 +45,42 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
return ResultCode.NullArgument;
|
return ResultCode.NullArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Service store the UserId and the boolean in an internal SaveDataBackupSettingDatabase object.
|
if (_saveDataBackupSettingDatabase[userId])
|
||||||
// Since we will not support online savedata backup. It's fine to stub it for now.
|
{
|
||||||
|
context.ResponseData.Write((byte)1); // TODO: Determine value.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((byte)2); // TODO: Determine value.
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(14)]
|
||||||
|
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
|
||||||
|
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool saveDataBackupSettingEnabled = context.RequestData.ReadUInt64() != 0;
|
||||||
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
|
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return ResultCode.NotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId.IsNull)
|
||||||
|
{
|
||||||
|
return ResultCode.NullArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveDataBackupSettingDatabase[userId] = saveDataBackupSettingEnabled;
|
||||||
|
|
||||||
|
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId, saveDataBackupSettingEnabled });
|
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId, saveDataBackupSettingEnabled });
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
using LibHac.Ns;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Arp;
|
using Ryujinx.HLE.HOS.Services.Arp;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
using static LibHac.Ns.ApplicationControlProperty;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
||||||
{
|
{
|
||||||
class IParentalControlService : IpcService
|
class IParentalControlService : IpcService
|
||||||
@@ -52,8 +53,8 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
|||||||
_titleId = titleId;
|
_titleId = titleId;
|
||||||
|
|
||||||
// TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields.
|
// TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields.
|
||||||
_ratingAge = Array.ConvertAll(context.Device.Application.ControlData.Value.RatingAge.ToArray(), Convert.ToInt32);
|
_ratingAge = Array.ConvertAll(context.Device.Application.ControlData.Value.RatingAge.ItemsRo.ToArray(), Convert.ToInt32);
|
||||||
_parentalControlFlag = context.Device.Application.ControlData.Value.ParentalControl;
|
_parentalControlFlag = context.Device.Application.ControlData.Value.ParentalControlFlag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
|||||||
private ResultCode IsStereoVisionPermittedImpl()
|
private ResultCode IsStereoVisionPermittedImpl()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
// TODO: Application Exemptions are readed from file "appExemptions.dat" in the service savedata.
|
// TODO: Application Exemptions are read from file "appExemptions.dat" in the service savedata.
|
||||||
// Since we don't support the pctl savedata for now, this can be implemented later.
|
// Since we don't support the pctl savedata for now, this can be implemented later.
|
||||||
|
|
||||||
if (appExemption)
|
if (appExemption)
|
||||||
|
@@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
|
|||||||
|
|
||||||
internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false)
|
internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false)
|
||||||
{
|
{
|
||||||
|
ref readonly var controlProperty = ref context.Device.Application.ControlData.Value;
|
||||||
|
|
||||||
ulong inputPosition = context.Request.SendBuff[0].Position;
|
ulong inputPosition = context.Request.SendBuff[0].Position;
|
||||||
ulong inputSize = context.Request.SendBuff[0].Size;
|
ulong inputSize = context.Request.SendBuff[0].Size;
|
||||||
|
|
||||||
@@ -31,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Application.ControlData.Value.PlayLogQueryCapability;
|
PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)controlProperty.PlayLogQueryCapability;
|
||||||
|
|
||||||
List<ulong> titleIds = new List<ulong>();
|
List<ulong> titleIds = new List<ulong>();
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
|
|||||||
// Check if input title ids are in the whitelist.
|
// Check if input title ids are in the whitelist.
|
||||||
foreach (ulong titleId in titleIds)
|
foreach (ulong titleId in titleIds)
|
||||||
{
|
{
|
||||||
if (!context.Device.Application.ControlData.Value.PlayLogQueryableApplicationId.Contains(titleId))
|
if (!controlProperty.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId))
|
||||||
{
|
{
|
||||||
return (ResultCode)Am.ResultCode.ObjectInvalid;
|
return (ResultCode)Am.ResultCode.ObjectInvalid;
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
|
|||||||
|
|
||||||
if (serviceType != ViServiceType.Application)
|
if (serviceType != ViServiceType.Application)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new IApplicationDisplayService(serviceType));
|
MakeObject(context, new IApplicationDisplayService(serviceType));
|
||||||
|
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
|
|||||||
|
|
||||||
if (serviceType != ViServiceType.Manager)
|
if (serviceType != ViServiceType.Manager)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new IApplicationDisplayService(serviceType));
|
MakeObject(context, new IApplicationDisplayService(serviceType));
|
||||||
|
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
|
|||||||
|
|
||||||
if (serviceType != ViServiceType.System)
|
if (serviceType != ViServiceType.System)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new IApplicationDisplayService(serviceType));
|
MakeObject(context, new IApplicationDisplayService(serviceType));
|
||||||
|
@@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
|
|||||||
|
|
||||||
InvalidArguments = (1 << ErrorCodeShift) | ModuleId,
|
InvalidArguments = (1 << ErrorCodeShift) | ModuleId,
|
||||||
InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId,
|
InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId,
|
||||||
InvalidRange = (5 << ErrorCodeShift) | ModuleId,
|
PermissionDenied = (5 << ErrorCodeShift) | ModuleId,
|
||||||
InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId,
|
InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId,
|
||||||
InvalidValue = (7 << ErrorCodeShift) | ModuleId,
|
InvalidValue = (7 << ErrorCodeShift) | ModuleId,
|
||||||
AlreadyOpened = (9 << ErrorCodeShift) | ModuleId
|
AlreadyOpened = (9 << ErrorCodeShift) | ModuleId
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Cpu;
|
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
@@ -22,16 +21,21 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
{
|
{
|
||||||
private readonly ViServiceType _serviceType;
|
private readonly ViServiceType _serviceType;
|
||||||
|
|
||||||
private readonly List<DisplayInfo> _displayInfo;
|
private class DisplayState
|
||||||
private readonly Dictionary<ulong, DisplayInfo> _openDisplayInfo;
|
{
|
||||||
|
public int RetrievedEventsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<DisplayInfo> _displayInfo;
|
||||||
|
private readonly Dictionary<ulong, DisplayState> _openDisplays;
|
||||||
|
|
||||||
private int _vsyncEventHandle;
|
private int _vsyncEventHandle;
|
||||||
|
|
||||||
public IApplicationDisplayService(ViServiceType serviceType)
|
public IApplicationDisplayService(ViServiceType serviceType)
|
||||||
{
|
{
|
||||||
_serviceType = serviceType;
|
_serviceType = serviceType;
|
||||||
_displayInfo = new List<DisplayInfo>();
|
_displayInfo = new List<DisplayInfo>();
|
||||||
_openDisplayInfo = new Dictionary<ulong, DisplayInfo>();
|
_openDisplays = new Dictionary<ulong, DisplayState>();
|
||||||
|
|
||||||
void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height)
|
void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height)
|
||||||
{
|
{
|
||||||
@@ -64,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
// FIXME: Should be _serviceType != ViServiceType.Application but guests crashes if we do this check.
|
// FIXME: Should be _serviceType != ViServiceType.Application but guests crashes if we do this check.
|
||||||
if (_serviceType > ViServiceType.System)
|
if (_serviceType > ViServiceType.System)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new HOSBinderDriverServer());
|
MakeObject(context, new HOSBinderDriverServer());
|
||||||
@@ -79,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
// FIXME: Should be _serviceType == ViServiceType.System but guests crashes if we do this check.
|
// FIXME: Should be _serviceType == ViServiceType.System but guests crashes if we do this check.
|
||||||
if (_serviceType > ViServiceType.System)
|
if (_serviceType > ViServiceType.System)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new ISystemDisplayService(this));
|
MakeObject(context, new ISystemDisplayService(this));
|
||||||
@@ -93,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
{
|
{
|
||||||
if (_serviceType > ViServiceType.System)
|
if (_serviceType > ViServiceType.System)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new IManagerDisplayService(this));
|
MakeObject(context, new IManagerDisplayService(this));
|
||||||
@@ -107,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
{
|
{
|
||||||
if (_serviceType > ViServiceType.System)
|
if (_serviceType > ViServiceType.System)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidRange;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeObject(context, new HOSBinderDriverServer());
|
MakeObject(context, new HOSBinderDriverServer());
|
||||||
@@ -174,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
return ResultCode.InvalidValue;
|
return ResultCode.InvalidValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_openDisplayInfo.TryAdd((ulong)displayId, _displayInfo[displayId]))
|
if (!_openDisplays.TryAdd((ulong)displayId, new DisplayState()))
|
||||||
{
|
{
|
||||||
return ResultCode.AlreadyOpened;
|
return ResultCode.AlreadyOpened;
|
||||||
}
|
}
|
||||||
@@ -190,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
{
|
{
|
||||||
ulong displayId = context.RequestData.ReadUInt64();
|
ulong displayId = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
if (!_openDisplayInfo.Remove(displayId))
|
if (!_openDisplays.Remove(displayId))
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidValue;
|
return ResultCode.InvalidValue;
|
||||||
}
|
}
|
||||||
@@ -454,11 +458,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
{
|
{
|
||||||
ulong displayId = context.RequestData.ReadUInt64();
|
ulong displayId = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
if (!_openDisplayInfo.ContainsKey(displayId))
|
if (!_openDisplays.TryGetValue(displayId, out DisplayState displayState))
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidValue;
|
return ResultCode.InvalidValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (displayState.RetrievedEventsCount > 0)
|
||||||
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
if (_vsyncEventHandle == 0)
|
if (_vsyncEventHandle == 0)
|
||||||
{
|
{
|
||||||
if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success)
|
if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success)
|
||||||
@@ -467,6 +476,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayState.RetrievedEventsCount++;
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Loader;
|
using LibHac.Loader;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
@@ -26,8 +26,8 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
public uint DataSize { get; }
|
public uint DataSize { get; }
|
||||||
public uint BssSize { get; }
|
public uint BssSize { get; }
|
||||||
|
|
||||||
public string Name;
|
public string Name;
|
||||||
public Buffer32 BuildId;
|
public Array32<byte> BuildId;
|
||||||
|
|
||||||
public NsoExecutable(IStorage inStorage, string name = null)
|
public NsoExecutable(IStorage inStorage, string name = null)
|
||||||
{
|
{
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Concentus" Version="1.1.7" />
|
<PackageReference Include="Concentus" Version="1.1.7" />
|
||||||
<PackageReference Include="LibHac" Version="0.15.0" />
|
<PackageReference Include="LibHac" Version="0.16.0" />
|
||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
|
@@ -220,6 +220,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
StickButton = ConfigGamepadInputId.LeftStick,
|
StickButton = ConfigGamepadInputId.LeftStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
@@ -241,6 +242,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
StickButton = ConfigGamepadInputId.RightStick,
|
StickButton = ConfigGamepadInputId.RightStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Motion = new StandardMotionConfigController
|
Motion = new StandardMotionConfigController
|
||||||
@@ -310,7 +312,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
{
|
{
|
||||||
controllerConfig.RangeLeft = 1.0f;
|
controllerConfig.RangeLeft = 1.0f;
|
||||||
controllerConfig.RangeRight = 1.0f;
|
controllerConfig.RangeRight = 1.0f;
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration");
|
Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,7 +398,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
if ((bool)option.EnableFileLog)
|
if ((bool)option.EnableFileLog)
|
||||||
{
|
{
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
new FileLogTarget(AppDomain.CurrentDomain.BaseDirectory, "file"),
|
new FileLogTarget(ReleaseInformations.GetBaseApplicationDirectory(), "file"),
|
||||||
1000,
|
1000,
|
||||||
AsyncLogTargetOverflowAction.Block
|
AsyncLogTargetOverflowAction.Block
|
||||||
));
|
));
|
||||||
|
@@ -350,6 +350,14 @@ namespace Ryujinx.Input.SDL2
|
|||||||
{
|
{
|
||||||
resultY = -resultY;
|
resultY = -resultY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.Rotate90CW) ||
|
||||||
|
(inputId == StickInputId.Right && _configuration.RightJoyconStick.Rotate90CW))
|
||||||
|
{
|
||||||
|
float temp = resultX;
|
||||||
|
resultX = resultY;
|
||||||
|
resultY = -temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (resultX, resultY);
|
return (resultX, resultY);
|
||||||
|
@@ -50,6 +50,13 @@ namespace Ryujinx.Memory.Range
|
|||||||
ulong otherAddress = other.Address;
|
ulong otherAddress = other.Address;
|
||||||
ulong otherEndAddress = other.EndAddress;
|
ulong otherEndAddress = other.EndAddress;
|
||||||
|
|
||||||
|
// If any of the ranges if invalid (address + size overflows),
|
||||||
|
// then they are never considered to overlap.
|
||||||
|
if (thisEndAddress < thisAddress || otherEndAddress < otherAddress)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
|
return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,6 +29,12 @@ namespace Ryujinx.Memory.Range
|
|||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
{
|
{
|
||||||
var subrange = range.GetSubRange(i);
|
var subrange = range.GetSubRange(i);
|
||||||
|
|
||||||
|
if (IsInvalid(ref subrange))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_items.Add(subrange.Address, subrange.EndAddress, item);
|
_items.Add(subrange.Address, subrange.EndAddress, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +55,12 @@ namespace Ryujinx.Memory.Range
|
|||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
{
|
{
|
||||||
var subrange = range.GetSubRange(i);
|
var subrange = range.GetSubRange(i);
|
||||||
|
|
||||||
|
if (IsInvalid(ref subrange))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
removed += _items.Remove(subrange.Address, item);
|
removed += _items.Remove(subrange.Address, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +98,12 @@ namespace Ryujinx.Memory.Range
|
|||||||
for (int i = 0; i < range.Count; i++)
|
for (int i = 0; i < range.Count; i++)
|
||||||
{
|
{
|
||||||
var subrange = range.GetSubRange(i);
|
var subrange = range.GetSubRange(i);
|
||||||
|
|
||||||
|
if (IsInvalid(ref subrange))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
overlapCount = _items.Get(subrange.Address, subrange.EndAddress, ref output, overlapCount);
|
overlapCount = _items.Get(subrange.Address, subrange.EndAddress, ref output, overlapCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +142,17 @@ namespace Ryujinx.Memory.Range
|
|||||||
return overlapCount;
|
return overlapCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a given sub-range of memory is invalid.
|
||||||
|
/// Those are used to represent unmapped memory regions (holes in the region mapping).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="subRange">Memory range to checl</param>
|
||||||
|
/// <returns>True if the memory range is considered invalid, false otherwise</returns>
|
||||||
|
private static bool IsInvalid(ref MemoryRange subRange)
|
||||||
|
{
|
||||||
|
return subRange.Address == ulong.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all items on the list starting at the specified memory address.
|
/// Gets all items on the list starting at the specified memory address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -81,7 +82,7 @@ namespace Ryujinx.SDL2.Common
|
|||||||
|
|
||||||
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
SDL_EventState(SDL_EventType.SDL_CONTROLLERSENSORUPDATE, SDL_DISABLE);
|
||||||
|
|
||||||
string gamepadDbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SDL_GameControllerDB.txt");
|
string gamepadDbPath = Path.Combine(ReleaseInformations.GetBaseApplicationDirectory(), "SDL_GameControllerDB.txt");
|
||||||
|
|
||||||
if (File.Exists(gamepadDbPath))
|
if (File.Exists(gamepadDbPath))
|
||||||
{
|
{
|
||||||
|
@@ -257,6 +257,35 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
return GetContext();
|
return GetContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RunPrecomputedTestCase(PrecomputedThumbTestCase test)
|
||||||
|
{
|
||||||
|
foreach (ushort instruction in test.Instructions)
|
||||||
|
{
|
||||||
|
ThumbOpcode(instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
GetContext().SetX(i, test.StartRegs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint startCpsr = test.StartRegs[15];
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
GetContext().SetPstateFlag((PState)i, (startCpsr & (1u << i)) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
Assert.That(GetContext().GetX(i), Is.EqualTo(test.FinalRegs[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint finalCpsr = test.FinalRegs[15];
|
||||||
|
Assert.That(GetContext().Pstate, Is.EqualTo(finalCpsr));
|
||||||
|
}
|
||||||
|
|
||||||
protected void SetWorkingMemory(uint offset, byte[] data)
|
protected void SetWorkingMemory(uint offset, byte[] data)
|
||||||
{
|
{
|
||||||
_memory.Write(DataBaseAddress + offset, data);
|
_memory.Write(DataBaseAddress + offset, data);
|
||||||
|
57
Ryujinx.Tests/Cpu/CpuTestAluImm32.cs
Normal file
57
Ryujinx.Tests/Cpu/CpuTestAluImm32.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#define AluRs32
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Cpu
|
||||||
|
{
|
||||||
|
[Category("AluImm32")]
|
||||||
|
public sealed class CpuTestAluImm32 : CpuTest32
|
||||||
|
{
|
||||||
|
#if AluRs32
|
||||||
|
|
||||||
|
#region "ValueSource (Opcodes)"
|
||||||
|
private static uint[] _opcodes()
|
||||||
|
{
|
||||||
|
return new uint[]
|
||||||
|
{
|
||||||
|
0xe2a00000u, // ADC R0, R0, #0
|
||||||
|
0xe2b00000u, // ADCS R0, R0, #0
|
||||||
|
0xe2800000u, // ADD R0, R0, #0
|
||||||
|
0xe2900000u, // ADDS R0, R0, #0
|
||||||
|
0xe3c00000u, // BIC R0, R0, #0
|
||||||
|
0xe3d00000u, // BICS R0, R0, #0
|
||||||
|
0xe2600000u, // RSB R0, R0, #0
|
||||||
|
0xe2700000u, // RSBS R0, R0, #0
|
||||||
|
0xe2e00000u, // RSC R0, R0, #0
|
||||||
|
0xe2f00000u, // RSCS R0, R0, #0
|
||||||
|
0xe2c00000u, // SBC R0, R0, #0
|
||||||
|
0xe2d00000u, // SBCS R0, R0, #0
|
||||||
|
0xe2400000u, // SUB R0, R0, #0
|
||||||
|
0xe2500000u, // SUBS R0, R0, #0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private const int RndCnt = 2;
|
||||||
|
private const int RndCntAmount = 2;
|
||||||
|
|
||||||
|
[Test, Pairwise]
|
||||||
|
public void TestCpuTestAluImm32([ValueSource("_opcodes")] uint opcode,
|
||||||
|
[Values(0u, 13u)] uint rd,
|
||||||
|
[Values(1u, 13u)] uint rn,
|
||||||
|
[Random(RndCnt)] uint imm,
|
||||||
|
[Random(RndCnt)] uint wn,
|
||||||
|
[Values(true, false)] bool carryIn)
|
||||||
|
{
|
||||||
|
opcode |= ((imm & 0xfff) << 0) | ((rn & 15) << 16) | ((rd & 15) << 12);
|
||||||
|
|
||||||
|
uint sp = TestContext.CurrentContext.Random.NextUInt();
|
||||||
|
|
||||||
|
SingleOpcode(opcode, r1: wn, sp: sp, carry: carryIn);
|
||||||
|
|
||||||
|
CompareAgainstUnicorn();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
1016
Ryujinx.Tests/Cpu/CpuTestT32Alu.cs
Normal file
1016
Ryujinx.Tests/Cpu/CpuTestT32Alu.cs
Normal file
File diff suppressed because it is too large
Load Diff
167
Ryujinx.Tests/Cpu/CpuTestT32Flow.cs
Normal file
167
Ryujinx.Tests/Cpu/CpuTestT32Flow.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
using ARMeilleure.State;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.Cpu
|
||||||
|
{
|
||||||
|
[Category("T32Flow")]
|
||||||
|
public sealed class CpuTestT32Flow : CpuTest32
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestT32B1()
|
||||||
|
{
|
||||||
|
// BNE label
|
||||||
|
ThumbOpcode(0xf040);
|
||||||
|
ThumbOpcode(0x8240);
|
||||||
|
for (int i = 0; i < 576; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0xe7fe);
|
||||||
|
}
|
||||||
|
// label: BX LR
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32B2()
|
||||||
|
{
|
||||||
|
// BNE label1
|
||||||
|
ThumbOpcode(0xf040);
|
||||||
|
ThumbOpcode(0x8242);
|
||||||
|
// label2: BNE label3
|
||||||
|
ThumbOpcode(0xf040);
|
||||||
|
ThumbOpcode(0x8242);
|
||||||
|
for (int i = 0; i < 576; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0xe7fe);
|
||||||
|
}
|
||||||
|
// label1: BNE label2
|
||||||
|
ThumbOpcode(0xf47f);
|
||||||
|
ThumbOpcode(0xadbc);
|
||||||
|
// label3: BX LR
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32B3()
|
||||||
|
{
|
||||||
|
// B.W label
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xba40);
|
||||||
|
for (int i = 0; i < 576; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0xe7fe);
|
||||||
|
}
|
||||||
|
// label: BX LR
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32B4()
|
||||||
|
{
|
||||||
|
// B.W label1
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xba42);
|
||||||
|
// label2: B.W label3
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xba42);
|
||||||
|
for (int i = 0; i < 576; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0xe7fe);
|
||||||
|
}
|
||||||
|
// label1: B.W label2
|
||||||
|
ThumbOpcode(0xf7ff);
|
||||||
|
ThumbOpcode(0xbdbc);
|
||||||
|
// label3: BX LR
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32Bl()
|
||||||
|
{
|
||||||
|
// BL label
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xf840);
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0xe7fe);
|
||||||
|
}
|
||||||
|
ThumbOpcode(0x4670); // label: MOV R0, LR
|
||||||
|
ThumbOpcode(0x2100); // MOVS R1, #0
|
||||||
|
ThumbOpcode(0x468e); // MOV LR, R1
|
||||||
|
ThumbOpcode(0x4770); // BX LR
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
|
||||||
|
Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32Blx1()
|
||||||
|
{
|
||||||
|
// BLX label
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xe840);
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
}
|
||||||
|
// .arm ; label: MOV R0, LR
|
||||||
|
Opcode(0xe1a0000e);
|
||||||
|
// MOV LR, #0
|
||||||
|
Opcode(0xe3a0e000);
|
||||||
|
// BX LR
|
||||||
|
Opcode(0xe12fff1e);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
|
||||||
|
Assert.That(GetContext().GetX(0), Is.EqualTo(0x1005));
|
||||||
|
Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestT32Blx2()
|
||||||
|
{
|
||||||
|
// NOP
|
||||||
|
ThumbOpcode(0xbf00);
|
||||||
|
// BLX label
|
||||||
|
ThumbOpcode(0xf000);
|
||||||
|
ThumbOpcode(0xe840);
|
||||||
|
for (int i = 0; i < 63; i++)
|
||||||
|
{
|
||||||
|
ThumbOpcode(0x4770);
|
||||||
|
}
|
||||||
|
// .arm ; label: MOV R0, LR
|
||||||
|
Opcode(0xe1a0000e);
|
||||||
|
// MOV LR, #0
|
||||||
|
Opcode(0xe3a0e000);
|
||||||
|
// BX LR
|
||||||
|
Opcode(0xe12fff1e);
|
||||||
|
|
||||||
|
GetContext().SetPstateFlag(PState.TFlag, true);
|
||||||
|
|
||||||
|
ExecuteOpcodes(runUnicorn: false);
|
||||||
|
|
||||||
|
Assert.That(GetContext().GetX(0), Is.EqualTo(0x1007));
|
||||||
|
Assert.That(GetContext().GetPstateFlag(PState.TFlag), Is.EqualTo(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
9
Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs
Normal file
9
Ryujinx.Tests/Cpu/PrecomputedThumbTestCase.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Tests.Cpu
|
||||||
|
{
|
||||||
|
public class PrecomputedThumbTestCase
|
||||||
|
{
|
||||||
|
public ushort[] Instructions;
|
||||||
|
public uint[] StartRegs;
|
||||||
|
public uint[] FinalRegs;
|
||||||
|
}
|
||||||
|
}
|
@@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 36;
|
public const int CurrentVersion = 37;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@@ -236,6 +236,11 @@ namespace Ryujinx.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool StartFullscreen { get; set; }
|
public bool StartFullscreen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show console window
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowConsole { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable or disable keyboard support (Independent from controllers binding)
|
/// Enable or disable keyboard support (Independent from controllers binding)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -6,6 +6,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Configuration.System;
|
using Ryujinx.Configuration.System;
|
||||||
using Ryujinx.Configuration.Ui;
|
using Ryujinx.Configuration.Ui;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -88,6 +89,11 @@ namespace Ryujinx.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> StartFullscreen { get; private set; }
|
public ReactiveObject<bool> StartFullscreen { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide / Show Console Window
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> ShowConsole { get; private set; }
|
||||||
|
|
||||||
public UiSection()
|
public UiSection()
|
||||||
{
|
{
|
||||||
GuiColumns = new Columns();
|
GuiColumns = new Columns();
|
||||||
@@ -96,6 +102,8 @@ namespace Ryujinx.Configuration
|
|||||||
EnableCustomTheme = new ReactiveObject<bool>();
|
EnableCustomTheme = new ReactiveObject<bool>();
|
||||||
CustomThemePath = new ReactiveObject<string>();
|
CustomThemePath = new ReactiveObject<string>();
|
||||||
StartFullscreen = new ReactiveObject<bool>();
|
StartFullscreen = new ReactiveObject<bool>();
|
||||||
|
ShowConsole = new ReactiveObject<bool>();
|
||||||
|
ShowConsole.Event += static (s, e) => { ConsoleHelper.SetConsoleWindowState(e.NewValue); };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,6 +516,7 @@ namespace Ryujinx.Configuration
|
|||||||
EnableCustomTheme = Ui.EnableCustomTheme,
|
EnableCustomTheme = Ui.EnableCustomTheme,
|
||||||
CustomThemePath = Ui.CustomThemePath,
|
CustomThemePath = Ui.CustomThemePath,
|
||||||
StartFullscreen = Ui.StartFullscreen,
|
StartFullscreen = Ui.StartFullscreen,
|
||||||
|
ShowConsole = Ui.ShowConsole,
|
||||||
EnableKeyboard = Hid.EnableKeyboard,
|
EnableKeyboard = Hid.EnableKeyboard,
|
||||||
EnableMouse = Hid.EnableMouse,
|
EnableMouse = Hid.EnableMouse,
|
||||||
Hotkeys = Hid.Hotkeys,
|
Hotkeys = Hid.Hotkeys,
|
||||||
@@ -574,6 +583,7 @@ namespace Ryujinx.Configuration
|
|||||||
Ui.EnableCustomTheme.Value = false;
|
Ui.EnableCustomTheme.Value = false;
|
||||||
Ui.CustomThemePath.Value = "";
|
Ui.CustomThemePath.Value = "";
|
||||||
Ui.StartFullscreen.Value = false;
|
Ui.StartFullscreen.Value = false;
|
||||||
|
Ui.ShowConsole.Value = true;
|
||||||
Hid.EnableKeyboard.Value = false;
|
Hid.EnableKeyboard.Value = false;
|
||||||
Hid.EnableMouse.Value = false;
|
Hid.EnableMouse.Value = false;
|
||||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||||
@@ -995,7 +1005,7 @@ namespace Ryujinx.Configuration
|
|||||||
controllerConfig.RangeRight = 1.0f;
|
controllerConfig.RangeRight = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,7 +1017,16 @@ namespace Ryujinx.Configuration
|
|||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configurationFileFormat.Version < 37)
|
||||||
|
{
|
||||||
|
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 37.");
|
||||||
|
|
||||||
|
configurationFileFormat.ShowConsole = true;
|
||||||
|
|
||||||
|
configurationFileUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||||
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
||||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||||
@@ -1061,6 +1080,7 @@ namespace Ryujinx.Configuration
|
|||||||
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
||||||
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
||||||
Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen;
|
Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen;
|
||||||
|
Ui.ShowConsole.Value = configurationFileFormat.ShowConsole;
|
||||||
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
|
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
|
||||||
Hid.EnableMouse.Value = configurationFileFormat.EnableMouse;
|
Hid.EnableMouse.Value = configurationFileFormat.EnableMouse;
|
||||||
Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
|
Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
|
||||||
|
@@ -80,7 +80,7 @@ namespace Ryujinx.Configuration
|
|||||||
if (e.NewValue)
|
if (e.NewValue)
|
||||||
{
|
{
|
||||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||||
new FileLogTarget(AppDomain.CurrentDomain.BaseDirectory, "file"),
|
new FileLogTarget(ReleaseInformations.GetBaseApplicationDirectory(), "file"),
|
||||||
1000,
|
1000,
|
||||||
AsyncLogTargetOverflowAction.Block
|
AsyncLogTargetOverflowAction.Block
|
||||||
));
|
));
|
||||||
|
@@ -311,7 +311,7 @@ namespace Ryujinx.Modules
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, e.Message);
|
Logger.Warning?.Print(LogClass.Application, e.Message);
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Multi-Threaded update failed, falling back to single-threaded updater.");
|
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||||
|
|
||||||
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
|
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
|
||||||
|
|
||||||
@@ -327,8 +327,8 @@ namespace Ryujinx.Modules
|
|||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Multi-Threaded update failed, falling back to single-threaded updater.");
|
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
|
||||||
|
|
||||||
for (int j = 0; j < webClients.Count; j++)
|
for (int j = 0; j < webClients.Count; j++)
|
||||||
{
|
{
|
||||||
webClients[j].CancelAsync();
|
webClients[j].CancelAsync();
|
||||||
@@ -567,7 +567,14 @@ namespace Ryujinx.Modules
|
|||||||
#else
|
#else
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
GtkDialog.CreateWarningDialog("Updater Disabled!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version.");
|
if (ReleaseInformations.IsFlatHubBuild())
|
||||||
|
{
|
||||||
|
GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GtkDialog.CreateWarningDialog("Updater Disabled!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@@ -27,6 +27,8 @@ namespace Ryujinx
|
|||||||
|
|
||||||
public static string ConfigurationPath { get; set; }
|
public static string ConfigurationPath { get; set; }
|
||||||
|
|
||||||
|
public static string CommandLineProfile { get; set; }
|
||||||
|
|
||||||
[DllImport("libX11")]
|
[DllImport("libX11")]
|
||||||
private extern static int XInitThreads();
|
private extern static int XInitThreads();
|
||||||
|
|
||||||
@@ -52,6 +54,17 @@ namespace Ryujinx
|
|||||||
|
|
||||||
baseDirPathArg = args[++i];
|
baseDirPathArg = args[++i];
|
||||||
}
|
}
|
||||||
|
else if (arg == "-p" || arg == "--profile")
|
||||||
|
{
|
||||||
|
if (i + 1 >= args.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineProfile = args[++i];
|
||||||
|
}
|
||||||
else if (arg == "-f" || arg == "--fullscreen")
|
else if (arg == "-f" || arg == "--fullscreen")
|
||||||
{
|
{
|
||||||
startFullscreenArg = true;
|
startFullscreenArg = true;
|
||||||
|
@@ -558,10 +558,10 @@ namespace Ryujinx.Ui.App
|
|||||||
{
|
{
|
||||||
_ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
_ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
|
||||||
|
|
||||||
if (controlData.Titles.Length > (int)desiredTitleLanguage)
|
if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
|
||||||
{
|
{
|
||||||
titleName = controlData.Titles[(int)desiredTitleLanguage].Name.ToString();
|
titleName = controlData.Title[(int)desiredTitleLanguage].NameString.ToString();
|
||||||
publisher = controlData.Titles[(int)desiredTitleLanguage].Publisher.ToString();
|
publisher = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -571,11 +571,11 @@ namespace Ryujinx.Ui.App
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(titleName))
|
if (string.IsNullOrWhiteSpace(titleName))
|
||||||
{
|
{
|
||||||
foreach (ApplicationControlTitle controlTitle in controlData.Titles)
|
foreach (ref readonly var controlTitle in controlData.Title.ItemsRo)
|
||||||
{
|
{
|
||||||
if (!((U8Span)controlTitle.Name).IsEmpty())
|
if (!controlTitle.NameString.IsEmpty())
|
||||||
{
|
{
|
||||||
titleName = controlTitle.Name.ToString();
|
titleName = controlTitle.NameString.ToString();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -584,11 +584,11 @@ namespace Ryujinx.Ui.App
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(publisher))
|
if (string.IsNullOrWhiteSpace(publisher))
|
||||||
{
|
{
|
||||||
foreach (ApplicationControlTitle controlTitle in controlData.Titles)
|
foreach (ref readonly var controlTitle in controlData.Title.ItemsRo)
|
||||||
{
|
{
|
||||||
if (!((U8Span)controlTitle.Publisher).IsEmpty())
|
if (!controlTitle.PublisherString.IsEmpty())
|
||||||
{
|
{
|
||||||
publisher = controlTitle.Publisher.ToString();
|
publisher = controlTitle.PublisherString.ToString();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -599,7 +599,7 @@ namespace Ryujinx.Ui.App
|
|||||||
{
|
{
|
||||||
titleId = controlData.PresenceGroupId.ToString("x16");
|
titleId = controlData.PresenceGroupId.ToString("x16");
|
||||||
}
|
}
|
||||||
else if (controlData.SaveDataOwnerId.Value != 0)
|
else if (controlData.SaveDataOwnerId != 0)
|
||||||
{
|
{
|
||||||
titleId = controlData.SaveDataOwnerId.ToString();
|
titleId = controlData.SaveDataOwnerId.ToString();
|
||||||
}
|
}
|
||||||
@@ -612,7 +612,7 @@ namespace Ryujinx.Ui.App
|
|||||||
titleId = "0000000000000000";
|
titleId = "0000000000000000";
|
||||||
}
|
}
|
||||||
|
|
||||||
version = controlData.DisplayVersion.ToString();
|
version = controlData.DisplayVersionString.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs)
|
private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs)
|
||||||
|
49
Ryujinx/Ui/Helper/ConsoleHelper.cs
Normal file
49
Ryujinx/Ui/Helper/ConsoleHelper.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Helper
|
||||||
|
{
|
||||||
|
public static class ConsoleHelper
|
||||||
|
{
|
||||||
|
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||||
|
|
||||||
|
public static void SetConsoleWindowState(bool show)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
SetConsoleWindowStateWindows(show);
|
||||||
|
}
|
||||||
|
else if (show == false)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "OS doesn't support hiding console window");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private static void SetConsoleWindowStateWindows(bool show)
|
||||||
|
{
|
||||||
|
const int SW_HIDE = 0;
|
||||||
|
const int SW_SHOW = 5;
|
||||||
|
|
||||||
|
IntPtr hWnd = GetConsoleWindow();
|
||||||
|
|
||||||
|
if (hWnd == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[DllImport("kernel32")]
|
||||||
|
static extern IntPtr GetConsoleWindow();
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[DllImport("user32")]
|
||||||
|
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
}
|
||||||
|
}
|
@@ -107,6 +107,7 @@ namespace Ryujinx.Ui
|
|||||||
[GUI] MenuItem _hideUi;
|
[GUI] MenuItem _hideUi;
|
||||||
[GUI] MenuItem _fullScreen;
|
[GUI] MenuItem _fullScreen;
|
||||||
[GUI] CheckMenuItem _startFullScreen;
|
[GUI] CheckMenuItem _startFullScreen;
|
||||||
|
[GUI] CheckMenuItem _showConsole;
|
||||||
[GUI] CheckMenuItem _favToggle;
|
[GUI] CheckMenuItem _favToggle;
|
||||||
[GUI] MenuItem _firmwareInstallDirectory;
|
[GUI] MenuItem _firmwareInstallDirectory;
|
||||||
[GUI] MenuItem _firmwareInstallFile;
|
[GUI] MenuItem _firmwareInstallFile;
|
||||||
@@ -178,7 +179,7 @@ namespace Ryujinx.Ui
|
|||||||
VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient);
|
VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient);
|
||||||
|
|
||||||
_contentManager = new ContentManager(_virtualFileSystem);
|
_contentManager = new ContentManager(_virtualFileSystem);
|
||||||
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
|
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, Program.CommandLineProfile);
|
||||||
_userChannelPersistence = new UserChannelPersistence();
|
_userChannelPersistence = new UserChannelPersistence();
|
||||||
|
|
||||||
// Instantiate GUI objects.
|
// Instantiate GUI objects.
|
||||||
@@ -213,6 +214,9 @@ namespace Ryujinx.Ui
|
|||||||
_startFullScreen.Active = true;
|
_startFullScreen.Active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showConsole.Active = ConfigurationState.Instance.Ui.ShowConsole.Value;
|
||||||
|
_showConsole.Visible = ConsoleHelper.SetConsoleWindowStateSupported;
|
||||||
|
|
||||||
_actionMenu.Sensitive = false;
|
_actionMenu.Sensitive = false;
|
||||||
_pauseEmulation.Sensitive = false;
|
_pauseEmulation.Sensitive = false;
|
||||||
_resumeEmulation.Sensitive = false;
|
_resumeEmulation.Sensitive = false;
|
||||||
@@ -1291,7 +1295,7 @@ namespace Ryujinx.Ui
|
|||||||
|
|
||||||
private void OpenLogsFolder_Pressed(object sender, EventArgs args)
|
private void OpenLogsFolder_Pressed(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
string logPath = System.IO.Path.Combine(ReleaseInformations.GetBaseApplicationDirectory(), "Logs");
|
||||||
|
|
||||||
new DirectoryInfo(logPath).Create();
|
new DirectoryInfo(logPath).Create();
|
||||||
|
|
||||||
@@ -1535,6 +1539,13 @@ namespace Ryujinx.Ui
|
|||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowConsole_Toggled(object sender, EventArgs args)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Ui.ShowConsole.Value = _showConsole.Active;
|
||||||
|
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
private void OptionMenu_StateChanged(object o, StateChangedArgs args)
|
private void OptionMenu_StateChanged(object o, StateChangedArgs args)
|
||||||
{
|
{
|
||||||
_manageUserProfiles.Sensitive = _emulationContext == null;
|
_manageUserProfiles.Sensitive = _emulationContext == null;
|
||||||
|
@@ -142,6 +142,15 @@
|
|||||||
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
|
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckMenuItem" id="_showConsole">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Show Log Console</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem">
|
<object class="GtkSeparatorMenuItem">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
11
Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
generated
11
Ryujinx/Ui/Widgets/GameTableContextMenu.Designer.cs
generated
@@ -11,6 +11,7 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
private MenuItem _manageDlcMenuItem;
|
private MenuItem _manageDlcMenuItem;
|
||||||
private MenuItem _manageCheatMenuItem;
|
private MenuItem _manageCheatMenuItem;
|
||||||
private MenuItem _openTitleModDirMenuItem;
|
private MenuItem _openTitleModDirMenuItem;
|
||||||
|
private MenuItem _openTitleSdModDirMenuItem;
|
||||||
private Menu _extractSubMenu;
|
private Menu _extractSubMenu;
|
||||||
private MenuItem _extractMenuItem;
|
private MenuItem _extractMenuItem;
|
||||||
private MenuItem _extractRomFsMenuItem;
|
private MenuItem _extractRomFsMenuItem;
|
||||||
@@ -88,6 +89,15 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
};
|
};
|
||||||
_openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked;
|
_openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked;
|
||||||
|
|
||||||
|
//
|
||||||
|
// _openTitleSdModDirMenuItem
|
||||||
|
//
|
||||||
|
_openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory")
|
||||||
|
{
|
||||||
|
TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods."
|
||||||
|
};
|
||||||
|
_openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked;
|
||||||
|
|
||||||
//
|
//
|
||||||
// _extractSubMenu
|
// _extractSubMenu
|
||||||
//
|
//
|
||||||
@@ -199,6 +209,7 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
Add(_manageDlcMenuItem);
|
Add(_manageDlcMenuItem);
|
||||||
Add(_manageCheatMenuItem);
|
Add(_manageCheatMenuItem);
|
||||||
Add(_openTitleModDirMenuItem);
|
Add(_openTitleModDirMenuItem);
|
||||||
|
Add(_openTitleSdModDirMenuItem);
|
||||||
Add(new SeparatorMenuItem());
|
Add(new SeparatorMenuItem());
|
||||||
Add(_manageCacheMenuItem);
|
Add(_manageCacheMenuItem);
|
||||||
Add(_extractMenuItem);
|
Add(_extractMenuItem);
|
||||||
|
@@ -6,7 +6,6 @@ using LibHac.Fs;
|
|||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Ncm;
|
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
@@ -26,8 +25,6 @@ using System.IO;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Widgets
|
namespace Ryujinx.Ui.Widgets
|
||||||
{
|
{
|
||||||
public partial class GameTableContextMenu : Menu
|
public partial class GameTableContextMenu : Menu
|
||||||
@@ -81,7 +78,7 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
PopupAtPointer(null);
|
PopupAtPointer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, SaveDataFilter filter, out ulong saveDataId)
|
private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, in SaveDataFilter filter, out ulong saveDataId)
|
||||||
{
|
{
|
||||||
saveDataId = default;
|
saveDataId = default;
|
||||||
|
|
||||||
@@ -121,7 +118,7 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
|
|
||||||
Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
|
Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
|
||||||
|
|
||||||
result = EnsureApplicationSaveData(_horizonClient.Fs, out _, new LibHac.Ncm.ApplicationId(titleId), ref control, ref user);
|
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user);
|
||||||
|
|
||||||
if (result.IsFailure())
|
if (result.IsFailure())
|
||||||
{
|
{
|
||||||
@@ -146,11 +143,9 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveDir(SaveDataFilter saveDataFilter)
|
private void OpenSaveDir(in SaveDataFilter saveDataFilter)
|
||||||
{
|
{
|
||||||
saveDataFilter.SetProgramId(new ProgramId(_titleId));
|
if (!TryFindSaveData(_titleName, _titleId, _controlData, in saveDataFilter, out ulong saveDataId))
|
||||||
|
|
||||||
if (!TryFindSaveData(_titleName, _titleId, _controlData, saveDataFilter, out ulong saveDataId))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -439,26 +434,24 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
//
|
//
|
||||||
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
|
||||||
saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low));
|
var saveDataFilter = SaveDataFilter.Make(_titleId, saveType: default, userId, saveDataId: default, index: default);
|
||||||
|
|
||||||
OpenSaveDir(saveDataFilter);
|
OpenSaveDir(in saveDataFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Device, userId: default, saveDataId: default, index: default);
|
||||||
saveDataFilter.SetSaveDataType(SaveDataType.Device);
|
|
||||||
|
|
||||||
OpenSaveDir(saveDataFilter);
|
OpenSaveDir(in saveDataFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
|
private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
SaveDataFilter saveDataFilter = new SaveDataFilter();
|
var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
|
||||||
saveDataFilter.SetSaveDataType(SaveDataType.Bcat);
|
|
||||||
|
|
||||||
OpenSaveDir(saveDataFilter);
|
OpenSaveDir(in saveDataFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
|
private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
|
||||||
@@ -484,6 +477,14 @@ namespace Ryujinx.Ui.Widgets
|
|||||||
OpenHelper.OpenFolder(titleModsPath);
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
|
||||||
|
{
|
||||||
|
string sdModsBasePath = _virtualFileSystem.ModLoader.GetSdModsBasePath();
|
||||||
|
string titleModsPath = _virtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, _titleIdText);
|
||||||
|
|
||||||
|
OpenHelper.OpenFolder(titleModsPath);
|
||||||
|
}
|
||||||
|
|
||||||
private void ExtractRomFs_Clicked(object sender, EventArgs args)
|
private void ExtractRomFs_Clicked(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
ExtractSection(NcaSectionType.Data);
|
ExtractSection(NcaSectionType.Data);
|
||||||
|
@@ -73,6 +73,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
[GUI] ToggleButton _lStick;
|
[GUI] ToggleButton _lStick;
|
||||||
[GUI] CheckButton _invertLStickX;
|
[GUI] CheckButton _invertLStickX;
|
||||||
[GUI] CheckButton _invertLStickY;
|
[GUI] CheckButton _invertLStickY;
|
||||||
|
[GUI] CheckButton _rotateL90CW;
|
||||||
[GUI] ToggleButton _lStickUp;
|
[GUI] ToggleButton _lStickUp;
|
||||||
[GUI] ToggleButton _lStickDown;
|
[GUI] ToggleButton _lStickDown;
|
||||||
[GUI] ToggleButton _lStickLeft;
|
[GUI] ToggleButton _lStickLeft;
|
||||||
@@ -88,6 +89,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
[GUI] ToggleButton _rStick;
|
[GUI] ToggleButton _rStick;
|
||||||
[GUI] CheckButton _invertRStickX;
|
[GUI] CheckButton _invertRStickX;
|
||||||
[GUI] CheckButton _invertRStickY;
|
[GUI] CheckButton _invertRStickY;
|
||||||
|
[GUI] CheckButton _rotateR90CW;
|
||||||
[GUI] ToggleButton _rStickUp;
|
[GUI] ToggleButton _rStickUp;
|
||||||
[GUI] ToggleButton _rStickDown;
|
[GUI] ToggleButton _rStickDown;
|
||||||
[GUI] ToggleButton _rStickLeft;
|
[GUI] ToggleButton _rStickLeft;
|
||||||
@@ -490,6 +492,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString();
|
_lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString();
|
||||||
_invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX;
|
_invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX;
|
||||||
_invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY;
|
_invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY;
|
||||||
|
_rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW;
|
||||||
_lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString();
|
_lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString();
|
||||||
_dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString();
|
_dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString();
|
||||||
_dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString();
|
_dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString();
|
||||||
@@ -503,6 +506,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString();
|
_rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString();
|
||||||
_invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX;
|
_invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX;
|
||||||
_invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY;
|
_invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY;
|
||||||
|
_rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW;
|
||||||
_rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString();
|
_rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString();
|
||||||
_a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
|
_a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
|
||||||
_b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
|
_b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
|
||||||
@@ -718,6 +722,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
Joystick = lStick,
|
Joystick = lStick,
|
||||||
InvertStickY = _invertLStickY.Active,
|
InvertStickY = _invertLStickY.Active,
|
||||||
StickButton = lStickButton,
|
StickButton = lStickButton,
|
||||||
|
Rotate90CW = _rotateL90CW.Active,
|
||||||
},
|
},
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
{
|
{
|
||||||
@@ -737,6 +742,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
Joystick = rStick,
|
Joystick = rStick,
|
||||||
InvertStickY = _invertRStickY.Active,
|
InvertStickY = _invertRStickY.Active,
|
||||||
StickButton = rStickButton,
|
StickButton = rStickButton,
|
||||||
|
Rotate90CW = _rotateR90CW.Active,
|
||||||
},
|
},
|
||||||
Motion = motionConfig,
|
Motion = motionConfig,
|
||||||
Rumble = new RumbleConfigController
|
Rumble = new RumbleConfigController
|
||||||
@@ -1056,6 +1062,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
StickButton = ConfigGamepadInputId.LeftStick,
|
StickButton = ConfigGamepadInputId.LeftStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
@@ -1077,6 +1084,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
StickButton = ConfigGamepadInputId.RightStick,
|
StickButton = ConfigGamepadInputId.RightStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Motion = new StandardMotionConfigController
|
Motion = new StandardMotionConfigController
|
||||||
|
@@ -740,6 +740,19 @@
|
|||||||
<property name="top_attach">1</property>
|
<property name="top_attach">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="_rotateL90CW">
|
||||||
|
<property name="label" translatable="yes">Rotate 90° Clockwise</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -1697,6 +1710,19 @@
|
|||||||
<property name="top_attach">1</property>
|
<property name="top_attach">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="_rotateR90CW">
|
||||||
|
<property name="label" translatable="yes">Rotate 90° Clockwise</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
@@ -105,7 +105,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
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();
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersion.ToString()} - {path}");
|
RadioButton radioButton = new RadioButton($"Version {controlData.DisplayVersionString.ToString()} - {path}");
|
||||||
radioButton.JoinGroup(_noUpdateRadioButton);
|
radioButton.JoinGroup(_noUpdateRadioButton);
|
||||||
|
|
||||||
_availableUpdatesBox.Add(radioButton);
|
_availableUpdatesBox.Add(radioButton);
|
||||||
|
1
distribution/linux/ryujinx-logo.svg
Normal file
1
distribution/linux/ryujinx-logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 255.76 255.76"><defs><style>.cls-1{fill:#02c5e5;}.cls-2{fill:#ff5f55;}.cls-3{fill:none;}</style></defs><g id="Ebene_2" data-name="Ebene 2"><g id="Ebene_1-2" data-name="Ebene 1"><g id="Ebene_2-2" data-name="Ebene 2"><g id="Ebene_1-2-2" data-name="Ebene 1-2"><path class="cls-1" d="M80.63,0V220.39H44.37c-14,0-35.74-20.74-35.74-39.13V40.13C8.63,19.19,31.36,0,49.06,0Z"/><path class="cls-2" d="M175.13,35.37V255.76h36.26c14,0,35.74-20.74,35.74-39.13V75.5c0-20.94-22.73-40.13-40.43-40.13Z"/><polygon class="cls-1" points="124.34 137.96 122.58 145.57 90.64 145.57 92.89 137.96 124.34 137.96"/><polygon class="cls-2" points="160.29 137.96 157.84 145.57 122.58 145.57 124.34 137.96 160.29 137.96"/><polygon class="cls-1" points="130.39 111.86 128.62 119.47 95.14 119.47 97.39 111.86 130.39 111.86"/><polygon class="cls-2" points="164.79 111.86 162.34 119.47 128.62 119.47 130.39 111.86 164.79 111.86"/><polygon class="cls-1" points="104.24 167.99 122.83 87.77 129.78 87.77 111.19 167.99 104.24 167.99"/><polygon class="cls-2" points="128.18 167.99 146.77 87.77 153.89 87.77 135.3 167.99 128.18 167.99"/></g><rect class="cls-3" width="255.76" height="255.76"/></g></g></g></svg>
|
After Width: | Height: | Size: 1.2 KiB |
23
distribution/linux/ryujinx-mime.xml
Normal file
23
distribution/linux/ryujinx-mime.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/x-nx-nca">
|
||||||
|
<comment>Nintendo Content Archive</comment>
|
||||||
|
<glob pattern="*.nca"/>
|
||||||
|
</mime-type>
|
||||||
|
<mime-type type="application/x-nx-nro">
|
||||||
|
<comment>Nintendo Relocatable Object</comment>
|
||||||
|
<glob pattern="*.nro"/>
|
||||||
|
</mime-type>
|
||||||
|
<mime-type type="application/x-nx-nso">
|
||||||
|
<comment>Nintendo Shared Object</comment>
|
||||||
|
<glob pattern="*.nso"/>
|
||||||
|
</mime-type>
|
||||||
|
<mime-type type="application/x-nx-nsp">
|
||||||
|
<comment>Nintendo Submission Package</comment>
|
||||||
|
<glob pattern="*.nsp"/>
|
||||||
|
</mime-type>
|
||||||
|
<mime-type type="application/x-nx-xci">
|
||||||
|
<comment>Nintendo Switch Cartridge</comment>
|
||||||
|
<glob pattern="*.xci"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
14
distribution/linux/ryujinx.desktop
Normal file
14
distribution/linux/ryujinx.desktop
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=Ryujinx
|
||||||
|
Comment=A Nintendo Switch Emulator
|
||||||
|
Type=Application
|
||||||
|
GenericName=Nintendo Switch Emulator
|
||||||
|
Icon=ryujinx
|
||||||
|
Terminal=false
|
||||||
|
Exec=Ryujinx %f
|
||||||
|
Categories=Game;Emulator;GTK;
|
||||||
|
MimeType=application/x-nx-nca;application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||||
|
Keywords=Switch;Nintendo;Emulator;
|
||||||
|
StartupWMClass=Ryujinx
|
||||||
|
PrefersNonDefaultGPU=true
|
Reference in New Issue
Block a user