Merge branch 'master' into aot
This commit is contained in:
commit
6bf0d302bd
67 changed files with 2970 additions and 1152 deletions
27
.github/workflows/build.yml
vendored
Normal file
27
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: "Build job"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
dotnet: ['3.1.100']
|
||||
environment: ['Debug', 'Release', 'Profile Debug', 'Profile Release']
|
||||
name: ${{ matrix.environment }} build (Dotnet ${{ matrix.dotnet }}, OS ${{ matrix.os }})
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: ${{ matrix.dotnet }}
|
||||
- name: Build
|
||||
run: dotnet build -c "${{ matrix.environment }}"
|
||||
- name: Test
|
||||
run: dotnet test -c "${{ matrix.environment }}"
|
|
@ -106,6 +106,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2));
|
||||
Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3));
|
||||
Add(X86Instruction.Cvtss2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF3));
|
||||
Add(X86Instruction.Cvtss2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF3));
|
||||
Add(X86Instruction.Div, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x060000f7, InstructionFlags.None));
|
||||
Add(X86Instruction.Divpd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex | InstructionFlags.Prefix66));
|
||||
Add(X86Instruction.Divps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5e, InstructionFlags.Vex));
|
||||
|
@ -784,6 +785,16 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
public void WriteInstruction(
|
||||
X86Instruction inst,
|
||||
Operand dest,
|
||||
Operand src1,
|
||||
Operand src2,
|
||||
OperandType type)
|
||||
{
|
||||
WriteInstruction(dest, src1, src2, inst, type);
|
||||
}
|
||||
|
||||
public void WriteInstruction(X86Instruction inst, Operand dest, Operand source, byte imm)
|
||||
{
|
||||
WriteInstruction(dest, null, source, inst);
|
||||
|
|
|
@ -269,11 +269,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
if (dest.Type == OperandType.I32)
|
||||
{
|
||||
context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32
|
||||
context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32(__m128i a)
|
||||
}
|
||||
else /* if (dest.Type == OperandType.I64) */
|
||||
{
|
||||
context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64
|
||||
context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64(__m128i a)
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -305,6 +305,26 @@ namespace ARMeilleure.CodeGen.X86
|
|||
break;
|
||||
}
|
||||
|
||||
case IntrinsicType.BinaryGpr:
|
||||
{
|
||||
Operand dest = operation.Destination;
|
||||
Operand src1 = operation.GetSource(0);
|
||||
Operand src2 = operation.GetSource(1);
|
||||
|
||||
EnsureSameType(dest, src1);
|
||||
|
||||
if (!HardwareCapabilities.SupportsVexEncoding)
|
||||
{
|
||||
EnsureSameReg(dest, src1);
|
||||
}
|
||||
|
||||
Debug.Assert(!dest.Type.IsInteger() && src2.Type.IsInteger());
|
||||
|
||||
context.Assembler.WriteInstruction(info.Inst, dest, src1, src2, src2.Type);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IntrinsicType.BinaryImm:
|
||||
{
|
||||
Operand dest = operation.Destination;
|
||||
|
@ -1070,11 +1090,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
if (source.Type == OperandType.I32)
|
||||
{
|
||||
context.Assembler.Movd(dest, source);
|
||||
context.Assembler.Movd(dest, source); // (__m128i _mm_cvtsi32_si128(int a))
|
||||
}
|
||||
else /* if (source.Type == OperandType.I64) */
|
||||
{
|
||||
context.Assembler.Movq(dest, source);
|
||||
context.Assembler.Movq(dest, source); // (__m128i _mm_cvtsi64_si128(__int64 a))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary));
|
||||
Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Cvtsi2sd, new IntrinsicInfo(X86Instruction.Cvtsi2sd, IntrinsicType.BinaryGpr));
|
||||
Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Cvtsi2ss, new IntrinsicInfo(X86Instruction.Cvtsi2ss, IntrinsicType.BinaryGpr));
|
||||
Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Cvtss2si, new IntrinsicInfo(X86Instruction.Cvtss2si, IntrinsicType.UnaryToGpr));
|
||||
Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary));
|
||||
Add(Intrinsic.X86Divsd, new IntrinsicInfo(X86Instruction.Divsd, IntrinsicType.Binary));
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Unary,
|
||||
UnaryToGpr,
|
||||
Binary,
|
||||
BinaryGpr,
|
||||
BinaryImm,
|
||||
Ternary,
|
||||
TernaryImm
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Cvtsi2sd,
|
||||
Cvtsi2ss,
|
||||
Cvtss2sd,
|
||||
Cvtss2si,
|
||||
Div,
|
||||
Divpd,
|
||||
Divps,
|
||||
|
|
|
@ -10,6 +10,11 @@ namespace ARMeilleure.Decoders
|
|||
{
|
||||
static class Decoder
|
||||
{
|
||||
// We define a limit on the number of instructions that a function may have,
|
||||
// this prevents functions being potentially too large, which would
|
||||
// take too long to compile and use too much memory.
|
||||
private const int MaxInstsPerFunction = 5000;
|
||||
|
||||
private delegate object MakeOp(InstDescriptor inst, ulong address, int opCode);
|
||||
|
||||
private static ConcurrentDictionary<Type, MakeOp> _opActivators;
|
||||
|
@ -36,10 +41,17 @@ namespace ARMeilleure.Decoders
|
|||
|
||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
||||
|
||||
int opsCount = 0;
|
||||
|
||||
Block GetBlock(ulong blkAddress)
|
||||
{
|
||||
if (!visited.TryGetValue(blkAddress, out Block block))
|
||||
{
|
||||
if (opsCount > MaxInstsPerFunction)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
block = new Block(blkAddress);
|
||||
|
||||
workQueue.Enqueue(block);
|
||||
|
@ -92,6 +104,8 @@ namespace ARMeilleure.Decoders
|
|||
|
||||
FillBlock(memory, mode, currBlock, limitAddress);
|
||||
|
||||
opsCount += currBlock.OpCodes.Count;
|
||||
|
||||
if (currBlock.OpCodes.Count != 0)
|
||||
{
|
||||
// Set child blocks. "Branch" is the block the branch instruction
|
||||
|
|
|
@ -2,6 +2,7 @@ using ARMeilleure.Decoders;
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitAluHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
@ -265,16 +266,52 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
d = context.SoftFallbackCall(nameof(SoftFallback.ReverseBits32), n);
|
||||
d = EmitReverseBits32Op(context, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = context.SoftFallbackCall(nameof(SoftFallback.ReverseBits64), n);
|
||||
d = EmitReverseBits64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBits64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaaaaaaaaaaul)), Const(1)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x5555555555555555ul)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccccccccccul)), Const(2)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x3333333333333333ul)), Const(2)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0f0f0f0f0ul)), Const(4)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0f0f0f0f0ful)), Const(4)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ff00ff00fful)), Const(8)));
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(32)), context.ShiftLeft(val, Const(32)));
|
||||
}
|
||||
|
||||
public static void Rev16(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
@ -284,32 +321,60 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
d = context.SoftFallbackCall(nameof(SoftFallback.ReverseBytes16_32), n);
|
||||
d = EmitReverseBytes16_32Op(context, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
d = context.SoftFallbackCall(nameof(SoftFallback.ReverseBytes16_64), n);
|
||||
d = EmitReverseBytes16_64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I32);
|
||||
|
||||
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
|
||||
|
||||
return context.ConvertI64ToI32(val);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
|
||||
}
|
||||
|
||||
public static void Rev32(ArmEmitterContext context)
|
||||
{
|
||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||
|
||||
Operand n = GetIntOrZR(context, op.Rn);
|
||||
Operand d;
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
SetAluDOrZR(context, context.ByteSwap(n));
|
||||
d = context.ByteSwap(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand d = context.SoftFallbackCall(nameof(SoftFallback.ReverseBytes32_64), n);
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
d = EmitReverseBytes32_64Op(context, n);
|
||||
}
|
||||
|
||||
SetAluDOrZR(context, d);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBytes32_64Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = EmitReverseBytes16_64Op(context, op);
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xffff0000ffff0000ul)), Const(16)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0000ffff0000fffful)), Const(16)));
|
||||
}
|
||||
|
||||
public static void Rev64(ArmEmitterContext context)
|
||||
|
|
|
@ -77,7 +77,14 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
public static void Addp_V(ArmEmitterContext context)
|
||||
{
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.Add(op1, op2));
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitSsse3VectorPairwiseOp(context, X86PaddInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.Add(op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Addv_V(ArmEmitterContext context)
|
||||
|
@ -390,7 +397,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorPairwiseOpF(context, Intrinsic.X86Addps, Intrinsic.X86Addpd);
|
||||
EmitSse2VectorPairwiseOpF(context, Intrinsic.X86Addps, Intrinsic.X86Addpd);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -538,7 +545,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorPairwiseOpF(context, Intrinsic.X86Maxps, Intrinsic.X86Maxpd);
|
||||
EmitSse2VectorPairwiseOpF(context, Intrinsic.X86Maxps, Intrinsic.X86Maxpd);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -613,7 +620,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
if (Optimizations.FastFP && Optimizations.UseSse2)
|
||||
{
|
||||
EmitVectorPairwiseOpF(context, Intrinsic.X86Minps, Intrinsic.X86Minpd);
|
||||
EmitSse2VectorPairwiseOpF(context, Intrinsic.X86Minps, Intrinsic.X86Minpd);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -655,7 +662,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
|
@ -663,7 +670,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -701,7 +708,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
|
@ -712,7 +719,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -755,7 +762,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
|
@ -763,7 +770,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -801,7 +808,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
|
@ -812,7 +819,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
|
||||
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2019,9 +2026,16 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
public static void Smaxp_V(ArmEmitterContext context)
|
||||
{
|
||||
string name = nameof(SoftFallback.MaxS64);
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitSsse3VectorPairwiseOp(context, X86PmaxsInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = nameof(SoftFallback.MaxS64);
|
||||
|
||||
EmitVectorPairwiseOpSx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
EmitVectorPairwiseOpSx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Smaxv_V(ArmEmitterContext context)
|
||||
|
@ -2061,9 +2075,16 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
public static void Sminp_V(ArmEmitterContext context)
|
||||
{
|
||||
string name = nameof(SoftFallback.MinS64);
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitSsse3VectorPairwiseOp(context, X86PminsInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = nameof(SoftFallback.MinS64);
|
||||
|
||||
EmitVectorPairwiseOpSx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
EmitVectorPairwiseOpSx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sminv_V(ArmEmitterContext context)
|
||||
|
@ -2644,9 +2665,16 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
public static void Umaxp_V(ArmEmitterContext context)
|
||||
{
|
||||
string name = nameof(SoftFallback.MaxU64);
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitSsse3VectorPairwiseOp(context, X86PmaxuInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = nameof(SoftFallback.MaxU64);
|
||||
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Umaxv_V(ArmEmitterContext context)
|
||||
|
@ -2686,9 +2714,16 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
public static void Uminp_V(ArmEmitterContext context)
|
||||
{
|
||||
string name = nameof(SoftFallback.MinU64);
|
||||
if (Optimizations.UseSsse3)
|
||||
{
|
||||
EmitSsse3VectorPairwiseOp(context, X86PminuInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = nameof(SoftFallback.MinU64);
|
||||
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
EmitVectorPairwiseOpZx(context, (op1, op2) => context.SoftFallbackCall(name, op1, op2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Uminv_V(ArmEmitterContext context)
|
||||
|
@ -3011,7 +3046,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
|
||||
|
||||
long roundConst = 1L << (eSize - 1);
|
||||
|
||||
|
@ -3032,7 +3069,7 @@ namespace ARMeilleure.Instructions
|
|||
res = EmitVectorInsert(context, res, de, part + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
public static void EmitScalarRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode)
|
||||
|
@ -3115,12 +3152,12 @@ namespace ARMeilleure.Instructions
|
|||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
Operand nQNaNMask = EmitSse2VectorIsQNaNOpF(context, n);
|
||||
Operand mQNaNMask = EmitSse2VectorIsQNaNOpF(context, m);
|
||||
|
||||
Operand nNum = context.Copy(n);
|
||||
Operand mNum = context.Copy(m);
|
||||
|
||||
Operand nQNaNMask = EmitSse2VectorIsQNaNOpF(context, nNum);
|
||||
Operand mQNaNMask = EmitSse2VectorIsQNaNOpF(context, mNum);
|
||||
|
||||
int sizeF = op.Size & 1;
|
||||
|
||||
if (sizeF == 0)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,24 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
static class InstEmitSimdHelper
|
||||
{
|
||||
#region "Masks"
|
||||
public static readonly long[] EvenMasks = new long[]
|
||||
{
|
||||
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0, // B
|
||||
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0, // H
|
||||
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0 // S
|
||||
};
|
||||
|
||||
public static readonly long[] OddMasks = new long[]
|
||||
{
|
||||
15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0, // B
|
||||
15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0, // H
|
||||
15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0 // S
|
||||
};
|
||||
|
||||
private static readonly long _zeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0;
|
||||
#endregion
|
||||
|
||||
#region "X86 SSE Intrinsics"
|
||||
public static readonly Intrinsic[] X86PaddInstruction = new Intrinsic[]
|
||||
{
|
||||
|
@ -189,11 +207,19 @@ namespace ARMeilleure.Instructions
|
|||
return vector;
|
||||
}
|
||||
|
||||
public static Operand X86GetElements(ArmEmitterContext context, long e1, long e0)
|
||||
{
|
||||
Operand vector0 = context.VectorCreateScalar(Const(e0));
|
||||
Operand vector1 = context.VectorCreateScalar(Const(e1));
|
||||
|
||||
return context.AddIntrinsic(Intrinsic.X86Punpcklqdq, vector0, vector1);
|
||||
}
|
||||
|
||||
public static int X86GetRoundControl(FPRoundingMode roundMode)
|
||||
{
|
||||
switch (roundMode)
|
||||
{
|
||||
case FPRoundingMode.ToNearest: return 8 | 0;
|
||||
case FPRoundingMode.ToNearest: return 8 | 0; // even
|
||||
case FPRoundingMode.TowardsPlusInfinity: return 8 | 2;
|
||||
case FPRoundingMode.TowardsMinusInfinity: return 8 | 1;
|
||||
case FPRoundingMode.TowardsZero: return 8 | 3;
|
||||
|
@ -948,6 +974,46 @@ namespace ARMeilleure.Instructions
|
|||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void EmitSsse3VectorPairwiseOp(ArmEmitterContext context, Intrinsic[] inst)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
Operand n = GetVec(op.Rn);
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Simd64)
|
||||
{
|
||||
Operand zeroEvenMask = X86GetElements(context, _zeroMask, EvenMasks[op.Size]);
|
||||
Operand zeroOddMask = X86GetElements(context, _zeroMask, OddMasks [op.Size]);
|
||||
|
||||
Operand mN = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, n, m); // m:n
|
||||
|
||||
Operand left = context.AddIntrinsic(Intrinsic.X86Pshufb, mN, zeroEvenMask); // 0:even from m:n
|
||||
Operand right = context.AddIntrinsic(Intrinsic.X86Pshufb, mN, zeroOddMask); // 0:odd from m:n
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[op.Size], left, right));
|
||||
}
|
||||
else if (op.Size < 3)
|
||||
{
|
||||
Operand oddEvenMask = X86GetElements(context, OddMasks[op.Size], EvenMasks[op.Size]);
|
||||
|
||||
Operand oddEvenN = context.AddIntrinsic(Intrinsic.X86Pshufb, n, oddEvenMask); // odd:even from n
|
||||
Operand oddEvenM = context.AddIntrinsic(Intrinsic.X86Pshufb, m, oddEvenMask); // odd:even from m
|
||||
|
||||
Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, oddEvenN, oddEvenM);
|
||||
Operand right = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, oddEvenN, oddEvenM);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[op.Size], left, right));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand left = context.AddIntrinsic(Intrinsic.X86Punpcklqdq, n, m);
|
||||
Operand right = context.AddIntrinsic(Intrinsic.X86Punpckhqdq, n, m);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.AddIntrinsic(inst[3], left, right));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitVectorAcrossVectorOpSx(ArmEmitterContext context, Func2I emit)
|
||||
{
|
||||
EmitVectorAcrossVectorOp(context, emit, signed: true, isLong: false);
|
||||
|
@ -1023,7 +1089,7 @@ namespace ARMeilleure.Instructions
|
|||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
public static void EmitVectorPairwiseOpF(ArmEmitterContext context, Intrinsic inst32, Intrinsic inst64)
|
||||
public static void EmitSse2VectorPairwiseOpF(ArmEmitterContext context, Intrinsic inst32, Intrinsic inst64)
|
||||
{
|
||||
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
|
||||
|
||||
|
@ -1272,7 +1338,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0;
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
|
@ -1283,7 +1351,7 @@ namespace ARMeilleure.Instructions
|
|||
res = EmitVectorInsert(context, res, temp, part + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
// TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned).
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
|
@ -107,7 +108,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -158,7 +159,7 @@ namespace ARMeilleure.Instructions
|
|||
res = context.VectorZeroUpper64(res);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -292,11 +293,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0);
|
||||
|
||||
ne = context.ConvertI64ToI32(ne);
|
||||
|
||||
Operand de = context.SoftFallbackCall(nameof(SoftFallback.ReverseBits8), ne);
|
||||
|
||||
de = context.ZeroExtend32(OperandType.I64, de);
|
||||
Operand de = EmitReverseBits8Op(context, ne);
|
||||
|
||||
res = EmitVectorInsert(context, res, de, index, 0);
|
||||
}
|
||||
|
@ -304,6 +301,20 @@ namespace ARMeilleure.Instructions
|
|||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static Operand EmitReverseBits8Op(ArmEmitterContext context, Operand op)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
|
||||
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaul)), Const(1)),
|
||||
context.ShiftLeft (context.BitwiseAnd(op, Const(0x55ul)), Const(1)));
|
||||
|
||||
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccul)), Const(2)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x33ul)), Const(2)));
|
||||
|
||||
return context.BitwiseOr(context.ShiftRightUI(val, Const(4)),
|
||||
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0ful)), Const(4)));
|
||||
}
|
||||
|
||||
public static void Rev16_V(ArmEmitterContext context)
|
||||
{
|
||||
if (Optimizations.UseSsse3)
|
||||
|
|
|
@ -12,20 +12,6 @@ namespace ARMeilleure.Instructions
|
|||
static partial class InstEmit
|
||||
{
|
||||
#region "Masks"
|
||||
private static readonly long[] _masksE0_TrnUzpXtn = new long[]
|
||||
{
|
||||
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
|
||||
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
|
||||
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masksE1_TrnUzp = new long[]
|
||||
{
|
||||
15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0,
|
||||
15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0,
|
||||
15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masksE0_Uzp = new long[]
|
||||
{
|
||||
13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0,
|
||||
|
@ -446,7 +432,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand res = context.VectorZeroUpper64(d);
|
||||
|
||||
Operand mask = X86GetAllElements(context, _masksE0_TrnUzpXtn[op.Size]);
|
||||
Operand mask = X86GetAllElements(context, EvenMasks[op.Size]);
|
||||
|
||||
Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, GetVec(op.Rn), mask);
|
||||
|
||||
|
@ -630,8 +616,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = _masksE0_TrnUzpXtn[op.Size];
|
||||
long maskE1 = _masksE1_TrnUzp [op.Size];
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks [op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
|
@ -698,8 +684,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
if (op.Size < 3)
|
||||
{
|
||||
long maskE0 = _masksE0_TrnUzpXtn[op.Size];
|
||||
long maskE1 = _masksE1_TrnUzp [op.Size];
|
||||
long maskE0 = EvenMasks[op.Size];
|
||||
long maskE1 = OddMasks [op.Size];
|
||||
|
||||
mask = X86GetScalar(context, maskE0);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ using ARMeilleure.Decoders;
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
using static ARMeilleure.Instructions.InstEmitSimdHelper;
|
||||
|
@ -16,13 +17,6 @@ namespace ARMeilleure.Instructions
|
|||
static partial class InstEmit
|
||||
{
|
||||
#region "Masks"
|
||||
private static readonly long[] _masks_RshrnShrn = new long[]
|
||||
{
|
||||
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
|
||||
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
|
||||
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
|
||||
};
|
||||
|
||||
private static readonly long[] _masks_SliSri = new long[] // Replication masks.
|
||||
{
|
||||
0x0101010101010101L, 0x0001000100010001L, 0x0000000100000001L, 0x0000000000000001L
|
||||
|
@ -42,7 +36,7 @@ namespace ARMeilleure.Instructions
|
|||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand dLow = context.AddIntrinsic(Intrinsic.X86Movlhps, d, context.VectorZero());
|
||||
Operand dLow = context.VectorZeroUpper64(d);
|
||||
|
||||
Operand mask = null;
|
||||
|
||||
|
@ -61,7 +55,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
res = context.AddIntrinsic(srlInst, res, Const(shift));
|
||||
|
||||
Operand mask2 = X86GetAllElements(context, _masks_RshrnShrn[op.Size]);
|
||||
Operand mask2 = X86GetAllElements(context, EvenMasks[op.Size]);
|
||||
|
||||
res = context.AddIntrinsic(Intrinsic.X86Pshufb, res, mask2);
|
||||
|
||||
|
@ -157,13 +151,13 @@ namespace ARMeilleure.Instructions
|
|||
Operand d = GetVec(op.Rd);
|
||||
Operand n = GetVec(op.Rn);
|
||||
|
||||
Operand dLow = context.AddIntrinsic(Intrinsic.X86Movlhps, d, context.VectorZero());
|
||||
Operand dLow = context.VectorZeroUpper64(d);
|
||||
|
||||
Intrinsic srlInst = X86PsrlInstruction[op.Size + 1];
|
||||
|
||||
Operand nShifted = context.AddIntrinsic(srlInst, n, Const(shift));
|
||||
|
||||
Operand mask = X86GetAllElements(context, _masks_RshrnShrn[op.Size]);
|
||||
Operand mask = X86GetAllElements(context, EvenMasks[op.Size]);
|
||||
|
||||
Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, nShifted, mask);
|
||||
|
||||
|
@ -702,9 +696,9 @@ namespace ARMeilleure.Instructions
|
|||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size);
|
||||
Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size);
|
||||
Operand me = EmitVectorExtractSx(context, op.Rm, index << op.Size, 0);
|
||||
|
||||
Operand e = context.SoftFallbackCall(nameof(SoftFallback.UnsignedShlReg), ne, me, Const(0), Const(op.Size));
|
||||
Operand e = EmitUnsignedShlRegOp(context, ne, context.ConvertI64ToI32(me), op.Size);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, index, op.Size);
|
||||
}
|
||||
|
@ -879,9 +873,7 @@ namespace ARMeilleure.Instructions
|
|||
e = context.Add(e, Const(roundConst));
|
||||
}
|
||||
|
||||
e = signed
|
||||
? context.ShiftRightSI(e, Const(shift))
|
||||
: context.ShiftRightUI(e, Const(shift));
|
||||
e = signed ? context.ShiftRightSI(e, Const(shift)) : context.ShiftRightUI(e, Const(shift));
|
||||
}
|
||||
else /* if (op.Size == 3) */
|
||||
{
|
||||
|
@ -901,6 +893,28 @@ namespace ARMeilleure.Instructions
|
|||
context.Copy(GetVec(op.Rd), res);
|
||||
}
|
||||
|
||||
private static Operand EmitUnsignedShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size)
|
||||
{
|
||||
Debug.Assert(op.Type == OperandType.I64);
|
||||
Debug.Assert(shiftLsB.Type == OperandType.I32);
|
||||
Debug.Assert((uint)size < 4u);
|
||||
|
||||
Operand negShiftLsB = context.Negate(shiftLsB);
|
||||
|
||||
Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
|
||||
|
||||
Operand shl = context.ShiftLeft (op, shiftLsB);
|
||||
Operand shr = context.ShiftRightUI(op, negShiftLsB);
|
||||
|
||||
Operand res = context.ConditionalSelect(isPositive, shl, shr);
|
||||
|
||||
Operand isOutOfRange = context.BitwiseOr(
|
||||
context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
|
||||
context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
|
||||
|
||||
return context.ConditionalSelect(isOutOfRange, Const(0UL), res);
|
||||
}
|
||||
|
||||
private static void EmitVectorShrImmNarrowOpZx(ArmEmitterContext context, bool round)
|
||||
{
|
||||
OpCodeSimdShImm op = (OpCodeSimdShImm)context.CurrOp;
|
||||
|
@ -913,7 +927,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
|
@ -929,7 +945,7 @@ namespace ARMeilleure.Instructions
|
|||
res = EmitVectorInsert(context, res, e, part + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -972,7 +988,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
int part = !scalar && (op.RegisterSize == RegisterSize.Simd128) ? elems : 0;
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(GetVec(op.Rd));
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand res = part == 0 ? context.VectorZero() : context.Copy(d);
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
|
@ -985,9 +1003,7 @@ namespace ARMeilleure.Instructions
|
|||
e = context.Add(e, Const(roundConst));
|
||||
}
|
||||
|
||||
e = signedSrc
|
||||
? context.ShiftRightSI(e, Const(shift))
|
||||
: context.ShiftRightUI(e, Const(shift));
|
||||
e = signedSrc ? context.ShiftRightSI(e, Const(shift)) : context.ShiftRightUI(e, Const(shift));
|
||||
}
|
||||
else /* if (op.Size == 2 && round) */
|
||||
{
|
||||
|
@ -999,7 +1015,7 @@ namespace ARMeilleure.Instructions
|
|||
res = EmitVectorInsert(context, res, e, part + index, op.Size);
|
||||
}
|
||||
|
||||
context.Copy(GetVec(op.Rd), res);
|
||||
context.Copy(d, res);
|
||||
}
|
||||
|
||||
// dst64 = (Int(src64, signed) + roundConst) >> shift;
|
||||
|
|
|
@ -1241,75 +1241,6 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region "Reverse"
|
||||
public static uint ReverseBits8(uint value)
|
||||
{
|
||||
value = ((value & 0xaa) >> 1) | ((value & 0x55) << 1);
|
||||
value = ((value & 0xcc) >> 2) | ((value & 0x33) << 2);
|
||||
|
||||
return (value >> 4) | ((value & 0x0f) << 4);
|
||||
}
|
||||
|
||||
public static uint ReverseBits32(uint value)
|
||||
{
|
||||
value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1);
|
||||
value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2);
|
||||
value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4);
|
||||
value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8);
|
||||
|
||||
return (value >> 16) | (value << 16);
|
||||
}
|
||||
|
||||
public static ulong ReverseBits64(ulong value)
|
||||
{
|
||||
value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((value & 0x5555555555555555) << 1 );
|
||||
value = ((value & 0xcccccccccccccccc) >> 2 ) | ((value & 0x3333333333333333) << 2 );
|
||||
value = ((value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((value & 0x0f0f0f0f0f0f0f0f) << 4 );
|
||||
value = ((value & 0xff00ff00ff00ff00) >> 8 ) | ((value & 0x00ff00ff00ff00ff) << 8 );
|
||||
value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16);
|
||||
|
||||
return (value >> 32) | (value << 32);
|
||||
}
|
||||
|
||||
public static uint ReverseBytes16_32(uint value) => (uint)ReverseBytes16_64(value);
|
||||
|
||||
public static ulong ReverseBytes16_64(ulong value) => ReverseBytes(value, RevSize.Rev16);
|
||||
public static ulong ReverseBytes32_64(ulong value) => ReverseBytes(value, RevSize.Rev32);
|
||||
|
||||
private enum RevSize
|
||||
{
|
||||
Rev16,
|
||||
Rev32,
|
||||
Rev64
|
||||
}
|
||||
|
||||
private static ulong ReverseBytes(ulong value, RevSize size)
|
||||
{
|
||||
value = ((value & 0xff00ff00ff00ff00) >> 8) | ((value & 0x00ff00ff00ff00ff) << 8);
|
||||
|
||||
if (size == RevSize.Rev16)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = ((value & 0xffff0000ffff0000) >> 16) | ((value & 0x0000ffff0000ffff) << 16);
|
||||
|
||||
if (size == RevSize.Rev32)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = ((value & 0xffffffff00000000) >> 32) | ((value & 0x00000000ffffffff) << 32);
|
||||
|
||||
if (size == RevSize.Rev64)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(size));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "MaxMin"
|
||||
public static long MaxS64(long val1, long val2) => (val1 >= val2) ? val1 : val2;
|
||||
public static ulong MaxU64(ulong val1, ulong val2) => (val1 >= val2) ? val1 : val2;
|
||||
|
|
|
@ -30,8 +30,11 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
X86Cvtps2pd,
|
||||
X86Cvtsd2si,
|
||||
X86Cvtsd2ss,
|
||||
X86Cvtsi2sd,
|
||||
X86Cvtsi2si,
|
||||
X86Cvtsi2ss,
|
||||
X86Cvtss2sd,
|
||||
X86Cvtss2si,
|
||||
X86Divpd,
|
||||
X86Divps,
|
||||
X86Divsd,
|
||||
|
|
|
@ -1,30 +1,22 @@
|
|||
using JsonPrettyPrinterPlus;
|
||||
using LibHac.FsSystem;
|
||||
using OpenTK.Input;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.HOS.Services;
|
||||
using Ryujinx.HLE.Input;
|
||||
using Ryujinx.Ui;
|
||||
using Ryujinx.Ui.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Utf8Json;
|
||||
using Utf8Json.Resolvers;
|
||||
using Ryujinx.Configuration.System;
|
||||
using Ryujinx.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.UI.Input;
|
||||
using Ryujinx.Configuration.Ui;
|
||||
|
||||
namespace Ryujinx
|
||||
namespace Ryujinx.Configuration
|
||||
{
|
||||
public class Configuration
|
||||
public class ConfigurationFileFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// The default configuration instance
|
||||
/// </summary>
|
||||
public static Configuration Instance { get; private set; }
|
||||
public int Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dumps shaders in this local directory
|
||||
|
@ -79,7 +71,7 @@ namespace Ryujinx
|
|||
/// <summary>
|
||||
/// Change System Language
|
||||
/// </summary>
|
||||
public SystemLanguage SystemLanguage { get; set; }
|
||||
public Language SystemLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
|
@ -119,7 +111,7 @@ namespace Ryujinx
|
|||
/// <summary>
|
||||
/// The primary controller's type
|
||||
/// </summary>
|
||||
public ControllerStatus ControllerType { get; set; }
|
||||
public ControllerType ControllerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to toggle columns in the GUI
|
||||
|
@ -154,13 +146,13 @@ namespace Ryujinx
|
|||
/// <summary>
|
||||
/// Controller control bindings
|
||||
/// </summary>
|
||||
public Ui.Input.NpadController JoystickControls { get; private set; }
|
||||
public NpadController JoystickControls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads a configuration file from disk
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the JSON configuration file</param>
|
||||
public static void Load(string path)
|
||||
public static ConfigurationFileFormat Load(string path)
|
||||
{
|
||||
var resolver = CompositeResolver.Create(
|
||||
new[] { new ConfigurationEnumFormatter<Key>() },
|
||||
|
@ -169,24 +161,7 @@ namespace Ryujinx
|
|||
|
||||
using (Stream stream = File.OpenRead(path))
|
||||
{
|
||||
Instance = JsonSerializer.Deserialize<Configuration>(stream, resolver);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a configuration file asynchronously from disk
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the JSON configuration file</param>
|
||||
public static async Task LoadAsync(string path)
|
||||
{
|
||||
IJsonFormatterResolver resolver = CompositeResolver.Create(
|
||||
new[] { new ConfigurationEnumFormatter<Key>() },
|
||||
new[] { StandardResolver.AllowPrivateSnakeCase }
|
||||
);
|
||||
|
||||
using (Stream stream = File.OpenRead(path))
|
||||
{
|
||||
Instance = await JsonSerializer.DeserializeAsync<Configuration>(stream, resolver);
|
||||
return JsonSerializer.Deserialize<ConfigurationFileFormat>(stream, resolver);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,108 +169,17 @@ namespace Ryujinx
|
|||
/// Save a configuration file to disk
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the JSON configuration file</param>
|
||||
public static void SaveConfig(Configuration config, string path)
|
||||
public void SaveConfig(string path)
|
||||
{
|
||||
IJsonFormatterResolver resolver = CompositeResolver.Create(
|
||||
new[] { new ConfigurationEnumFormatter<Key>() },
|
||||
new[] { StandardResolver.AllowPrivateSnakeCase }
|
||||
);
|
||||
|
||||
byte[] data = JsonSerializer.Serialize(config, resolver);
|
||||
byte[] data = JsonSerializer.Serialize(this, resolver);
|
||||
File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures a <see cref="Switch"/> instance
|
||||
/// </summary>
|
||||
/// <param name="device">The instance to configure</param>
|
||||
public static void InitialConfigure(Switch device)
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
throw new InvalidOperationException("Configuration has not been loaded yet.");
|
||||
}
|
||||
|
||||
SwitchSettings.ConfigureSettings(Instance);
|
||||
|
||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||
new ConsoleLogTarget(),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Block
|
||||
));
|
||||
|
||||
if (Instance.EnableFileLog)
|
||||
{
|
||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||
new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log")),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Block
|
||||
));
|
||||
}
|
||||
|
||||
Configure(device, Instance);
|
||||
}
|
||||
|
||||
public static void Configure(Switch device, Configuration SwitchConfig)
|
||||
{
|
||||
GraphicsConfig.ShadersDumpPath = SwitchConfig.GraphicsShadersDumpPath;
|
||||
|
||||
Logger.SetEnable(LogLevel.Debug, SwitchConfig.LoggingEnableDebug );
|
||||
Logger.SetEnable(LogLevel.Stub, SwitchConfig.LoggingEnableStub );
|
||||
Logger.SetEnable(LogLevel.Info, SwitchConfig.LoggingEnableInfo );
|
||||
Logger.SetEnable(LogLevel.Warning, SwitchConfig.LoggingEnableWarn );
|
||||
Logger.SetEnable(LogLevel.Error, SwitchConfig.LoggingEnableError );
|
||||
Logger.SetEnable(LogLevel.Guest, SwitchConfig.LoggingEnableGuest );
|
||||
Logger.SetEnable(LogLevel.AccessLog, SwitchConfig.LoggingEnableFsAccessLog);
|
||||
|
||||
if (SwitchConfig.LoggingFilteredClasses.Length > 0)
|
||||
{
|
||||
foreach (var logClass in EnumExtensions.GetValues<LogClass>())
|
||||
{
|
||||
Logger.SetEnable(logClass, false);
|
||||
}
|
||||
|
||||
foreach (var logClass in SwitchConfig.LoggingFilteredClasses)
|
||||
{
|
||||
Logger.SetEnable(logClass, true);
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow.DiscordIntegrationEnabled = SwitchConfig.EnableDiscordIntegration;
|
||||
|
||||
device.EnableDeviceVsync = SwitchConfig.EnableVsync;
|
||||
|
||||
device.System.State.DockedMode = SwitchConfig.DockedMode;
|
||||
|
||||
device.System.State.SetLanguage(SwitchConfig.SystemLanguage);
|
||||
|
||||
if (SwitchConfig.EnableMulticoreScheduling)
|
||||
{
|
||||
device.System.EnableMultiCoreScheduling();
|
||||
}
|
||||
|
||||
device.System.FsIntegrityCheckLevel = SwitchConfig.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
device.System.GlobalAccessLogMode = SwitchConfig.FsGlobalAccessLogMode;
|
||||
|
||||
ServiceConfiguration.IgnoreMissingServices = SwitchConfig.IgnoreMissingServices;
|
||||
}
|
||||
|
||||
public static void ConfigureHid(Switch device, Configuration SwitchConfig)
|
||||
{
|
||||
if (SwitchConfig.JoystickControls.Enabled)
|
||||
{
|
||||
if (!Joystick.GetState(SwitchConfig.JoystickControls.Index).IsConnected)
|
||||
{
|
||||
SwitchConfig.JoystickControls.SetEnabled(false);
|
||||
}
|
||||
}
|
||||
device.Hid.InitializePrimaryController(SwitchConfig.ControllerType);
|
||||
device.Hid.InitializeKeyboard();
|
||||
}
|
||||
|
||||
private class ConfigurationEnumFormatter<T> : IJsonFormatter<T>
|
||||
where T : struct
|
||||
{
|
502
Ryujinx.Common/Configuration/ConfigurationState.cs
Normal file
502
Ryujinx.Common/Configuration/ConfigurationState.cs
Normal file
|
@ -0,0 +1,502 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Configuration.Hid;
|
||||
using Ryujinx.Configuration.System;
|
||||
using Ryujinx.Configuration.Ui;
|
||||
using Ryujinx.UI.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Configuration
|
||||
{
|
||||
public class ConfigurationState
|
||||
{
|
||||
/// <summary>
|
||||
/// UI configuration section
|
||||
/// </summary>
|
||||
public class UiSection
|
||||
{
|
||||
public class Columns
|
||||
{
|
||||
public ReactiveObject<bool> FavColumn { get; private set; }
|
||||
public ReactiveObject<bool> IconColumn { get; private set; }
|
||||
public ReactiveObject<bool> AppColumn { get; private set; }
|
||||
public ReactiveObject<bool> DevColumn { get; private set; }
|
||||
public ReactiveObject<bool> VersionColumn { get; private set; }
|
||||
public ReactiveObject<bool> TimePlayedColumn { get; private set; }
|
||||
public ReactiveObject<bool> LastPlayedColumn { get; private set; }
|
||||
public ReactiveObject<bool> FileExtColumn { get; private set; }
|
||||
public ReactiveObject<bool> FileSizeColumn { get; private set; }
|
||||
public ReactiveObject<bool> PathColumn { get; private set; }
|
||||
|
||||
public Columns()
|
||||
{
|
||||
FavColumn = new ReactiveObject<bool>();
|
||||
IconColumn = new ReactiveObject<bool>();
|
||||
AppColumn = new ReactiveObject<bool>();
|
||||
DevColumn = new ReactiveObject<bool>();
|
||||
VersionColumn = new ReactiveObject<bool>();
|
||||
TimePlayedColumn = new ReactiveObject<bool>();
|
||||
LastPlayedColumn = new ReactiveObject<bool>();
|
||||
FileExtColumn = new ReactiveObject<bool>();
|
||||
FileSizeColumn = new ReactiveObject<bool>();
|
||||
PathColumn = new ReactiveObject<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to toggle columns in the GUI
|
||||
/// </summary>
|
||||
public Columns GuiColumns { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of directories containing games to be used to load games into the games list
|
||||
/// </summary>
|
||||
public ReactiveObject<List<string>> GameDirs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable custom themes in the GUI
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableCustomTheme { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to custom GUI theme
|
||||
/// </summary>
|
||||
public ReactiveObject<string> CustomThemePath { get; private set; }
|
||||
|
||||
public UiSection()
|
||||
{
|
||||
GuiColumns = new Columns();
|
||||
GameDirs = new ReactiveObject<List<string>>();
|
||||
EnableCustomTheme = new ReactiveObject<bool>();
|
||||
CustomThemePath = new ReactiveObject<string>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logger configuration section
|
||||
/// </summary>
|
||||
public class LoggerSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables printing debug log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableDebug { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing stub log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableStub { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing info log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing warning log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableWarn { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing error log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableError { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing guest log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableGuest { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables printing FS access log messages
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls which log messages are written to the log targets
|
||||
/// </summary>
|
||||
public ReactiveObject<LogClass[]> FilteredClasses { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables logging to a file on disk
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableFileLog { get; private set; }
|
||||
|
||||
public LoggerSection()
|
||||
{
|
||||
EnableDebug = new ReactiveObject<bool>();
|
||||
EnableStub = new ReactiveObject<bool>();
|
||||
EnableInfo = new ReactiveObject<bool>();
|
||||
EnableWarn = new ReactiveObject<bool>();
|
||||
EnableError = new ReactiveObject<bool>();
|
||||
EnableGuest = new ReactiveObject<bool>();
|
||||
EnableFsAccessLog = new ReactiveObject<bool>();
|
||||
FilteredClasses = new ReactiveObject<LogClass[]>();
|
||||
EnableFileLog = new ReactiveObject<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System configuration section
|
||||
/// </summary>
|
||||
public class SystemSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Change System Language
|
||||
/// </summary>
|
||||
public ReactiveObject<Language> Language { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableDockedMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables multi-core scheduling of threads
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables integrity checks on Game content files
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableFsIntegrityChecks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables FS access log output to the console. Possible modes are 0-3
|
||||
/// </summary>
|
||||
public ReactiveObject<int> FsGlobalAccessLogMode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable ignoring missing services
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> IgnoreMissingServices { get; private set; }
|
||||
|
||||
public SystemSection()
|
||||
{
|
||||
Language = new ReactiveObject<Language>();
|
||||
EnableDockedMode = new ReactiveObject<bool>();
|
||||
EnableMulticoreScheduling = new ReactiveObject<bool>();
|
||||
EnableFsIntegrityChecks = new ReactiveObject<bool>();
|
||||
FsGlobalAccessLogMode = new ReactiveObject<int>();
|
||||
IgnoreMissingServices = new ReactiveObject<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hid configuration section
|
||||
/// </summary>
|
||||
public class HidSection
|
||||
{
|
||||
/// <summary>
|
||||
/// The primary controller's type
|
||||
/// </summary>
|
||||
public ReactiveObject<ControllerType> ControllerType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable keyboard support (Independent from controllers binding)
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableKeyboard { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Keyboard control bindings
|
||||
/// </summary>
|
||||
public ReactiveObject<NpadKeyboard> KeyboardControls { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller control bindings
|
||||
/// </summary>
|
||||
public ReactiveObject<NpadController> JoystickControls { get; private set; }
|
||||
|
||||
public HidSection()
|
||||
{
|
||||
ControllerType = new ReactiveObject<ControllerType>();
|
||||
EnableKeyboard = new ReactiveObject<bool>();
|
||||
KeyboardControls = new ReactiveObject<NpadKeyboard>();
|
||||
JoystickControls = new ReactiveObject<NpadController>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Graphics configuration section
|
||||
/// </summary>
|
||||
public class GraphicsSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Dumps shaders in this local directory
|
||||
/// </summary>
|
||||
public ReactiveObject<string> ShadersDumpPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Vertical Sync
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableVsync { get; private set; }
|
||||
|
||||
public GraphicsSection()
|
||||
{
|
||||
ShadersDumpPath = new ReactiveObject<string>();
|
||||
EnableVsync = new ReactiveObject<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default configuration instance
|
||||
/// </summary>
|
||||
public static ConfigurationState Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Ui section
|
||||
/// </summary>
|
||||
public UiSection Ui { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Logger section
|
||||
/// </summary>
|
||||
public LoggerSection Logger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The System section
|
||||
/// </summary>
|
||||
public SystemSection System { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Graphics section
|
||||
/// </summary>
|
||||
public GraphicsSection Graphics { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Hid section
|
||||
/// </summary>
|
||||
public HidSection Hid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Discord Rich Presence
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
|
||||
|
||||
private ConfigurationState()
|
||||
{
|
||||
Ui = new UiSection();
|
||||
Logger = new LoggerSection();
|
||||
System = new SystemSection();
|
||||
Graphics = new GraphicsSection();
|
||||
Hid = new HidSection();
|
||||
EnableDiscordIntegration = new ReactiveObject<bool>();
|
||||
}
|
||||
|
||||
public ConfigurationFileFormat ToFileFormat()
|
||||
{
|
||||
ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
|
||||
{
|
||||
Version = 1,
|
||||
GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
|
||||
LoggingEnableDebug = Logger.EnableDebug,
|
||||
LoggingEnableStub = Logger.EnableStub,
|
||||
LoggingEnableInfo = Logger.EnableInfo,
|
||||
LoggingEnableWarn = Logger.EnableWarn,
|
||||
LoggingEnableError = Logger.EnableError,
|
||||
LoggingEnableGuest = Logger.EnableGuest,
|
||||
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
|
||||
LoggingFilteredClasses = Logger.FilteredClasses,
|
||||
EnableFileLog = Logger.EnableFileLog,
|
||||
SystemLanguage = System.Language,
|
||||
DockedMode = System.EnableDockedMode,
|
||||
EnableDiscordIntegration = EnableDiscordIntegration,
|
||||
EnableVsync = Graphics.EnableVsync,
|
||||
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
|
||||
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
|
||||
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
|
||||
IgnoreMissingServices = System.IgnoreMissingServices,
|
||||
ControllerType = Hid.ControllerType,
|
||||
GuiColumns = new GuiColumns()
|
||||
{
|
||||
FavColumn = Ui.GuiColumns.FavColumn,
|
||||
IconColumn = Ui.GuiColumns.IconColumn,
|
||||
AppColumn = Ui.GuiColumns.AppColumn,
|
||||
DevColumn = Ui.GuiColumns.DevColumn,
|
||||
VersionColumn = Ui.GuiColumns.VersionColumn,
|
||||
TimePlayedColumn = Ui.GuiColumns.TimePlayedColumn,
|
||||
LastPlayedColumn = Ui.GuiColumns.LastPlayedColumn,
|
||||
FileExtColumn = Ui.GuiColumns.FileExtColumn,
|
||||
FileSizeColumn = Ui.GuiColumns.FileSizeColumn,
|
||||
PathColumn = Ui.GuiColumns.PathColumn,
|
||||
},
|
||||
GameDirs = Ui.GameDirs,
|
||||
EnableCustomTheme = Ui.EnableCustomTheme,
|
||||
CustomThemePath = Ui.CustomThemePath,
|
||||
EnableKeyboard = Hid.EnableKeyboard,
|
||||
KeyboardControls = Hid.KeyboardControls,
|
||||
JoystickControls = Hid.JoystickControls
|
||||
};
|
||||
|
||||
return configurationFile;
|
||||
}
|
||||
|
||||
public void LoadDefault()
|
||||
{
|
||||
Graphics.ShadersDumpPath.Value = "";
|
||||
Logger.EnableDebug.Value = false;
|
||||
Logger.EnableStub.Value = true;
|
||||
Logger.EnableInfo.Value = true;
|
||||
Logger.EnableWarn.Value = true;
|
||||
Logger.EnableError.Value = true;
|
||||
Logger.EnableGuest.Value = true;
|
||||
Logger.EnableFsAccessLog.Value = false;
|
||||
Logger.FilteredClasses.Value = new LogClass[] { };
|
||||
Logger.EnableFileLog.Value = true;
|
||||
System.Language.Value = Language.AmericanEnglish;
|
||||
System.EnableDockedMode.Value = false;
|
||||
EnableDiscordIntegration.Value = true;
|
||||
Graphics.EnableVsync.Value = true;
|
||||
System.EnableMulticoreScheduling.Value = true;
|
||||
System.EnableFsIntegrityChecks.Value = true;
|
||||
System.FsGlobalAccessLogMode.Value = 0;
|
||||
System.IgnoreMissingServices.Value = false;
|
||||
Hid.ControllerType.Value = ControllerType.Handheld;
|
||||
Ui.GuiColumns.FavColumn.Value = true;
|
||||
Ui.GuiColumns.IconColumn.Value = true;
|
||||
Ui.GuiColumns.AppColumn.Value = true;
|
||||
Ui.GuiColumns.DevColumn.Value = true;
|
||||
Ui.GuiColumns.VersionColumn.Value = true;
|
||||
Ui.GuiColumns.TimePlayedColumn.Value = true;
|
||||
Ui.GuiColumns.LastPlayedColumn.Value = true;
|
||||
Ui.GuiColumns.FileExtColumn.Value = true;
|
||||
Ui.GuiColumns.FileSizeColumn.Value = true;
|
||||
Ui.GuiColumns.PathColumn.Value = true;
|
||||
Ui.GameDirs.Value = new List<string>();
|
||||
Ui.EnableCustomTheme.Value = false;
|
||||
Ui.CustomThemePath.Value = "";
|
||||
Hid.EnableKeyboard.Value = false;
|
||||
|
||||
Hid.KeyboardControls.Value = new NpadKeyboard
|
||||
{
|
||||
LeftJoycon = new NpadKeyboardLeft
|
||||
{
|
||||
StickUp = Key.W,
|
||||
StickDown = Key.S,
|
||||
StickLeft = Key.A,
|
||||
StickRight = Key.D,
|
||||
StickButton = Key.F,
|
||||
DPadUp = Key.Up,
|
||||
DPadDown = Key.Down,
|
||||
DPadLeft = Key.Left,
|
||||
DPadRight = Key.Right,
|
||||
ButtonMinus = Key.Minus,
|
||||
ButtonL = Key.E,
|
||||
ButtonZl = Key.Q,
|
||||
},
|
||||
RightJoycon = new NpadKeyboardRight
|
||||
{
|
||||
StickUp = Key.I,
|
||||
StickDown = Key.K,
|
||||
StickLeft = Key.J,
|
||||
StickRight = Key.L,
|
||||
StickButton = Key.H,
|
||||
ButtonA = Key.Z,
|
||||
ButtonB = Key.X,
|
||||
ButtonX = Key.C,
|
||||
ButtonY = Key.V,
|
||||
ButtonPlus = Key.Plus,
|
||||
ButtonR = Key.U,
|
||||
ButtonZr = Key.O,
|
||||
},
|
||||
Hotkeys = new KeyboardHotkeys
|
||||
{
|
||||
ToggleVsync = Key.Tab
|
||||
}
|
||||
};
|
||||
|
||||
Hid.JoystickControls.Value = new NpadController
|
||||
{
|
||||
Enabled = true,
|
||||
Index = 0,
|
||||
Deadzone = 0.05f,
|
||||
TriggerThreshold = 0.5f,
|
||||
LeftJoycon = new NpadControllerLeft
|
||||
{
|
||||
Stick = ControllerInputId.Axis0,
|
||||
StickButton = ControllerInputId.Button8,
|
||||
DPadUp = ControllerInputId.Hat0Up,
|
||||
DPadDown = ControllerInputId.Hat0Down,
|
||||
DPadLeft = ControllerInputId.Hat0Left,
|
||||
DPadRight = ControllerInputId.Hat0Right,
|
||||
ButtonMinus = ControllerInputId.Button6,
|
||||
ButtonL = ControllerInputId.Button4,
|
||||
ButtonZl = ControllerInputId.Axis2,
|
||||
},
|
||||
RightJoycon = new NpadControllerRight
|
||||
{
|
||||
Stick = ControllerInputId.Axis3,
|
||||
StickButton = ControllerInputId.Button9,
|
||||
ButtonA = ControllerInputId.Button1,
|
||||
ButtonB = ControllerInputId.Button0,
|
||||
ButtonX = ControllerInputId.Button3,
|
||||
ButtonY = ControllerInputId.Button2,
|
||||
ButtonPlus = ControllerInputId.Button7,
|
||||
ButtonR = ControllerInputId.Button5,
|
||||
ButtonZr = ControllerInputId.Axis5,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Load(ConfigurationFileFormat configurationFileFormat)
|
||||
{
|
||||
if (configurationFileFormat.Version != 1 && configurationFileFormat.Version != 0)
|
||||
{
|
||||
Common.Logging.Logger.PrintWarning(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
|
||||
|
||||
LoadDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath;
|
||||
Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
|
||||
Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
|
||||
Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
|
||||
Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn;
|
||||
Logger.EnableError.Value = configurationFileFormat.LoggingEnableError;
|
||||
Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest;
|
||||
Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
|
||||
Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;
|
||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||
System.Language.Value = configurationFileFormat.SystemLanguage;
|
||||
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
|
||||
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
|
||||
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
|
||||
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
|
||||
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
|
||||
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
|
||||
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
|
||||
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
|
||||
Hid.ControllerType.Value = configurationFileFormat.ControllerType;
|
||||
Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn;
|
||||
Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn;
|
||||
Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn;
|
||||
Ui.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn;
|
||||
Ui.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn;
|
||||
Ui.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn;
|
||||
Ui.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn;
|
||||
Ui.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn;
|
||||
Ui.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn;
|
||||
Ui.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn;
|
||||
Ui.GameDirs.Value = configurationFileFormat.GameDirs;
|
||||
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
||||
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
||||
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
|
||||
Hid.KeyboardControls.Value = configurationFileFormat.KeyboardControls;
|
||||
Hid.JoystickControls.Value = configurationFileFormat.JoystickControls;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
throw new InvalidOperationException("Configuration is already initialized");
|
||||
}
|
||||
|
||||
Instance = new ConfigurationState();
|
||||
}
|
||||
}
|
||||
}
|
45
Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
Normal file
45
Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public enum ControllerInputId
|
||||
{
|
||||
Button0,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
Button4,
|
||||
Button5,
|
||||
Button6,
|
||||
Button7,
|
||||
Button8,
|
||||
Button9,
|
||||
Button10,
|
||||
Button11,
|
||||
Button12,
|
||||
Button13,
|
||||
Button14,
|
||||
Button15,
|
||||
Button16,
|
||||
Button17,
|
||||
Button18,
|
||||
Button19,
|
||||
Button20,
|
||||
Axis0,
|
||||
Axis1,
|
||||
Axis2,
|
||||
Axis3,
|
||||
Axis4,
|
||||
Axis5,
|
||||
Hat0Up,
|
||||
Hat0Down,
|
||||
Hat0Left,
|
||||
Hat0Right,
|
||||
Hat1Up,
|
||||
Hat1Down,
|
||||
Hat1Left,
|
||||
Hat1Right,
|
||||
Hat2Up,
|
||||
Hat2Down,
|
||||
Hat2Left,
|
||||
Hat2Right
|
||||
}
|
||||
}
|
11
Ryujinx.Common/Configuration/Hid/ControllerType.cs
Normal file
11
Ryujinx.Common/Configuration/Hid/ControllerType.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Configuration.Hid
|
||||
{
|
||||
public enum ControllerType
|
||||
{
|
||||
ProController,
|
||||
Handheld,
|
||||
NpadPair,
|
||||
NpadLeft,
|
||||
NpadRight
|
||||
}
|
||||
}
|
153
Ryujinx.Common/Configuration/Hid/Key.cs
Normal file
153
Ryujinx.Common/Configuration/Hid/Key.cs
Normal file
|
@ -0,0 +1,153 @@
|
|||
namespace Ryujinx.Configuration.Hid
|
||||
{
|
||||
public enum Key
|
||||
{
|
||||
Unknown = 0,
|
||||
ShiftLeft = 1,
|
||||
LShift = 1,
|
||||
ShiftRight = 2,
|
||||
RShift = 2,
|
||||
ControlLeft = 3,
|
||||
LControl = 3,
|
||||
ControlRight = 4,
|
||||
RControl = 4,
|
||||
AltLeft = 5,
|
||||
LAlt = 5,
|
||||
AltRight = 6,
|
||||
RAlt = 6,
|
||||
WinLeft = 7,
|
||||
LWin = 7,
|
||||
WinRight = 8,
|
||||
RWin = 8,
|
||||
Menu = 9,
|
||||
F1 = 10,
|
||||
F2 = 11,
|
||||
F3 = 12,
|
||||
F4 = 13,
|
||||
F5 = 14,
|
||||
F6 = 15,
|
||||
F7 = 16,
|
||||
F8 = 17,
|
||||
F9 = 18,
|
||||
F10 = 19,
|
||||
F11 = 20,
|
||||
F12 = 21,
|
||||
F13 = 22,
|
||||
F14 = 23,
|
||||
F15 = 24,
|
||||
F16 = 25,
|
||||
F17 = 26,
|
||||
F18 = 27,
|
||||
F19 = 28,
|
||||
F20 = 29,
|
||||
F21 = 30,
|
||||
F22 = 31,
|
||||
F23 = 32,
|
||||
F24 = 33,
|
||||
F25 = 34,
|
||||
F26 = 35,
|
||||
F27 = 36,
|
||||
F28 = 37,
|
||||
F29 = 38,
|
||||
F30 = 39,
|
||||
F31 = 40,
|
||||
F32 = 41,
|
||||
F33 = 42,
|
||||
F34 = 43,
|
||||
F35 = 44,
|
||||
Up = 45,
|
||||
Down = 46,
|
||||
Left = 47,
|
||||
Right = 48,
|
||||
Enter = 49,
|
||||
Escape = 50,
|
||||
Space = 51,
|
||||
Tab = 52,
|
||||
BackSpace = 53,
|
||||
Back = 53,
|
||||
Insert = 54,
|
||||
Delete = 55,
|
||||
PageUp = 56,
|
||||
PageDown = 57,
|
||||
Home = 58,
|
||||
End = 59,
|
||||
CapsLock = 60,
|
||||
ScrollLock = 61,
|
||||
PrintScreen = 62,
|
||||
Pause = 63,
|
||||
NumLock = 64,
|
||||
Clear = 65,
|
||||
Sleep = 66,
|
||||
Keypad0 = 67,
|
||||
Keypad1 = 68,
|
||||
Keypad2 = 69,
|
||||
Keypad3 = 70,
|
||||
Keypad4 = 71,
|
||||
Keypad5 = 72,
|
||||
Keypad6 = 73,
|
||||
Keypad7 = 74,
|
||||
Keypad8 = 75,
|
||||
Keypad9 = 76,
|
||||
KeypadDivide = 77,
|
||||
KeypadMultiply = 78,
|
||||
KeypadSubtract = 79,
|
||||
KeypadMinus = 79,
|
||||
KeypadAdd = 80,
|
||||
KeypadPlus = 80,
|
||||
KeypadDecimal = 81,
|
||||
KeypadPeriod = 81,
|
||||
KeypadEnter = 82,
|
||||
A = 83,
|
||||
B = 84,
|
||||
C = 85,
|
||||
D = 86,
|
||||
E = 87,
|
||||
F = 88,
|
||||
G = 89,
|
||||
H = 90,
|
||||
I = 91,
|
||||
J = 92,
|
||||
K = 93,
|
||||
L = 94,
|
||||
M = 95,
|
||||
N = 96,
|
||||
O = 97,
|
||||
P = 98,
|
||||
Q = 99,
|
||||
R = 100,
|
||||
S = 101,
|
||||
T = 102,
|
||||
U = 103,
|
||||
V = 104,
|
||||
W = 105,
|
||||
X = 106,
|
||||
Y = 107,
|
||||
Z = 108,
|
||||
Number0 = 109,
|
||||
Number1 = 110,
|
||||
Number2 = 111,
|
||||
Number3 = 112,
|
||||
Number4 = 113,
|
||||
Number5 = 114,
|
||||
Number6 = 115,
|
||||
Number7 = 116,
|
||||
Number8 = 117,
|
||||
Number9 = 118,
|
||||
Tilde = 119,
|
||||
Grave = 119,
|
||||
Minus = 120,
|
||||
Plus = 121,
|
||||
BracketLeft = 122,
|
||||
LBracket = 122,
|
||||
BracketRight = 123,
|
||||
RBracket = 123,
|
||||
Semicolon = 124,
|
||||
Quote = 125,
|
||||
Comma = 126,
|
||||
Period = 127,
|
||||
Slash = 128,
|
||||
BackSlash = 129,
|
||||
NonUSBackSlash = 130,
|
||||
LastKey = 131
|
||||
}
|
||||
}
|
7
Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
Normal file
7
Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.Configuration.Hid
|
||||
{
|
||||
public struct KeyboardHotkeys
|
||||
{
|
||||
public Key ToggleVsync;
|
||||
}
|
||||
}
|
35
Ryujinx.Common/Configuration/Hid/NpadController.cs
Normal file
35
Ryujinx.Common/Configuration/Hid/NpadController.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public class NpadController
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables or disables controller support
|
||||
/// </summary>
|
||||
public bool Enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Controller Device Index
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
/// <summary>
|
||||
/// Controller Analog Stick Deadzone
|
||||
/// </summary>
|
||||
public float Deadzone;
|
||||
|
||||
/// <summary>
|
||||
/// Controller Trigger Threshold
|
||||
/// </summary>
|
||||
public float TriggerThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// Left JoyCon Controller Bindings
|
||||
/// </summary>
|
||||
public NpadControllerLeft LeftJoycon;
|
||||
|
||||
/// <summary>
|
||||
/// Right JoyCon Controller Bindings
|
||||
/// </summary>
|
||||
public NpadControllerRight RightJoycon;
|
||||
}
|
||||
}
|
15
Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
Normal file
15
Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public struct NpadControllerLeft
|
||||
{
|
||||
public ControllerInputId Stick;
|
||||
public ControllerInputId StickButton;
|
||||
public ControllerInputId ButtonMinus;
|
||||
public ControllerInputId ButtonL;
|
||||
public ControllerInputId ButtonZl;
|
||||
public ControllerInputId DPadUp;
|
||||
public ControllerInputId DPadDown;
|
||||
public ControllerInputId DPadLeft;
|
||||
public ControllerInputId DPadRight;
|
||||
}
|
||||
}
|
15
Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
Normal file
15
Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public struct NpadControllerRight
|
||||
{
|
||||
public ControllerInputId Stick;
|
||||
public ControllerInputId StickButton;
|
||||
public ControllerInputId ButtonA;
|
||||
public ControllerInputId ButtonB;
|
||||
public ControllerInputId ButtonX;
|
||||
public ControllerInputId ButtonY;
|
||||
public ControllerInputId ButtonPlus;
|
||||
public ControllerInputId ButtonR;
|
||||
public ControllerInputId ButtonZr;
|
||||
}
|
||||
}
|
20
Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
Normal file
20
Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace Ryujinx.UI.Input
|
||||
{
|
||||
public class NpadKeyboard
|
||||
{
|
||||
/// <summary>
|
||||
/// Left JoyCon Keyboard Bindings
|
||||
/// </summary>
|
||||
public Configuration.Hid.NpadKeyboardLeft LeftJoycon;
|
||||
|
||||
/// <summary>
|
||||
/// Right JoyCon Keyboard Bindings
|
||||
/// </summary>
|
||||
public Configuration.Hid.NpadKeyboardRight RightJoycon;
|
||||
|
||||
/// <summary>
|
||||
/// Hotkey Keyboard Bindings
|
||||
/// </summary>
|
||||
public Configuration.Hid.KeyboardHotkeys Hotkeys;
|
||||
}
|
||||
}
|
18
Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
Normal file
18
Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Configuration.Hid
|
||||
{
|
||||
public struct NpadKeyboardLeft
|
||||
{
|
||||
public Key StickUp;
|
||||
public Key StickDown;
|
||||
public Key StickLeft;
|
||||
public Key StickRight;
|
||||
public Key StickButton;
|
||||
public Key DPadUp;
|
||||
public Key DPadDown;
|
||||
public Key DPadLeft;
|
||||
public Key DPadRight;
|
||||
public Key ButtonMinus;
|
||||
public Key ButtonL;
|
||||
public Key ButtonZl;
|
||||
}
|
||||
}
|
18
Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
Normal file
18
Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Configuration.Hid
|
||||
{
|
||||
public struct NpadKeyboardRight
|
||||
{
|
||||
public Key StickUp;
|
||||
public Key StickDown;
|
||||
public Key StickLeft;
|
||||
public Key StickRight;
|
||||
public Key StickButton;
|
||||
public Key ButtonA;
|
||||
public Key ButtonB;
|
||||
public Key ButtonX;
|
||||
public Key ButtonY;
|
||||
public Key ButtonPlus;
|
||||
public Key ButtonR;
|
||||
public Key ButtonZr;
|
||||
}
|
||||
}
|
109
Ryujinx.Common/Configuration/LoggerModule.cs
Normal file
109
Ryujinx.Common/Configuration/LoggerModule.cs
Normal file
|
@ -0,0 +1,109 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Configuration
|
||||
{
|
||||
public static class LoggerModule
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
||||
|
||||
ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug;
|
||||
ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub;
|
||||
ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo;
|
||||
ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning;
|
||||
ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError;
|
||||
ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest;
|
||||
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog;
|
||||
ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses;
|
||||
ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger;
|
||||
}
|
||||
|
||||
private static void ReloadEnableDebug(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Debug, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableStub(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Stub, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableInfo(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Info, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableWarning(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Warning, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableError(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Error, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableGuest(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.Guest, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadEnableFsAccessLog(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
|
||||
}
|
||||
|
||||
private static void ReloadFilteredClasses(object sender, ReactiveEventArgs<LogClass[]> e)
|
||||
{
|
||||
bool noFilter = e.NewValue.Length == 0;
|
||||
|
||||
foreach (var logClass in EnumExtensions.GetValues<LogClass>())
|
||||
{
|
||||
Logger.SetEnable(logClass, noFilter);
|
||||
}
|
||||
|
||||
foreach (var logClass in e.NewValue)
|
||||
{
|
||||
Logger.SetEnable(logClass, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReloadFileLogger(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
Logger.AddTarget(new AsyncLogTargetWrapper(
|
||||
new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log"), "file"),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Block
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.RemoveTarget("file");
|
||||
}
|
||||
}
|
||||
|
||||
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Shutdown();
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
var exception = e.ExceptionObject as Exception;
|
||||
|
||||
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
Logger.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Common/Configuration/System/Language.cs
Normal file
23
Ryujinx.Common/Configuration/System/Language.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ryujinx.Configuration.System
|
||||
{
|
||||
public enum Language
|
||||
{
|
||||
Japanese,
|
||||
AmericanEnglish,
|
||||
French,
|
||||
German,
|
||||
Italian,
|
||||
Spanish,
|
||||
Chinese,
|
||||
Korean,
|
||||
Dutch,
|
||||
Portuguese,
|
||||
Russian,
|
||||
Taiwanese,
|
||||
BritishEnglish,
|
||||
CanadianFrench,
|
||||
LatinAmericanSpanish,
|
||||
SimplifiedChinese,
|
||||
TraditionalChinese
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.Ui
|
||||
namespace Ryujinx.Configuration.Ui
|
||||
{
|
||||
public struct GuiColumns
|
||||
{
|
|
@ -37,6 +37,12 @@ namespace Ryujinx.Common.Logging
|
|||
m_LogTargets = new List<ILogTarget>();
|
||||
|
||||
m_Time = Stopwatch.StartNew();
|
||||
|
||||
// Logger should log to console by default
|
||||
AddTarget(new AsyncLogTargetWrapper(
|
||||
new ConsoleLogTarget("console"),
|
||||
1000,
|
||||
AsyncLogTargetOverflowAction.Block));
|
||||
}
|
||||
|
||||
public static void RestartTime()
|
||||
|
@ -44,6 +50,19 @@ namespace Ryujinx.Common.Logging
|
|||
m_Time.Restart();
|
||||
}
|
||||
|
||||
private static ILogTarget GetTarget(string targetName)
|
||||
{
|
||||
foreach (var target in m_LogTargets)
|
||||
{
|
||||
if (target.Name.Equals(targetName))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void AddTarget(ILogTarget target)
|
||||
{
|
||||
m_LogTargets.Add(target);
|
||||
|
@ -51,6 +70,20 @@ namespace Ryujinx.Common.Logging
|
|||
Updated += target.Log;
|
||||
}
|
||||
|
||||
public static void RemoveTarget(string target)
|
||||
{
|
||||
ILogTarget logTarget = GetTarget(target);
|
||||
|
||||
if (logTarget != null)
|
||||
{
|
||||
Updated -= logTarget.Log;
|
||||
|
||||
m_LogTargets.Remove(logTarget);
|
||||
|
||||
logTarget.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Shutdown()
|
||||
{
|
||||
Updated = null;
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace Ryujinx.Common.Logging
|
|||
|
||||
private readonly int _overflowTimeout;
|
||||
|
||||
string ILogTarget.Name { get => _target.Name; }
|
||||
|
||||
public AsyncLogTargetWrapper(ILogTarget target)
|
||||
: this(target, -1, AsyncLogTargetOverflowAction.Block)
|
||||
{ }
|
||||
|
|
|
@ -9,6 +9,10 @@ namespace Ryujinx.Common.Logging
|
|||
|
||||
private readonly ILogFormatter _formatter;
|
||||
|
||||
private readonly string _name;
|
||||
|
||||
string ILogTarget.Name { get => _name; }
|
||||
|
||||
static ConsoleLogTarget()
|
||||
{
|
||||
_logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> {
|
||||
|
@ -19,9 +23,10 @@ namespace Ryujinx.Common.Logging
|
|||
};
|
||||
}
|
||||
|
||||
public ConsoleLogTarget()
|
||||
public ConsoleLogTarget(string name)
|
||||
{
|
||||
_formatter = new DefaultLogFormatter();
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public void Log(object sender, LogEventArgs args)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
|
@ -9,13 +10,17 @@ namespace Ryujinx.Common.Logging
|
|||
|
||||
private readonly StreamWriter _logWriter;
|
||||
private readonly ILogFormatter _formatter;
|
||||
private readonly string _name;
|
||||
|
||||
public FileLogTarget(string path)
|
||||
: this(path, FileShare.Read, FileMode.Append)
|
||||
string ILogTarget.Name { get => _name; }
|
||||
|
||||
public FileLogTarget(string path, string name)
|
||||
: this(path, name, FileShare.Read, FileMode.Append)
|
||||
{ }
|
||||
|
||||
public FileLogTarget(string path, FileShare fileShare, FileMode fileMode)
|
||||
public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode)
|
||||
{
|
||||
_name = name;
|
||||
_logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
|
||||
_formatter = new DefaultLogFormatter();
|
||||
}
|
||||
|
|
|
@ -5,5 +5,7 @@ namespace Ryujinx.Common.Logging
|
|||
public interface ILogTarget : IDisposable
|
||||
{
|
||||
void Log(object sender, LogEventArgs args);
|
||||
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Utf8Json;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
|
@ -7,10 +8,14 @@ namespace Ryujinx.Common.Logging
|
|||
{
|
||||
private Stream _stream;
|
||||
private bool _leaveOpen;
|
||||
private string _name;
|
||||
|
||||
public JsonLogTarget(Stream stream)
|
||||
string ILogTarget.Name { get => _name; }
|
||||
|
||||
public JsonLogTarget(Stream stream, string name)
|
||||
{
|
||||
_stream = stream;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public JsonLogTarget(Stream stream, bool leaveOpen)
|
||||
|
|
57
Ryujinx.Common/ReactiveObject.cs
Normal file
57
Ryujinx.Common/ReactiveObject.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
public class ReactiveObject<T>
|
||||
{
|
||||
private ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
|
||||
private T _value;
|
||||
|
||||
public event EventHandler<ReactiveEventArgs<T>> Event;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
_readerWriterLock.AcquireReaderLock(Timeout.Infinite);
|
||||
T value = _value;
|
||||
_readerWriterLock.ReleaseReaderLock();
|
||||
|
||||
return value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_readerWriterLock.AcquireWriterLock(Timeout.Infinite);
|
||||
|
||||
T oldValue = _value;
|
||||
|
||||
_value = value;
|
||||
|
||||
_readerWriterLock.ReleaseWriterLock();
|
||||
|
||||
if (oldValue == null || !oldValue.Equals(_value))
|
||||
{
|
||||
Event?.Invoke(this, new ReactiveEventArgs<T>(oldValue, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator T(ReactiveObject<T> obj)
|
||||
{
|
||||
return obj.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReactiveEventArgs<T>
|
||||
{
|
||||
public T OldValue { get; }
|
||||
public T NewValue { get; }
|
||||
|
||||
public ReactiveEventArgs(T oldValue, T newValue)
|
||||
{
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
|
||||
<PackageReference Include="Utf8Json" Version="1.3.7" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -759,21 +759,42 @@ namespace Ryujinx.HLE.HOS
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Force all threads to exit.
|
||||
lock (Processes)
|
||||
KProcess terminationProcess = new KProcess(this);
|
||||
|
||||
KThread terminationThread = new KThread(this);
|
||||
|
||||
terminationThread.Initialize(0, 0, 0, 3, 0, terminationProcess, ThreadType.Kernel, () =>
|
||||
{
|
||||
foreach (KProcess process in Processes.Values)
|
||||
// Force all threads to exit.
|
||||
lock (Processes)
|
||||
{
|
||||
process.StopAllThreads();
|
||||
foreach (KProcess process in Processes.Values)
|
||||
{
|
||||
process.Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
// Exit ourself now!
|
||||
Scheduler.ExitThread(terminationThread);
|
||||
Scheduler.GetCurrentThread().Exit();
|
||||
Scheduler.RemoveThread(terminationThread);
|
||||
});
|
||||
|
||||
terminationThread.Start();
|
||||
|
||||
// Signal the vsync event to avoid issues of KThread waiting on it.
|
||||
if (Device.EnableDeviceVsync)
|
||||
{
|
||||
Device.VsyncEvent.Set();
|
||||
}
|
||||
|
||||
// This is needed as the IPC Dummy KThread is also counted in the ThreadCounter.
|
||||
ThreadCounter.Signal();
|
||||
|
||||
// It's only safe to release resources once all threads
|
||||
// have exited.
|
||||
ThreadCounter.Signal();
|
||||
//ThreadCounter.Wait(); // FIXME: Uncomment this
|
||||
// BODY: Right now, guest processes don't exit properly because the logic waits for them to exit.
|
||||
// BODY: However, this doesn't happen when you close the main window so we need to find a way to make them exit gracefully
|
||||
ThreadCounter.Wait();
|
||||
|
||||
Scheduler.Dispose();
|
||||
|
||||
|
|
|
@ -272,6 +272,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
disposableObj.Dispose();
|
||||
}
|
||||
|
||||
entry.Obj.DecrementReferenceCount();
|
||||
entry.Obj = null;
|
||||
entry.Next = _nextFreeEntry;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
public ulong PersonalMmHeapPagesCount { get; private set; }
|
||||
|
||||
private ProcessState _state;
|
||||
public ProcessState State { get; private set; }
|
||||
|
||||
private object _processLock;
|
||||
private object _threadingLock;
|
||||
|
@ -383,7 +383,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
Name = creationInfo.Name;
|
||||
|
||||
_state = ProcessState.Created;
|
||||
State = ProcessState.Created;
|
||||
|
||||
_creationTimestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
|
@ -579,7 +579,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
{
|
||||
lock (_processLock)
|
||||
{
|
||||
if (_state > ProcessState.CreatedAttached)
|
||||
if (State > ProcessState.CreatedAttached)
|
||||
{
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
@ -733,8 +733,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
mainThread.SetEntryArguments(0, mainThreadHandle);
|
||||
|
||||
ProcessState oldState = _state;
|
||||
ProcessState newState = _state != ProcessState.Created
|
||||
ProcessState oldState = State;
|
||||
ProcessState newState = State != ProcessState.Created
|
||||
? ProcessState.Attached
|
||||
: ProcessState.Started;
|
||||
|
||||
|
@ -768,9 +768,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
private void SetState(ProcessState newState)
|
||||
{
|
||||
if (_state != newState)
|
||||
if (State != newState)
|
||||
{
|
||||
_state = newState;
|
||||
State = newState;
|
||||
_signaled = true;
|
||||
|
||||
Signal();
|
||||
|
@ -820,6 +820,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
}
|
||||
}
|
||||
|
||||
public void DecrementToZeroWhileTerminatingCurrent()
|
||||
{
|
||||
System.ThreadCounter.Signal();
|
||||
|
||||
while (Interlocked.Decrement(ref _threadCount) != 0)
|
||||
{
|
||||
Destroy();
|
||||
TerminateCurrentProcess();
|
||||
}
|
||||
|
||||
// Nintendo panic here because if it reaches this point, the current thread should be already dead.
|
||||
// As we handle the death of the thread in the post SVC handler and inside the CPU emulator, we don't panic here.
|
||||
}
|
||||
|
||||
public ulong GetMemoryCapacity()
|
||||
{
|
||||
ulong totalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
|
||||
|
@ -909,12 +923,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
lock (_processLock)
|
||||
{
|
||||
if (_state >= ProcessState.Started)
|
||||
if (State >= ProcessState.Started)
|
||||
{
|
||||
if (_state == ProcessState.Started ||
|
||||
_state == ProcessState.Crashed ||
|
||||
_state == ProcessState.Attached ||
|
||||
_state == ProcessState.DebugSuspended)
|
||||
if (State == ProcessState.Started ||
|
||||
State == ProcessState.Crashed ||
|
||||
State == ProcessState.Attached ||
|
||||
State == ProcessState.DebugSuspended)
|
||||
{
|
||||
SetState(ProcessState.Exiting);
|
||||
|
||||
|
@ -933,23 +947,98 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
if (shallTerminate)
|
||||
{
|
||||
// UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||
|
||||
HandleTable.Destroy();
|
||||
|
||||
SignalExitForDebugEvent();
|
||||
SignalExitToDebugTerminated();
|
||||
SignalExit();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UnpauseAndTerminateAllThreadsExcept(KThread thread)
|
||||
public void TerminateCurrentProcess()
|
||||
{
|
||||
// TODO.
|
||||
bool shallTerminate = false;
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
lock (_processLock)
|
||||
{
|
||||
if (State >= ProcessState.Started)
|
||||
{
|
||||
if (State == ProcessState.Started ||
|
||||
State == ProcessState.Attached ||
|
||||
State == ProcessState.DebugSuspended)
|
||||
{
|
||||
SetState(ProcessState.Exiting);
|
||||
|
||||
shallTerminate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
if (shallTerminate)
|
||||
{
|
||||
UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||
|
||||
HandleTable.Destroy();
|
||||
|
||||
// NOTE: this is supposed to be called in receiving of the mailbox.
|
||||
SignalExitToDebugExited();
|
||||
SignalExit();
|
||||
}
|
||||
}
|
||||
|
||||
private void SignalExitForDebugEvent()
|
||||
private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
|
||||
{
|
||||
lock (_threadingLock)
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
foreach (KThread thread in _threads)
|
||||
{
|
||||
if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
thread.PrepareForTermination();
|
||||
}
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
KThread blockedThread = null;
|
||||
|
||||
lock (_threadingLock)
|
||||
{
|
||||
foreach (KThread thread in _threads)
|
||||
{
|
||||
if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
thread.IncrementReferenceCount();
|
||||
|
||||
blockedThread = thread;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockedThread != null)
|
||||
{
|
||||
blockedThread.Terminate();
|
||||
blockedThread.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
|
||||
private void SignalExitToDebugTerminated()
|
||||
{
|
||||
// TODO: Debug events.
|
||||
}
|
||||
|
||||
private void SignalExitToDebugExited()
|
||||
{
|
||||
// TODO: Debug events.
|
||||
}
|
||||
|
@ -976,7 +1065,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
lock (_processLock)
|
||||
{
|
||||
if (_state != ProcessState.Exited && _signaled)
|
||||
if (State != ProcessState.Exited && _signaled)
|
||||
{
|
||||
_signaled = false;
|
||||
|
||||
|
@ -999,7 +1088,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
{
|
||||
foreach (KThread thread in _threads)
|
||||
{
|
||||
thread.Context.Running = false;
|
||||
System.Scheduler.ExitThread(thread);
|
||||
|
||||
System.Scheduler.CoreManager.Set(thread.HostThread);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using ARMeilleure.State;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||
|
@ -29,6 +30,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
ExecutionContext context = (ExecutionContext)sender;
|
||||
|
||||
svcFunc(this, context);
|
||||
|
||||
PostSvcHandler();
|
||||
}
|
||||
|
||||
private void PostSvcHandler()
|
||||
{
|
||||
KThread currentThread = _system.Scheduler.GetCurrentThread();
|
||||
|
||||
currentThread.HandlePostSyscall();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,9 +17,41 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
ExitProcess();
|
||||
}
|
||||
|
||||
public KernelResult TerminateProcess64(int handle)
|
||||
{
|
||||
return TerminateProcess(handle);
|
||||
}
|
||||
|
||||
private KernelResult TerminateProcess(int handle)
|
||||
{
|
||||
KProcess process = _process.HandleTable.GetObject<KProcess>(handle);
|
||||
|
||||
KernelResult result;
|
||||
|
||||
if (process != null)
|
||||
{
|
||||
if (process == _system.Scheduler.GetCurrentProcess())
|
||||
{
|
||||
result = KernelResult.Success;
|
||||
process.DecrementToZeroWhileTerminatingCurrent();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = process.Terminate();
|
||||
process.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = KernelResult.InvalidHandle;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ExitProcess()
|
||||
{
|
||||
_system.Scheduler.GetCurrentProcess().Terminate();
|
||||
_system.Scheduler.GetCurrentProcess().TerminateCurrentProcess();
|
||||
}
|
||||
|
||||
public KernelResult SignalEvent64(int handle)
|
||||
|
@ -184,6 +216,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{
|
||||
currentThread.PrintGuestStackTrace();
|
||||
|
||||
// As the process is exiting, this is probably caused by emulation termination.
|
||||
if (currentThread.Owner.State == ProcessState.Exiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Debug events.
|
||||
currentThread.Owner.TerminateCurrentProcess();
|
||||
|
||||
throw new GuestBrokeExecutionException();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -74,7 +74,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{ 0x72, nameof(SvcHandler.ConnectToPort64) },
|
||||
{ 0x73, nameof(SvcHandler.SetProcessMemoryPermission64) },
|
||||
{ 0x77, nameof(SvcHandler.MapProcessCodeMemory64) },
|
||||
{ 0x78, nameof(SvcHandler.UnmapProcessCodeMemory64) }
|
||||
{ 0x78, nameof(SvcHandler.UnmapProcessCodeMemory64) },
|
||||
{ 0x7B, nameof(SvcHandler.TerminateProcess64) }
|
||||
};
|
||||
|
||||
_svcTable64 = new Action<SvcHandler, ExecutionContext>[0x80];
|
||||
|
|
|
@ -70,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public ThreadSchedState SchedFlags { get; private set; }
|
||||
|
||||
public bool ShallBeTerminated { get; private set; }
|
||||
private int _shallBeTerminated;
|
||||
|
||||
public bool ShallBeTerminated { get => _shallBeTerminated != 0; set => _shallBeTerminated = value ? 1 : 0; }
|
||||
|
||||
public bool SyncCancelled { get; set; }
|
||||
public bool WaitingSync { get; set; }
|
||||
|
@ -104,7 +106,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
int priority,
|
||||
int defaultCpuCore,
|
||||
KProcess owner,
|
||||
ThreadType type = ThreadType.User)
|
||||
ThreadType type = ThreadType.User,
|
||||
ThreadStart customHostThreadStart = null)
|
||||
{
|
||||
if ((uint)type > 3)
|
||||
{
|
||||
|
@ -156,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
is64Bits = true;
|
||||
}
|
||||
|
||||
HostThread = new Thread(() => ThreadStart(entrypoint));
|
||||
HostThread = new Thread(customHostThreadStart == null ? () => ThreadStart(entrypoint) : customHostThreadStart);
|
||||
|
||||
Context = new ARMeilleure.State.ExecutionContext();
|
||||
|
||||
|
@ -182,6 +185,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
ThreadUid = System.GetThreadUid();
|
||||
|
||||
HostThread.Name = $"Host Thread (thread id {ThreadUid})";
|
||||
|
||||
_hasBeenInitialized = true;
|
||||
|
||||
if (owner != null)
|
||||
|
@ -300,6 +305,100 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
DecrementReferenceCount();
|
||||
}
|
||||
|
||||
public ThreadSchedState PrepareForTermination()
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState result;
|
||||
|
||||
if (Interlocked.CompareExchange(ref _shallBeTerminated, 1, 0) == 0)
|
||||
{
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.None)
|
||||
{
|
||||
SchedFlags = ThreadSchedState.TerminationPending;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_forcePauseFlags != ThreadSchedState.None)
|
||||
{
|
||||
_forcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
|
||||
|
||||
ThreadSchedState oldSchedFlags = SchedFlags;
|
||||
|
||||
SchedFlags &= ThreadSchedState.LowMask;
|
||||
|
||||
AdjustScheduling(oldSchedFlags);
|
||||
}
|
||||
|
||||
if (BasePriority >= 0x10)
|
||||
{
|
||||
SetPriority(0xF);
|
||||
}
|
||||
|
||||
if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Running)
|
||||
{
|
||||
// TODO: GIC distributor stuffs (sgir changes ect)
|
||||
}
|
||||
|
||||
SignaledObj = null;
|
||||
ObjSyncResult = KernelResult.ThreadTerminating;
|
||||
|
||||
ReleaseAndResume();
|
||||
}
|
||||
}
|
||||
|
||||
result = SchedFlags;
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
|
||||
return result & ThreadSchedState.LowMask;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ThreadSchedState state = PrepareForTermination();
|
||||
|
||||
if (state != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.Synchronization.WaitFor(new KSynchronizationObject[] { this }, -1, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandlePostSyscall()
|
||||
{
|
||||
ThreadSchedState state;
|
||||
|
||||
do
|
||||
{
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
System.Scheduler.ExitThread(this);
|
||||
Exit();
|
||||
|
||||
// As the death of the thread is handled by the CPU emulator, we differ from the official kernel and return here.
|
||||
break;
|
||||
}
|
||||
|
||||
System.CriticalSection.Enter();
|
||||
|
||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||
{
|
||||
state = ThreadSchedState.TerminationPending;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_forcePauseFlags != ThreadSchedState.None)
|
||||
{
|
||||
CombineForcePauseFlags();
|
||||
}
|
||||
|
||||
state = ThreadSchedState.Running;
|
||||
}
|
||||
|
||||
System.CriticalSection.Leave();
|
||||
} while (state == ThreadSchedState.TerminationPending);
|
||||
}
|
||||
|
||||
private void ExitImpl()
|
||||
{
|
||||
System.CriticalSection.Enter();
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using LibHac;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
||||
{
|
||||
class IStorage : IpcService
|
||||
class IStorage : IpcService, IDisposable
|
||||
{
|
||||
private LibHac.Fs.IStorage _baseStorage;
|
||||
|
||||
|
@ -51,5 +52,18 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
|
|||
|
||||
return (ResultCode)result.Value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_baseStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -293,13 +293,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||
return NvInternalResult.InvalidInput;
|
||||
}
|
||||
|
||||
long result = vmm.Map(map.Address, (long)arguments[index].Offset << 16,
|
||||
(long)arguments[index].Pages << 16);
|
||||
long result = vmm.Map(
|
||||
((long)arguments[index].MapOffset << 16) + map.Address,
|
||||
(long)arguments[index].GpuOffset << 16,
|
||||
(long)arguments[index].Pages << 16);
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
Logger.PrintWarning(LogClass.ServiceNv,
|
||||
$"Page 0x{arguments[index].Offset:x16} size 0x{arguments[index].Pages:x16} not allocated!");
|
||||
$"Page 0x{arguments[index].GpuOffset:x16} size 0x{arguments[index].Pages:x16} not allocated!");
|
||||
|
||||
return NvInternalResult.InvalidInput;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
|||
public ushort Flags;
|
||||
public ushort Kind;
|
||||
public int NvMapHandle;
|
||||
public int Padding;
|
||||
public uint Offset;
|
||||
public uint MapOffset;
|
||||
public uint GpuOffset;
|
||||
public uint Pages;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
|
@ -379,13 +380,26 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
}
|
||||
}
|
||||
|
||||
try
|
||||
if (fdsCount != 0)
|
||||
{
|
||||
System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout);
|
||||
try
|
||||
{
|
||||
System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout);
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WriteWinSock2Error(context, (WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
catch (SocketException exception)
|
||||
else if (timeout == -1)
|
||||
{
|
||||
return WriteWinSock2Error(context, (WsaError)exception.ErrorCode);
|
||||
// FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProces. (need to check that with re)
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: We should make the KThread sleep but we can't do much about it yet.
|
||||
Thread.Sleep(timeout);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fdsCount; i++)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Configuration.Hid;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Input
|
||||
{
|
||||
|
@ -47,18 +49,31 @@ namespace Ryujinx.HLE.Input
|
|||
_keyboardOffset = HidPosition + HidKeyboardOffset;
|
||||
}
|
||||
|
||||
public void InitializePrimaryController(ControllerStatus controllerType)
|
||||
private static ControllerStatus ConvertControllerTypeToState(ControllerType controllerType)
|
||||
{
|
||||
ControllerId controllerId = controllerType == ControllerStatus.Handheld ?
|
||||
switch (controllerType)
|
||||
{
|
||||
case ControllerType.Handheld: return ControllerStatus.Handheld;
|
||||
case ControllerType.NpadLeft: return ControllerStatus.NpadLeft;
|
||||
case ControllerType.NpadRight: return ControllerStatus.NpadRight;
|
||||
case ControllerType.NpadPair: return ControllerStatus.NpadPair;
|
||||
case ControllerType.ProController: return ControllerStatus.ProController;
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializePrimaryController(ControllerType controllerType)
|
||||
{
|
||||
ControllerId controllerId = controllerType == ControllerType.Handheld ?
|
||||
ControllerId.ControllerHandheld : ControllerId.ControllerPlayer1;
|
||||
|
||||
if (controllerType == ControllerStatus.ProController)
|
||||
if (controllerType == ControllerType.ProController)
|
||||
{
|
||||
PrimaryController = new ProController(_device, NpadColor.Black, NpadColor.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimaryController = new NpadController(controllerType,
|
||||
PrimaryController = new NpadController(ConvertControllerTypeToState(controllerType),
|
||||
_device,
|
||||
(NpadColor.BodyNeonRed, NpadColor.BodyNeonRed),
|
||||
(NpadColor.ButtonsNeonBlue, NpadColor.ButtonsNeonBlue));
|
||||
|
@ -67,11 +82,6 @@ namespace Ryujinx.HLE.Input
|
|||
PrimaryController.Connect(controllerId);
|
||||
}
|
||||
|
||||
public void InitializeKeyboard()
|
||||
{
|
||||
_device.Memory.FillWithZeros(HidPosition + HidKeyboardOffset, HidKeyboardSize);
|
||||
}
|
||||
|
||||
public ControllerButtons UpdateStickButtons(
|
||||
JoystickPosition leftStick,
|
||||
JoystickPosition rightStick)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
using LibHac.FsSystem;
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Configuration;
|
||||
using Ryujinx.Graphics;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Input;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
@ -60,6 +64,29 @@ namespace Ryujinx.HLE
|
|||
VsyncEvent = new AutoResetEvent(true);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value);
|
||||
|
||||
EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync;
|
||||
|
||||
// TODO: Make this reloadable and implement Docking/Undocking logic.
|
||||
System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode;
|
||||
|
||||
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
|
||||
{
|
||||
System.EnableMultiCoreScheduling();
|
||||
}
|
||||
|
||||
System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
||||
|
||||
ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
|
||||
}
|
||||
|
||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||
{
|
||||
System.LoadCart(exeFsDir, romFsFile);
|
||||
|
|
|
@ -2856,7 +2856,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
SingleOpcode(opcodes, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise] [Explicit]
|
||||
|
@ -2892,7 +2892,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
SingleOpcode(opcodes, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
|
|
|
@ -582,7 +582,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0);
|
||||
|
||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise] [Explicit]
|
||||
|
@ -666,7 +666,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0);
|
||||
|
||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -564,7 +564,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
SingleOpcode(opcodes, v0: v0, v1: v1);
|
||||
|
||||
CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"version": 1,
|
||||
"graphics_shaders_dump_path": "",
|
||||
"logging_enable_debug": false,
|
||||
"logging_enable_stub": true,
|
||||
|
@ -7,9 +8,7 @@
|
|||
"logging_enable_error": true,
|
||||
"logging_enable_guest": true,
|
||||
"logging_enable_fs_access_log": false,
|
||||
"logging_filtered_classes": [
|
||||
|
||||
],
|
||||
"logging_filtered_classes": [],
|
||||
"enable_file_log": true,
|
||||
"system_language": "AmericanEnglish",
|
||||
"docked_mode": false,
|
||||
|
@ -32,9 +31,7 @@
|
|||
"file_size_column": true,
|
||||
"path_column": true
|
||||
},
|
||||
"game_dirs": [
|
||||
|
||||
],
|
||||
"game_dirs": [],
|
||||
"enable_custom_theme": false,
|
||||
"custom_theme_path": "",
|
||||
"enable_keyboard": false,
|
||||
|
|
92
Ryujinx/Configuration/DiscordIntegrationModule.cs
Normal file
92
Ryujinx/Configuration/DiscordIntegrationModule.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using DiscordRPC;
|
||||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Configuration
|
||||
{
|
||||
static class DiscordIntegrationModule
|
||||
{
|
||||
private static DiscordRpcClient DiscordClient;
|
||||
|
||||
private static string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
|
||||
|
||||
public static RichPresence DiscordPresence { get; private set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
DiscordPresence = new RichPresence
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = "ryujinx",
|
||||
LargeImageText = LargeDescription
|
||||
},
|
||||
Details = "Main Menu",
|
||||
State = "Idling",
|
||||
Timestamps = new Timestamps(DateTime.UtcNow)
|
||||
};
|
||||
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||
}
|
||||
|
||||
private static void Update(object sender, ReactiveEventArgs<bool> e)
|
||||
{
|
||||
if (e.OldValue != e.NewValue)
|
||||
{
|
||||
// If the integration was active, disable it and unload everything
|
||||
if (e.OldValue)
|
||||
{
|
||||
DiscordClient?.Dispose();
|
||||
|
||||
DiscordClient = null;
|
||||
}
|
||||
|
||||
// If we need to activate it and the client isn't active, initialize it
|
||||
if (e.NewValue && DiscordClient == null)
|
||||
{
|
||||
DiscordClient = new DiscordRpcClient("568815339807309834");
|
||||
|
||||
DiscordClient.Initialize();
|
||||
DiscordClient.SetPresence(DiscordPresence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SwitchToPlayingState(string titleId, string titleName)
|
||||
{
|
||||
if (File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(titleId))
|
||||
{
|
||||
DiscordPresence.Assets.LargeImageKey = titleId;
|
||||
}
|
||||
|
||||
string state = titleId;
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
state = "Ryujinx";
|
||||
}
|
||||
else
|
||||
{
|
||||
state = state.ToUpper();
|
||||
}
|
||||
|
||||
string details = "Idling";
|
||||
|
||||
if (titleName != null)
|
||||
{
|
||||
details = $"Playing {titleName}";
|
||||
}
|
||||
|
||||
DiscordPresence.Details = details;
|
||||
DiscordPresence.State = state;
|
||||
DiscordPresence.Assets.LargeImageText = titleName;
|
||||
DiscordPresence.Assets.SmallImageKey = "ryujinx";
|
||||
DiscordPresence.Assets.SmallImageText = LargeDescription;
|
||||
DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
|
||||
|
||||
DiscordClient?.SetPresence(DiscordPresence);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using ARMeilleure.Translation.AOT;
|
||||
using Gtk;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Configuration;
|
||||
using Ryujinx.Profiler;
|
||||
using Ryujinx.Ui;
|
||||
using System;
|
||||
|
@ -17,9 +18,32 @@ namespace Ryujinx
|
|||
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
|
||||
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
|
||||
GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
|
||||
GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
|
||||
|
||||
// Initialize the configuration
|
||||
ConfigurationState.Initialize();
|
||||
|
||||
// Initialize the logger system
|
||||
LoggerModule.Initialize();
|
||||
|
||||
// Initialize Discord integration
|
||||
DiscordIntegrationModule.Initialize();
|
||||
|
||||
string configurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
|
||||
|
||||
// Now load the configuration as the other subsystems are now registered
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(configurationPath);
|
||||
ConfigurationState.Instance.Load(configurationFileFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No configuration, we load the default values and save it on disk
|
||||
ConfigurationState.Instance.LoadDefault();
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
|
||||
}
|
||||
|
||||
|
||||
Profile.Initialize();
|
||||
|
||||
|
@ -43,25 +67,6 @@ namespace Ryujinx
|
|||
Application.Run();
|
||||
}
|
||||
|
||||
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
|
||||
{
|
||||
Aot.Dispose();
|
||||
Logger.Shutdown();
|
||||
}
|
||||
|
||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Exception exception = e.ExceptionObject as Exception;
|
||||
|
||||
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
|
||||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
Aot.Dispose();
|
||||
Logger.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Glib_UnhandledException(GLib.UnhandledExceptionArgs e)
|
||||
{
|
||||
Exception exception = e.ExceptionObject as Exception;
|
||||
|
@ -70,6 +75,7 @@ namespace Ryujinx
|
|||
|
||||
if (e.IsTerminating)
|
||||
{
|
||||
Aot.Dispose();
|
||||
Logger.Shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<None Remove="Ui\assets\PatreonLogo.png" />
|
||||
<None Remove="Ui\assets\Icon.png" />
|
||||
<None Remove="Ui\assets\TwitterLogo.png" />
|
||||
<None Remove="Ui\GameTableContextMenu.glade" />
|
||||
<None Remove="Ui\MainWindow.glade" />
|
||||
<None Remove="Ui\SwitchSettings.glade" />
|
||||
</ItemGroup>
|
||||
|
@ -63,6 +64,7 @@
|
|||
<EmbeddedResource Include="Ui\assets\PatreonLogo.png" />
|
||||
<EmbeddedResource Include="Ui\assets\Icon.png" />
|
||||
<EmbeddedResource Include="Ui\assets\TwitterLogo.png" />
|
||||
<EmbeddedResource Include="Ui\GameTableContextMenu.glade" />
|
||||
<EmbeddedResource Include="Ui\MainWindow.glade" />
|
||||
<EmbeddedResource Include="Ui\SwitchSettings.glade" />
|
||||
</ItemGroup>
|
||||
|
@ -71,7 +73,6 @@
|
|||
<PackageReference Include="DiscordRichPresence" Version="1.0.121" />
|
||||
<PackageReference Include="GtkSharp" Version="3.22.25.24" />
|
||||
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||
<PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
|
||||
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using Ryujinx.Configuration;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.Input;
|
||||
using Ryujinx.Profiler.UI;
|
||||
using Ryujinx.Ui;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
|
@ -29,6 +31,8 @@ namespace Ryujinx.Ui
|
|||
|
||||
private MouseState? _mouse = null;
|
||||
|
||||
private Input.NpadController _primaryController;
|
||||
|
||||
private Thread _renderThread;
|
||||
|
||||
private bool _resizeEvent;
|
||||
|
@ -50,6 +54,8 @@ namespace Ryujinx.Ui
|
|||
_device = device;
|
||||
_renderer = renderer;
|
||||
|
||||
_primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
|
||||
|
||||
Location = new Point(
|
||||
(DisplayDevice.Default.Width / 2) - (Width / 2),
|
||||
(DisplayDevice.Default.Height / 2) - (Height / 2));
|
||||
|
@ -162,16 +168,16 @@ namespace Ryujinx.Ui
|
|||
#endif
|
||||
|
||||
// Normal Input
|
||||
currentHotkeyButtons = Configuration.Instance.KeyboardControls.GetHotkeyButtons(keyboard);
|
||||
currentButton = Configuration.Instance.KeyboardControls.GetButtons(keyboard);
|
||||
currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
||||
currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
||||
|
||||
if (Configuration.Instance.EnableKeyboard)
|
||||
if (ConfigurationState.Instance.Hid.EnableKeyboard)
|
||||
{
|
||||
hidKeyboard = Configuration.Instance.KeyboardControls.GetKeysDown(keyboard);
|
||||
hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
||||
}
|
||||
|
||||
(leftJoystickDx, leftJoystickDy) = Configuration.Instance.KeyboardControls.GetLeftStick(keyboard);
|
||||
(rightJoystickDx, rightJoystickDy) = Configuration.Instance.KeyboardControls.GetRightStick(keyboard);
|
||||
(leftJoystickDx, leftJoystickDy) = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
||||
(rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
|
||||
}
|
||||
|
||||
if (!hidKeyboard.HasValue)
|
||||
|
@ -183,17 +189,17 @@ namespace Ryujinx.Ui
|
|||
};
|
||||
}
|
||||
|
||||
currentButton |= Configuration.Instance.JoystickControls.GetButtons();
|
||||
currentButton |= _primaryController.GetButtons();
|
||||
|
||||
// Keyboard has priority stick-wise
|
||||
if (leftJoystickDx == 0 && leftJoystickDy == 0)
|
||||
{
|
||||
(leftJoystickDx, leftJoystickDy) = Configuration.Instance.JoystickControls.GetLeftStick();
|
||||
(leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
|
||||
}
|
||||
|
||||
if (rightJoystickDx == 0 && rightJoystickDy == 0)
|
||||
{
|
||||
(rightJoystickDx, rightJoystickDy) = Configuration.Instance.JoystickControls.GetRightStick();
|
||||
(rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
|
||||
}
|
||||
|
||||
leftJoystick = new JoystickPosition
|
||||
|
@ -269,7 +275,7 @@ namespace Ryujinx.Ui
|
|||
_device.Hid.SetTouchPoints();
|
||||
}
|
||||
|
||||
if (Configuration.Instance.EnableKeyboard && hidKeyboard.HasValue)
|
||||
if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
|
||||
{
|
||||
_device.Hid.WriteKeyboard(hidKeyboard.Value);
|
||||
}
|
||||
|
|
75
Ryujinx/Ui/GameTableContextMenu.cs
Normal file
75
Ryujinx/Ui/GameTableContextMenu.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using Gtk;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
using GUI = Gtk.Builder.ObjectAttribute;
|
||||
|
||||
namespace Ryujinx.Ui
|
||||
{
|
||||
public class GameTableContextMenu : Menu
|
||||
{
|
||||
private static ListStore _gameTableStore;
|
||||
private static TreeIter _rowIter;
|
||||
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable IDE0044
|
||||
[GUI] MenuItem _openSaveDir;
|
||||
#pragma warning restore CS0649
|
||||
#pragma warning restore IDE0044
|
||||
|
||||
public GameTableContextMenu(ListStore gameTableStore, TreeIter rowIter) : this(new Builder("Ryujinx.Ui.GameTableContextMenu.glade"), gameTableStore, rowIter) { }
|
||||
|
||||
private GameTableContextMenu(Builder builder, ListStore gameTableStore, TreeIter rowIter) : base(builder.GetObject("_contextMenu").Handle)
|
||||
{
|
||||
builder.Autoconnect(this);
|
||||
|
||||
_openSaveDir.Activated += OpenSaveDir_Clicked;
|
||||
|
||||
_gameTableStore = gameTableStore;
|
||||
_rowIter = rowIter;
|
||||
}
|
||||
|
||||
//Events
|
||||
private void OpenSaveDir_Clicked(object sender, EventArgs args)
|
||||
{
|
||||
string titleName = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[0];
|
||||
string titleId = _gameTableStore.GetValue(_rowIter, 2).ToString().Split("\n")[1].ToLower();
|
||||
string saveDir = System.IO.Path.Combine(new VirtualFileSystem().GetNandPath(), "user", "save", "0000000000000000", "00000000000000000000000000000001", titleId, "0");
|
||||
|
||||
if (!Directory.Exists(saveDir))
|
||||
{
|
||||
MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.YesNo, null)
|
||||
{
|
||||
Title = "Ryujinx",
|
||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
|
||||
Text = $"Could not find save directory for {titleName} [{titleId}]",
|
||||
SecondaryText = "Would you like to create the directory?",
|
||||
WindowPosition = WindowPosition.Center
|
||||
};
|
||||
|
||||
if (messageDialog.Run() == (int)ResponseType.Yes)
|
||||
{
|
||||
Directory.CreateDirectory(saveDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
messageDialog.Dispose();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
messageDialog.Dispose();
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = saveDir,
|
||||
UseShellExecute = true,
|
||||
Verb = "open"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx/Ui/GameTableContextMenu.glade
Normal file
18
Ryujinx/Ui/GameTableContextMenu.glade
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkMenu" id="_contextMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="_openSaveDir">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Open the folder where saves for the application is loaded</property>
|
||||
<property name="label" translatable="yes">Open Save Directory</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
|
@ -1,118 +1,67 @@
|
|||
using OpenTK.Input;
|
||||
using OpenTK.Input;
|
||||
using Ryujinx.HLE.Input;
|
||||
using Ryujinx.UI.Input;
|
||||
|
||||
namespace Ryujinx.Ui.Input
|
||||
namespace Ryujinx.Ui
|
||||
{
|
||||
public struct NpadKeyboardLeft
|
||||
public static class KeyboardControls
|
||||
{
|
||||
public Key StickUp;
|
||||
public Key StickDown;
|
||||
public Key StickLeft;
|
||||
public Key StickRight;
|
||||
public Key StickButton;
|
||||
public Key DPadUp;
|
||||
public Key DPadDown;
|
||||
public Key DPadLeft;
|
||||
public Key DPadRight;
|
||||
public Key ButtonMinus;
|
||||
public Key ButtonL;
|
||||
public Key ButtonZl;
|
||||
}
|
||||
|
||||
public struct NpadKeyboardRight
|
||||
{
|
||||
public Key StickUp;
|
||||
public Key StickDown;
|
||||
public Key StickLeft;
|
||||
public Key StickRight;
|
||||
public Key StickButton;
|
||||
public Key ButtonA;
|
||||
public Key ButtonB;
|
||||
public Key ButtonX;
|
||||
public Key ButtonY;
|
||||
public Key ButtonPlus;
|
||||
public Key ButtonR;
|
||||
public Key ButtonZr;
|
||||
}
|
||||
|
||||
public struct KeyboardHotkeys
|
||||
{
|
||||
public Key ToggleVsync;
|
||||
}
|
||||
|
||||
public class NpadKeyboard
|
||||
{
|
||||
/// <summary>
|
||||
/// Left JoyCon Keyboard Bindings
|
||||
/// </summary>
|
||||
public NpadKeyboardLeft LeftJoycon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Right JoyCon Keyboard Bindings
|
||||
/// </summary>
|
||||
public NpadKeyboardRight RightJoycon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hotkey Keyboard Bindings
|
||||
/// </summary>
|
||||
public KeyboardHotkeys Hotkeys { get; private set; }
|
||||
|
||||
public ControllerButtons GetButtons(KeyboardState keyboard)
|
||||
public static ControllerButtons GetButtons(NpadKeyboard npad, KeyboardState keyboard)
|
||||
{
|
||||
ControllerButtons buttons = 0;
|
||||
|
||||
if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
|
||||
if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp;
|
||||
if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown;
|
||||
if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft;
|
||||
if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight;
|
||||
if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
|
||||
if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= ControllerButtons.L;
|
||||
if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl;
|
||||
if (keyboard[(Key)npad.LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
|
||||
if (keyboard[(Key)npad.LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp;
|
||||
if (keyboard[(Key)npad.LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown;
|
||||
if (keyboard[(Key)npad.LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft;
|
||||
if (keyboard[(Key)npad.LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight;
|
||||
if (keyboard[(Key)npad.LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
|
||||
if (keyboard[(Key)npad.LeftJoycon.ButtonL]) buttons |= ControllerButtons.L;
|
||||
if (keyboard[(Key)npad.LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl;
|
||||
|
||||
if (keyboard[(Key)RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
|
||||
if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= ControllerButtons.A;
|
||||
if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= ControllerButtons.B;
|
||||
if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= ControllerButtons.X;
|
||||
if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= ControllerButtons.Y;
|
||||
if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus;
|
||||
if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= ControllerButtons.R;
|
||||
if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr;
|
||||
if (keyboard[(Key)npad.RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonA]) buttons |= ControllerButtons.A;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonB]) buttons |= ControllerButtons.B;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonX]) buttons |= ControllerButtons.X;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonY]) buttons |= ControllerButtons.Y;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonR]) buttons |= ControllerButtons.R;
|
||||
if (keyboard[(Key)npad.RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr;
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public (short, short) GetLeftStick(KeyboardState keyboard)
|
||||
public static (short, short) GetLeftStick(NpadKeyboard npad, KeyboardState keyboard)
|
||||
{
|
||||
short dx = 0;
|
||||
short dy = 0;
|
||||
|
||||
if (keyboard[(Key)LeftJoycon.StickUp]) dy = short.MaxValue;
|
||||
if (keyboard[(Key)LeftJoycon.StickDown]) dy = -short.MaxValue;
|
||||
if (keyboard[(Key)LeftJoycon.StickLeft]) dx = -short.MaxValue;
|
||||
if (keyboard[(Key)LeftJoycon.StickRight]) dx = short.MaxValue;
|
||||
if (keyboard[(Key)npad.LeftJoycon.StickUp]) dy = short.MaxValue;
|
||||
if (keyboard[(Key)npad.LeftJoycon.StickDown]) dy = -short.MaxValue;
|
||||
if (keyboard[(Key)npad.LeftJoycon.StickLeft]) dx = -short.MaxValue;
|
||||
if (keyboard[(Key)npad.LeftJoycon.StickRight]) dx = short.MaxValue;
|
||||
|
||||
return (dx, dy);
|
||||
}
|
||||
|
||||
public (short, short) GetRightStick(KeyboardState keyboard)
|
||||
public static (short, short) GetRightStick(NpadKeyboard npad, KeyboardState keyboard)
|
||||
{
|
||||
short dx = 0;
|
||||
short dy = 0;
|
||||
|
||||
if (keyboard[(Key)RightJoycon.StickUp]) dy = short.MaxValue;
|
||||
if (keyboard[(Key)RightJoycon.StickDown]) dy = -short.MaxValue;
|
||||
if (keyboard[(Key)RightJoycon.StickLeft]) dx = -short.MaxValue;
|
||||
if (keyboard[(Key)RightJoycon.StickRight]) dx = short.MaxValue;
|
||||
if (keyboard[(Key)npad.RightJoycon.StickUp]) dy = short.MaxValue;
|
||||
if (keyboard[(Key)npad.RightJoycon.StickDown]) dy = -short.MaxValue;
|
||||
if (keyboard[(Key)npad.RightJoycon.StickLeft]) dx = -short.MaxValue;
|
||||
if (keyboard[(Key)npad.RightJoycon.StickRight]) dx = short.MaxValue;
|
||||
|
||||
return (dx, dy);
|
||||
}
|
||||
|
||||
public HotkeyButtons GetHotkeyButtons(KeyboardState keyboard)
|
||||
public static HotkeyButtons GetHotkeyButtons(NpadKeyboard npad, KeyboardState keyboard)
|
||||
{
|
||||
HotkeyButtons buttons = 0;
|
||||
|
||||
if (keyboard[(Key)Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
|
||||
if (keyboard[(Key)npad.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
@ -267,7 +216,7 @@ namespace Ryujinx.Ui.Input
|
|||
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 },
|
||||
};
|
||||
|
||||
public HLE.Input.Keyboard GetKeysDown(KeyboardState keyboard)
|
||||
public static HLE.Input.Keyboard GetKeysDown(NpadKeyboard npad, KeyboardState keyboard)
|
||||
{
|
||||
HLE.Input.Keyboard hidKeyboard = new HLE.Input.Keyboard
|
||||
{
|
|
@ -1,21 +1,20 @@
|
|||
using ARMeilleure.Translation.AOT;
|
||||
using DiscordRPC;
|
||||
using Gtk;
|
||||
using JsonPrettyPrinterPlus;
|
||||
using Ryujinx.Audio;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Gal.OpenGL;
|
||||
using Ryujinx.Configuration;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.Graphics.Gal.OpenGL;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Profiler;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Utf8Json;
|
||||
using Utf8Json.Resolvers;
|
||||
|
||||
|
@ -39,24 +38,8 @@ namespace Ryujinx.Ui
|
|||
private static bool _gameLoaded;
|
||||
private static bool _ending;
|
||||
|
||||
private static TreeViewColumn _favColumn;
|
||||
private static TreeViewColumn _appColumn;
|
||||
private static TreeViewColumn _devColumn;
|
||||
private static TreeViewColumn _versionColumn;
|
||||
private static TreeViewColumn _timePlayedColumn;
|
||||
private static TreeViewColumn _lastPlayedColumn;
|
||||
private static TreeViewColumn _fileExtColumn;
|
||||
private static TreeViewColumn _fileSizeColumn;
|
||||
private static TreeViewColumn _pathColumn;
|
||||
|
||||
private static TreeView _treeView;
|
||||
|
||||
public static bool DiscordIntegrationEnabled { get; set; }
|
||||
|
||||
public static DiscordRpcClient DiscordClient;
|
||||
|
||||
public static RichPresence DiscordPresence;
|
||||
|
||||
#pragma warning disable CS0649
|
||||
#pragma warning disable IDE0044
|
||||
[GUI] Window _mainWin;
|
||||
|
@ -73,6 +56,7 @@ namespace Ryujinx.Ui
|
|||
[GUI] CheckMenuItem _fileSizeToggle;
|
||||
[GUI] CheckMenuItem _pathToggle;
|
||||
[GUI] TreeView _gameTable;
|
||||
[GUI] TreeSelection _gameTableSelection;
|
||||
[GUI] Label _progressLabel;
|
||||
[GUI] LevelBar _progressBar;
|
||||
#pragma warning restore CS0649
|
||||
|
@ -88,64 +72,45 @@ namespace Ryujinx.Ui
|
|||
|
||||
ApplicationLibrary.ApplicationAdded += Application_Added;
|
||||
|
||||
_gameTable.ButtonReleaseEvent += Row_Clicked;
|
||||
|
||||
_renderer = new OglRenderer();
|
||||
|
||||
_audioOut = InitializeAudioEngine();
|
||||
|
||||
_device = new HLE.Switch(_renderer, _audioOut);
|
||||
// TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation.
|
||||
_device = InitializeSwitchInstance();
|
||||
|
||||
_treeView = _gameTable;
|
||||
|
||||
Configuration.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
Configuration.InitialConfigure(_device);
|
||||
|
||||
ApplyTheme();
|
||||
|
||||
if (DiscordIntegrationEnabled)
|
||||
{
|
||||
DiscordClient = new DiscordRpcClient("568815339807309834");
|
||||
DiscordPresence = new RichPresence
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = "ryujinx",
|
||||
LargeImageText = "Ryujinx is an emulator for the Nintendo Switch"
|
||||
},
|
||||
Details = "Main Menu",
|
||||
State = "Idling",
|
||||
Timestamps = new Timestamps(DateTime.UtcNow)
|
||||
};
|
||||
|
||||
DiscordClient.Initialize();
|
||||
DiscordClient.SetPresence(DiscordPresence);
|
||||
}
|
||||
|
||||
_mainWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
||||
_stopEmulation.Sensitive = false;
|
||||
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _iconToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _appToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _developerToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _versionToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeToggle.Active = true; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathToggle.Active = true; }
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _developerToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true;
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true;
|
||||
|
||||
_gameTable.Model = _tableStore = new ListStore(
|
||||
typeof(bool),
|
||||
typeof(Gdk.Pixbuf),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(bool),
|
||||
typeof(Gdk.Pixbuf),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string),
|
||||
typeof(string));
|
||||
|
||||
|
||||
_tableStore.SetSortFunc(5, TimePlayedSort);
|
||||
_tableStore.SetSortFunc(6, LastPlayedSort);
|
||||
_tableStore.SetSortFunc(8, FileSizeSort);
|
||||
|
@ -159,22 +124,22 @@ namespace Ryujinx.Ui
|
|||
|
||||
internal static void ApplyTheme()
|
||||
{
|
||||
if (!SwitchSettings.SwitchConfig.EnableCustomTheme)
|
||||
if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(SwitchSettings.SwitchConfig.CustomThemePath) && (System.IO.Path.GetExtension(SwitchSettings.SwitchConfig.CustomThemePath) == ".css"))
|
||||
if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (System.IO.Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
|
||||
{
|
||||
CssProvider cssProvider = new CssProvider();
|
||||
|
||||
cssProvider.LoadFromPath(SwitchSettings.SwitchConfig.CustomThemePath);
|
||||
cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
|
||||
|
||||
StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{SwitchSettings.SwitchConfig.CustomThemePath}\".");
|
||||
Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,39 +153,38 @@ namespace Ryujinx.Ui
|
|||
CellRendererToggle favToggle = new CellRendererToggle();
|
||||
favToggle.Toggled += FavToggle_Toggled;
|
||||
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _gameTable.AppendColumn("Fav", favToggle, "active", 0); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); }
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8);
|
||||
if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9);
|
||||
|
||||
foreach (TreeViewColumn column in _gameTable.Columns)
|
||||
{
|
||||
if (column.Title == "Fav") { _favColumn = column; }
|
||||
else if (column.Title == "Application") { _appColumn = column; }
|
||||
else if (column.Title == "Developer") { _devColumn = column; }
|
||||
else if (column.Title == "Version") { _versionColumn = column; }
|
||||
else if (column.Title == "Time Played") { _timePlayedColumn = column; }
|
||||
else if (column.Title == "Last Played") { _lastPlayedColumn = column; }
|
||||
else if (column.Title == "File Ext") { _fileExtColumn = column; }
|
||||
else if (column.Title == "File Size") { _fileSizeColumn = column; }
|
||||
else if (column.Title == "Path") { _pathColumn = column; }
|
||||
if (column.Title == "Fav" && ConfigurationState.Instance.Ui.GuiColumns.FavColumn) column.SortColumnId = 0;
|
||||
else if (column.Title == "Application" && ConfigurationState.Instance.Ui.GuiColumns.AppColumn) column.SortColumnId = 2;
|
||||
else if (column.Title == "Developer" && ConfigurationState.Instance.Ui.GuiColumns.DevColumn) column.SortColumnId = 3;
|
||||
else if (column.Title == "Version" && ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) column.SortColumnId = 4;
|
||||
else if (column.Title == "Time Played" && ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) column.SortColumnId = 5;
|
||||
else if (column.Title == "Last Played" && ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) column.SortColumnId = 6;
|
||||
else if (column.Title == "File Ext" && ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) column.SortColumnId = 7;
|
||||
else if (column.Title == "File Size" && ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) column.SortColumnId = 8;
|
||||
else if (column.Title == "Path" && ConfigurationState.Instance.Ui.GuiColumns.PathColumn) column.SortColumnId = 9;
|
||||
}
|
||||
}
|
||||
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favColumn.SortColumnId = 0; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _appColumn.SortColumnId = 2; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _devColumn.SortColumnId = 3; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _versionColumn.SortColumnId = 4; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedColumn.SortColumnId = 5; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedColumn.SortColumnId = 6; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtColumn.SortColumnId = 7; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeColumn.SortColumnId = 8; }
|
||||
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathColumn.SortColumnId = 9; }
|
||||
private HLE.Switch InitializeSwitchInstance()
|
||||
{
|
||||
HLE.Switch instance = new HLE.Switch(_renderer, _audioOut);
|
||||
|
||||
instance.Initialize();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
internal static async Task UpdateGameTable()
|
||||
|
@ -234,7 +198,7 @@ namespace Ryujinx.Ui
|
|||
|
||||
_tableStore.Clear();
|
||||
|
||||
await Task.Run(() => ApplicationLibrary.LoadApplications(SwitchSettings.SwitchConfig.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
|
||||
await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
|
||||
|
||||
_updatingGameTable = false;
|
||||
}
|
||||
|
@ -249,6 +213,9 @@ namespace Ryujinx.Ui
|
|||
{
|
||||
Logger.RestartTime();
|
||||
|
||||
// TODO: Move this somewhere else + reloadable?
|
||||
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
|
||||
|
@ -314,40 +281,7 @@ namespace Ryujinx.Ui
|
|||
_gameLoaded = true;
|
||||
_stopEmulation.Sensitive = true;
|
||||
|
||||
if (DiscordIntegrationEnabled)
|
||||
{
|
||||
if (File.ReadAllLines(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(_device.System.TitleId))
|
||||
{
|
||||
DiscordPresence.Assets.LargeImageKey = _device.System.TitleId;
|
||||
}
|
||||
|
||||
string state = _device.System.TitleId;
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
state = "Ryujinx";
|
||||
}
|
||||
else
|
||||
{
|
||||
state = state.ToUpper();
|
||||
}
|
||||
|
||||
string details = "Idling";
|
||||
|
||||
if (_device.System.TitleName != null)
|
||||
{
|
||||
details = $"Playing {_device.System.TitleName}";
|
||||
}
|
||||
|
||||
DiscordPresence.Details = details;
|
||||
DiscordPresence.State = state;
|
||||
DiscordPresence.Assets.LargeImageText = _device.System.TitleName;
|
||||
DiscordPresence.Assets.SmallImageKey = "ryujinx";
|
||||
DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch";
|
||||
DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
|
||||
|
||||
DiscordClient.SetPresence(DiscordPresence);
|
||||
}
|
||||
DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName);
|
||||
|
||||
string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui");
|
||||
string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json");
|
||||
|
@ -385,8 +319,8 @@ namespace Ryujinx.Ui
|
|||
|
||||
private static void CreateGameWindow()
|
||||
{
|
||||
Configuration.ConfigureHid(_device, SwitchSettings.SwitchConfig);
|
||||
|
||||
_device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType);
|
||||
|
||||
using (_screen = new GlScreen(_device, _renderer))
|
||||
{
|
||||
_screen.MainLoop();
|
||||
|
@ -443,10 +377,9 @@ namespace Ryujinx.Ui
|
|||
}
|
||||
|
||||
Profile.FinishProfiling();
|
||||
Aot.Dispose();
|
||||
_device.Dispose();
|
||||
_audioOut.Dispose();
|
||||
DiscordClient?.Dispose();
|
||||
Aot.Dispose();
|
||||
Logger.Shutdown();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
@ -472,24 +405,24 @@ namespace Ryujinx.Ui
|
|||
}
|
||||
|
||||
//Events
|
||||
private void Application_Added(object sender, ApplicationAddedEventArgs e)
|
||||
private void Application_Added(object sender, ApplicationAddedEventArgs args)
|
||||
{
|
||||
Application.Invoke(delegate
|
||||
{
|
||||
_tableStore.AppendValues(
|
||||
e.AppData.Favorite,
|
||||
new Gdk.Pixbuf(e.AppData.Icon, 75, 75),
|
||||
$"{e.AppData.TitleName}\n{e.AppData.TitleId.ToUpper()}",
|
||||
e.AppData.Developer,
|
||||
e.AppData.Version,
|
||||
e.AppData.TimePlayed,
|
||||
e.AppData.LastPlayed,
|
||||
e.AppData.FileExtension,
|
||||
e.AppData.FileSize,
|
||||
e.AppData.Path);
|
||||
args.AppData.Favorite,
|
||||
new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
|
||||
$"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
|
||||
args.AppData.Developer,
|
||||
args.AppData.Version,
|
||||
args.AppData.TimePlayed,
|
||||
args.AppData.LastPlayed,
|
||||
args.AppData.FileExtension,
|
||||
args.AppData.FileSize,
|
||||
args.AppData.Path);
|
||||
|
||||
_progressLabel.Text = $"{e.NumAppsLoaded}/{e.NumAppsFound} Games Loaded";
|
||||
_progressBar.Value = (float)e.NumAppsLoaded / e.NumAppsFound;
|
||||
_progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
|
||||
_progressBar.Value = (float)args.NumAppsLoaded / args.NumAppsFound;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -528,12 +461,25 @@ namespace Ryujinx.Ui
|
|||
|
||||
private void Row_Activated(object sender, RowActivatedArgs args)
|
||||
{
|
||||
_tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path.ToString()));
|
||||
_gameTableSelection.GetSelected(out TreeIter treeIter);
|
||||
string path = (string)_tableStore.GetValue(treeIter, 9);
|
||||
|
||||
LoadApplication(path);
|
||||
}
|
||||
|
||||
private void Row_Clicked(object sender, ButtonReleaseEventArgs args)
|
||||
{
|
||||
if (args.Event.Button != 3) return;
|
||||
|
||||
_gameTableSelection.GetSelected(out TreeIter treeIter);
|
||||
|
||||
if (treeIter.UserData == IntPtr.Zero) return;
|
||||
|
||||
GameTableContextMenu contextMenu = new GameTableContextMenu(_tableStore, treeIter);
|
||||
contextMenu.ShowAll();
|
||||
contextMenu.PopupAtPointer(null);
|
||||
}
|
||||
|
||||
private void Load_Application_File(object sender, EventArgs args)
|
||||
{
|
||||
FileChooserDialog fileChooser = new FileChooserDialog("Choose the file to open", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept);
|
||||
|
@ -609,7 +555,7 @@ namespace Ryujinx.Ui
|
|||
|
||||
private void Settings_Pressed(object sender, EventArgs args)
|
||||
{
|
||||
SwitchSettings settingsWin = new SwitchSettings(_device);
|
||||
SwitchSettings settingsWin = new SwitchSettings();
|
||||
settingsWin.Show();
|
||||
}
|
||||
|
||||
|
@ -635,121 +581,81 @@ namespace Ryujinx.Ui
|
|||
|
||||
private void Fav_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.FavColumn = _favToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.FavColumn.Value = _favToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void Icon_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.IconColumn = _iconToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.IconColumn.Value = _iconToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void Title_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.AppColumn = _appToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.AppColumn.Value = _appToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void Developer_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.DevColumn = _developerToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.DevColumn.Value = _developerToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void Version_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.VersionColumn = _versionToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.VersionColumn.Value = _versionToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void TimePlayed_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.TimePlayedColumn = _timePlayedToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void LastPlayed_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.LastPlayedColumn = _lastPlayedToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void FileExt_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.FileExtColumn = _fileExtToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void FileSize_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.FileSizeColumn = _fileSizeToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
private void Path_Toggled(object sender, EventArgs args)
|
||||
{
|
||||
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns;
|
||||
|
||||
updatedColumns.PathColumn = _pathToggle.Active;
|
||||
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
|
||||
|
||||
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
ConfigurationState.Instance.Ui.GuiColumns.PathColumn.Value = _pathToggle.Active;
|
||||
|
||||
SaveConfig();
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
|
@ -867,5 +773,10 @@ namespace Ryujinx.Ui
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveConfig()
|
||||
{
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -330,7 +330,7 @@
|
|||
<property name="hover_selection">True</property>
|
||||
<signal name="row-activated" handler="Row_Activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
<object class="GtkTreeSelection" id="_gameTableSelection"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
|
|
@ -1,160 +1,57 @@
|
|||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.HLE.Input;
|
||||
using System;
|
||||
|
||||
using InnerNpadController = Ryujinx.Common.Configuration.Hid.NpadController;
|
||||
|
||||
namespace Ryujinx.Ui.Input
|
||||
{
|
||||
public enum ControllerInputId
|
||||
{
|
||||
Button0,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
Button4,
|
||||
Button5,
|
||||
Button6,
|
||||
Button7,
|
||||
Button8,
|
||||
Button9,
|
||||
Button10,
|
||||
Button11,
|
||||
Button12,
|
||||
Button13,
|
||||
Button14,
|
||||
Button15,
|
||||
Button16,
|
||||
Button17,
|
||||
Button18,
|
||||
Button19,
|
||||
Button20,
|
||||
Axis0,
|
||||
Axis1,
|
||||
Axis2,
|
||||
Axis3,
|
||||
Axis4,
|
||||
Axis5,
|
||||
Hat0Up,
|
||||
Hat0Down,
|
||||
Hat0Left,
|
||||
Hat0Right,
|
||||
Hat1Up,
|
||||
Hat1Down,
|
||||
Hat1Left,
|
||||
Hat1Right,
|
||||
Hat2Up,
|
||||
Hat2Down,
|
||||
Hat2Left,
|
||||
Hat2Right,
|
||||
}
|
||||
|
||||
public struct NpadControllerLeft
|
||||
{
|
||||
public ControllerInputId Stick;
|
||||
public ControllerInputId StickButton;
|
||||
public ControllerInputId ButtonMinus;
|
||||
public ControllerInputId ButtonL;
|
||||
public ControllerInputId ButtonZl;
|
||||
public ControllerInputId DPadUp;
|
||||
public ControllerInputId DPadDown;
|
||||
public ControllerInputId DPadLeft;
|
||||
public ControllerInputId DPadRight;
|
||||
}
|
||||
|
||||
public struct NpadControllerRight
|
||||
{
|
||||
public ControllerInputId Stick;
|
||||
public ControllerInputId StickButton;
|
||||
public ControllerInputId ButtonA;
|
||||
public ControllerInputId ButtonB;
|
||||
public ControllerInputId ButtonX;
|
||||
public ControllerInputId ButtonY;
|
||||
public ControllerInputId ButtonPlus;
|
||||
public ControllerInputId ButtonR;
|
||||
public ControllerInputId ButtonZr;
|
||||
}
|
||||
|
||||
public class NpadController
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables or disables controller support
|
||||
/// </summary>
|
||||
public bool Enabled { get; private set; }
|
||||
private InnerNpadController _inner;
|
||||
|
||||
/// <summary>
|
||||
/// Controller Device Index
|
||||
/// </summary>
|
||||
public int Index { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller Analog Stick Deadzone
|
||||
/// </summary>
|
||||
public float Deadzone { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller Trigger Threshold
|
||||
/// </summary>
|
||||
public float TriggerThreshold { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Left JoyCon Controller Bindings
|
||||
/// </summary>
|
||||
public NpadControllerLeft LeftJoycon { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Right JoyCon Controller Bindings
|
||||
/// </summary>
|
||||
public NpadControllerRight RightJoycon { get; private set; }
|
||||
|
||||
public NpadController(
|
||||
bool enabled,
|
||||
int index,
|
||||
float deadzone,
|
||||
float triggerThreshold,
|
||||
NpadControllerLeft leftJoycon,
|
||||
NpadControllerRight rightJoycon)
|
||||
// NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
|
||||
// BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
|
||||
public NpadController(InnerNpadController inner)
|
||||
{
|
||||
Enabled = enabled;
|
||||
Index = index;
|
||||
Deadzone = deadzone;
|
||||
TriggerThreshold = triggerThreshold;
|
||||
LeftJoycon = leftJoycon;
|
||||
RightJoycon = rightJoycon;
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public void SetEnabled(bool enabled)
|
||||
private bool IsEnabled()
|
||||
{
|
||||
Enabled = enabled;
|
||||
return _inner.Enabled && Joystick.GetState(_inner.Index).IsConnected;
|
||||
}
|
||||
|
||||
public ControllerButtons GetButtons()
|
||||
{
|
||||
if (!Enabled)
|
||||
if (!IsEnabled())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
JoystickState joystickState = Joystick.GetState(Index);
|
||||
JoystickState joystickState = Joystick.GetState(_inner.Index);
|
||||
|
||||
ControllerButtons buttons = 0;
|
||||
|
||||
if (IsActivated(joystickState, LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp;
|
||||
if (IsActivated(joystickState, LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown;
|
||||
if (IsActivated(joystickState, LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft;
|
||||
if (IsActivated(joystickState, LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight;
|
||||
if (IsActivated(joystickState, LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft;
|
||||
if (IsActivated(joystickState, LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus;
|
||||
if (IsActivated(joystickState, LeftJoycon.ButtonL)) buttons |= ControllerButtons.L;
|
||||
if (IsActivated(joystickState, LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.ButtonL)) buttons |= ControllerButtons.L;
|
||||
if (IsActivated(joystickState, _inner.LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl;
|
||||
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonA)) buttons |= ControllerButtons.A;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonB)) buttons |= ControllerButtons.B;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonX)) buttons |= ControllerButtons.X;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonY)) buttons |= ControllerButtons.Y;
|
||||
if (IsActivated(joystickState, RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonR)) buttons |= ControllerButtons.R;
|
||||
if (IsActivated(joystickState, RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonA)) buttons |= ControllerButtons.A;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonB)) buttons |= ControllerButtons.B;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonX)) buttons |= ControllerButtons.X;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonY)) buttons |= ControllerButtons.Y;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonR)) buttons |= ControllerButtons.R;
|
||||
if (IsActivated(joystickState, _inner.RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr;
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
@ -169,7 +66,7 @@ namespace Ryujinx.Ui.Input
|
|||
{
|
||||
int axis = controllerInputId - ControllerInputId.Axis0;
|
||||
|
||||
return joystickState.GetAxis(axis) > TriggerThreshold;
|
||||
return joystickState.GetAxis(axis) > _inner.TriggerThreshold;
|
||||
}
|
||||
else if (controllerInputId <= ControllerInputId.Hat2Right)
|
||||
{
|
||||
|
@ -190,22 +87,22 @@ namespace Ryujinx.Ui.Input
|
|||
|
||||
public (short, short) GetLeftStick()
|
||||
{
|
||||
if (!Enabled)
|
||||
if (!IsEnabled())
|
||||
{
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
return GetStick(LeftJoycon.Stick);
|
||||
return GetStick(_inner.LeftJoycon.Stick);
|
||||
}
|
||||
|
||||
public (short, short) GetRightStick()
|
||||
{
|
||||
if (!Enabled)
|
||||
if (!IsEnabled())
|
||||
{
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
return GetStick(RightJoycon.Stick);
|
||||
return GetStick(_inner.RightJoycon.Stick);
|
||||
}
|
||||
|
||||
private (short, short) GetStick(ControllerInputId stickInputId)
|
||||
|
@ -215,7 +112,7 @@ namespace Ryujinx.Ui.Input
|
|||
return (0, 0);
|
||||
}
|
||||
|
||||
JoystickState jsState = Joystick.GetState(Index);
|
||||
JoystickState jsState = Joystick.GetState(_inner.Index);
|
||||
|
||||
int xAxis = stickInputId - ControllerInputId.Axis0;
|
||||
|
||||
|
@ -227,8 +124,8 @@ namespace Ryujinx.Ui.Input
|
|||
|
||||
private (short, short) ApplyDeadzone(Vector2 axis)
|
||||
{
|
||||
return (ClampAxis(MathF.Abs(axis.X) > Deadzone ? axis.X : 0f),
|
||||
ClampAxis(MathF.Abs(axis.Y) > Deadzone ? axis.Y : 0f));
|
||||
return (ClampAxis(MathF.Abs(axis.X) > _inner.Deadzone ? axis.X : 0f),
|
||||
ClampAxis(MathF.Abs(axis.Y) > _inner.Deadzone ? axis.Y : 0f));
|
||||
}
|
||||
|
||||
private static short ClampAxis(float value)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
using Gtk;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Input;
|
||||
using Ryujinx.Ui.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Ryujinx.Configuration;
|
||||
using Ryujinx.Configuration.System;
|
||||
using Ryujinx.Configuration.Hid;
|
||||
|
||||
using GUI = Gtk.Builder.ObjectAttribute;
|
||||
|
||||
|
@ -14,10 +14,6 @@ namespace Ryujinx.Ui
|
|||
{
|
||||
public class SwitchSettings : Window
|
||||
{
|
||||
internal static Configuration SwitchConfig { get; set; }
|
||||
|
||||
private readonly HLE.Switch _device;
|
||||
|
||||
private static ListStore _gameDirsBoxStore;
|
||||
|
||||
private static bool _listeningForKeypress;
|
||||
|
@ -83,16 +79,12 @@ namespace Ryujinx.Ui
|
|||
#pragma warning restore CS0649
|
||||
#pragma warning restore IDE0044
|
||||
|
||||
public static void ConfigureSettings(Configuration instance) { SwitchConfig = instance; }
|
||||
public SwitchSettings() : this(new Builder("Ryujinx.Ui.SwitchSettings.glade")) { }
|
||||
|
||||
public SwitchSettings(HLE.Switch device) : this(new Builder("Ryujinx.Ui.SwitchSettings.glade"), device) { }
|
||||
|
||||
private SwitchSettings(Builder builder, HLE.Switch device) : base(builder.GetObject("_settingsWin").Handle)
|
||||
private SwitchSettings(Builder builder) : base(builder.GetObject("_settingsWin").Handle)
|
||||
{
|
||||
builder.Autoconnect(this);
|
||||
|
||||
_device = device;
|
||||
|
||||
_settingsWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
|
||||
_controller1Image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500);
|
||||
|
||||
|
@ -124,60 +116,123 @@ namespace Ryujinx.Ui
|
|||
_controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image);
|
||||
|
||||
//Setup Currents
|
||||
if (SwitchConfig.EnableFileLog) _fileLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableError) _errorLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableWarn) _warningLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableInfo) _infoLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableStub) _stubLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableDebug) _debugLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableGuest) _guestLogToggle.Click();
|
||||
if (SwitchConfig.LoggingEnableFsAccessLog) _fsAccessLogToggle.Click();
|
||||
if (SwitchConfig.DockedMode) _dockedModeToggle.Click();
|
||||
if (SwitchConfig.EnableDiscordIntegration) _discordToggle.Click();
|
||||
if (SwitchConfig.EnableVsync) _vSyncToggle.Click();
|
||||
if (SwitchConfig.EnableMulticoreScheduling) _multiSchedToggle.Click();
|
||||
if (SwitchConfig.EnableFsIntegrityChecks) _fsicToggle.Click();
|
||||
if (SwitchConfig.IgnoreMissingServices) _ignoreToggle.Click();
|
||||
if (SwitchConfig.EnableKeyboard) _directKeyboardAccess.Click();
|
||||
if (SwitchConfig.EnableCustomTheme) _custThemeToggle.Click();
|
||||
if (ConfigurationState.Instance.Logger.EnableFileLog)
|
||||
{
|
||||
_fileLogToggle.Click();
|
||||
}
|
||||
|
||||
_systemLanguageSelect.SetActiveId(SwitchConfig.SystemLanguage.ToString());
|
||||
_controller1Type .SetActiveId(SwitchConfig.ControllerType.ToString());
|
||||
if (ConfigurationState.Instance.Logger.EnableError)
|
||||
{
|
||||
_errorLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableWarn)
|
||||
{
|
||||
_warningLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableInfo)
|
||||
{
|
||||
_infoLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableStub)
|
||||
{
|
||||
_stubLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableDebug)
|
||||
{
|
||||
_debugLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableGuest)
|
||||
{
|
||||
_guestLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Logger.EnableFsAccessLog)
|
||||
{
|
||||
_fsAccessLogToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.System.EnableDockedMode)
|
||||
{
|
||||
_dockedModeToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.EnableDiscordIntegration)
|
||||
{
|
||||
_discordToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Graphics.EnableVsync)
|
||||
{
|
||||
_vSyncToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
|
||||
{
|
||||
_multiSchedToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.System.EnableFsIntegrityChecks)
|
||||
{
|
||||
_fsicToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.System.IgnoreMissingServices)
|
||||
{
|
||||
_ignoreToggle.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Hid.EnableKeyboard)
|
||||
{
|
||||
_directKeyboardAccess.Click();
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.Ui.EnableCustomTheme)
|
||||
{
|
||||
_custThemeToggle.Click();
|
||||
}
|
||||
|
||||
_systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
|
||||
_controller1Type .SetActiveId(ConfigurationState.Instance.Hid.ControllerType.Value.ToString());
|
||||
Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image);
|
||||
|
||||
_lStickUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickUp.ToString();
|
||||
_lStickDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickDown.ToString();
|
||||
_lStickLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickLeft.ToString();
|
||||
_lStickRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickRight.ToString();
|
||||
_lStickButton1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickButton.ToString();
|
||||
_dpadUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadUp.ToString();
|
||||
_dpadDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadDown.ToString();
|
||||
_dpadLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadLeft.ToString();
|
||||
_dpadRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadRight.ToString();
|
||||
_minus1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonMinus.ToString();
|
||||
_l1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonL.ToString();
|
||||
_zL1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonZl.ToString();
|
||||
_rStickUp1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickUp.ToString();
|
||||
_rStickDown1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickDown.ToString();
|
||||
_rStickLeft1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickLeft.ToString();
|
||||
_rStickRight1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickRight.ToString();
|
||||
_rStickButton1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickButton.ToString();
|
||||
_a1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonA.ToString();
|
||||
_b1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonB.ToString();
|
||||
_x1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonX.ToString();
|
||||
_y1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonY.ToString();
|
||||
_plus1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonPlus.ToString();
|
||||
_r1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonR.ToString();
|
||||
_zR1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonZr.ToString();
|
||||
_lStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickUp.ToString();
|
||||
_lStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickDown.ToString();
|
||||
_lStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickLeft.ToString();
|
||||
_lStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickRight.ToString();
|
||||
_lStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickButton.ToString();
|
||||
_dpadUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadUp.ToString();
|
||||
_dpadDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadDown.ToString();
|
||||
_dpadLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadLeft.ToString();
|
||||
_dpadRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadRight.ToString();
|
||||
_minus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonMinus.ToString();
|
||||
_l1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonL.ToString();
|
||||
_zL1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonZl.ToString();
|
||||
_rStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickUp.ToString();
|
||||
_rStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickDown.ToString();
|
||||
_rStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickLeft.ToString();
|
||||
_rStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickRight.ToString();
|
||||
_rStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickButton.ToString();
|
||||
_a1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonA.ToString();
|
||||
_b1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonB.ToString();
|
||||
_x1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonX.ToString();
|
||||
_y1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonY.ToString();
|
||||
_plus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonPlus.ToString();
|
||||
_r1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonR.ToString();
|
||||
_zR1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonZr.ToString();
|
||||
|
||||
_custThemePath.Buffer.Text = SwitchConfig.CustomThemePath;
|
||||
_graphicsShadersDumpPath.Buffer.Text = SwitchConfig.GraphicsShadersDumpPath;
|
||||
_fsLogSpinAdjustment.Value = SwitchConfig.FsGlobalAccessLogMode;
|
||||
_custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
|
||||
_graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||
_fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
||||
|
||||
_gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
|
||||
_gameDirsBoxStore = new ListStore(typeof(string));
|
||||
_gameDirsBox.Model = _gameDirsBoxStore;
|
||||
foreach (string gameDir in SwitchConfig.GameDirs)
|
||||
foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
|
||||
{
|
||||
_gameDirsBoxStore.AppendValues(gameDir);
|
||||
}
|
||||
|
@ -208,7 +263,7 @@ namespace Ryujinx.Ui
|
|||
string key = keyPressed.Event.Key.ToString();
|
||||
string capKey = key.First().ToString().ToUpper() + key.Substring(1);
|
||||
|
||||
if (Enum.IsDefined(typeof(OpenTK.Input.Key), capKey))
|
||||
if (Enum.IsDefined(typeof(Configuration.Hid.Key), capKey))
|
||||
{
|
||||
button.Label = capKey;
|
||||
}
|
||||
|
@ -325,65 +380,63 @@ namespace Ryujinx.Ui
|
|||
_gameDirsBoxStore.IterNext(ref treeIter);
|
||||
}
|
||||
|
||||
SwitchConfig.LoggingEnableError = _errorLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableWarn = _warningLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableInfo = _infoLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableStub = _stubLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableDebug = _debugLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableGuest = _guestLogToggle.Active;
|
||||
SwitchConfig.LoggingEnableFsAccessLog = _fsAccessLogToggle.Active;
|
||||
SwitchConfig.EnableFileLog = _fileLogToggle.Active;
|
||||
SwitchConfig.DockedMode = _dockedModeToggle.Active;
|
||||
SwitchConfig.EnableDiscordIntegration = _discordToggle.Active;
|
||||
SwitchConfig.EnableVsync = _vSyncToggle.Active;
|
||||
SwitchConfig.EnableMulticoreScheduling = _multiSchedToggle.Active;
|
||||
SwitchConfig.EnableFsIntegrityChecks = _fsicToggle.Active;
|
||||
SwitchConfig.IgnoreMissingServices = _ignoreToggle.Active;
|
||||
SwitchConfig.EnableKeyboard = _directKeyboardAccess.Active;
|
||||
SwitchConfig.EnableCustomTheme = _custThemeToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
|
||||
ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
|
||||
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
|
||||
ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
|
||||
ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
|
||||
ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
|
||||
|
||||
SwitchConfig.KeyboardControls.LeftJoycon = new NpadKeyboardLeft()
|
||||
ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon = new NpadKeyboardLeft()
|
||||
{
|
||||
StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickUp1.Label),
|
||||
StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickDown1.Label),
|
||||
StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickLeft1.Label),
|
||||
StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickRight1.Label),
|
||||
StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickButton1.Label),
|
||||
DPadUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadUp1.Label),
|
||||
DPadDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadDown1.Label),
|
||||
DPadLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadLeft1.Label),
|
||||
DPadRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadRight1.Label),
|
||||
ButtonMinus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _minus1.Label),
|
||||
ButtonL = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _l1.Label),
|
||||
ButtonZl = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zL1.Label),
|
||||
StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickUp1.Label),
|
||||
StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickDown1.Label),
|
||||
StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickLeft1.Label),
|
||||
StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickRight1.Label),
|
||||
StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickButton1.Label),
|
||||
DPadUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadUp1.Label),
|
||||
DPadDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadDown1.Label),
|
||||
DPadLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadLeft1.Label),
|
||||
DPadRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadRight1.Label),
|
||||
ButtonMinus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _minus1.Label),
|
||||
ButtonL = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _l1.Label),
|
||||
ButtonZl = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zL1.Label),
|
||||
};
|
||||
|
||||
SwitchConfig.KeyboardControls.RightJoycon = new NpadKeyboardRight()
|
||||
ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon = new NpadKeyboardRight()
|
||||
{
|
||||
StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickUp1.Label),
|
||||
StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickDown1.Label),
|
||||
StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickLeft1.Label),
|
||||
StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickRight1.Label),
|
||||
StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickButton1.Label),
|
||||
ButtonA = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _a1.Label),
|
||||
ButtonB = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _b1.Label),
|
||||
ButtonX = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _x1.Label),
|
||||
ButtonY = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _y1.Label),
|
||||
ButtonPlus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _plus1.Label),
|
||||
ButtonR = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _r1.Label),
|
||||
ButtonZr = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zR1.Label),
|
||||
StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickUp1.Label),
|
||||
StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickDown1.Label),
|
||||
StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickLeft1.Label),
|
||||
StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickRight1.Label),
|
||||
StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickButton1.Label),
|
||||
ButtonA = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _a1.Label),
|
||||
ButtonB = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _b1.Label),
|
||||
ButtonX = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _x1.Label),
|
||||
ButtonY = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _y1.Label),
|
||||
ButtonPlus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _plus1.Label),
|
||||
ButtonR = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _r1.Label),
|
||||
ButtonZr = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zR1.Label),
|
||||
};
|
||||
|
||||
SwitchConfig.SystemLanguage = (SystemLanguage)Enum.Parse(typeof(SystemLanguage), _systemLanguageSelect.ActiveId);
|
||||
SwitchConfig.ControllerType = (ControllerStatus)Enum.Parse(typeof(ControllerStatus), _controller1Type.ActiveId);
|
||||
SwitchConfig.CustomThemePath = _custThemePath.Buffer.Text;
|
||||
SwitchConfig.GraphicsShadersDumpPath = _graphicsShadersDumpPath.Buffer.Text;
|
||||
SwitchConfig.GameDirs = gameDirs;
|
||||
SwitchConfig.FsGlobalAccessLogMode = (int)_fsLogSpinAdjustment.Value;
|
||||
|
||||
Configuration.SaveConfig(SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
|
||||
Configuration.Configure(_device, SwitchConfig);
|
||||
ConfigurationState.Instance.System.Language.Value = (Language)Enum.Parse(typeof(Language), _systemLanguageSelect.ActiveId);
|
||||
ConfigurationState.Instance.Hid.ControllerType.Value = (ControllerType)Enum.Parse(typeof(ControllerType), _controller1Type.ActiveId);
|
||||
ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
|
||||
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
|
||||
ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs;
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
|
||||
|
||||
MainWindow.SaveConfig();
|
||||
MainWindow.ApplyTheme();
|
||||
#pragma warning disable CS4014
|
||||
MainWindow.UpdateGameTable();
|
||||
|
|
Loading…
Add table
Reference in a new issue