diff options
269 files changed, 6524 insertions, 3010 deletions
diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index c6f7528c..7145b7b6 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -14,13 +14,12 @@ jobs: const {owner, repo} = context.repo; const run_id = ${{github.event.workflow_run.id}}; const pull_head_sha = '${{github.event.workflow_run.head_sha}}'; - const pull_user_id = ${{github.event.sender.id}}; const issue_number = await (async () => { const pulls = await github.pulls.list({owner, repo}); for await (const {data} of github.paginate.iterator(pulls)) { for (const pull of data) { - if (pull.head.sha === pull_head_sha && pull.user.id === pull_user_id) { + if (pull.head.sha === pull_head_sha) { return pull.number; } } diff --git a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs index aa10aea0..2f68c43f 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs @@ -83,9 +83,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators int intFreeRegisters = regMasks.IntAvailableRegisters; int vecFreeRegisters = regMasks.VecAvailableRegisters; - BlockInfo[] blockInfo = new BlockInfo[cfg.Blocks.Count]; + var blockInfo = new BlockInfo[cfg.Blocks.Count]; - List<LocalInfo> locInfo = new List<LocalInfo>(); + var locInfo = new List<LocalInfo>(); + var locVisited = new HashSet<Operand>(); for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--) { @@ -109,7 +110,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (source.Kind == OperandKind.LocalVariable) { - locInfo[source.AsInt32() - 1].SetBlockIndex(block.Index); + locInfo[source.GetLocalNumber() - 1].SetBlockIndex(block.Index); } else if (source.Kind == OperandKind.Memory) { @@ -117,12 +118,12 @@ namespace ARMeilleure.CodeGen.RegisterAllocators if (memOp.BaseAddress != null) { - locInfo[memOp.BaseAddress.AsInt32() - 1].SetBlockIndex(block.Index); + locInfo[memOp.BaseAddress.GetLocalNumber() - 1].SetBlockIndex(block.Index); } if (memOp.Index != null) { - locInfo[memOp.Index.AsInt32() - 1].SetBlockIndex(block.Index); + locInfo[memOp.Index.GetLocalNumber() - 1].SetBlockIndex(block.Index); } } } @@ -135,9 +136,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { LocalInfo info; - if (dest.Value != 0) + if (!locVisited.Add(dest)) { - info = locInfo[dest.AsInt32() - 1]; + info = locInfo[dest.GetLocalNumber() - 1]; } else { @@ -198,7 +199,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators void AllocateRegister(Operand source, MemoryOperand memOp, int srcIndex) { - LocalInfo info = locInfo[source.AsInt32() - 1]; + LocalInfo info = locInfo[source.GetLocalNumber() - 1]; info.UseCount++; @@ -317,7 +318,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators continue; } - LocalInfo info = locInfo[dest.AsInt32() - 1]; + LocalInfo info = locInfo[dest.GetLocalNumber() - 1]; if (info.UseCount == 0 && !info.PreAllocated) { diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index cd36bdc0..88adeeb0 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -976,7 +976,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators { if (operand.Kind == OperandKind.LocalVariable) { - return operand.AsInt32(); + return operand.GetLocalNumber(); } else if (operand.Kind == OperandKind.Register) { diff --git a/ARMeilleure/CodeGen/X86/X86Optimizer.cs b/ARMeilleure/CodeGen/X86/X86Optimizer.cs index 643b515f..fa8b54e8 100644 --- a/ARMeilleure/CodeGen/X86/X86Optimizer.cs +++ b/ARMeilleure/CodeGen/X86/X86Optimizer.cs @@ -1,6 +1,7 @@ using ARMeilleure.CodeGen.Optimizations; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; +using System.Collections.Generic; using static ARMeilleure.IntermediateRepresentation.OperandHelper; using static ARMeilleure.IntermediateRepresentation.OperationHelper; @@ -11,8 +12,28 @@ namespace ARMeilleure.CodeGen.X86 { public static void RunPass(ControlFlowGraph cfg) { + var constants = new Dictionary<ulong, Operand>(); + + Operand GetConstantCopy(BasicBlock block, Operation operation, Operand source) + { + if (!constants.TryGetValue(source.Value, out var constant)) + { + constant = Local(source.Type); + + Operation copyOp = Operation(Instruction.Copy, constant, source); + + block.Operations.AddBefore(operation, copyOp); + + constants.Add(source.Value, constant); + } + + return constant; + } + for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext) { + constants.Clear(); + Node nextNode; for (Node node = block.Operations.First; node != null; node = nextNode) @@ -33,24 +54,12 @@ namespace ARMeilleure.CodeGen.X86 if (src1.Kind == OperandKind.Constant && (src1.Relocatable || CodeGenCommon.IsLongConst(src1))) { - Operand temp = Local(src1.Type); - - Operation copyOp = Operation(Instruction.Copy, temp, src1); - - block.Operations.AddBefore(operation, copyOp); - - operation.SetSource(0, temp); + operation.SetSource(0, GetConstantCopy(block, operation, src1)); } if (src2.Kind == OperandKind.Constant && (src2.Relocatable || CodeGenCommon.IsLongConst(src2))) { - Operand temp = Local(src2.Type); - - Operation copyOp = Operation(Instruction.Copy, temp, src2); - - block.Operations.AddBefore(operation, copyOp); - - operation.SetSource(1, temp); + operation.SetSource(1, GetConstantCopy(block, operation, src2)); } } @@ -111,6 +120,11 @@ namespace ARMeilleure.CodeGen.X86 return null; } + if (imm == 0 && scale == Multiplier.x1 && indexOp != null) + { + imm = GetConstOp(ref indexOp); + } + return MemoryOp(type, baseOp, indexOp, scale, imm); } diff --git a/ARMeilleure/Common/Counter.cs b/ARMeilleure/Common/Counter.cs new file mode 100644 index 00000000..defb5aba --- /dev/null +++ b/ARMeilleure/Common/Counter.cs @@ -0,0 +1,99 @@ +using System; + +namespace ARMeilleure.Common +{ + /// <summary> + /// Represents a numeric counter which can be used for instrumentation of compiled code. + /// </summary> + /// <typeparam name="T">Type of the counter</typeparam> + class Counter<T> : IDisposable where T : unmanaged + { + private bool _disposed; + private readonly int _index; + private readonly EntryTable<T> _countTable; + + /// <summary> + /// Initializes a new instance of the <see cref="Counter{T}"/> class from the specified + /// <see cref="EntryTable{T}"/> instance and index. + /// </summary> + /// <param name="countTable"><see cref="EntryTable{T}"/> instance</param> + /// <param name="index">Index in the <see cref="EntryTable{T}"/></param> + /// <exception cref="ArgumentNullException"><paramref name="countTable"/> is <see langword="null"/></exception> + /// <exception cref="ArgumentException"><typeparamref name="T"/> is unsupported</exception> + public Counter(EntryTable<T> countTable) + { + if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) && + typeof(T) != typeof(short) && typeof(T) != typeof(ushort) && + typeof(T) != typeof(int) && typeof(T) != typeof(uint) && + typeof(T) != typeof(long) && typeof(T) != typeof(ulong) && + typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) && + typeof(T) != typeof(float) && typeof(T) != typeof(double)) + { + throw new ArgumentException("Counter does not support the specified type."); + } + + _countTable = countTable ?? throw new ArgumentNullException(nameof(countTable)); + _index = countTable.Allocate(); + } + + /// <summary> + /// Gets a reference to the value of the counter. + /// </summary> + /// <exception cref="ObjectDisposedException"><see cref="Counter{T}"/> instance was disposed</exception> + /// <remarks> + /// This can refer to freed memory if the owning <see cref="EntryTable{TEntry}"/> is disposed. + /// </remarks> + public ref T Value + { + get + { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + + return ref _countTable.GetValue(_index); + } + } + + /// <summary> + /// Releases all resources used by the <see cref="Counter{T}"/> instance. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases all unmanaged and optionally managed resources used by the <see cref="Counter{T}"/> instance. + /// </summary> + /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param> + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + try + { + // The index into the EntryTable is essentially an unmanaged resource since we allocate and free the + // resource ourselves. + _countTable.Free(_index); + } + catch (ObjectDisposedException) + { + // Can happen because _countTable may be disposed before the Counter instance. + } + + _disposed = true; + } + } + + /// <summary> + /// Frees resources used by the <see cref="Counter{T}"/> instance. + /// </summary> + ~Counter() + { + Dispose(false); + } + } +} diff --git a/ARMeilleure/Common/EntryTable.cs b/ARMeilleure/Common/EntryTable.cs new file mode 100644 index 00000000..a0ed7c8e --- /dev/null +++ b/ARMeilleure/Common/EntryTable.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace ARMeilleure.Common +{ + /// <summary> + /// Represents an expandable table of the type <typeparamref name="TEntry"/>, whose entries will remain at the same + /// address through out the table's lifetime. + /// </summary> + /// <typeparam name="TEntry">Type of the entry in the table</typeparam> + class EntryTable<TEntry> : IDisposable where TEntry : unmanaged + { + private bool _disposed; + private int _freeHint; + private readonly int _pageCapacity; // Number of entries per page. + private readonly int _pageLogCapacity; + private readonly Dictionary<int, IntPtr> _pages; + private readonly BitMap _allocated; + + /// <summary> + /// Initializes a new instance of the <see cref="EntryTable{TEntry}"/> class with the desired page size in + /// bytes. + /// </summary> + /// <param name="pageSize">Desired page size in bytes</param> + /// <exception cref="ArgumentOutOfRangeException"><paramref name="pageSize"/> is less than 0</exception> + /// <exception cref="ArgumentException"><typeparamref name="TEntry"/>'s size is zero</exception> + /// <remarks> + /// The actual page size may be smaller or larger depending on the size of <typeparamref name="TEntry"/>. + /// </remarks> + public unsafe EntryTable(int pageSize = 4096) + { + if (pageSize < 0) + { + throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size cannot be negative."); + } + + if (sizeof(TEntry) == 0) + { + throw new ArgumentException("Size of TEntry cannot be zero."); + } + + _allocated = new BitMap(); + _pages = new Dictionary<int, IntPtr>(); + _pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry))); + _pageCapacity = 1 << _pageLogCapacity; + } + + /// <summary> + /// Allocates an entry in the <see cref="EntryTable{TEntry}"/>. + /// </summary> + /// <returns>Index of entry allocated in the table</returns> + /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception> + public int Allocate() + { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + + lock (_allocated) + { + if (_allocated.IsSet(_freeHint)) + { + _freeHint = _allocated.FindFirstUnset(); + } + + int index = _freeHint++; + var page = GetPage(index); + + _allocated.Set(index); + + GetValue(page, index) = default; + + return index; + } + } + + /// <summary> + /// Frees the entry at the specified <paramref name="index"/>. + /// </summary> + /// <param name="index">Index of entry to free</param> + /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception> + public void Free(int index) + { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + + lock (_allocated) + { + if (_allocated.IsSet(index)) + { + _allocated.Clear(index); + + _freeHint = index; + } + } + } + + /// <summary> + /// Gets a reference to the entry at the specified allocated <paramref name="index"/>. + /// </summary> + /// <param name="index">Index of the entry</param> + /// <returns>Reference to the entry at the specified <paramref name="index"/></returns> + /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception> + /// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception> + public ref TEntry GetValue(int index) + { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + + lock (_allocated) + { + if (!_allocated.IsSet(index)) + { + throw new ArgumentException("Entry at the specified index was not allocated", nameof(index)); + } + + var page = GetPage(index); + + return ref GetValue(page, index); + } + } + + /// <summary> + /// Gets a reference to the entry at using the specified <paramref name="index"/> from the specified + /// <paramref name="page"/>. + /// </summary> + /// <param name="page">Page to use</param> + /// <param name="index">Index to use</param> + /// <returns>Reference to the entry</returns> + private ref TEntry GetValue(Span<TEntry> page, int index) + { + return ref page[index & (_pageCapacity - 1)]; + } + + /// <summary> + /// Gets the page for the specified <see cref="index"/>. + /// </summary> + /// <param name="index">Index to use</param> + /// <returns>Page for the specified <see cref="index"/></returns> + private unsafe Span<TEntry> GetPage(int index) + { + var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity); + + if (!_pages.TryGetValue(pageIndex, out IntPtr page)) + { + page = Marshal.AllocHGlobal(sizeof(TEntry) * _pageCapacity); + + _pages.Add(pageIndex, page); + } + + return new Span<TEntry>((void*)page, _pageCapacity); + } + + /// <summary> + /// Releases all resources used by the <see cref="EntryTable{TEntry}"/> instance. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases all unmanaged and optionally managed resources used by the <see cref="EntryTable{TEntry}{T}"/> + /// instance. + /// </summary> + /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param> + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + foreach (var page in _pages.Values) + { + Marshal.FreeHGlobal(page); + } + + _disposed = true; + } + } + + /// <summary> + /// Frees resources used by the <see cref="EntryTable{TEntry}"/> instance. + /// </summary> + ~EntryTable() + { + Dispose(false); + } + } +} diff --git a/ARMeilleure/Decoders/Block.cs b/ARMeilleure/Decoders/Block.cs index 9d380bca..f296d299 100644 --- a/ARMeilleure/Decoders/Block.cs +++ b/ARMeilleure/Decoders/Block.cs @@ -11,8 +11,7 @@ namespace ARMeilleure.Decoders public Block Next { get; set; } public Block Branch { get; set; } - public bool TailCall { get; set; } - public bool Exit { get; set; } + public bool Exit { get; set; } public List<OpCode> OpCodes { get; } diff --git a/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs b/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs index e64f9a54..17c17812 100644 --- a/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs +++ b/ARMeilleure/Decoders/Optimizations/TailCallRemover.cs @@ -58,15 +58,14 @@ namespace ARMeilleure.Decoders.Optimizations return blocks.ToArray(); // Nothing to do here. } - // Mark branches outside of contiguous region as exit blocks. + // Mark branches whose target is outside of the contiguous region as an exit block. for (int i = startBlockIndex; i <= endBlockIndex; i++) { Block block = blocks[i]; if (block.Branch != null && (block.Branch.Address > endBlock.EndAddress || block.Branch.EndAddress < startBlock.Address)) { - block.Branch.Exit = true; - block.Branch.TailCall = true; + block.Branch.Exit = true; } } diff --git a/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/ARMeilleure/Instructions/InstEmitFlowHelper.cs index 296e20a5..f995ffa1 100644 --- a/ARMeilleure/Instructions/InstEmitFlowHelper.cs +++ b/ARMeilleure/Instructions/InstEmitFlowHelper.cs @@ -200,7 +200,7 @@ namespace ARMeilleure.Instructions } } - public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit) + public static void EmitTailContinue(ArmEmitterContext context, Operand address) { // Left option here as it may be useful if we need to return to managed rather than tail call in future. // (eg. for debug) @@ -218,9 +218,7 @@ namespace ARMeilleure.Instructions { context.StoreToContext(); - Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(allowRejit - ? nameof(NativeInterface.GetFunctionAddress) - : nameof(NativeInterface.GetFunctionAddressWithoutRejit)), address); + Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address); EmitNativeCall(context, fallbackAddr, isJump: true); } diff --git a/ARMeilleure/Instructions/InstEmitMemory.cs b/ARMeilleure/Instructions/InstEmitMemory.cs index 1d5953fb..87564fdc 100644 --- a/ARMeilleure/Instructions/InstEmitMemory.cs +++ b/ARMeilleure/Instructions/InstEmitMemory.cs @@ -87,8 +87,7 @@ namespace ARMeilleure.Instructions } Operand address = GetAddress(context); - - Operand address2 = context.Add(address, Const(1L << op.Size)); + Operand address2 = GetAddress(context, 1L << op.Size); EmitLoad(op.Rt, address); EmitLoad(op.Rt2, address2); @@ -112,8 +111,7 @@ namespace ARMeilleure.Instructions OpCodeMemPair op = (OpCodeMemPair)context.CurrOp; Operand address = GetAddress(context); - - Operand address2 = context.Add(address, Const(1L << op.Size)); + Operand address2 = GetAddress(context, 1L << op.Size); InstEmitMemoryHelper.EmitStore(context, address, op.Rt, op.Size); InstEmitMemoryHelper.EmitStore(context, address2, op.Rt2, op.Size); @@ -121,7 +119,7 @@ namespace ARMeilleure.Instructions EmitWBackIfNeeded(context, address); } - private static Operand GetAddress(ArmEmitterContext context) + private static Operand GetAddress(ArmEmitterContext context, long addend = 0) { Operand address = null; @@ -134,7 +132,11 @@ namespace ARMeilleure.Instructions // Pre-indexing. if (!op.PostIdx) { - address = context.Add(address, Const(op.Immediate)); + address = context.Add(address, Const(op.Immediate + addend)); + } + else if (addend != 0) + { + address = context.Add(address, Const(addend)); } break; @@ -153,6 +155,11 @@ namespace ARMeilleure.Instructions address = context.Add(n, m); + if (addend != 0) + { + address = context.Add(address, Const(addend)); + } + break; } } diff --git a/ARMeilleure/Instructions/NativeInterface.cs b/ARMeilleure/Instructions/NativeInterface.cs index b8b7ff0e..fa17d334 100644 --- a/ARMeilleure/Instructions/NativeInterface.cs +++ b/ARMeilleure/Instructions/NativeInterface.cs @@ -220,6 +220,11 @@ namespace ARMeilleure.Instructions } #endregion + public static void EnqueueForRejit(ulong address) + { + Context.Translator.EnqueueForRejit(address, GetContext().ExecutionMode); + } + public static void SignalMemoryTracking(ulong address, ulong size, bool write) { GetMemoryManager().SignalMemoryTracking(address, size, write); @@ -232,24 +237,14 @@ namespace ARMeilleure.Instructions public static ulong GetFunctionAddress(ulong address) { - return GetFunctionAddressWithHint(address, true); - } - - public static ulong GetFunctionAddressWithoutRejit(ulong address) - { - return GetFunctionAddressWithHint(address, false); - } - - private static ulong GetFunctionAddressWithHint(ulong address, bool hintRejit) - { - TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit); + TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); return (ulong)function.FuncPtr.ToInt64(); } public static ulong GetIndirectFunctionAddress(ulong address, ulong entryAddress) { - TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode, hintRejit: true); + TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); ulong ptr = (ulong)function.FuncPtr.ToInt64(); diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs index 7b486c55..ec023939 100644 --- a/ARMeilleure/IntermediateRepresentation/Operand.cs +++ b/ARMeilleure/IntermediateRepresentation/Operand.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace ARMeilleure.IntermediateRepresentation @@ -91,6 +92,13 @@ namespace ARMeilleure.IntermediateRepresentation return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24)); } + public int GetLocalNumber() + { + Debug.Assert(Kind == OperandKind.LocalVariable); + + return (int)Value; + } + public byte AsByte() { return (byte)Value; diff --git a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs index 26d66478..1b748f6a 100644 --- a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs +++ b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs @@ -1,4 +1,5 @@ using ARMeilleure.Common; +using System.Runtime.CompilerServices; namespace ARMeilleure.IntermediateRepresentation { @@ -34,6 +35,11 @@ namespace ARMeilleure.IntermediateRepresentation return Operand().With(value); } + public static unsafe Operand Const<T>(ref T reference, int? index = null) + { + return Operand().With((long)Unsafe.AsPointer(ref reference), index != null, index); + } + public static Operand ConstF(float value) { return Operand().With(value); diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs index 33153903..cacfc4ac 100644 --- a/ARMeilleure/Memory/IMemoryManager.cs +++ b/ARMeilleure/Memory/IMemoryManager.cs @@ -12,6 +12,8 @@ namespace ARMeilleure.Memory T ReadTracked<T>(ulong va) where T : unmanaged; void Write<T>(ulong va, T value) where T : unmanaged; + ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false); + ref T GetRef<T>(ulong va) where T : unmanaged; bool IsMapped(ulong va); diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index 8f153192..ad44b0cf 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Common; using ARMeilleure.Decoders; using ARMeilleure.Instructions; using ARMeilleure.IntermediateRepresentation; @@ -41,18 +42,26 @@ namespace ARMeilleure.Translation public IMemoryManager Memory { get; } public JumpTable JumpTable { get; } + public EntryTable<uint> CountTable { get; } public ulong EntryAddress { get; } public bool HighCq { get; } public Aarch32Mode Mode { get; } - public ArmEmitterContext(IMemoryManager memory, JumpTable jumpTable, ulong entryAddress, bool highCq, Aarch32Mode mode) + public ArmEmitterContext( + IMemoryManager memory, + JumpTable jumpTable, + EntryTable<uint> countTable, + ulong entryAddress, + bool highCq, + Aarch32Mode mode) { - Memory = memory; - JumpTable = jumpTable; + Memory = memory; + JumpTable = jumpTable; + CountTable = countTable; EntryAddress = entryAddress; - HighCq = highCq; - Mode = mode; + HighCq = highCq; + Mode = mode; _labels = new Dictionary<ulong, Operand>(); } diff --git a/ARMeilleure/Translation/ControlFlowGraph.cs b/ARMeilleure/Translation/ControlFlowGraph.cs index ee1a245e..4c76d5dd 100644 --- a/ARMeilleure/Translation/ControlFlowGraph.cs +++ b/ARMeilleure/Translation/ControlFlowGraph.cs @@ -10,15 +10,17 @@ namespace ARMeilleure.Translation private BasicBlock[] _postOrderBlocks; private int[] _postOrderMap; + public int LocalsCount { get; } public BasicBlock Entry { get; } public IntrusiveList<BasicBlock> Blocks { get; } public BasicBlock[] PostOrderBlocks => _postOrderBlocks; public int[] PostOrderMap => _postOrderMap; - public ControlFlowGraph(BasicBlock entry, IntrusiveList<BasicBlock> blocks) + public ControlFlowGraph(BasicBlock entry, IntrusiveList<BasicBlock> blocks, int localsCount) { Entry = entry; Blocks = blocks; + LocalsCount = localsCount; Update(removeUnreachableBlocks: true); } diff --git a/ARMeilleure/Translation/Delegates.cs b/ARMeilleure/Translation/Delegates.cs index 5ad71872..a561d265 100644 --- a/ARMeilleure/Translation/Delegates.cs +++ b/ARMeilleure/Translation/Delegates.cs @@ -103,6 +103,7 @@ namespace ARMeilleure.Translation SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.Break))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); + SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0))); @@ -113,7 +114,6 @@ namespace ARMeilleure.Translation SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpscr))); // A32 only. SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFpsr))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); - SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddressWithoutRejit))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetIndirectFunctionAddress))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only. diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs index 5c608b3d..cc2205ce 100644 --- a/ARMeilleure/Translation/EmitterContext.cs +++ b/ARMeilleure/Translation/EmitterContext.cs @@ -12,6 +12,8 @@ namespace ARMeilleure.Translation { class EmitterContext { + private int _localsCount; + private readonly Dictionary<Operand, BasicBlock> _irLabels; private readonly IntrusiveList<BasicBlock> _irBlocks; @@ -23,6 +25,8 @@ namespace ARMeilleure.Translation public EmitterContext() { + _localsCount = 0; + _irLabels = new Dictionary<Operand, BasicBlock>(); _irBlocks = new IntrusiveList<BasicBlock>(); @@ -30,6 +34,15 @@ namespace ARMeilleure.Translation _nextBlockFreq = BasicBlockFrequency.Default; } + public Operand AllocateLocal(OperandType type) + { + Operand local = Local(type); + + local.NumberLocal(++_localsCount); + + return local; + } + public Operand Add(Operand op1, Operand op2) { return Add(Instruction.Add, Local(op1.Type), op1, op2); @@ -223,9 +236,10 @@ namespace ARMeilleure.Translation public Operand Copy(Operand dest, Operand op1) { - if (dest.Kind != OperandKind.Register) + if (dest.Kind != OperandKind.Register && + (dest.Kind != OperandKind.LocalVariable || dest.GetLocalNumber() == 0)) { - throw new ArgumentException($"Invalid dest operand kind \"{dest.Kind}\"."); + throw new ArgumentException($"Destination operand must be a Register or a numbered LocalVariable."); } return Add(Instruction.Copy, dest, op1); @@ -670,7 +684,7 @@ namespace ARMeilleure.Translation public ControlFlowGraph GetControlFlowGraph() { - return new ControlFlowGraph(_irBlocks.First, _irBlocks); + return new ControlFlowGraph(_irBlocks.First, _irBlocks, _localsCount); } } } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 32e0e7e8..1bb8659c 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -1,6 +1,7 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.CodeGen.X86; +using ARMeilleure.Common; using ARMeilleure.Memory; using ARMeilleure.Translation.Cache; using Ryujinx.Common; @@ -27,7 +28,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 2169; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 2285; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -38,6 +39,7 @@ namespace ARMeilleure.Translation.PTC internal const int PageTablePointerIndex = -1; // Must be a negative value. internal const int JumpPointerIndex = -2; // Must be a negative value. internal const int DynamicPointerIndex = -3; // Must be a negative value. + internal const int CountTableIndex = -4; // Must be a negative value. private const byte FillingByte = 0x00; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; @@ -48,8 +50,6 @@ namespace ARMeilleure.Translation.PTC private static MemoryStream _relocsStream; private static MemoryStream _unwindInfosStream; - private static BinaryWriter _infosWriter; - private static readonly ulong _outerHeaderMagic; private static readonly ulong _innerHeaderMagic; @@ -151,14 +151,10 @@ namespace ARMeilleure.Translation.PTC _codesList = new List<byte[]>(); _relocsStream = new MemoryStream(); _unwindInfosStream = new MemoryStream(); - - _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true); } private static void DisposeCarriers() { - _infosWriter.Dispose(); - _infosStream.Dispose(); _codesList.Clear(); _relocsStream.Dispose(); @@ -538,68 +534,89 @@ namespace ARMeilleure.Translation.PTC } } - internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) + internal static void LoadTranslations( + ConcurrentDictionary<ulong, TranslatedFunction> funcs, + IMemoryManager memory, + JumpTable jumpTable, + EntryTable<uint> countTable) { if (AreCarriersEmpty()) { return; } + long infosStreamLength = _infosStream.Length; + long relocsStreamLength = _relocsStream.Length; + long unwindInfosStreamLength = _unwindInfosStream.Length; + _infosStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin); - using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) { for (int index = 0; index < GetEntriesCount(); index++) { - InfoEntry infoEntry = ReadInfo(infosReader); + InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream); if (infoEntry.Stubbed) { SkipCode(index, infoEntry.CodeLength); SkipReloc(infoEntry.RelocEntriesCount); SkipUnwindInfo(unwindInfosReader); + + continue; } - else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.HighCq) + + bool isEntryChanged = infoEntry.Hash != ComputeHash(memory, infoEntry.Address, infoEntry.GuestSize); + + if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) { - byte[] code = ReadCode(index, infoEntry.CodeLength); + infoEntry.Stubbed = true; + infoEntry.CodeLength = 0; + UpdateInfo(infoEntry); - if (infoEntry.RelocEntriesCount != 0) + StubCode(index); + StubReloc(infoEntry.RelocEntriesCount); + StubUnwindInfo(unwindInfosReader); + + if (isEntryChanged) { - RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); + PtcJumpTable.Clean(infoEntry.Address); - PatchCode(code.AsSpan(), relocEntries, memory.PageTablePointer, jumpTable); + Logger.Info?.Print(LogClass.Ptc, $"Invalidated translated function (address: 0x{infoEntry.Address:X16})"); } - UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + continue; + } - TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); + byte[] code = ReadCode(index, infoEntry.CodeLength); - bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); + Counter<uint> callCounter = null; - Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); - } - else + if (infoEntry.RelocEntriesCount != 0) { - infoEntry.Stubbed = true; - infoEntry.CodeLength = 0; - UpdateInfo(infoEntry); + RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); - StubCode(index); - StubReloc(infoEntry.RelocEntriesCount); - StubUnwindInfo(unwindInfosReader); + PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter); } + + UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); + + TranslatedFunction func = FastTranslate(code, callCounter, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq); + + bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func); + + Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); } } - if (_infosStream.Position < _infosStream.Length || - _relocsStream.Position < _relocsStream.Length || - _unwindInfosStream.Position < _unwindInfosStream.Length) + if (_infosStream.Length != infosStreamLength || _infosStream.Position != infosStreamLength || + _relocsStream.Length != relocsStreamLength || _relocsStream.Position != relocsStreamLength || + _unwindInfosStream.Length != unwindInfosStreamLength || _unwindInfosStream.Position != unwindInfosStreamLength) { - throw new Exception("Could not reach the end of one or more memory streams."); + throw new Exception("The length of a memory stream has changed, or its position has not reached or has exceeded its end."); } jumpTable.Initialize(PtcJumpTable, funcs); @@ -615,20 +632,6 @@ namespace ARMeilleure.Translation.PTC return _codesList.Count; } - private static InfoEntry ReadInfo(BinaryReader infosReader) - { - InfoEntry infoEntry = new InfoEntry(); - - infoEntry.Address = infosReader.ReadUInt64(); - infoEntry.GuestSize = infosReader.ReadUInt64(); - infoEntry.HighCq = infosReader.ReadBoolean(); - infoEntry.Stubbed = infosReader.ReadBoolean(); - infoEntry.CodeLength = infosReader.ReadInt32(); - infoEntry.RelocEntriesCount = infosReader.ReadInt32(); - - return infoEntry; - } - [Conditional("DEBUG")] private static void SkipCode(int index, int codeLength) { @@ -670,8 +673,16 @@ namespace ARMeilleure.Translation.PTC return relocEntries; } - private static void PatchCode(Span<byte> code, RelocEntry[] relocEntries, IntPtr pageTablePointer, JumpTable jumpTable) + private static void PatchCode( + Span<byte> code, + RelocEntry[] relocEntries, + IntPtr pageTablePointer, + JumpTable jumpTable, + EntryTable<uint> countTable, + out Counter<uint> callCounter) { + callCounter = null; + foreach (RelocEntry relocEntry in relocEntries) { ulong imm; @@ -688,6 +699,12 @@ namespace ARMeilleure.Translation.PTC { imm = (ulong)jumpTable.DynamicPointer.ToInt64(); } + else if (relocEntry.Index == CountTableIndex) + { + callCounter = new Counter<uint>(countTable); + + unsafe { imm = (ulong)Unsafe.AsPointer(ref callCounter.Value); } + } else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr)) { imm = (ulong)funcPtr.ToInt64(); @@ -722,7 +739,12 @@ namespace ARMeilleure.Translation.PTC return new UnwindInfo(pushEntries, prologueSize); } - private static TranslatedFunction FastTranslate(byte[] code, ulong guestSize, UnwindInfo unwindInfo, bool highCq) + private static TranslatedFunction FastTranslate( + byte[] code, + Counter<uint> callCounter, + ulong guestSize, + UnwindInfo unwindInfo, + bool highCq) { CompiledFunction cFunc = new CompiledFunction(code, unwindInfo); @@ -730,22 +752,16 @@ namespace ARMeilleure.Translation.PTC GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer<GuestFunction>(codePtr); - TranslatedFunction tFunc = new TranslatedFunction(gFunc, guestSize, highCq); + TranslatedFunction tFunc = new TranslatedFunction(gFunc, callCounter, guestSize, highCq); return tFunc; } private static void UpdateInfo(InfoEntry infoEntry) { - _infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current); - - // WriteInfo. - _infosWriter.Write((ulong)infoEntry.Address); - _infosWriter.Write((ulong)infoEntry.GuestSize); - _infosWriter.Write((bool)infoEntry.HighCq); - _infosWriter.Write((bool)infoEntry.Stubbed); - _infosWriter.Write((int)infoEntry.CodeLength); - _infosWriter.Write((int)infoEntry.RelocEntriesCount); + _infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current); + + SerializeStructure(_infosStream, infoEntry); } private static void StubCode(int index) @@ -771,7 +787,11 @@ namespace ARMeilleure.Translation.PTC } } - internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable) + internal static void MakeAndSaveTranslations( + ConcurrentDictionary<ulong, TranslatedFunction> funcs, + IMemoryManager memory, + JumpTable jumpTable, + EntryTable<uint> countTable) { var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs); @@ -813,7 +833,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); - TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.mode, item.highCq); + TranslatedFunction func = Translator.Translate(memory, jumpTable, countTable, address, item.funcProfile.Mode, item.funcProfile.HighCq); bool isAddressUnique = funcs.TryAdd(address, func); @@ -888,17 +908,26 @@ namespace ARMeilleure.Translation.PTC while (!endEvent.WaitOne(refreshRate)); } - internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo) + internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) + { + return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); + } + + internal static void WriteInfoCodeRelocUnwindInfo(ulong address, ulong guestSize, Hash128 hash, bool highCq, PtcInfo ptcInfo) { lock (_lock) { - // WriteInfo. - _infosWriter.Write((ulong)address); // InfoEntry.Address - _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize - _infosWriter.Write((bool)highCq); // InfoEntry.HighCq - _infosWriter.Write((bool)false); // InfoEntry.Stubbed - _infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLength - _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount + InfoEntry infoEntry = new InfoEntry(); + + infoEntry.Address = address; + infoEntry.GuestSize = guestSize; + infoEntry.Hash = hash; + infoEntry.HighCq = highCq; + infoEntry.Stubbed = false; + infoEntry.CodeLength = ptcInfo.Code.Length; + infoEntry.RelocEntriesCount = ptcInfo.RelocEntriesCount; + + SerializeStructure(_infosStream, infoEntry); WriteCode(ptcInfo.Code.AsSpan()); @@ -915,7 +944,7 @@ namespace ARMeilleure.Translation.PTC _codesList.Add(code.ToArray()); } - private static bool GetEndianness() + internal static bool GetEndianness() { return BitConverter.IsLittleEndian; } @@ -1001,12 +1030,12 @@ namespace ARMeilleure.Translation.PTC } } + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 42*/)] private struct InfoEntry { - public const int Stride = 26; // Bytes. - public ulong Address; public ulong GuestSize; + public Hash128 Hash; public bool HighCq; public bool Stubbed; public int CodeLength; @@ -1058,4 +1087,4 @@ namespace ARMeilleure.Translation.PTC } } } -}
\ No newline at end of file +} diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/ARMeilleure/Translation/PTC/PtcFormatter.cs index 753c01c8..2f7a9c21 100644 --- a/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -50,7 +50,12 @@ namespace ARMeilleure.Translation.PTC T structure = default(T); Span<T> spanT = MemoryMarshal.CreateSpan(ref structure, 1); - stream.Read(MemoryMarshal.AsBytes(spanT)); + int bytesCount = stream.Read(MemoryMarshal.AsBytes(spanT)); + + if (bytesCount != Unsafe.SizeOf<T>()) + { + throw new EndOfStreamException(); + } return structure; } @@ -130,7 +135,12 @@ namespace ARMeilleure.Translation.PTC T[] item = new T[itemLength]; - stream.Read(MemoryMarshal.AsBytes(item.AsSpan())); + int bytesCount = stream.Read(MemoryMarshal.AsBytes(item.AsSpan())); + + if (bytesCount != itemLength) + { + throw new EndOfStreamException(); + } list.Add(item); } diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs index 40a30329..67719623 100644 --- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs +++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs @@ -129,7 +129,6 @@ namespace ARMeilleure.Translation.PTC } } - // For future use. public void Clean(ulong guestAddress) { if (Owners.TryGetValue(guestAddress, out List<int> entries)) diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index d7b2b0f8..e31ff230 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -1,13 +1,15 @@ using ARMeilleure.State; +using Ryujinx.Common; using Ryujinx.Common.Logging; using System; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Threading; using static ARMeilleure.Translation.PTC.PtcFormatter; @@ -16,9 +18,9 @@ namespace ARMeilleure.Translation.PTC { public static class PtcProfiler { - private const string HeaderMagic = "Phd"; + private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; - private const uint InternalVersion = 1713; //! Not to be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project. private const int SaveInterval = 30; // Seconds. @@ -26,13 +28,15 @@ namespace ARMeilleure.Translation.PTC private static readonly System.Timers.Timer _timer; + private static readonly ulong _outerHeaderMagic; + private static readonly ManualResetEvent _waitEvent; private static readonly object _lock; private static bool _disposed; - private static byte[] _lastHash; + private static Hash128 _lastHash; internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; } @@ -46,6 +50,8 @@ namespace ARMeilleure.Translation.PTC _timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer.Elapsed += PreSave; + _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); + _waitEvent = new ManualResetEvent(true); _lock = new object(); @@ -90,17 +96,15 @@ namespace ARMeilleure.Translation.PTC return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; } - internal static ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs) + internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary<ulong, TranslatedFunction> funcs) { - var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, ExecutionMode mode, bool highCq)>(); + var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); foreach (var profiledFunc in ProfiledFuncs) { - ulong address = profiledFunc.Key; - - if (!funcs.ContainsKey(address)) + if (!funcs.ContainsKey(profiledFunc.Key)) { - profiledFuncsToTranslate.Enqueue((address, profiledFunc.Value.Mode, profiledFunc.Value.HighCq)); + profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value)); } } @@ -115,7 +119,7 @@ namespace ARMeilleure.Translation.PTC internal static void PreLoad() { - _lastHash = Array.Empty<byte>(); + _lastHash = default; string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); @@ -141,96 +145,82 @@ namespace ARMeilleure.Translation.PTC private static bool Load(string fileName, bool isBackup) { - using (FileStream compressedStream = new FileStream(fileName, FileMode.Open)) - using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true)) - using (MemoryStream stream = new MemoryStream()) - using (MD5 md5 = MD5.Create()) + using (FileStream compressedStream = new(fileName, FileMode.Open)) + using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) { - try - { - deflateStream.CopyTo(stream); - } - catch + OuterHeader outerHeader = DeserializeStructure<OuterHeader>(compressedStream); + + if (!outerHeader.IsHeaderValid()) { InvalidateCompressedStream(compressedStream); return false; } - int hashSize = md5.HashSize / 8; - - stream.Seek(0L, SeekOrigin.Begin); - - byte[] currentHash = new byte[hashSize]; - stream.Read(currentHash, 0, hashSize); - - byte[] expectedHash = md5.ComputeHash(stream); - - if (!CompareHash(currentHash, expectedHash)) + if (outerHeader.Magic != _outerHeaderMagic) { InvalidateCompressedStream(compressedStream); return false; } - stream.Seek((long)hashSize, SeekOrigin.Begin); - - Header header = ReadHeader(stream); - - if (header.Magic != HeaderMagic) + if (outerHeader.InfoFileVersion != InternalVersion) { InvalidateCompressedStream(compressedStream); return false; } - if (header.InfoFileVersion != InternalVersion) + if (outerHeader.Endianness != Ptc.GetEndianness()) { InvalidateCompressedStream(compressedStream); return false; } - try + using (MemoryStream stream = new MemoryStream()) { - ProfiledFuncs = Deserialize(stream); - } - catch - { - ProfiledFuncs = new Dictionary<ulong, FuncProfile>(); + Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - InvalidateCompressedStream(compressedStream); + try + { + deflateStream.CopyTo(stream); + } + catch + { + InvalidateCompressedStream(compressedStream); - return false; - } + return false; + } - _lastHash = expectedHash; - } + Debug.Assert(stream.Position == stream.Length); - long fileSize = new FileInfo(fileName).Length; + stream.Seek(0L, SeekOrigin.Begin); - Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count})."); + Hash128 expectedHash = DeserializeStructure<Hash128>(stream); - return true; - } + Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); - private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash) - { - return currentHash.SequenceEqual(expectedHash); - } + if (actualHash != expectedHash) + { + InvalidateCompressedStream(compressedStream); - private static Header ReadHeader(Stream stream) - { - using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true)) - { - Header header = new Header(); + return false; + } - header.Magic = headerReader.ReadString(); + ProfiledFuncs = Deserialize(stream); - header.InfoFileVersion = headerReader.ReadUInt32(); + Debug.Assert(stream.Position == stream.Length); - return header; + _lastHash = actualHash; + } } + + long fileSize = new FileInfo(fileName).Length; + + Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Profiling Info" : "Loaded Profiling Info")} (size: {fileSize} bytes, profiled functions: {ProfiledFuncs.Count})."); + + return true; } private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream) @@ -238,6 +228,11 @@ namespace ARMeilleure.Translation.PTC return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream)); } + private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) + { + return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); + } + private static void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); @@ -266,14 +261,20 @@ namespace ARMeilleure.Translation.PTC { int profiledFuncsCount; + OuterHeader outerHeader = new OuterHeader(); + + outerHeader.Magic = _outerHeaderMagic; + + outerHeader.InfoFileVersion = InternalVersion; + outerHeader.Endianness = Ptc.GetEndianness(); + + outerHeader.SetHeaderHash(); + using (MemoryStream stream = new MemoryStream()) - using (MD5 md5 = MD5.Create()) { - int hashSize = md5.HashSize / 8; + Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); - stream.Seek((long)hashSize, SeekOrigin.Begin); - - WriteHeader(stream); + stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); lock (_lock) { @@ -282,22 +283,26 @@ namespace ARMeilleure.Translation.PTC profiledFuncsCount = ProfiledFuncs.Count; } - stream.Seek((long)hashSize, SeekOrigin.Begin); - byte[] hash = md5.ComputeHash(stream); + Debug.Assert(stream.Position == stream.Length); + + stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); + Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); stream.Seek(0L, SeekOrigin.Begin); - stream.Write(hash, 0, hashSize); + SerializeStructure(stream, hash); - if (CompareHash(hash, _lastHash)) + if (hash == _lastHash) { return; } - using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate)) - using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true)) + using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate)) + using (DeflateStream deflateStream = new(compressedStream, SaveCompressionLevel, true)) { try { + SerializeStructure(compressedStream, outerHeader); + stream.WriteTo(deflateStream); _lastHash = hash; @@ -306,7 +311,7 @@ namespace ARMeilleure.Translation.PTC { compressedStream.Position = 0L; - _lastHash = Array.Empty<byte>(); + _lastHash = default; } if (compressedStream.Position < compressedStream.Length) @@ -324,26 +329,35 @@ namespace ARMeilleure.Translation.PTC } } - private static void WriteHeader(Stream stream) - { - using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true)) - { - headerWriter.Write((string)HeaderMagic); // Header.Magic - - headerWriter.Write((uint)InternalVersion); // Header.InfoFileVersion - } - } - private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) { SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); } - private struct Header + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)] + private struct OuterHeader { - public string Magic; + public ulong Magic; public uint InfoFileVersion; + + public bool Endianness; + + public Hash128 HeaderHash; + + public void SetHeaderHash() + { + Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())); + } + + public bool IsHeaderValid() + { + Span<OuterHeader> spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf<OuterHeader>() - Unsafe.SizeOf<Hash128>())) == HeaderHash; + } } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)] diff --git a/ARMeilleure/Translation/SsaConstruction.cs b/ARMeilleure/Translation/SsaConstruction.cs index 1c6e83c9..76cb9a44 100644 --- a/ARMeilleure/Translation/SsaConstruction.cs +++ b/ARMeilleure/Translation/SsaConstruction.cs @@ -45,7 +45,7 @@ namespace ARMeilleure.Translation public static void Construct(ControlFlowGraph cfg) { var globalDefs = new DefMap[cfg.Blocks.Count]; - var localDefs = new Operand[RegisterConsts.TotalCount]; + var localDefs = new Operand[cfg.LocalsCount + RegisterConsts.TotalCount]; var dfPhiBlocks = new Queue<BasicBlock>(); @@ -264,6 +264,12 @@ namespace ARMeilleure.Translation return true; } + else if (operand is { Kind: OperandKind.LocalVariable } && operand.GetLocalNumber() > 0) + { + result = RegisterConsts.TotalCount + operand.GetLocalNumber() - 1; + + return true; + } result = -1; @@ -274,7 +280,7 @@ namespace ARMeilleure.Translation { if (!TryGetId(operand, out int key)) { - Debug.Fail("OperandKind must be Register."); + Debug.Fail("OperandKind must be Register or a numbered LocalVariable."); } return key; diff --git a/ARMeilleure/Translation/TranslatedFunction.cs b/ARMeilleure/Translation/TranslatedFunction.cs index 8b075928..04dd769c 100644 --- a/ARMeilleure/Translation/TranslatedFunction.cs +++ b/ARMeilleure/Translation/TranslatedFunction.cs @@ -1,24 +1,22 @@ +using ARMeilleure.Common; using System; using System.Runtime.InteropServices; -using System.Threading; namespace ARMeilleure.Translation { class TranslatedFunction { - private const int MinCallsForRejit = 100; - private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. - private int _callCount; - + public Counter<uint> CallCounter { get; } public ulong GuestSize { get; } public bool HighCq { get; } public IntPtr FuncPtr { get; } - public TranslatedFunction(GuestFunction func, ulong guestSize, bool highCq) + public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq) { _func = func; + CallCounter = callCounter; GuestSize = guestSize; HighCq = highCq; FuncPtr = Marshal.GetFunctionPointerForDelegate(func); @@ -28,15 +26,5 @@ namespace ARMeilleure.Translation { return _func(context.NativeContextPtr); } - - public bool ShouldRejit() - { - return !HighCq && Interlocked.Increment(ref _callCount) == MinCallsForRejit; - } - - public void ResetCallCount() - { - Interlocked.Exchange(ref _callCount, 0); - } } }
\ No newline at end of file diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 73a321fd..81af0681 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Common; using ARMeilleure.Decoders; using ARMeilleure.Diagnostics; using ARMeilleure.Instructions; @@ -6,11 +7,11 @@ using ARMeilleure.Memory; using ARMeilleure.State; using ARMeilleure.Translation.Cache; using ARMeilleure.Translation.PTC; +using Ryujinx.Common; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime; using System.Threading; @@ -22,23 +23,27 @@ namespace ARMeilleure.Translation { public class Translator { + private const int CountTableCapacity = 4 * 1024 * 1024; + private readonly IJitMemoryAllocator _allocator; private readonly IMemoryManager _memory; private readonly ConcurrentDictionary<ulong, TranslatedFunction> _funcs; - private readonly ConcurrentQueue<KeyValuePair<ulong, IntPtr>> _oldFuncs; + private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs; + private readonly ConcurrentDictionary<ulong, object> _backgroundSet; private readonly ConcurrentStack<RejitRequest> _backgroundStack; private readonly AutoResetEvent _backgroundTranslatorEvent; private readonly ReaderWriterLock _backgroundTranslatorLock; private JumpTable _jumpTable; internal JumpTable JumpTable => _jumpTable; + internal EntryTable<uint> CountTable { get; } private volatile int _threadCount; // FIXME: Remove this once the init logic of the emulator will be redone. - public static ManualResetEvent IsReadyForTranslation = new ManualResetEvent(false); + public static readonly ManualResetEvent IsReadyForTranslation = new(false); public Translator(IJitMemoryAllocator allocator, IMemoryManager memory) { @@ -46,12 +51,15 @@ namespace ARMeilleure.Translation _memory = memory; _funcs = new ConcurrentDictionary<ulong, TranslatedFunction>(); - _oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, IntPtr>>(); + _oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>(); + _backgroundSet = new ConcurrentDictionary<ulong, object>(); _backgroundStack = new ConcurrentStack<RejitRequest>(); _backgroundTranslatorEvent = new AutoResetEvent(false); _backgroundTranslatorLock = new ReaderWriterLock(); + CountTable = new EntryTable<uint>(); + JitCache.Initialize(allocator); DirectCallStubs.InitializeStubs(); @@ -63,9 +71,16 @@ namespace ARMeilleure.Translation { _backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite); - if (_backgroundStack.TryPop(out RejitRequest request)) + if (_backgroundStack.TryPop(out RejitRequest request) && + _backgroundSet.TryRemove(request.Address, out _)) { - TranslatedFunction func = Translate(_memory, _jumpTable, request.Address, request.Mode, highCq: true); + TranslatedFunction func = Translate( + _memory, + _jumpTable, + CountTable, + request.Address, + request.Mode, + highCq: true); _funcs.AddOrUpdate(request.Address, func, (key, oldFunc) => { @@ -89,7 +104,8 @@ namespace ARMeilleure.Translation } } - _backgroundTranslatorEvent.Set(); // Wake up any other background translator threads, to encourage them to exit. + // Wake up any other background translator threads, to encourage them to exit. + _backgroundTranslatorEvent.Set(); } public void Execute(State.ExecutionContext context, ulong address) @@ -104,18 +120,21 @@ namespace ARMeilleure.Translation if (Ptc.State == PtcState.Enabled) { Debug.Assert(_funcs.Count == 0); - Ptc.LoadTranslations(_funcs, _memory, _jumpTable); - Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable); + Ptc.LoadTranslations(_funcs, _memory, _jumpTable, CountTable); + Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable, CountTable); } PtcProfiler.Start(); Ptc.Disable(); - // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core+ht etc). - // All threads are normal priority except from the last, which just fills as much of the last core as the os lets it with a low priority. - // If we only have one rejit thread, it should be normal priority as highCq code is performance critical. - // TODO: Use physical cores rather than logical. This only really makes sense for processors with hyperthreading. Requires OS specific code. + // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht + // etc). All threads are normal priority except from the last, which just fills as much of the last core + // as the os lets it with a low priority. If we only have one rejit thread, it should be normal priority + // as highCq code is performance critical. + // + // TODO: Use physical cores rather than logical. This only really makes sense for processors with + // hyperthreading. Requires OS specific code. int unboundedThreadCount = Math.Max(1, (Environment.ProcessorCount - 6) / 3); int threadCount = Math.Min(4, unboundedThreadCount); @@ -156,6 +175,8 @@ namespace ARMeilleure.Translation _jumpTable.Dispose(); _jumpTable = null; + CountTable.Dispose(); + GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; } } @@ -173,11 +194,11 @@ namespace ARMeilleure.Translation return nextAddr; } - internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode, bool hintRejit = false) + internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode) { if (!_funcs.TryGetValue(address, out TranslatedFunction func)) { - func = Translate(_memory, _jumpTable, address, mode, highCq: false); + func = Translate(_memory, _jumpTable, CountTable, address, mode, highCq: false); TranslatedFunction getFunc = _funcs.GetOrAdd(address, func); @@ -193,18 +214,18 @@ namespace ARMeilleure.Translation } } - if (hintRejit && func.ShouldRejit()) - { - _backgroundStack.Push(new RejitRequest(address, mode)); - _backgroundTranslatorEvent.Set(); - } - return func; } - internal static TranslatedFunction Translate(IMemoryManager memory, JumpTable jumpTable, ulong address, ExecutionMode mode, bool highCq) + internal static TranslatedFunction Translate( + IMemoryManager memory, + JumpTable jumpTable, + EntryTable<uint> countTable, + ulong address, + ExecutionMode mode, + bool highCq) { - ArmEmitterContext context = new ArmEmitterContext(memory, jumpTable, address, highCq, Aarch32Mode.User); + var context = new ArmEmitterContext(memory, jumpTable, countTable, address, highCq, Aarch32Mode.User); Logger.StartPass(PassName.Decoding); @@ -216,6 +237,13 @@ namespace ARMeilleure.Translation Logger.StartPass(PassName.Translation); + Counter<uint> counter = null; + + if (!context.HighCq) + { + EmitRejitCheck(context, out counter); + } + EmitSynchronization(context); if (blocks[0].Address != address) @@ -255,10 +283,12 @@ namespace ARMeilleure.Translation ResetPool(highCq ? 1 : 0); - Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, highCq, ptcInfo); + Hash128 hash = Ptc.ComputeHash(memory, address, funcSize); + + Ptc.WriteInfoCodeRelocUnwindInfo(address, funcSize, hash, highCq, ptcInfo); } - return new TranslatedFunction(func, funcSize, highCq); + return new TranslatedFunction(func, counter, funcSize, highCq); } internal static void PreparePool(int groupId = 0) @@ -320,7 +350,7 @@ namespace ARMeilleure.Translation if (block.Exit) { - InstEmitFlowHelper.EmitTailContinue(context, Const(block.Address), block.TailCall); + InstEmitFlowHelper.EmitTailContinue(context, Const(block.Address)); } else { @@ -368,29 +398,43 @@ namespace ARMeilleure.Translation return context.GetControlFlowGraph(); } - internal static void EmitSynchronization(EmitterContext context) + internal static void EmitRejitCheck(ArmEmitterContext context, out Counter<uint> counter) { - long countOffs = NativeContext.GetCounterOffset(); + const int MinsCallForRejit = 100; - Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs)); + counter = new Counter<uint>(context.CountTable); - Operand count = context.Load(OperandType.I32, countAddr); + Operand lblEnd = Label(); + + Operand address = Const(ref counter.Value, Ptc.CountTableIndex); + Operand curCount = context.Load(OperandType.I32, address); + Operand count = context.Add(curCount, Const(1)); + context.Store(address, count); + context.BranchIf(lblEnd, curCount, Const(MinsCallForRejit), Comparison.NotEqual, BasicBlockFrequency.Cold); + + context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress)); + + context.MarkLabel(lblEnd); + } + + internal static void EmitSynchronization(EmitterContext context) + { + long countOffs = NativeContext.GetCounterOffset(); Operand lblNonZero = Label(); - Operand lblExit = Label(); + Operand lblExit = Label(); + Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs)); + Operand count = context.Load(OperandType.I32, countAddr); context.BranchIfTrue(lblNonZero, count, BasicBlockFrequency.Cold); Operand running = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.CheckSynchronization))); - context.BranchIfTrue(lblExit, running, BasicBlockFrequency.Cold); context.Return(Const(0L)); context.MarkLabel(lblNonZero); - count = context.Subtract(count, Const(1)); - context.Store(countAddr, count); context.MarkLabel(lblExit); @@ -404,9 +448,18 @@ namespace ARMeilleure.Translation // TODO: Completely remove functions overlapping the specified range from the cache. } + internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode) + { + if (_backgroundSet.TryAdd(guestAddress, null)) + { + _backgroundStack.Push(new RejitRequest(guestAddress, mode)); + _backgroundTranslatorEvent.Set(); + } + } + private void EnqueueForDeletion(ulong guestAddress, TranslatedFunction func) { - _oldFuncs.Enqueue(new KeyValuePair<ulong, IntPtr>(guestAddress, func.FuncPtr)); + _oldFuncs.Enqueue(new(guestAddress, func)); } private void ClearJitCache() @@ -414,16 +467,20 @@ namespace ARMeilleure.Translation // Ensure no attempt will be made to compile new functions due to rejit. ClearRejitQueue(allowRequeue: false); - foreach (var kv in _funcs) + foreach (var func in _funcs.Values) { - JitCache.Unmap(kv.Value.FuncPtr); + JitCache.Unmap(func.FuncPtr); + + func.CallCounter?.Dispose(); } _funcs.Clear(); while (_oldFuncs.TryDequeue(out var kv)) { - JitCache.Unmap(kv.Value); + JitCache.Unmap(kv.Value.FuncPtr); + + kv.Value.CallCounter?.Dispose(); } } @@ -435,10 +492,12 @@ namespace ARMeilleure.Translation { while (_backgroundStack.TryPop(out var request)) { - if (_funcs.TryGetValue(request.Address, out var func)) + if (_funcs.TryGetValue(request.Address, out var func) && func.CallCounter != null) { - func.ResetCallCount(); + Volatile.Write(ref func.CallCounter.Value, 0); } + + _backgroundSet.TryRemove(request.Address, out _); } } else diff --git a/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj new file mode 100644 index 00000000..6619a500 --- /dev/null +++ b/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" /> + <ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs b/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs new file mode 100644 index 00000000..71ef414a --- /dev/null +++ b/Ryujinx.Audio.Backends.SDL2/SDL2AudioBuffer.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Audio.Backends.SDL2 +{ + class SDL2AudioBuffer + { + public readonly ulong DriverIdentifier; + public readonly ulong SampleCount; + public ulong SamplePlayed; + + public SDL2AudioBuffer(ulong driverIdentifier, ulong sampleCount) + { + DriverIdentifier = driverIdentifier; + SampleCount = sampleCount; + SamplePlayed = 0; + } + } +} diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs new file mode 100644 index 00000000..07131d1d --- /dev/null +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -0,0 +1,184 @@ +using Ryujinx.Audio.Backends.Common; +using Ryujinx.Audio.Common; +using Ryujinx.Audio.Integration; +using Ryujinx.Memory; +using Ryujinx.SDL2.Common; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; + +using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; +using static SDL2.SDL; + +namespace Ryujinx.Audio.Backends.SDL2 +{ + public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver + { + private object _lock = new object(); + + private ManualResetEvent _updateRequiredEvent; + private List<SDL2HardwareDeviceSession> _sessions; + + public SDL2HardwareDeviceDriver() + { + _updateRequiredEvent = new ManualResetEvent(false); + _sessions = new List<SDL2HardwareDeviceSession>(); + + SDL2Driver.Instance.Initialize(); + } + + public static bool IsSupported => IsSupportedInternal(); + + private static bool IsSupportedInternal() + { + uint device = OpenStream(SampleFormat.PcmInt16, Constants.TargetSampleRate, Constants.ChannelCountMax, Constants.TargetSampleCount, null); + + if (device != 0) + { + SDL_CloseAudioDevice(device); + } + + return device != 0; + } + + public ManualResetEvent GetUpdateRequiredEvent() + { + return _updateRequiredEvent; + } + + public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount) + { + if (channelCount == 0) + { + channelCount = 2; + } + + if (sampleRate == 0) + { + sampleRate = Constants.TargetSampleRate; + } + + if (direction != Direction.Output) + { + throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!"); + } + + lock (_lock) + { + SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount); + + _sessions.Add(session); + + return session; + } + } + + internal void Unregister(SDL2HardwareDeviceSession session) + { + lock (_lock) + { + _sessions.Remove(session); + } + } + + private static SDL_AudioSpec GetSDL2Spec(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount) + { + return new SDL_AudioSpec + { + channels = (byte)requestedChannelCount, + format = GetSDL2Format(requestedSampleFormat), + freq = (int)requestedSampleRate, + samples = (ushort)sampleCount + }; + } + + internal static ushort GetSDL2Format(SampleFormat format) + { + return format switch + { + SampleFormat.PcmInt8 => AUDIO_S8, + SampleFormat.PcmInt16 => AUDIO_S16, + SampleFormat.PcmInt32 => AUDIO_S32, + SampleFormat.PcmFloat => AUDIO_F32, + _ => throw new ArgumentException($"Unsupported sample format {format}"), + }; + } + + // TODO: Fix this in SDL2-CS. + [DllImport("SDL2", EntryPoint = "SDL_OpenAudioDevice", CallingConvention = CallingConvention.Cdecl)] + private static extern uint SDL_OpenAudioDevice_Workaround( + IntPtr name, + int iscapture, + ref SDL_AudioSpec desired, + out SDL_AudioSpec obtained, + uint allowed_changes + ); + + internal static uint OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, uint sampleCount, SDL_AudioCallback callback) + { + SDL_AudioSpec desired = GetSDL2Spec(requestedSampleFormat, requestedSampleRate, requestedChannelCount, sampleCount); + + desired.callback = callback; + + uint device = SDL_OpenAudioDevice_Workaround(IntPtr.Zero, 0, ref desired, out SDL_AudioSpec got, 0); + + if (device == 0) + { + return 0; + } + + bool isValid = got.format == desired.format && got.freq == desired.freq && got.channels == desired.channels; + + if (!isValid) + { + SDL_CloseAudioDevice(device); + + return 0; + } + + return device; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + while (_sessions.Count > 0) + { + SDL2HardwareDeviceSession session = _sessions[_sessions.Count - 1]; + + session.Dispose(); + } + + SDL2Driver.Instance.Dispose(); + } + } + + public bool SupportsSampleRate(uint sampleRate) + { + return true; + } + + public bool SupportsSampleFormat(SampleFormat sampleFormat) + { + return sampleFormat != SampleFormat.PcmInt24; + } + + public bool SupportsChannelCount(uint channelCount) + { + return true; + } + + public bool SupportsDirection(Direction direction) + { + // TODO: add direction input when supported. + return direction == Direction.Output; + } + } +} diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs new file mode 100644 index 00000000..344dd9b6 --- /dev/null +++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -0,0 +1,223 @@ +using Ryujinx.Audio.Backends.Common; +using Ryujinx.Audio.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Memory; +using System; +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Threading; + +using static SDL2.SDL; + +namespace Ryujinx.Audio.Backends.SDL2 +{ + class SDL2HardwareDeviceSession : HardwareDeviceSessionOutputBase + { + private SDL2HardwareDeviceDriver _driver; + private ConcurrentQueue<SDL2AudioBuffer> _queuedBuffers; + private DynamicRingBuffer _ringBuffer; + private ulong _playedSampleCount; + private ManualResetEvent _updateRequiredEvent; + private uint _outputStream; + private SDL_AudioCallback _callbackDelegate; + private int _bytesPerFrame; + private uint _sampleCount; + private bool _started; + private float _volume; + private ushort _nativeSampleFormat; + + public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) + { + _driver = driver; + _updateRequiredEvent = _driver.GetUpdateRequiredEvent(); + _queuedBuffers = new ConcurrentQueue<SDL2AudioBuffer>(); + _ringBuffer = new DynamicRingBuffer(); + _callbackDelegate = Update; + _bytesPerFrame = BackendHelper.GetSampleSize(RequestedSampleFormat) * (int)RequestedChannelCount; + _nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat); + _sampleCount = uint.MaxValue; + _started = false; + _volume = 1.0f; + } + + private void EnsureAudioStreamSetup(AudioBuffer buffer) + { + bool needAudioSetup = _outputStream == 0 || ((uint)GetSampleCount(buffer) % _sampleCount) != 0; + + if (needAudioSetup) + { + _sampleCount = Math.Max(Constants.TargetSampleCount, (uint)GetSampleCount(buffer)); + + uint newOutputStream = SDL2HardwareDeviceDriver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount, _sampleCount, _callbackDelegate); + + if (newOutputStream == 0) + { + // No stream in place, this is unexpected. + throw new InvalidOperationException($"OpenStream failed with error: \"{SDL_GetError()}\""); + } + else + { + if (_outputStream != 0) + { + SDL_CloseAudioDevice(_outputStream); + } + + _outputStream = newOutputStream; + + SDL_PauseAudioDevice(_outputStream, _started ? 0 : 1); + + Logger.Info?.Print(LogClass.Audio, $"New audio stream setup with a target sample count of {_sampleCount}"); + } + } + } + + // TODO: Add this variant with pointer to SDL2-CS. + [DllImport("SDL2", EntryPoint = "SDL_MixAudioFormat", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe uint SDL_MixAudioFormat(IntPtr dst, IntPtr src, ushort format, uint len, int volume); + + private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength) + { + Span<byte> streamSpan = new Span<byte>((void*)stream, streamLength); + + int maxFrameCount = (int)GetSampleCount(streamLength); + int bufferedFrames = _ringBuffer.Length / _bytesPerFrame; + + int frameCount = Math.Min(bufferedFrames, maxFrameCount); + + if (frameCount == 0) + { + // SDL2 left the responsability to the user to clear the buffer. + streamSpan.Fill(0); + + return; + } + + byte[] samples = new byte[frameCount * _bytesPerFrame]; + + _ringBuffer.Read(samples, 0, samples.Length); + + samples.AsSpan().CopyTo(streamSpan); + streamSpan.Slice(samples.Length).Fill(0); + + // Apply volume to written data + SDL_MixAudioFormat(stream, stream, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME)); + + ulong sampleCount = GetSampleCount(samples.Length); + + ulong availaibleSampleCount = sampleCount; + + bool needUpdate = false; + + while (availaibleSampleCount > 0 && _queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer)) + { + ulong sampleStillNeeded = driverBuffer.SampleCount - Interlocked.Read(ref driverBuffer.SamplePlayed); + ulong playedAudioBufferSampleCount = Math.Min(sampleStillNeeded, availaibleSampleCount); + + ulong currentSamplePlayed = Interlocked.Add(ref driverBuffer.SamplePlayed, playedAudioBufferSampleCount); + availaibleSampleCount -= playedAudioBufferSampleCount; + + if (currentSamplePlayed == driverBuffer.SampleCount) + { + _queuedBuffers.TryDequeue(out _); + + needUpdate = true; + } + + Interlocked.Add(ref _playedSampleCount, playedAudioBufferSampleCount); + } + + // Notify the output if needed. + if (needUpdate) + { + _updateRequiredEvent.Set(); + } + } + + public override ulong GetPlayedSampleCount() + { + return Interlocked.Read(ref _playedSampleCount); + } + + public override float GetVolume() + { + return _volume; + } + + public override void PrepareToClose() { } + + public override void QueueBuffer(AudioBuffer buffer) + { + EnsureAudioStreamSetup(buffer); + + SDL2AudioBuffer driverBuffer = new SDL2AudioBuffer(buffer.DataPointer, GetSampleCount(buffer)); + + _ringBuffer.Write(buffer.Data, 0, buffer.Data.Length); + + _queuedBuffers.Enqueue(driverBuffer); + } + + public override void SetVolume(float volume) + { + _volume = volume; + } + + public override void Start() + { + if (!_started) + { + if (_outputStream != 0) + { + SDL_PauseAudioDevice(_outputStream, 0); + } + + _started = true; + } + } + + public override void Stop() + { + if (_started) + { + if (_outputStream != 0) + { + SDL_PauseAudioDevice(_outputStream, 1); + } + + _started = false; + } + } + + public override void UnregisterBuffer(AudioBuffer buffer) { } + + public override bool WasBufferFullyConsumed(AudioBuffer buffer) + { + if (!_queuedBuffers.TryPeek(out SDL2AudioBuffer driverBuffer)) + { + return true; + } + + return driverBuffer.DriverIdentifier != buffer.DataPointer; + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + PrepareToClose(); + Stop(); + + if (_outputStream != 0) + { + SDL_CloseAudioDevice(_outputStream); + } + + _driver.Unregister(this); + } + } + + public override void Dispose() + { + Dispose(true); + } + } +} diff --git a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs index 1e000e4c..f1f0039c 100644 --- a/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs +++ b/Ryujinx.Audio/Backends/Common/HardwareDeviceSessionOutputBase.cs @@ -18,6 +18,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Integration; using Ryujinx.Memory; +using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Backends.Common { @@ -52,7 +53,13 @@ namespace Ryujinx.Audio.Backends.Common protected ulong GetSampleCount(AudioBuffer buffer) { - return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, (int)buffer.DataSize); + return GetSampleCount((int)buffer.DataSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected ulong GetSampleCount(int dataSize) + { + return (ulong)BackendHelper.GetSampleCount(RequestedSampleFormat, (int)RequestedChannelCount, dataSize); } public abstract void Dispose(); diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 1597805c..28b344cd 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Common.Logging Cpu, Font, Emulation, + FFmpeg, Gpu, Hid, Host1x, diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs index 7c400e91..6194d5b2 100644 --- a/Ryujinx.Cpu/MemoryHelper.cs +++ b/Ryujinx.Cpu/MemoryHelper.cs @@ -8,28 +8,28 @@ namespace Ryujinx.Cpu { public static class MemoryHelper { - public static void FillWithZeros(IVirtualMemoryManager memory, long position, int size) + public static void FillWithZeros(IVirtualMemoryManager memory, ulong position, int size) { int size8 = size & ~(8 - 1); for (int offs = 0; offs < size8; offs += 8) { - memory.Write<long>((ulong)(position + offs), 0); + memory.Write<long>(position + (ulong)offs, 0); } for (int offs = size8; offs < (size - size8); offs++) { - memory.Write<byte>((ulong)(position + offs), 0); + memory.Write<byte>(position + (ulong)offs, 0); } } - public unsafe static T Read<T>(IVirtualMemoryManager memory, long position) where T : struct + public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : struct { long size = Marshal.SizeOf<T>(); byte[] data = new byte[size]; - memory.Read((ulong)position, data); + memory.Read(position, data); fixed (byte* ptr = data) { @@ -37,7 +37,7 @@ namespace Ryujinx.Cpu } } - public unsafe static long Write<T>(IVirtualMemoryManager memory, long position, T value) where T : struct + public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : struct { long size = Marshal.SizeOf<T>(); @@ -48,18 +48,18 @@ namespace Ryujinx.Cpu Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false); } - memory.Write((ulong)position, data); + memory.Write(position, data); - return size; + return (ulong)size; } - public static string ReadAsciiString(IVirtualMemoryManager memory, long position, long maxSize = -1) + public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1) { using (MemoryStream ms = new MemoryStream()) { for (long offs = 0; offs < maxSize || maxSize == -1; offs++) { - byte value = memory.Read<byte>((ulong)(position + offs)); + byte value = memory.Read<byte>(position + (ulong)offs); if (value == 0) { diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs index d496de67..e388f0e5 100644 --- a/Ryujinx.Graphics.GAL/Capabilities.cs +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.GAL public bool SupportsAstcCompression { get; } public bool SupportsImageLoadFormatted { get; } public bool SupportsNonConstantTextureOffset { get; } + public bool SupportsMismatchingViewFormat { get; } public bool SupportsViewportSwizzle { get; } public int MaximumComputeSharedMemorySize { get; } @@ -15,6 +16,7 @@ namespace Ryujinx.Graphics.GAL bool supportsAstcCompression, bool supportsImageLoadFormatted, bool supportsNonConstantTextureOffset, + bool supportsMismatchingViewFormat, bool supportsViewportSwizzle, int maximumComputeSharedMemorySize, float maximumSupportedAnisotropy, @@ -23,6 +25,7 @@ namespace Ryujinx.Graphics.GAL SupportsAstcCompression = supportsAstcCompression; SupportsImageLoadFormatted = supportsImageLoadFormatted; SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset; + SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsViewportSwizzle = supportsViewportSwizzle; MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumSupportedAnisotropy = maximumSupportedAnisotropy; diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 033311b2..ae9bdb0d 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine private bool _isAnyVbInstanced; private bool _vsUsesInstanceId; + private byte _vsClipDistancesWritten; private bool _forceShaderUpdate; @@ -993,7 +994,15 @@ namespace Ryujinx.Graphics.Gpu.Engine ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses); - _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false; + byte oldVsClipDistancesWritten = _vsClipDistancesWritten; + + _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false; + _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0; + + if (oldVsClipDistancesWritten != _vsClipDistancesWritten) + { + UpdateUserClipState(state); + } int storageBufferBindingsCount = 0; int uniformBufferBindingsCount = 0; @@ -1098,7 +1107,7 @@ namespace Ryujinx.Graphics.Gpu.Engine /// <param name="state">Current GPU state</param> private void UpdateUserClipState(GpuState state) { - int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable); + int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable) & _vsClipDistancesWritten; for (int i = 0; i < Constants.TotalClipDistances; ++i) { diff --git a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs index af980e77..7ef102e2 100644 --- a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs +++ b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu /// <summary> /// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide. /// </summary> - public static float MaxAnisotropy; + public static float MaxAnisotropy = -1; /// <summary> /// Base directory used to write shader code dumps. diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 6d2510f1..4d7e31c5 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1014,6 +1014,15 @@ namespace Ryujinx.Graphics.Gpu.Image result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel)); + if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat) + { + // AMD and Intel have a bug where the view format is always ignored; + // they use the parent format instead. + // Create a copy dependency to avoid this issue. + + result = TextureViewCompatibility.CopyOnly; + } + return (Info.SamplesInX == info.SamplesInX && Info.SamplesInY == info.SamplesInY) ? result : TextureViewCompatibility.Incompatible; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 7fea7ebe..ae610a76 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -398,10 +398,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="state">The current GPU state</param> /// <param name="stageIndex">The stage number where the texture is bound</param> /// <param name="handle">The texture handle</param> + /// <param name="cbufSlot">The texture handle's constant buffer slot</param> /// <returns>The texture descriptor for the specified texture</returns> - public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle) + public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle, int cbufSlot) { - int packedId = ReadPackedId(stageIndex, handle, state.Get<int>(MethodOffset.TextureBufferIndex)); + int packedId = ReadPackedId(stageIndex, handle, cbufSlot < 0 ? state.Get<int>(MethodOffset.TextureBufferIndex) : cbufSlot); int textureId = UnpackTextureId(packedId); var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 17bb553f..e08e55ee 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -340,10 +340,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// </summary> /// <param name="state">Current GPU state</param> /// <param name="handle">Shader "fake" handle of the texture</param> + /// <param name="cbufSlot">Shader constant buffer slot of the texture</param> /// <returns>The texture descriptor</returns> - public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle) + public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle, int cbufSlot) { - return _cpBindingsManager.GetTextureDescriptor(state, 0, handle); + return _cpBindingsManager.GetTextureDescriptor(state, 0, handle, cbufSlot); } /// <summary> @@ -352,10 +353,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// <param name="state">Current GPU state</param> /// <param name="stageIndex">Index of the shader stage where the texture is bound</param> /// <param name="handle">Shader "fake" handle of the texture</param> + /// <param name="cbufSlot">Shader constant buffer slot of the texture</param> /// <returns>The texture descriptor</returns> - public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) + public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle, int cbufSlot) { - return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle); + return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle, cbufSlot); } /// <summary> @@ -1297,4 +1299,4 @@ namespace Ryujinx.Graphics.Gpu.Image } } } -}
\ No newline at end of file +} diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs index 1ec4ab74..f6caddef 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs @@ -417,7 +417,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache { foreach (int textureHandle in context.TextureHandlesForCache) { - GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle)).ToCache(); + GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle, -1)).ToCache(); textureDescriptor.Handle = (uint)textureHandle; diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs index f592919f..b538e2de 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntry.cs @@ -75,7 +75,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition programInfo.SBuffers.Count, programInfo.Textures.Count, programInfo.Images.Count, - programInfo.UsesInstanceId); + programInfo.UsesInstanceId, + programInfo.ClipDistancesWritten); CBuffers = programInfo.CBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray(); Textures = programInfo.Textures.ToArray(); @@ -88,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition /// <returns>A new <see cref="ShaderProgramInfo"/> from this instance</returns> internal ShaderProgramInfo ToShaderProgramInfo() { - return new ShaderProgramInfo(CBuffers, SBuffers, Textures, Images, Header.UsesInstanceId); + return new ShaderProgramInfo(CBuffers, SBuffers, Textures, Images, Header.UsesInstanceId, Header.ClipDistancesWritten); } /// <summary> diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs index 9b1af8fb..7f27124f 100644 --- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs +++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/HostShaderCacheEntryHeader.cs @@ -42,9 +42,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition public bool InUse; /// <summary> + /// Mask of clip distances that are written to on the shader. + /// </summary> + public byte ClipDistancesWritten; + + /// <summary> /// Reserved / unused. /// </summary> - public short Reserved; + public byte Reserved; /// <summary> /// Create a new host shader cache entry header. @@ -54,14 +59,21 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition /// <param name="texturesCount">Count of texture descriptors</param> /// <param name="imagesCount">Count of image descriptors</param> /// <param name="usesInstanceId">Set to true if the shader uses instance id</param> - public HostShaderCacheEntryHeader(int cBuffersCount, int sBuffersCount, int texturesCount, int imagesCount, bool usesInstanceId) : this() + public HostShaderCacheEntryHeader( + int cBuffersCount, + int sBuffersCount, + int texturesCount, + int imagesCount, + bool usesInstanceId, + byte clipDistancesWritten) : this() { - CBuffersCount = cBuffersCount; - SBuffersCount = sBuffersCount; - TexturesCount = texturesCount; - ImagesCount = imagesCount; - UsesInstanceId = usesInstanceId; - InUse = true; + CBuffersCount = cBuffersCount; + SBuffersCount = sBuffersCount; + TexturesCount = texturesCount; + ImagesCount = imagesCount; + UsesInstanceId = usesInstanceId; + ClipDistancesWritten = clipDistancesWritten; + InUse = true; } } } diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs index f714d97b..a7bd4edb 100644 --- a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs @@ -140,8 +140,9 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Gets the texture descriptor for a given texture on the pool. /// </summary> /// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param> + /// <param name="cbufSlot">Constant buffer slot for the texture handle</param> /// <returns>Texture descriptor</returns> - public override Image.ITextureDescriptor GetTextureDescriptor(int handle) + public override Image.ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot) { if (!_textureDescriptors.TryGetValue(handle, out GuestTextureDescriptor textureDescriptor)) { diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index c0ad481e..f5373bd6 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -184,16 +184,17 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Gets the texture descriptor for a given texture on the pool. /// </summary> /// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param> + /// <param name="cbufSlot">Constant buffer slot for the texture handle</param> /// <returns>Texture descriptor</returns> - public override Image.ITextureDescriptor GetTextureDescriptor(int handle) + public override Image.ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot) { if (_compute) { - return _context.Methods.TextureManager.GetComputeTextureDescriptor(_state, handle); + return _context.Methods.TextureManager.GetComputeTextureDescriptor(_state, handle, cbufSlot); } else { - return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(_state, _stageIndex, handle); + return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(_state, _stageIndex, handle, cbufSlot); } } diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index bfd46c82..f6dcf052 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// <summary> /// Version of the codegen (to be changed when codegen or guest format change). /// </summary> - private const ulong ShaderCodeGenVersion = 2200; + private const ulong ShaderCodeGenVersion = 2261; // Progress reporting helpers private volatile int _shaderCount; diff --git a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs index dc0e392b..904a0fd4 100644 --- a/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/TextureDescriptorCapableGpuAccessor.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { public abstract T MemoryRead<T>(ulong address) where T : unmanaged; - public abstract ITextureDescriptor GetTextureDescriptor(int handle); + public abstract ITextureDescriptor GetTextureDescriptor(int handle, int cbufSlot); /// <summary> /// Queries texture format information, for shaders using image load or store. @@ -18,10 +18,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// If the format of the texture is a compressed, depth or unsupported format, then a default value is returned. /// </remarks> /// <param name="handle">Texture handle</param> + /// <param name="cbufSlot">Constant buffer slot for the texture handle</param> /// <returns>Color format of the non-compressed texture</returns> - public TextureFormat QueryTextureFormat(int handle) + public TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1) { - var descriptor = GetTextureDescriptor(handle); + var descriptor = GetTextureDescriptor(handle, cbufSlot); if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo)) { @@ -78,20 +79,22 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Queries texture target information. /// </summary> /// <param name="handle">Texture handle</param> + /// <param name="cbufSlot">Constant buffer slot for the texture handle</param> /// <returns>True if the texture is a buffer texture, false otherwise</returns> - public bool QueryIsTextureBuffer(int handle) + public bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) { - return GetTextureDescriptor(handle).UnpackTextureTarget() == TextureTarget.TextureBuffer; + return GetTextureDescriptor(handle, cbufSlot).UnpackTextureTarget() == TextureTarget.TextureBuffer; } /// <summary> /// Queries texture target information. /// </summary> /// <param name="handle">Texture handle</param> + /// <param name="cbufSlot">Constant buffer slot for the texture handle</param> /// <returns>True if the texture is a rectangle texture, false otherwise</returns> - public bool QueryIsTextureRectangle(int handle) + public bool QueryIsTextureRectangle(int handle, int cbufSlot = -1) { - var descriptor = GetTextureDescriptor(handle); + var descriptor = GetTextureDescriptor(handle, cbufSlot); TextureTarget target = descriptor.UnpackTextureTarget(); diff --git a/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs b/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs index b4f9206b..c402c574 100644 --- a/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs +++ b/Ryujinx.Graphics.Nvdec.H264/FFmpegContext.cs @@ -1,33 +1,80 @@ using FFmpeg.AutoGen; +using Ryujinx.Common.Logging; using System; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Nvdec.H264 { unsafe class FFmpegContext : IDisposable { + private readonly av_log_set_callback_callback _logFunc; private readonly AVCodec* _codec; + private AVPacket* _packet; private AVCodecContext* _context; public FFmpegContext() { + _logFunc = Log; + + // Redirect log output + ffmpeg.av_log_set_level(ffmpeg.AV_LOG_MAX_OFFSET); + ffmpeg.av_log_set_callback(_logFunc); + _codec = ffmpeg.avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264); _context = ffmpeg.avcodec_alloc_context3(_codec); ffmpeg.avcodec_open2(_context, _codec, null); + + _packet = ffmpeg.av_packet_alloc(); } - public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream) + private void Log(void* p0, int level, string format, byte* vl) { - AVPacket packet; + if (level > ffmpeg.av_log_get_level()) + { + return; + } + + int lineSize = 1024; + byte* lineBuffer = stackalloc byte[lineSize]; + int printPrefix = 1; + + ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix); + + string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer).Trim(); - ffmpeg.av_init_packet(&packet); + switch (level) + { + case ffmpeg.AV_LOG_PANIC: + case ffmpeg.AV_LOG_FATAL: + case ffmpeg.AV_LOG_ERROR: + Logger.Error?.Print(LogClass.FFmpeg, line); + break; + case ffmpeg.AV_LOG_WARNING: + Logger.Warning?.Print(LogClass.FFmpeg, line); + break; + case ffmpeg.AV_LOG_INFO: + Logger.Info?.Print(LogClass.FFmpeg, line); + break; + case ffmpeg.AV_LOG_VERBOSE: + case ffmpeg.AV_LOG_DEBUG: + case ffmpeg.AV_LOG_TRACE: + Logger.Debug?.Print(LogClass.FFmpeg, line); + break; + } + } + + public int DecodeFrame(Surface output, ReadOnlySpan<byte> bitstream) + { + // Ensure the packet is clean before proceeding + ffmpeg.av_packet_unref(_packet); fixed (byte* ptr = bitstream) { - packet.data = ptr; - packet.size = bitstream.Length; + _packet->data = ptr; + _packet->size = bitstream.Length; - int rc = ffmpeg.avcodec_send_packet(_context, &packet); + int rc = ffmpeg.avcodec_send_packet(_context, _packet); if (rc != 0) { @@ -40,6 +87,11 @@ namespace Ryujinx.Graphics.Nvdec.H264 public void Dispose() { + fixed (AVPacket** ppPacket = &_packet) + { + ffmpeg.av_packet_free(ppPacket); + } + ffmpeg.avcodec_close(_context); fixed (AVCodecContext** ppContext = &_context) diff --git a/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj b/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj index 2d54173c..fdcdae06 100644 --- a/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj +++ b/Ryujinx.Graphics.Nvdec.H264/Ryujinx.Graphics.Nvdec.H264.csproj @@ -6,7 +6,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="FFmpeg.AutoGen" Version="4.3.0" /> + <PackageReference Include="FFmpeg.AutoGen" Version="4.4.0" /> </ItemGroup> <ItemGroup> diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index e6b70376..76b321b7 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -40,15 +40,7 @@ namespace Ryujinx.Graphics.OpenGL FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index; - if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd || - HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows) - { - GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.GetIncompatibleFormatViewHandle() ?? 0, 0); - } - else - { - GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0); - } + GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0); _colors[index] = color; } @@ -92,21 +84,6 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SignalModified() - { - if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd || - HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows) - { - for (int i = 0; i < 8; i++) - { - if (_colors[i] != null) - { - _colors[i].SignalModified(); - } - } - } - } - public void SetDualSourceBlend(bool enable) { bool oldEnable = _dualSourceBlend; diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index 08b0e6af..c994113d 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.OpenGL public static bool SupportsParallelShaderCompile => _supportsParallelShaderCompile.Value; public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia; public static bool RequiresSyncFlush => _gpuVendor.Value == GpuVendor.Amd || _gpuVendor.Value == GpuVendor.IntelWindows || _gpuVendor.Value == GpuVendor.IntelUnix; + public static bool SupportsMismatchingViewFormat => _gpuVendor.Value != GpuVendor.Amd && _gpuVendor.Value != GpuVendor.IntelWindows; public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value; public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value; diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index e8c3e3be..8e8d3c78 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -10,8 +10,6 @@ namespace Ryujinx.Graphics.OpenGL.Image private readonly TextureStorage _parent; - private TextureView _incompatibleFormatView; - public ITextureInfo Storage => _parent; public int FirstLayer { get; private set; } @@ -102,35 +100,6 @@ namespace Ryujinx.Graphics.OpenGL.Image return _parent.CreateView(info, firstLayer, firstLevel); } - public int GetIncompatibleFormatViewHandle() - { - // AMD and Intel have a bug where the view format is always ignored; - // they use the parent format instead. - // As a workaround we create a new texture with the correct - // format, and then do a copy after the draw. - if (_parent.Info.Format != Format) - { - if (_incompatibleFormatView == null) - { - _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info, ScaleFactor); - } - - _renderer.TextureCopy.CopyUnscaled(_parent, _incompatibleFormatView, FirstLayer, 0, FirstLevel, 0); - - return _incompatibleFormatView.Handle; - } - - return Handle; - } - - public void SignalModified() - { - if (_incompatibleFormatView != null) - { - _renderer.TextureCopy.CopyUnscaled(_incompatibleFormatView, _parent, 0, FirstLayer, 0, FirstLevel); - } - } - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) { TextureView destinationView = (TextureView)destination; @@ -634,13 +603,6 @@ namespace Ryujinx.Graphics.OpenGL.Image private void DisposeHandles() { - if (_incompatibleFormatView != null) - { - _incompatibleFormatView.Dispose(); - - _incompatibleFormatView = null; - } - if (Handle != 0) { GL.DeleteTexture(Handle); diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 28478a72..fcb0fdf1 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -110,8 +110,6 @@ namespace Ryujinx.Graphics.OpenGL GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors); RestoreComponentMask(index); - - _framebuffer.SignalModified(); } public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) @@ -154,8 +152,6 @@ namespace Ryujinx.Graphics.OpenGL { GL.DepthMask(_depthMask); } - - _framebuffer.SignalModified(); } public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) @@ -1224,8 +1220,6 @@ namespace Ryujinx.Graphics.OpenGL private void PostDraw() { - _framebuffer?.SignalModified(); - if (_tfEnabled) { for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++) diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 0382ba86..7416cdd7 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.OpenGL HwCapabilities.SupportsAstcCompression, HwCapabilities.SupportsImageLoadFormatted, HwCapabilities.SupportsNonConstantTextureOffset, + HwCapabilities.SupportsMismatchingViewFormat, HwCapabilities.SupportsViewportSwizzle, HwCapabilities.MaximumComputeSharedMemorySize, HwCapabilities.MaximumSupportedAnisotropy, diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index 622ac646..0ea7f151 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -61,8 +61,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions switch (memRegion) { - case Instruction.MrShared: args += LoadShared (context, operation); break; - case Instruction.MrStorage: args += LoadStorage(context, operation); break; + case Instruction.MrShared: args += LoadShared(context, operation); break; + case Instruction.MrStorage: args += LoadStorage(context, operation, forAtomic: true); break; default: throw new InvalidOperationException($"Invalid memory region \"{memRegion}\"."); } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index fc5b06b1..273aaef8 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -205,7 +205,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions return $"{arrayName}[{offsetExpr}]"; } - public static string LoadStorage(CodeGenContext context, AstOperation operation) + public static string LoadStorage(CodeGenContext context, AstOperation operation, bool forAtomic = false) { IAstNode src1 = operation.GetSource(0); IAstNode src2 = operation.GetSource(1); @@ -213,6 +213,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + if (forAtomic) + { + SetStorageWriteFlag(context, src1, context.Config.Stage); + } + return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); } @@ -485,7 +490,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { flags |= TextureUsageFlags.ResScaleUnsupported; } - } + } else { // Resolution scaling cannot be applied to this texture right now. diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs index fe034c57..1e05cdcd 100644 --- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs +++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs @@ -44,12 +44,12 @@ return 0xffff; } - bool QueryIsTextureBuffer(int handle) + bool QueryIsTextureBuffer(int handle, int cbufSlot = -1) { return false; } - bool QueryIsTextureRectangle(int handle) + bool QueryIsTextureRectangle(int handle, int cbufSlot = -1) { return false; } @@ -74,7 +74,7 @@ return true; } - TextureFormat QueryTextureFormat(int handle) + TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1) { return TextureFormat.R8G8B8A8Unorm; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs index 81d5c7af..7afdbf4e 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.Shader.Instructions Operand dest = Attribute(op.AttributeOffset + index * 4); + context.FlagAttributeWritten(dest.Value); + context.Copy(dest, Register(rd)); } } diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs index 2324fac2..9329442f 100644 --- a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -11,13 +11,15 @@ namespace Ryujinx.Graphics.Shader public ReadOnlyCollection<TextureDescriptor> Images { get; } public bool UsesInstanceId { get; } + public byte ClipDistancesWritten { get; } public ShaderProgramInfo( BufferDescriptor[] cBuffers, BufferDescriptor[] sBuffers, TextureDescriptor[] textures, TextureDescriptor[] images, - bool usesInstanceId) + bool usesInstanceId, + byte clipDistancesWritten) { CBuffers = Array.AsReadOnly(cBuffers); SBuffers = Array.AsReadOnly(sBuffers); @@ -25,6 +27,7 @@ namespace Ryujinx.Graphics.Shader Images = Array.AsReadOnly(images); UsesInstanceId = usesInstanceId; + ClipDistancesWritten = clipDistancesWritten; } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 2667be1d..c4d8b85f 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -291,10 +291,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { Info.IAttributes.Add(attrIndex); } - else if (operand.Type == OperandType.Attribute && operand.Value == AttributeConsts.InstanceId) - { - Info.UsesInstanceId = true; - } else if (operand.Type == OperandType.ConstantBuffer) { Info.CBuffers.Add(operand.GetCbufSlot()); diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index 16a27f51..d1619bfa 100644 --- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -12,7 +12,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HashSet<int> IAttributes { get; } public HashSet<int> OAttributes { get; } - public bool UsesInstanceId { get; set; } public bool UsesCbIndexing { get; set; } public HelperFunctionsMask HelperFunctionsMask { get; set; } diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index a4c21c1d..9b220177 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -55,7 +55,11 @@ namespace Ryujinx.Graphics.Shader.Translation public void FlagAttributeRead(int attribute) { - if (Config.Stage == ShaderStage.Fragment) + if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) + { + Config.SetUsedFeature(FeatureFlags.InstanceId); + } + else if (Config.Stage == ShaderStage.Fragment) { switch (attribute) { @@ -67,6 +71,26 @@ namespace Ryujinx.Graphics.Shader.Translation } } + public void FlagAttributeWritten(int attribute) + { + if (Config.Stage == ShaderStage.Vertex) + { + switch (attribute) + { + case AttributeConsts.ClipDistance0: + case AttributeConsts.ClipDistance1: + case AttributeConsts.ClipDistance2: + case AttributeConsts.ClipDistance3: + case AttributeConsts.ClipDistance4: + case AttributeConsts.ClipDistance5: + case AttributeConsts.ClipDistance6: + case AttributeConsts.ClipDistance7: + Config.SetClipDistanceWritten((attribute - AttributeConsts.ClipDistance0) / 4); + break; + } + } + } + public void MarkLabel(Operand label) { Add(Instruction.MarkLabel, label); diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index d2b53f84..1b712896 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -12,9 +12,11 @@ namespace Ryujinx.Graphics.Shader.Translation None = 0, // Affected by resolution scaling. - FragCoordXY = 1 << 1, IntegerSampling = 1 << 0, + FragCoordXY = 1 << 1, + + Bindless = 1 << 2, - Bindless = 1 << 2, + InstanceId = 1 << 3 } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index f462cedb..f91a00eb 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (src0.Type == OperandType.ConstantBuffer) { texOp.SetHandle(src0.GetCbufOffset(), src0.GetCbufSlot()); - texOp.Format = config.GetTextureFormat(texOp.Handle); + texOp.Format = config.GetTextureFormat(texOp.Handle, texOp.CbufSlot); } } } diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index a928f935..5427c013 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Shader.Translation bool hasInvalidOffset = (hasOffset || hasOffsets) && !config.GpuAccessor.QuerySupportsNonConstantTextureOffset(); - bool isRect = config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle); + bool isRect = config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle, texOp.CbufSlot); if (!(hasInvalidOffset || isRect)) { @@ -433,7 +433,7 @@ namespace Ryujinx.Graphics.Shader.Translation { TextureOperation texOp = (TextureOperation)node.Value; - TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle); + TextureFormat format = config.GpuAccessor.QueryTextureFormat(texOp.Handle, texOp.CbufSlot); int maxPositive = format switch { diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index b1fd6470..077ce70d 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Shader.Translation public int Size { get; private set; } + public byte ClipDistancesWritten { get; private set; } + public FeatureFlags UsedFeatures { get; private set; } public HashSet<int> TextureHandlesForCache { get; } @@ -89,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Translation return count + 1; } - public TextureFormat GetTextureFormat(int handle) + public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1) { // When the formatted load extension is supported, we don't need to // specify a format, we can just declare it without a format and the GPU will handle it. @@ -98,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.Translation return TextureFormat.Unknown; } - var format = GpuAccessor.QueryTextureFormat(handle); + var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot); if (format == TextureFormat.Unknown) { @@ -115,6 +117,11 @@ namespace Ryujinx.Graphics.Shader.Translation Size += size; } + public void SetClipDistanceWritten(int index) + { + ClipDistancesWritten |= (byte)(1 << index); + } + public void SetUsedFeature(FeatureFlags flags) { UsedFeatures |= flags; diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 9f0f9010..1c15ccf2 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -94,7 +94,8 @@ namespace Ryujinx.Graphics.Shader.Translation program.SBufferDescriptors, program.TextureDescriptors, program.ImageDescriptors, - sInfo.UsesInstanceId); + config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), + config.ClipDistancesWritten); string glslCode = program.Code; diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs index 1630835d..a18838ab 100644 --- a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs @@ -24,8 +24,8 @@ namespace Ryujinx.HLE.FileSystem.Content private Dictionary<StorageId, LinkedList<LocationEntry>> _locationEntries; - private Dictionary<string, long> _sharedFontTitleDictionary; - private Dictionary<long, string> _systemTitlesNameDictionary; + private Dictionary<string, ulong> _sharedFontTitleDictionary; + private Dictionary<ulong, string> _systemTitlesNameDictionary; private Dictionary<string, string> _sharedFontFilenameDictionary; private SortedDictionary<(ulong titleId, NcaContentType type), string> _contentDictionary; @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.FileSystem.Content _contentDictionary = new SortedDictionary<(ulong, NcaContentType), string>(); _locationEntries = new Dictionary<StorageId, LinkedList<LocationEntry>>(); - _sharedFontTitleDictionary = new Dictionary<string, long> + _sharedFontTitleDictionary = new Dictionary<string, ulong> { { "FontStandard", 0x0100000000000811 }, { "FontChineseSimplified", 0x0100000000000814 }, @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.FileSystem.Content { "FontNintendoExtended", 0x0100000000000810 } }; - _systemTitlesNameDictionary = new Dictionary<long, string>() + _systemTitlesNameDictionary = new Dictionary<ulong, string>() { { 0x010000000000080E, "TimeZoneBinary" }, { 0x0100000000000810, "FontNintendoExtension" }, @@ -140,7 +140,7 @@ namespace Ryujinx.HLE.FileSystem.Content LocationEntry entry = new LocationEntry(switchPath, 0, - (long)nca.Header.TitleId, + nca.Header.TitleId, nca.Header.ContentType); AddEntry(entry); @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.FileSystem.Content LocationEntry entry = new LocationEntry(switchPath, 0, - (long)nca.Header.TitleId, + nca.Header.TitleId, nca.Header.ContentType); AddEntry(entry); @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.FileSystem.Content } // fs must contain AOC nca files in its root - public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId) + public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel) { _virtualFileSystem.ImportTickets(fs); @@ -214,7 +214,7 @@ namespace Ryujinx.HLE.FileSystem.Content continue; } - using var pfs0 = nca.OpenFileSystem(0, Switch.GetIntegrityCheckLevel()); + using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel); pfs0.OpenFile(out IFile cnmtFile, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read); @@ -265,7 +265,7 @@ namespace Ryujinx.HLE.FileSystem.Content public IList<ulong> GetAocTitleIds() => _aocData.Where(e => e.Value.Enabled).Select(e => e.Key).ToList(); - public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage) + public bool GetAocDataStorage(ulong aocTitleId, out IStorage aocStorage, IntegrityCheckLevel integrityCheckLevel) { aocStorage = null; @@ -289,7 +289,7 @@ namespace Ryujinx.HLE.FileSystem.Content return false; // Print error? } - aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()).OpenStorage(NcaSectionType.Data, Switch.GetIntegrityCheckLevel()); + aocStorage = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage()).OpenStorage(NcaSectionType.Data, integrityCheckLevel); return true; } @@ -297,7 +297,7 @@ namespace Ryujinx.HLE.FileSystem.Content return false; } - public void ClearEntry(long titleId, NcaContentType contentType, StorageId storageId) + public void ClearEntry(ulong titleId, NcaContentType contentType, StorageId storageId) { lock (_lock) { @@ -333,7 +333,7 @@ namespace Ryujinx.HLE.FileSystem.Content if (_contentDictionary.ContainsValue(ncaId)) { var content = _contentDictionary.FirstOrDefault(x => x.Value == ncaId); - long titleId = (long)content.Key.Item1; + ulong titleId = content.Key.Item1; NcaContentType contentType = content.Key.type; StorageId storage = GetInstalledStorage(titleId, contentType, storageId); @@ -345,20 +345,20 @@ namespace Ryujinx.HLE.FileSystem.Content return false; } - public UInt128 GetInstalledNcaId(long titleId, NcaContentType contentType) + public UInt128 GetInstalledNcaId(ulong titleId, NcaContentType contentType) { lock (_lock) { - if (_contentDictionary.ContainsKey(((ulong)titleId, contentType))) + if (_contentDictionary.ContainsKey((titleId, contentType))) { - return new UInt128(_contentDictionary[((ulong)titleId, contentType)]); + return new UInt128(_contentDictionary[(titleId, contentType)]); } } return new UInt128(); } - public StorageId GetInstalledStorage(long titleId, NcaContentType contentType, StorageId storageId) + public StorageId GetInstalledStorage(ulong titleId, NcaContentType contentType, StorageId storageId) { lock (_lock) { @@ -369,7 +369,7 @@ namespace Ryujinx.HLE.FileSystem.Content } } - public string GetInstalledContentPath(long titleId, StorageId storageId, NcaContentType contentType) + public string GetInstalledContentPath(ulong titleId, StorageId storageId, NcaContentType contentType) { lock (_lock) { @@ -445,7 +445,7 @@ namespace Ryujinx.HLE.FileSystem.Content } } - private void RemoveLocationEntry(long titleId, NcaContentType contentType, StorageId storageId) + private void RemoveLocationEntry(ulong titleId, NcaContentType contentType, StorageId storageId) { LinkedList<LocationEntry> locationList = null; @@ -466,7 +466,7 @@ namespace Ryujinx.HLE.FileSystem.Content } } - public bool TryGetFontTitle(string fontName, out long titleId) + public bool TryGetFontTitle(string fontName, out ulong titleId) { return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId); } @@ -476,12 +476,12 @@ namespace Ryujinx.HLE.FileSystem.Content return _sharedFontFilenameDictionary.TryGetValue(fontName, out filename); } - public bool TryGetSystemTitlesName(long titleId, out string name) + public bool TryGetSystemTitlesName(ulong titleId, out string name) { return _systemTitlesNameDictionary.TryGetValue(titleId, out name); } - private LocationEntry GetLocation(long titleId, NcaContentType contentType, StorageId storageId) + private LocationEntry GetLocation(ulong titleId, NcaContentType contentType, StorageId storageId) { LinkedList<LocationEntry> locationList = _locationEntries[storageId]; @@ -710,8 +710,6 @@ namespace Ryujinx.HLE.FileSystem.Content SystemVersion VerifyAndGetVersionZip(ZipArchive archive) { - IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel(); - SystemVersion systemVersion = null; foreach (var entry in archive.Entries) @@ -751,7 +749,7 @@ namespace Ryujinx.HLE.FileSystem.Content { Nca metaNca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -781,7 +779,7 @@ namespace Ryujinx.HLE.FileSystem.Content { Nca nca = new Nca(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { @@ -816,7 +814,7 @@ namespace Ryujinx.HLE.FileSystem.Content { Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -873,8 +871,6 @@ namespace Ryujinx.HLE.FileSystem.Content SystemVersion VerifyAndGetVersion(IFileSystem filesystem) { - IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel(); - SystemVersion systemVersion = null; CnmtContentMetaEntry[] metaEntries = null; @@ -887,7 +883,7 @@ namespace Ryujinx.HLE.FileSystem.Content if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta) { - IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -905,7 +901,7 @@ namespace Ryujinx.HLE.FileSystem.Content } else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { @@ -952,7 +948,7 @@ namespace Ryujinx.HLE.FileSystem.Content Nca metaNca = new Nca(_virtualFileSystem.KeySet, metaStorage); - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; @@ -1004,8 +1000,6 @@ namespace Ryujinx.HLE.FileSystem.Content public SystemVersion GetCurrentFirmwareVersion() { - IntegrityCheckLevel integrityCheckLevel = Switch.GetIntegrityCheckLevel(); - LoadEntries(); lock (_lock) @@ -1024,7 +1018,7 @@ namespace Ryujinx.HLE.FileSystem.Content if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, integrityCheckLevel); + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); if (romfs.OpenFile(out IFile systemVersionFile, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) { diff --git a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs index 15485148..cc259c3a 100644 --- a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs +++ b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs @@ -6,10 +6,10 @@ namespace Ryujinx.HLE.FileSystem.Content { public string ContentPath { get; private set; } public int Flag { get; private set; } - public long TitleId { get; private set; } + public ulong TitleId { get; private set; } public NcaContentType ContentType { get; private set; } - public LocationEntry(string contentPath, int flag, long titleId, NcaContentType contentType) + public LocationEntry(string contentPath, int flag, ulong titleId, NcaContentType contentType) { ContentPath = contentPath; Flag = flag; diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs new file mode 100644 index 00000000..00c79169 --- /dev/null +++ b/Ryujinx.HLE/HLEConfiguration.cs @@ -0,0 +1,179 @@ +using LibHac.FsSystem; +using Ryujinx.Audio.Integration; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Graphics.GAL; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.SystemState; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE +{ + /// <summary> + /// HLE configuration. + /// </summary> + public class HLEConfiguration + { + /// <summary> + /// The virtual file system used by the FS service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly VirtualFileSystem VirtualFileSystem; + + /// <summary> + /// The account manager used by the account service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly AccountManager AccountManager; + + /// <summary> + /// The content manager used by the NCM service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly ContentManager ContentManager; + + /// <summary> + /// The persistant information between run for multi-application capabilities. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + public readonly UserChannelPersistence UserChannelPersistence; + + /// <summary> + /// The GPU renderer to use for all GPU operations. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly IRenderer GpuRenderer; + + /// <summary> + /// The audio device driver to use for all audio operations. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly IHardwareDeviceDriver AudioDeviceDriver; + + /// <summary> + /// The handler for various UI related operations needed outside of HLE. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly IHostUiHandler HostUiHandler; + + /// <summary> + /// Control the memory configuration used by the emulation context. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly MemoryConfiguration MemoryConfiguration; + + /// <summary> + /// The system language to use in the settings service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly SystemLanguage SystemLanguage; + + /// <summary> + /// The system region to use in the settings service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly RegionCode Region; + + /// <summary> + /// Control the initial state of the vertical sync in the SurfaceFlinger service. + /// </summary> + internal readonly bool EnableVsync; + + /// <summary> + /// Control the initial state of the docked mode. + /// </summary> + internal readonly bool EnableDockedMode; + + /// <summary> + /// Control if the Profiled Translation Cache (PTC) should be used. + /// </summary> + internal readonly bool EnablePtc; + + /// <summary> + /// Control LibHac's integrity check level. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly IntegrityCheckLevel FsIntegrityCheckLevel; + + /// <summary> + /// Control LibHac's global access logging level. Value must be between 0 and 3. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly int FsGlobalAccessLogMode; + + /// <summary> + /// The system time offset to apply to the time service steady and local clocks. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly long SystemTimeOffset; + + /// <summary> + /// The system timezone used by the time service. + /// </summary> + /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> + internal readonly string TimeZone; + + /// <summary> + /// Control the inital state of the ignore missing services setting. + /// If this is set to true, when a missing service is encountered, it will try to automatically handle it instead of throwing an exception. + /// </summary> + /// TODO: Update this again. + public bool IgnoreMissingServices { internal get; set; } + + /// <summary> + /// Aspect Ratio applied to the renderer window by the SurfaceFlinger service. + /// </summary> + public AspectRatio AspectRatio { internal get; set; } + + /// <summary> + /// An action called when HLE force a refresh of output after docked mode changed. + /// </summary> + public Action RefreshInputConfig { internal get; set; } + + public HLEConfiguration(VirtualFileSystem virtualFileSystem, + ContentManager contentManager, + AccountManager accountManager, + UserChannelPersistence userChannelPersistence, + IRenderer gpuRenderer, + IHardwareDeviceDriver audioDeviceDriver, + MemoryConfiguration memoryConfiguration, + IHostUiHandler hostUiHandler, + SystemLanguage systemLanguage, + RegionCode region, + bool enableVsync, + bool enableDockedMode, + bool enablePtc, + IntegrityCheckLevel fsIntegrityCheckLevel, + int fsGlobalAccessLogMode, + long systemTimeOffset, + string timeZone, + bool ignoreMissingServices, + AspectRatio aspectRatio) + { + VirtualFileSystem = virtualFileSystem; + AccountManager = accountManager; + ContentManager = contentManager; + UserChannelPersistence = userChannelPersistence; + GpuRenderer = gpuRenderer; + AudioDeviceDriver = audioDeviceDriver; + MemoryConfiguration = memoryConfiguration; + HostUiHandler = hostUiHandler; + SystemLanguage = systemLanguage; + Region = region; + EnableVsync = enableVsync; + EnableDockedMode = enableDockedMode; + EnablePtc = enablePtc; + FsIntegrityCheckLevel = fsIntegrityCheckLevel; + FsGlobalAccessLogMode = fsGlobalAccessLogMode; + SystemTimeOffset = systemTimeOffset; + TimeZone = timeZone; + IgnoreMissingServices = ignoreMissingServices; + AspectRatio = aspectRatio; + } + } +} diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index fd89e8f6..0597cf9b 100644 --- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -4,6 +4,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Hid.Types; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils; diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 40e3f646..3832dd3e 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -10,7 +10,6 @@ using LibHac.Ns; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; @@ -49,10 +48,7 @@ namespace Ryujinx.HLE.HOS "sdk" }; - private readonly Switch _device; - private readonly ContentManager _contentManager; - private readonly VirtualFileSystem _fileSystem; - + private readonly Switch _device; private string _titleName; private string _displayVersion; private BlitStruct<ApplicationControlProperty> _controlData; @@ -66,12 +62,9 @@ namespace Ryujinx.HLE.HOS public string TitleIdText => TitleId.ToString("x16"); - public ApplicationLoader(Switch device, VirtualFileSystem fileSystem, ContentManager contentManager) + public ApplicationLoader(Switch device) { - _device = device; - _contentManager = contentManager; - _fileSystem = fileSystem; - + _device = device; _controlData = new BlitStruct<ApplicationControlProperty>(1); } @@ -79,14 +72,14 @@ namespace Ryujinx.HLE.HOS { if (romFsFile != null) { - _fileSystem.LoadRomFs(romFsFile); + _device.Configuration.VirtualFileSystem.LoadRomFs(romFsFile); } LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); Npdm metaData = ReadNpdm(codeFs); - _fileSystem.ModLoader.CollectMods(new[] { TitleId }, _fileSystem.ModLoader.GetModsBasePath()); + _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { TitleId }, _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath()); if (TitleId != 0) { @@ -209,7 +202,7 @@ namespace Ryujinx.HLE.HOS public void LoadXci(string xciFile) { FileStream file = new FileStream(xciFile, FileMode.Open, FileAccess.Read); - Xci xci = new Xci(_fileSystem.KeySet, file.AsStorage()); + Xci xci = new Xci(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage()); if (!xci.HasPartition(XciPartitionType.Secure)) { @@ -226,7 +219,7 @@ namespace Ryujinx.HLE.HOS try { - (mainNca, patchNca, controlNca) = GetGameData(_fileSystem, securePartition, _device.UserChannelPersistence.Index); + (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, securePartition, _device.Configuration.UserChannelPersistence.Index); } catch (Exception e) { @@ -242,9 +235,9 @@ namespace Ryujinx.HLE.HOS return; } - _contentManager.LoadEntries(_device); - _contentManager.ClearAocData(); - _contentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId); + _device.Configuration.ContentManager.LoadEntries(_device); + _device.Configuration.ContentManager.ClearAocData(); + _device.Configuration.ContentManager.AddAocData(securePartition, xciFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); LoadNca(mainNca, patchNca, controlNca); } @@ -260,7 +253,7 @@ namespace Ryujinx.HLE.HOS try { - (mainNca, patchNca, controlNca) = GetGameData(_fileSystem, nsp, _device.UserChannelPersistence.Index); + (mainNca, patchNca, controlNca) = GetGameData(_device.Configuration.VirtualFileSystem, nsp, _device.Configuration.UserChannelPersistence.Index); } catch (Exception e) { @@ -278,8 +271,8 @@ namespace Ryujinx.HLE.HOS if (mainNca != null) { - _contentManager.ClearAocData(); - _contentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId); + _device.Configuration.ContentManager.ClearAocData(); + _device.Configuration.ContentManager.AddAocData(nsp, nspFile, mainNca.Header.TitleId, _device.Configuration.FsIntegrityCheckLevel); LoadNca(mainNca, patchNca, controlNca); @@ -293,7 +286,7 @@ namespace Ryujinx.HLE.HOS public void LoadNca(string ncaFile) { FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca nca = new Nca(_fileSystem.KeySet, file.AsStorage(false)); + Nca nca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); LoadNca(nca, null, null); } @@ -310,7 +303,7 @@ namespace Ryujinx.HLE.HOS IStorage dataStorage = null; IFileSystem codeFs = null; - (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_fileSystem, mainNca.Header.TitleId.ToString("x16"), _device.UserChannelPersistence.Index, out _); + (Nca updatePatchNca, Nca updateControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), _device.Configuration.UserChannelPersistence.Index, out _); if (updatePatchNca != null) { @@ -323,7 +316,7 @@ namespace Ryujinx.HLE.HOS } // Load program 0 control NCA as we are going to need it for display version. - (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_fileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); + (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _); // Load Aoc string titleAocMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json"); @@ -336,7 +329,7 @@ namespace Ryujinx.HLE.HOS { foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) { - _contentManager.AddAocItem(dlcNca.TitleId, dlcContainer.Path, dlcNca.Path, dlcNca.Enabled); + _device.Configuration.ContentManager.AddAocItem(dlcNca.TitleId, dlcContainer.Path, dlcNca.Path, dlcNca.Enabled); } } } @@ -375,7 +368,7 @@ namespace Ryujinx.HLE.HOS Npdm metaData = ReadNpdm(codeFs); - _fileSystem.ModLoader.CollectMods(_contentManager.GetAocTitleIds().Prepend(TitleId), _fileSystem.ModLoader.GetModsBasePath()); + _device.Configuration.VirtualFileSystem.ModLoader.CollectMods(_device.Configuration.ContentManager.GetAocTitleIds().Prepend(TitleId), _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath()); if (controlNca != null) { @@ -388,7 +381,7 @@ namespace Ryujinx.HLE.HOS // NOTE: Nintendo doesn't guarantee that the display version will be updated on sub programs when updating a multi program application. // BODY: As such, to avoid PTC cache confusion, we only trust the the program 0 display version when launching a sub program. - if (updateProgram0ControlNca != null && _device.UserChannelPersistence.Index != 0) + if (updateProgram0ControlNca != null && _device.Configuration.UserChannelPersistence.Index != 0) { string dummyTitleName = ""; BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1); @@ -402,9 +395,9 @@ namespace Ryujinx.HLE.HOS } else { - IStorage newStorage = _fileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); + IStorage newStorage = _device.Configuration.VirtualFileSystem.ModLoader.ApplyRomFsMods(TitleId, dataStorage); - _fileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read)); + _device.Configuration.VirtualFileSystem.SetRomFs(newStorage.AsStream(FileAccess.Read)); } if (TitleId != 0) @@ -470,7 +463,7 @@ namespace Ryujinx.HLE.HOS private void LoadExeFs(IFileSystem codeFs, Npdm metaData = null) { - if (_fileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) + if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) { metaData = null; //TODO: Check if we should retain old npdm } @@ -496,7 +489,7 @@ namespace Ryujinx.HLE.HOS } // ExeFs file replacements - ModLoadResult modLoadResult = _fileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); + ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); // collect the nsos, ignoring ones that aren't used NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); @@ -507,20 +500,18 @@ namespace Ryujinx.HLE.HOS metaData = modLoadResult.Npdm; } - bool hasPatches = _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); + _device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); - _contentManager.LoadEntries(_device); + _device.Configuration.ContentManager.LoadEntries(_device); bool usePtc = _device.System.EnablePtc; - // don't use PTC if exefs files have been replaced + // Don't use PPTC if ExeFs files have been replaced. usePtc &= !modLoadResult.Modified; - // don't use PTC if exefs files have been patched - usePtc &= !hasPatches; if (_device.System.EnablePtc && !usePtc) { - Logger.Warning?.Print(LogClass.Ptc, $"Detected exefs modifications. PPTC disabled."); + Logger.Warning?.Print(LogClass.Ptc, $"Detected unsupported ExeFs modifications. PPTC disabled."); } Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText; @@ -530,7 +521,7 @@ namespace Ryujinx.HLE.HOS ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, executables: programs); - _fileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); } public void LoadProgram(string filePath) @@ -571,7 +562,7 @@ namespace Ryujinx.HLE.HOS if (romfsSize != 0) { - _fileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset)); + _device.Configuration.VirtualFileSystem.SetRomFs(new HomebrewRomFsStream(input, obj.FileSize + (long)romfsOffset)); } if (nacpSize != 0) @@ -619,7 +610,7 @@ namespace Ryujinx.HLE.HOS executable = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read), Path.GetFileNameWithoutExtension(filePath)); } - _contentManager.LoadEntries(_device); + _device.Configuration.ContentManager.LoadEntries(_device); _titleName = metaData.TitleName; TitleId = metaData.Aci0.TitleId; @@ -631,7 +622,7 @@ namespace Ryujinx.HLE.HOS ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, executables: executable); - _fileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); } private Npdm GetDefaultNpdm() @@ -666,7 +657,7 @@ namespace Ryujinx.HLE.HOS "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - FileSystemClient fileSystem = _fileSystem.FsClient; + FileSystemClient fileSystem = _device.Configuration.VirtualFileSystem.FsClient; Result resultCode = fileSystem.EnsureApplicationCacheStorage(out _, applicationId, ref control); if (resultCode.IsFailure()) diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs index ac96b865..d5763ff8 100644 --- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Font FontInfo CreateFont(string name) { - if (contentManager.TryGetFontTitle(name, out long fontTitle) && + if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename)) { string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data); diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index fa88d775..13d7a2af 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -8,8 +8,6 @@ using Ryujinx.Audio.Integration; using Ryujinx.Audio.Output; using Ryujinx.Audio.Renderer.Device; using Ryujinx.Audio.Renderer.Server; -using Ryujinx.Common; -using Ryujinx.Configuration; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Kernel; @@ -111,13 +109,13 @@ namespace Ryujinx.HLE.HOS internal LibHac.Horizon LibHacHorizonServer { get; private set; } internal HorizonClient LibHacHorizonClient { get; private set; } - public Horizon(Switch device, ContentManager contentManager, AccountManager accountManager, MemoryConfiguration memoryConfiguration) + public Horizon(Switch device) { KernelContext = new KernelContext( device, device.Memory, - memoryConfiguration.ToKernelMemorySize(), - memoryConfiguration.ToKernelMemoryArrange()); + device.Configuration.MemoryConfiguration.ToKernelMemorySize(), + device.Configuration.MemoryConfiguration.ToKernelMemoryArrange()); Device = device; @@ -166,8 +164,8 @@ namespace Ryujinx.HLE.HOS DisplayResolutionChangeEvent = new KEvent(KernelContext); - AccountManager = accountManager; - ContentManager = contentManager; + AccountManager = device.Configuration.AccountManager; + ContentManager = device.Configuration.ContentManager; CaptureManager = new CaptureManager(device); // TODO: use set:sys (and get external clock source id from settings) @@ -179,7 +177,7 @@ namespace Ryujinx.HLE.HOS TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue); // Configure and setup internal offset - TimeSpanType internalOffset = TimeSpanType.FromSeconds(ConfigurationState.Instance.System.SystemTimeOffset); + TimeSpanType internalOffset = TimeSpanType.FromSeconds(device.Configuration.SystemTimeOffset); TimeSpanType systemTimeOffset = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); @@ -219,8 +217,6 @@ namespace Ryujinx.HLE.HOS SurfaceFlinger = new SurfaceFlinger(device); - ConfigurationState.Instance.System.EnableDockedMode.Event += OnDockedModeChange; - InitLibHacHorizon(); InitializeAudioRenderer(); } @@ -274,6 +270,7 @@ namespace Ryujinx.HLE.HOS public void InitializeServices() { IUserInterface sm = new IUserInterface(KernelContext); + sm.TrySetServer(new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext))); // Wait until SM server thread is done with initialization, // only then doing connections to SM is safe. @@ -312,11 +309,11 @@ namespace Ryujinx.HLE.HOS LibHacHorizonClient = ryujinxClient; } - private void OnDockedModeChange(object sender, ReactiveEventArgs<bool> e) + public void ChangeDockedModeState(bool newState) { - if (e.NewValue != State.DockedMode) + if (newState != State.DockedMode) { - State.DockedMode = e.NewValue; + State.DockedMode = newState; PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; AppletState.Messages.Enqueue(MessageInfo.OperationModeChanged); @@ -325,8 +322,7 @@ namespace Ryujinx.HLE.HOS SignalDisplayResolutionChange(); - // Reconfigure controllers - Device.Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value); + Device.Configuration.RefreshInputConfig?.Invoke(); } } @@ -387,8 +383,6 @@ namespace Ryujinx.HLE.HOS { if (!_isDisposed && disposing) { - ConfigurationState.Instance.System.EnableDockedMode.Event -= OnDockedModeChange; - _isDisposed = true; KProcess terminationProcess = new KProcess(KernelContext); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs index dddd2671..b61d5697 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcBuffDesc.cs @@ -4,15 +4,15 @@ namespace Ryujinx.HLE.HOS.Ipc { struct IpcBuffDesc { - public long Position { get; private set; } - public long Size { get; private set; } - public int Flags { get; private set; } + public ulong Position { get; private set; } + public ulong Size { get; private set; } + public byte Flags { get; private set; } public IpcBuffDesc(BinaryReader reader) { - long word0 = reader.ReadUInt32(); - long word1 = reader.ReadUInt32(); - long word2 = reader.ReadUInt32(); + ulong word0 = reader.ReadUInt32(); + ulong word1 = reader.ReadUInt32(); + ulong word2 = reader.ReadUInt32(); Position = word1; Position |= (word2 << 4) & 0x0f00000000; @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Ipc Size = word0; Size |= (word2 << 8) & 0xf00000000; - Flags = (int)word2 & 3; + Flags = (byte)(word2 & 3); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 9fe3ae60..e5b9bf04 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics; using System.IO; namespace Ryujinx.HLE.HOS.Ipc @@ -185,6 +186,53 @@ namespace Ryujinx.HLE.HOS.Ipc } } + public byte[] GetBytesTipc() + { + Debug.Assert(PtrBuff.Count == 0); + + using (MemoryStream ms = new MemoryStream()) + { + BinaryWriter writer = new BinaryWriter(ms); + + int word0; + int word1; + + word0 = (int)Type; + word0 |= (SendBuff.Count & 0xf) << 20; + word0 |= (ReceiveBuff.Count & 0xf) << 24; + word0 |= (ExchangeBuff.Count & 0xf) << 28; + + byte[] handleData = new byte[0]; + + if (HandleDesc != null) + { + handleData = HandleDesc.GetBytes(); + } + + int dataLength = RawData?.Length ?? 0; + + dataLength = ((dataLength + 3) & ~3) / 4; + + word1 = (dataLength & 0x3ff); + + if (HandleDesc != null) + { + word1 |= 1 << 31; + } + + writer.Write(word0); + writer.Write(word1); + writer.Write(handleData); + + if (RawData != null) + { + writer.Write(RawData); + } + + return ms.ToArray(); + } + } + private long GetPadSize16(long position) { if ((position & 0xf) != 0) @@ -196,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Ipc } // ReSharper disable once InconsistentNaming - public (long Position, long Size) GetBufferType0x21(int index = 0) + public (ulong Position, ulong Size) GetBufferType0x21(int index = 0) { if (PtrBuff.Count > index && PtrBuff[index].Position != 0 && @@ -216,7 +264,7 @@ namespace Ryujinx.HLE.HOS.Ipc } // ReSharper disable once InconsistentNaming - public (long Position, long Size) GetBufferType0x22(int index = 0) + public (ulong Position, ulong Size) GetBufferType0x22(int index = 0) { if (RecvListBuff.Count > index && RecvListBuff[index].Position != 0 && diff --git a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs index 67cf17c9..05798fe1 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcPtrBuffDesc.cs @@ -4,11 +4,11 @@ namespace Ryujinx.HLE.HOS.Ipc { struct IpcPtrBuffDesc { - public long Position { get; private set; } - public int Index { get; private set; } - public long Size { get; private set; } + public ulong Position { get; private set; } + public uint Index { get; private set; } + public ulong Size { get; private set; } - public IpcPtrBuffDesc(long position, int index, long size) + public IpcPtrBuffDesc(ulong position, uint index, ulong size) { Position = position; Index = index; @@ -17,20 +17,20 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcPtrBuffDesc(BinaryReader reader) { - long word0 = reader.ReadUInt32(); - long word1 = reader.ReadUInt32(); + ulong word0 = reader.ReadUInt32(); + ulong word1 = reader.ReadUInt32(); - Position = word1; + Position = word1; Position |= (word0 << 20) & 0x0f00000000; Position |= (word0 << 30) & 0x7000000000; - Index = ((int)word0 >> 0) & 0x03f; - Index |= ((int)word0 >> 3) & 0x1c0; + Index = ((uint)word0 >> 0) & 0x03f; + Index |= ((uint)word0 >> 3) & 0x1c0; Size = (ushort)(word0 >> 16); } - public IpcPtrBuffDesc WithSize(long size) + public IpcPtrBuffDesc WithSize(ulong size) { return new IpcPtrBuffDesc(Position, Index, size); } @@ -42,8 +42,8 @@ namespace Ryujinx.HLE.HOS.Ipc word0 = (uint)((Position & 0x0f00000000) >> 20); word0 |= (uint)((Position & 0x7000000000) >> 30); - word0 |= (uint)(Index & 0x03f) << 0; - word0 |= (uint)(Index & 0x1c0) << 3; + word0 |= (Index & 0x03f) << 0; + word0 |= (Index & 0x1c0) << 3; word0 |= (uint)Size << 16; diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs index 78732550..10406ac7 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs @@ -4,10 +4,10 @@ namespace Ryujinx.HLE.HOS.Ipc { struct IpcRecvListBuffDesc { - public long Position { get; private set; } - public long Size { get; private set; } + public ulong Position { get; private set; } + public ulong Size { get; private set; } - public IpcRecvListBuffDesc(long position, long size) + public IpcRecvListBuffDesc(ulong position, ulong size) { Position = position; Size = size; @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Ipc public IpcRecvListBuffDesc(BinaryReader reader) { - long value = reader.ReadInt64(); + ulong value = reader.ReadUInt64(); Position = value & 0xffffffffffff; diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs index 3002b6a9..53e539a2 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common if (currentProcess.CpuMemory.IsMapped(address) && currentProcess.CpuMemory.IsMapped(address + (ulong)size - 1)) { - value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, (long)address, size); + value = MemoryHelper.ReadAsciiString(currentProcess.CpuMemory, address, size); return true; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 429c23e3..15ab82b8 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -340,7 +340,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process UserExceptionContextAddress = userExceptionContextAddress; - MemoryHelper.FillWithZeros(CpuMemory, (long)userExceptionContextAddress, KTlsPageInfo.TlsEntrySize); + MemoryHelper.FillWithZeros(CpuMemory, userExceptionContextAddress, KTlsPageInfo.TlsEntrySize); Name = creationInfo.Name; @@ -461,7 +461,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { pageInfo = new KTlsPageInfo(tlsPageVa); - MemoryHelper.FillWithZeros(CpuMemory, (long)tlsPageVa, KMemoryManager.PageSize); + MemoryHelper.FillWithZeros(CpuMemory, tlsPageVa, KMemoryManager.PageSize); } return result; diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 418a02f0..238f1226 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -1431,7 +1431,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { KProcess process = KernelStatic.GetCurrentProcess(); - string str = MemoryHelper.ReadAsciiString(process.CpuMemory, (long)strPtr, (long)size); + string str = MemoryHelper.ReadAsciiString(process.CpuMemory, strPtr, (long)size); Logger.Warning?.Print(LogClass.KernelSvc, str); } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index e427f24d..0982ceff 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -22,8 +22,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private struct SchedulingState { - public bool NeedsScheduling; - public KThread SelectedThread; + public volatile bool NeedsScheduling; + public volatile KThread SelectedThread; } private SchedulingState _state; @@ -349,31 +349,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading nextThread ??= _idleThread; - if (currentThread == nextThread) + if (currentThread != nextThread) { - return; - } + long previousTicks = LastContextSwitchTime; + long currentTicks = PerformanceCounter.ElapsedTicks; + long ticksDelta = currentTicks - previousTicks; - long previousTicks = LastContextSwitchTime; - long currentTicks = PerformanceCounter.ElapsedTicks; - long ticksDelta = currentTicks - previousTicks; + currentThread.AddCpuTime(ticksDelta); - currentThread.AddCpuTime(ticksDelta); - - if (currentProcess != null) - { - currentProcess.AddCpuTime(ticksDelta); - } + if (currentProcess != null) + { + currentProcess.AddCpuTime(ticksDelta); + } - LastContextSwitchTime = currentTicks; + LastContextSwitchTime = currentTicks; - if (currentProcess != null) - { - _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null; - } - else if (currentThread == _idleThread) - { - _previousThread = null; + if (currentProcess != null) + { + _previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null; + } + else if (currentThread == _idleThread) + { + _previousThread = null; + } } if (nextThread.CurrentCore != _coreId) @@ -469,6 +467,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread currentThread = KernelStatic.GetCurrentThread(); + if (!currentThread.IsSchedulable) + { + return; + } + context.CriticalSection.Enter(); if (currentThread.SchedFlags != ThreadSchedState.Running) @@ -491,6 +494,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread currentThread = KernelStatic.GetCurrentThread(); + if (!currentThread.IsSchedulable) + { + return; + } + context.CriticalSection.Enter(); if (currentThread.SchedFlags != ThreadSchedState.Running) @@ -550,6 +558,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread currentThread = KernelStatic.GetCurrentThread(); + if (!currentThread.IsSchedulable) + { + return; + } + context.CriticalSection.Enter(); if (currentThread.SchedFlags != ThreadSchedState.Running) diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index b95b1e8e..7ba9e43a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -161,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading TlsDramAddress = owner.MemoryManager.GetDramAddressFromVa(_tlsAddress); - MemoryHelper.FillWithZeros(owner.CpuMemory, (long)_tlsAddress, KTlsPageInfo.TlsEntrySize); + MemoryHelper.FillWithZeros(owner.CpuMemory, _tlsAddress, KTlsPageInfo.TlsEntrySize); } bool is64Bits; diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs index d36ea931..2cea57e9 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs @@ -1,41 +1,85 @@ -using Ryujinx.Common; +using LibHac; +using LibHac.Fs; +using LibHac.Fs.Shim; +using Ryujinx.Common; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; +using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Linq; namespace Ryujinx.HLE.HOS.Services.Account.Acc { public class AccountManager { + public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000"); + + private readonly VirtualFileSystem _virtualFileSystem; + private readonly AccountSaveDataManager _accountSaveDataManager; + private ConcurrentDictionary<string, UserProfile> _profiles; public UserProfile LastOpenedUser { get; private set; } - public AccountManager() + public AccountManager(VirtualFileSystem virtualFileSystem) { + _virtualFileSystem = virtualFileSystem; + _profiles = new ConcurrentDictionary<string, UserProfile>(); - UserId defaultUserId = new UserId("00000000000000010000000000000000"); - byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg"); + _accountSaveDataManager = new AccountSaveDataManager(_profiles); + + if (!_profiles.TryGetValue(DefaultUserId.ToString(), out _)) + { + byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg"); - AddUser(defaultUserId, "Player", defaultUserImage); - - OpenUser(defaultUserId); + AddUser("RyuPlayer", defaultUserImage, DefaultUserId); + + OpenUser(DefaultUserId); + } + else + { + OpenUser(_accountSaveDataManager.LastOpened); + } } - public void AddUser(UserId userId, string name, byte[] image) + public void AddUser(string name, byte[] image, UserId userId = new UserId()) { + if (userId.IsNull) + { + userId = new UserId(Guid.NewGuid().ToString().Replace("-", "")); + } + UserProfile profile = new UserProfile(userId, name, image); _profiles.AddOrUpdate(userId.ToString(), profile, (key, old) => profile); + + _accountSaveDataManager.Save(_profiles); } public void OpenUser(UserId userId) { if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile)) { + // TODO: Support multiple open users ? + foreach (UserProfile userProfile in GetAllUsers()) + { + if (userProfile == LastOpenedUser) + { + userProfile.AccountState = AccountState.Closed; + + break; + } + } + (LastOpenedUser = profile).AccountState = AccountState.Open; + + _accountSaveDataManager.LastOpened = userId; } + + _accountSaveDataManager.Save(_profiles); } public void CloseUser(UserId userId) @@ -44,9 +88,117 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { profile.AccountState = AccountState.Closed; } + + _accountSaveDataManager.Save(_profiles); + } + + public void OpenUserOnlinePlay(UserId userId) + { + if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile)) + { + // TODO: Support multiple open online users ? + foreach (UserProfile userProfile in GetAllUsers()) + { + if (userProfile == LastOpenedUser) + { + userProfile.OnlinePlayState = AccountState.Closed; + + break; + } + } + + profile.OnlinePlayState = AccountState.Open; + } + + _accountSaveDataManager.Save(_profiles); + } + + public void CloseUserOnlinePlay(UserId userId) + { + if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile)) + { + profile.OnlinePlayState = AccountState.Closed; + } + + _accountSaveDataManager.Save(_profiles); + } + + public void SetUserImage(UserId userId, byte[] image) + { + foreach (UserProfile userProfile in GetAllUsers()) + { + if (userProfile.UserId == userId) + { + userProfile.Image = image; + + break; + } + } + + _accountSaveDataManager.Save(_profiles); + } + + public void SetUserName(UserId userId, string name) + { + foreach (UserProfile userProfile in GetAllUsers()) + { + if (userProfile.UserId == userId) + { + userProfile.Name = name; + + break; + } + } + + _accountSaveDataManager.Save(_profiles); + } + + public void DeleteUser(UserId userId) + { + DeleteSaveData(userId); + + _profiles.Remove(userId.ToString(), out _); + + OpenUser(DefaultUserId); + + _accountSaveDataManager.Save(_profiles); + } + + private void DeleteSaveData(UserId userId) + { + SaveDataFilter saveDataFilter = new SaveDataFilter(); + saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)userId.High, (ulong)userId.Low)); + + Result result = _virtualFileSystem.FsClient.OpenSaveDataIterator(out SaveDataIterator saveDataIterator, SaveDataSpaceId.User, ref saveDataFilter); + if (result.IsSuccess()) + { + Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; + + while (true) + { + saveDataIterator.ReadSaveDataInfo(out long readCount, saveDataInfo); + + if (readCount == 0) + { + break; + } + + for (int i = 0; i < readCount; i++) + { + // TODO: We use Directory.Delete workaround because DeleteSaveData softlock without, due to a bug in LibHac 0.12.0. + string savePath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/save/{saveDataInfo[i].SaveDataId:x16}"); + string saveMetaPath = Path.Combine(_virtualFileSystem.GetNandPath(), $"user/saveMeta/{saveDataInfo[i].SaveDataId:x16}"); + + Directory.Delete(savePath, true); + Directory.Delete(saveMetaPath, true); + + _virtualFileSystem.FsClient.DeleteSaveData(SaveDataSpaceId.User, saveDataInfo[i].SaveDataId); + } + } + } } - public int GetUserCount() + internal int GetUserCount() { return _profiles.Count; } @@ -56,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return _profiles.TryGetValue(userId.ToString(), out profile); } - internal IEnumerable<UserProfile> GetAllUsers() + public IEnumerable<UserProfile> GetAllUsers() { return _profiles.Values; } diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs new file mode 100644 index 00000000..44ef3f33 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs @@ -0,0 +1,87 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class AccountSaveDataManager + { + private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); + + private struct ProfilesJson + { + [JsonPropertyName("profiles")] + public List<UserProfileJson> Profiles { get; set; } + [JsonPropertyName("last_opened")] + public string LastOpened { get; set; } + } + + private struct UserProfileJson + { + [JsonPropertyName("user_id")] + public string UserId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("account_state")] + public AccountState AccountState { get; set; } + [JsonPropertyName("online_play_state")] + public AccountState OnlinePlayState { get; set; } + [JsonPropertyName("last_modified_timestamp")] + public long LastModifiedTimestamp { get; set; } + [JsonPropertyName("image")] + public byte[] Image { get; set; } + } + + public UserId LastOpened { get; set; } + + public AccountSaveDataManager(ConcurrentDictionary<string, UserProfile> profiles) + { + // TODO: Use 0x8000000000000010 system savedata instead of a JSON file if needed. + + if (File.Exists(_profilesJsonPath)) + { + ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); + + foreach (var profile in profilesJson.Profiles) + { + UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp); + + profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile); + } + + LastOpened = new UserId(profilesJson.LastOpened); + } + else + { + LastOpened = AccountManager.DefaultUserId; + } + } + + public void Save(ConcurrentDictionary<string, UserProfile> profiles) + { + ProfilesJson profilesJson = new ProfilesJson() + { + Profiles = new List<UserProfileJson>(), + LastOpened = LastOpened.ToString() + }; + + foreach (var profile in profiles) + { + profilesJson.Profiles.Add(new UserProfileJson() + { + UserId = profile.Value.UserId.ToString(), + Name = profile.Value.Name, + AccountState = profile.Value.AccountState, + OnlinePlayState = profile.Value.OnlinePlayState, + LastModifiedTimestamp = profile.Value.LastModifiedTimestamp, + Image = profile.Value.Image, + }); + } + + File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true)); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs index c7efe778..471942f1 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs @@ -73,8 +73,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode LoadIdTokenCache(ServiceCtx context) { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; // NOTE: This opens the file at "su/cache/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x") // in the "account:/" savedata and writes some data in the buffer. diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs index 18534393..8e29f94b 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs @@ -16,16 +16,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode Get(ServiceCtx context) { - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80L); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80UL); - long bufferPosition = context.Request.RecvListBuff[0].Position; + ulong bufferPosition = context.Request.RecvListBuff[0].Position; MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x80); // TODO: Determine the struct. - context.Memory.Write((ulong)bufferPosition, 0); // Unknown - context.Memory.Write((ulong)bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs. - context.Memory.Write((ulong)bufferPosition + 8, (byte)1); // Profile icon background color ID + context.Memory.Write(bufferPosition, 0); // Unknown + context.Memory.Write(bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs. + context.Memory.Write(bufferPosition + 8, (byte)1); // Profile icon background color ID // 0x07 bytes - Unknown // 0x10 bytes - Some ID related to the Mii? All zeros when a character icon is used. // 0x60 bytes - Usually zeros? @@ -57,15 +57,15 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode LoadImage(ServiceCtx context) { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferLen = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; - if (_profile.Image.Length > bufferLen) + if ((ulong)_profile.Image.Length > bufferLen) { return ResultCode.InvalidBufferSize; } - context.Memory.Write((ulong)bufferPosition, _profile.Image); + context.Memory.Write(bufferPosition, _profile.Image); context.ResponseData.Write(_profile.Image.Length); @@ -74,12 +74,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode Store(ServiceCtx context) { - long userDataPosition = context.Request.PtrBuff[0].Position; - long userDataSize = context.Request.PtrBuff[0].Size; + ulong userDataPosition = context.Request.PtrBuff[0].Position; + ulong userDataSize = context.Request.PtrBuff[0].Size; byte[] userData = new byte[userDataSize]; - context.Memory.Read((ulong)userDataPosition, userData); + context.Memory.Read(userDataPosition, userData); // TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata. @@ -90,19 +90,19 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService public ResultCode StoreWithImage(ServiceCtx context) { - long userDataPosition = context.Request.PtrBuff[0].Position; - long userDataSize = context.Request.PtrBuff[0].Size; + ulong userDataPosition = context.Request.PtrBuff[0].Position; + ulong userDataSize = context.Request.PtrBuff[0].Size; byte[] userData = new byte[userDataSize]; - context.Memory.Read((ulong)userDataPosition, userData); + context.Memory.Read(userDataPosition, userData); - long profileImagePosition = context.Request.SendBuff[0].Position; - long profileImageSize = context.Request.SendBuff[0].Size; + ulong profileImagePosition = context.Request.SendBuff[0].Position; + ulong profileImageSize = context.Request.SendBuff[0].Size; byte[] profileImageData = new byte[profileImageSize]; - context.Memory.Read((ulong)profileImagePosition, profileImageData); + context.Memory.Read(profileImagePosition, profileImageData); // TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata. diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs index 29cce5d7..794c72ce 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs @@ -53,8 +53,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.InvalidBuffer; } - long outputPosition = context.Request.RecvListBuff[0].Position; - long outputSize = context.Request.RecvListBuff[0].Size; + ulong outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputSize = context.Request.RecvListBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); @@ -67,8 +67,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc break; } - context.Memory.Write((ulong)outputPosition + offset, userProfile.UserId.High); - context.Memory.Write((ulong)outputPosition + offset + 8, userProfile.UserId.Low); + context.Memory.Write(outputPosition + offset, userProfile.UserId.High); + context.Memory.Write(outputPosition + offset + 8, userProfile.UserId.Low); offset += 0x10; } @@ -156,8 +156,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.InvalidBuffer; } - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; if (inputSize != 0x24000) { @@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc byte[] thumbnailBuffer = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, thumbnailBuffer); + context.Memory.Read(inputPosition, thumbnailBuffer); // NOTE: Account service call nn::fs::WriteSaveDataThumbnailFile(). // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ? diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 6067dc44..2fbf950c 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -142,8 +142,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // ListOpenContextStoredUsers() -> array<nn::account::Uid, 0xa> public ResultCode ListOpenContextStoredUsers(ServiceCtx context) { - long outputPosition = context.Request.RecvListBuff[0].Position; - long outputSize = context.Request.RecvListBuff[0].Size; + ulong outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputSize = context.Request.RecvListBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs index a57796c9..ef0a1a64 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs @@ -8,31 +8,80 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public UserId UserId { get; } - public string Name { get; } + public long LastModifiedTimestamp { get; set; } - public byte[] Image { get; } + private string _name; - public long LastModifiedTimestamp { get; private set; } + public string Name + { + get => _name; + set + { + _name = value; + + UpdateLastModifiedTimestamp(); + } + } - public AccountState AccountState { get; set; } - public AccountState OnlinePlayState { get; set; } + private byte[] _image; - public UserProfile(UserId userId, string name, byte[] image) + public byte[] Image { - UserId = userId; - Name = name; + get => _image; + set + { + _image = value; - Image = image; + UpdateLastModifiedTimestamp(); + } + } + + private AccountState _accountState; - LastModifiedTimestamp = 0; + public AccountState AccountState + { + get => _accountState; + set + { + _accountState = value; + + UpdateLastModifiedTimestamp(); + } + } + + public AccountState _onlinePlayState; + + public AccountState OnlinePlayState + { + get => _onlinePlayState; + set + { + _onlinePlayState = value; + + UpdateLastModifiedTimestamp(); + } + } + + public UserProfile(UserId userId, string name, byte[] image, long lastModifiedTimestamp = 0) + { + UserId = userId; + Name = name; + Image = image; AccountState = AccountState.Closed; OnlinePlayState = AccountState.Closed; - UpdateTimestamp(); + if (lastModifiedTimestamp != 0) + { + LastModifiedTimestamp = lastModifiedTimestamp; + } + else + { + UpdateLastModifiedTimestamp(); + } } - private void UpdateTimestamp() + private void UpdateLastModifiedTimestamp() { LastModifiedTimestamp = (long)(DateTime.Now - Epoch).TotalSeconds; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs index cdc59678..33f5393f 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IStorageAccessor.cs @@ -29,20 +29,20 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.ObjectInvalid; } - long writePosition = context.RequestData.ReadInt64(); + ulong writePosition = context.RequestData.ReadUInt64(); - if (writePosition > _storage.Data.Length) + if (writePosition > (ulong)_storage.Data.Length) { return ResultCode.OutOfBounds; } - (long position, long size) = context.Request.GetBufferType0x21(); + (ulong position, ulong size) = context.Request.GetBufferType0x21(); - size = Math.Min(size, _storage.Data.Length - writePosition); + size = Math.Min(size, (ulong)_storage.Data.Length - writePosition); if (size > 0) { - long maxSize = _storage.Data.Length - writePosition; + ulong maxSize = (ulong)_storage.Data.Length - writePosition; if (size > maxSize) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE byte[] data = new byte[size]; - context.Memory.Read((ulong)position, data); + context.Memory.Read(position, data); Buffer.BlockCopy(data, 0, _storage.Data, (int)writePosition, (int)size); } @@ -63,22 +63,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE // Read(u64) -> buffer<bytes, 0x22> public ResultCode Read(ServiceCtx context) { - long readPosition = context.RequestData.ReadInt64(); + ulong readPosition = context.RequestData.ReadUInt64(); - if (readPosition > _storage.Data.Length) + if (readPosition > (ulong)_storage.Data.Length) { return ResultCode.OutOfBounds; } - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - size = Math.Min(size, _storage.Data.Length - readPosition); + size = Math.Min(size, (ulong)_storage.Data.Length - readPosition); byte[] data = new byte[size]; Buffer.BlockCopy(_storage.Data, (int)readPosition, data, 0, (int)size); - context.Memory.Write((ulong)position, data); + context.Memory.Write(position, data); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index a1a5d8c4..e0633145 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati switch (kind) { case LaunchParameterKind.UserChannel: - storageData = context.Device.UserChannelPersistence.Pop(); + storageData = context.Device.Configuration.UserChannelPersistence.Pop(); break; case LaunchParameterKind.PreselectedUser: // Only the first 0x18 bytes of the Data seems to be actually used. @@ -359,13 +359,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // SetApplicationCopyrightImage(buffer<bytes, 0x45> frame_buffer, s32 x, s32 y, s32 width, s32 height, s32 window_origin_mode) public ResultCode SetApplicationCopyrightImage(ServiceCtx context) { - long frameBufferPos = context.Request.SendBuff[0].Position; - long frameBufferSize = context.Request.SendBuff[0].Size; - int x = context.RequestData.ReadInt32(); - int y = context.RequestData.ReadInt32(); - int width = context.RequestData.ReadInt32(); - int height = context.RequestData.ReadInt32(); - uint windowOriginMode = context.RequestData.ReadUInt32(); + ulong frameBufferPos = context.Request.SendBuff[0].Position; + ulong frameBufferSize = context.Request.SendBuff[0].Size; + int x = context.RequestData.ReadInt32(); + int y = context.RequestData.ReadInt32(); + int width = context.RequestData.ReadInt32(); + int height = context.RequestData.ReadInt32(); + uint windowOriginMode = context.RequestData.ReadUInt32(); ResultCode resultCode = ResultCode.InvalidParameters; @@ -388,7 +388,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return resultCode; } - private ResultCode SetApplicationCopyrightImageImpl(int x, int y, int width, int height, long frameBufferPos, long frameBufferSize, uint windowOriginMode) + private ResultCode SetApplicationCopyrightImageImpl(int x, int y, int width, int height, ulong frameBufferPos, ulong frameBufferSize, uint windowOriginMode) { /* if (_copyrightBuffer == null) @@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // ClearUserChannel() public ResultCode ClearUserChannel(ServiceCtx context) { - context.Device.UserChannelPersistence.Clear(); + context.Device.Configuration.UserChannelPersistence.Clear(); return ResultCode.Success; } @@ -464,7 +464,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati { AppletAE.IStorage data = GetObject<AppletAE.IStorage>(context, 0); - context.Device.UserChannelPersistence.Push(data.Data); + context.Device.Configuration.UserChannelPersistence.Push(data.Data); return ResultCode.Success; } @@ -473,7 +473,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // GetPreviousProgramIndex() -> s32 program_index public ResultCode GetPreviousProgramIndex(ServiceCtx context) { - int previousProgramIndex = context.Device.UserChannelPersistence.PreviousIndex; + int previousProgramIndex = context.Device.Configuration.UserChannelPersistence.PreviousIndex; context.ResponseData.Write(previousProgramIndex); diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs index a89abce7..b45a4d2c 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioIn/AudioInServer.cs @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // AppendAudioInBuffer(u64 tag, buffer<nn::audio::AudioInBuffer, 5>) public ResultCode AppendAudioInBuffer(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; + ulong position = context.Request.SendBuff[0].Position; ulong bufferTag = context.RequestData.ReadUInt64(); @@ -74,8 +74,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // GetReleasedAudioInBuffers() -> (u32 count, buffer<u64, 6> tags) public ResultCode GetReleasedAudioInBuffers(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size)) { @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // AppendUacInBuffer(u64 tag, handle<copy, unknown>, buffer<nn::audio::AudioInBuffer, 5>) public ResultCode AppendUacInBuffer(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; + ulong position = context.Request.SendBuff[0].Position; ulong bufferTag = context.RequestData.ReadUInt64(); uint handle = (uint)context.Request.HandleDesc.ToCopy[0]; @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // AppendAudioInBufferAuto(u64 tag, buffer<nn::audio::AudioInBuffer, 0x21>) public ResultCode AppendAudioInBufferAuto(ServiceCtx context) { - (long position, _) = context.Request.GetBufferType0x21(); + (ulong position, _) = context.Request.GetBufferType0x21(); ulong bufferTag = context.RequestData.ReadUInt64(); @@ -129,9 +129,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // GetReleasedAudioInBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags) public ResultCode GetReleasedAudioInBuffersAuto(ServiceCtx context) { - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size)) + using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) { ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioIn // AppendUacInBufferAuto(u64 tag, handle<copy, event>, buffer<nn::audio::AudioInBuffer, 0x21>) public ResultCode AppendUacInBufferAuto(ServiceCtx context) { - (long position, _) = context.Request.GetBufferType0x21(); + (ulong position, _) = context.Request.GetBufferType0x21(); ulong bufferTag = context.RequestData.ReadUInt64(); uint handle = (uint)context.Request.HandleDesc.ToCopy[0]; diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs index 4806ebe9..7b243c71 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioInManagerServer.cs @@ -27,10 +27,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio { string[] deviceNames = _impl.ListAudioIns(false); - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -38,15 +38,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); position += AudioInNameSize; count++; @@ -65,15 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - long deviceNameInputPosition = context.Request.SendBuff[0].Position; - long deviceNameInputSize = context.Request.SendBuff[0].Size; + ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; + ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; - long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; + ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; + ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize); + string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); @@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); + context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); + MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); MakeObject(context, new AudioInServer(obj)); } @@ -98,9 +98,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio { string[] deviceNames = _impl.ListAudioIns(false); - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -108,15 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); position += AudioInNameSize; count++; @@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21(); - (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22(); + (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); + (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize); + string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); @@ -150,8 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); + context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); + MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); MakeObject(context, new AudioInServer(obj)); } @@ -165,9 +165,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio { string[] deviceNames = _impl.ListAudioIns(true); - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -175,15 +175,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioInNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioInNameSize - buffer.Length); position += AudioInNameSize; count++; @@ -205,15 +205,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - long deviceNameInputPosition = context.Request.SendBuff[0].Position; - long deviceNameInputSize = context.Request.SendBuff[0].Size; + ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; + ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; - long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; + ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; + ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize); + string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); ResultCode resultCode = _impl.OpenAudioIn(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioIn obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); @@ -223,8 +223,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); + context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); + MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioInNameSize - outputDeviceNameRaw.Length); MakeObject(context, new AudioInServer(obj)); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs index 72ff4da7..b7515e0f 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOut/AudioOutServer.cs @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut // AppendAudioOutBuffer(u64 bufferTag, buffer<nn::audio::AudioOutBuffer, 5> buffer) public ResultCode AppendAudioOutBuffer(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; + ulong position = context.Request.SendBuff[0].Position; ulong bufferTag = context.RequestData.ReadUInt64(); @@ -74,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut // GetReleasedAudioOutBuffers() -> (u32 count, buffer<u64, 6> tags) public ResultCode GetReleasedAudioOutBuffers(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; - using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size)) + using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) { ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut // AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>) public ResultCode AppendAudioOutBufferAuto(ServiceCtx context) { - (long position, _) = context.Request.GetBufferType0x21(); + (ulong position, _) = context.Request.GetBufferType0x21(); ulong bufferTag = context.RequestData.ReadUInt64(); @@ -115,9 +115,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOut // GetReleasedAudioOutBuffersAuto() -> (u32 count, buffer<u64, 0x22> tags) public ResultCode GetReleasedAudioOutBuffersAuto(ServiceCtx context) { - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - using (WritableRegion outputRegion = context.Memory.GetWritableRegion((ulong)position, (int)size)) + using (WritableRegion outputRegion = context.Memory.GetWritableRegion(position, (int)size)) { ResultCode result = _impl.GetReleasedBuffers(MemoryMarshal.Cast<byte, ulong>(outputRegion.Memory.Span), out uint releasedCount); diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs index a220f90b..3040696e 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs @@ -27,10 +27,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio { string[] deviceNames = _impl.ListAudioOuts(); - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -38,15 +38,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length); position += AudioOutNameSize; count++; @@ -65,15 +65,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - long deviceNameInputPosition = context.Request.SendBuff[0].Position; - long deviceNameInputSize = context.Request.SendBuff[0].Size; + ulong deviceNameInputPosition = context.Request.SendBuff[0].Position; + ulong deviceNameInputSize = context.Request.SendBuff[0].Size; - long deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; - long deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; + ulong deviceNameOutputPosition = context.Request.ReceiveBuff[0].Position; + ulong deviceNameOutputSize = context.Request.ReceiveBuff[0].Size; uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize); + string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); @@ -83,8 +83,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); + context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); + MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); MakeObject(context, new AudioOutServer(obj)); } @@ -98,9 +98,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio { string[] deviceNames = _impl.ListAudioOuts(); - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -108,15 +108,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioOutNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioOutNameSize - buffer.Length); position += AudioOutNameSize; count++; @@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Audio AudioInputConfiguration inputConfiguration = context.RequestData.ReadStruct<AudioInputConfiguration>(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - (long deviceNameInputPosition, long deviceNameInputSize) = context.Request.GetBufferType0x21(); - (long deviceNameOutputPosition, long deviceNameOutputSize) = context.Request.GetBufferType0x22(); + (ulong deviceNameInputPosition, ulong deviceNameInputSize) = context.Request.GetBufferType0x21(); + (ulong deviceNameOutputPosition, ulong deviceNameOutputSize) = context.Request.GetBufferType0x22(); uint processHandle = (uint)context.Request.HandleDesc.ToCopy[0]; - string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, deviceNameInputSize); + string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize); ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle); @@ -150,8 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio byte[] outputDeviceNameRaw = Encoding.ASCII.GetBytes(outputDeviceName); - context.Memory.Write((ulong)deviceNameOutputPosition, outputDeviceNameRaw); - MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); + context.Memory.Write(deviceNameOutputPosition, outputDeviceNameRaw); + MemoryHelper.FillWithZeros(context.Memory, deviceNameOutputPosition + (ulong)outputDeviceNameRaw.Length, AudioOutNameSize - outputDeviceNameRaw.Length); MakeObject(context, new AudioOutServer(obj)); } diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs index 437b8745..87ec2f6a 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioDeviceServer.cs @@ -25,10 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { string[] deviceNames = _impl.ListAudioDeviceName(); - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -36,15 +36,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); position += AudioDeviceNameSize; count++; @@ -61,10 +61,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { float volume = context.RequestData.ReadSingle(); - long position = context.Request.SendBuff[0].Position; - long size = context.Request.SendBuff[0].Size; + ulong position = context.Request.SendBuff[0].Position; + ulong size = context.Request.SendBuff[0].Size; - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); return _impl.SetAudioDeviceOutputVolume(deviceName, volume); } @@ -73,10 +73,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer // GetAudioDeviceOutputVolume(buffer<bytes, 5> name) -> f32 volume public ResultCode GetAudioDeviceOutputVolume(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; - long size = context.Request.SendBuff[0].Size; + ulong position = context.Request.SendBuff[0].Position; + ulong size = context.Request.SendBuff[0].Size; - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); @@ -94,14 +94,14 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { string name = _impl.GetActiveAudioDeviceName(); - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(name + "\0"); - if ((ulong)deviceNameBuffer.Length <= (ulong)size) + if ((ulong)deviceNameBuffer.Length <= size) { - context.Memory.Write((ulong)position, deviceNameBuffer); + context.Memory.Write(position, deviceNameBuffer); } else { @@ -146,9 +146,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { string[] deviceNames = _impl.ListAudioDeviceName(); - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); - long basePosition = position; + ulong basePosition = position; int count = 0; @@ -156,15 +156,15 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { byte[] buffer = Encoding.ASCII.GetBytes(name); - if ((position - basePosition) + buffer.Length > size) + if ((position - basePosition) + (ulong)buffer.Length > size) { Logger.Error?.Print(LogClass.ServiceAudio, $"Output buffer size {size} too small!"); break; } - context.Memory.Write((ulong)position, buffer); - MemoryHelper.FillWithZeros(context.Memory, position + buffer.Length, AudioDeviceNameSize - buffer.Length); + context.Memory.Write(position, buffer); + MemoryHelper.FillWithZeros(context.Memory, position + (ulong)buffer.Length, AudioDeviceNameSize - buffer.Length); position += AudioDeviceNameSize; count++; @@ -181,9 +181,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { float volume = context.RequestData.ReadSingle(); - (long position, long size) = context.Request.GetBufferType0x21(); + (ulong position, ulong size) = context.Request.GetBufferType0x21(); - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); return _impl.SetAudioDeviceOutputVolume(deviceName, volume); } @@ -192,9 +192,9 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer // GetAudioDeviceOutputVolumeAuto(buffer<bytes, 0x21> name) -> f32 public ResultCode GetAudioDeviceOutputVolumeAuto(ServiceCtx context) { - (long position, long size) = context.Request.GetBufferType0x21(); + (ulong position, ulong size) = context.Request.GetBufferType0x21(); - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); ResultCode result = _impl.GetAudioDeviceOutputVolume(deviceName, out float volume); @@ -212,13 +212,13 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer { string name = _impl.GetActiveAudioDeviceName(); - (long position, long size) = context.Request.GetBufferType0x22(); + (ulong position, ulong size) = context.Request.GetBufferType0x22(); byte[] deviceNameBuffer = Encoding.UTF8.GetBytes(name + '\0'); - if ((ulong)deviceNameBuffer.Length <= (ulong)size) + if ((ulong)deviceNameBuffer.Length <= size) { - context.Memory.Write((ulong)position, deviceNameBuffer); + context.Memory.Write(position, deviceNameBuffer); } else { @@ -268,10 +268,10 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer // GetAudioSystemMasterVolumeSetting(buffer<bytes, 5> name) -> f32 public ResultCode GetAudioSystemMasterVolumeSetting(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; - long size = context.Request.SendBuff[0].Size; + ulong position = context.Request.SendBuff[0].Position; + ulong size = context.Request.SendBuff[0].Size; - string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, size); + string deviceName = MemoryHelper.ReadAsciiString(context.Memory, position, (long)size); ResultCode result = _impl.GetAudioSystemMasterVolumeSetting(deviceName, out float systemMasterVolume); diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs index 56eb173d..bb51b506 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs @@ -57,16 +57,16 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> performanceOutput) public ResultCode RequestUpdate(ServiceCtx context) { - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; - long performanceOutputPosition = context.Request.ReceiveBuff[1].Position; - long performanceOutputSize = context.Request.ReceiveBuff[1].Size; + ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position; + ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size; - ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)inputPosition, (int)inputSize).ToArray(); + ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); Memory<byte> output = new byte[outputSize]; Memory<byte> performanceOutput = new byte[performanceOutputSize]; @@ -78,8 +78,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer if (result == ResultCode.Success) { - context.Memory.Write((ulong)outputPosition, output.Span); - context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span); + context.Memory.Write(outputPosition, output.Span); + context.Memory.Write(performanceOutputPosition, performanceOutput.Span); } else { @@ -149,11 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer // -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> performanceOutput) public ResultCode RequestUpdateAuto(ServiceCtx context) { - (long inputPosition, long inputSize) = context.Request.GetBufferType0x21(); - (long outputPosition, long outputSize) = context.Request.GetBufferType0x22(0); - (long performanceOutputPosition, long performanceOutputSize) = context.Request.GetBufferType0x22(1); + (ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21(); + (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0); + (ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1); - ReadOnlyMemory<byte> input = context.Memory.GetSpan((ulong)inputPosition, (int)inputSize).ToArray(); + ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray(); Memory<byte> output = new byte[outputSize]; Memory<byte> performanceOutput = new byte[performanceOutputSize]; @@ -165,8 +165,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer if (result == ResultCode.Success) { - context.Memory.Write((ulong)outputPosition, output.Span); - context.Memory.Write((ulong)performanceOutputPosition, performanceOutput.Span); + context.Memory.Write(outputPosition, output.Span); + context.Memory.Write(performanceOutputPosition, performanceOutput.Span); } return result; diff --git a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs index a405457a..44eeb32d 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/HardwareOpusDecoderManager/IHardwareOpusDecoder.cs @@ -106,24 +106,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager { ResultCode result; - long inPosition = context.Request.SendBuff[0].Position; - long inSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; byte[] buffer = new byte[inSize]; - context.Memory.Read((ulong)inPosition, buffer); + context.Memory.Read(inPosition, buffer); using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer))) { - result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples); + result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples); if (result == ResultCode.Success) { byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)]; Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length); - context.Memory.Write((ulong)outputPosition, pcmDataBytes); + context.Memory.Write(outputPosition, pcmDataBytes); context.ResponseData.Write(outConsumed); context.ResponseData.Write(outSamples); @@ -139,24 +139,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager { ResultCode result; - long inPosition = context.Request.SendBuff[0].Position; - long inSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; byte[] buffer = new byte[inSize]; - context.Memory.Read((ulong)inPosition, buffer); + context.Memory.Read(inPosition, buffer); using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer))) { - result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples); + result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples); if (result == ResultCode.Success) { byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)]; Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length); - context.Memory.Write((ulong)outputPosition, pcmDataBytes); + context.Memory.Write(outputPosition, pcmDataBytes); context.ResponseData.Write(outConsumed); context.ResponseData.Write(outSamples); @@ -177,24 +177,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager _reset = context.RequestData.ReadBoolean(); - long inPosition = context.Request.SendBuff[0].Position; - long inSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; byte[] buffer = new byte[inSize]; - context.Memory.Read((ulong)inPosition, buffer); + context.Memory.Read(inPosition, buffer); using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer))) { - result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples); + result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples); if (result == ResultCode.Success) { byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)]; Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length); - context.Memory.Write((ulong)outputPosition, pcmDataBytes); + context.Memory.Write(outputPosition, pcmDataBytes); context.ResponseData.Write(outConsumed); context.ResponseData.Write(outSamples); @@ -215,24 +215,24 @@ namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager _reset = context.RequestData.ReadBoolean(); - long inPosition = context.Request.SendBuff[0].Position; - long inSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong inPosition = context.Request.SendBuff[0].Position; + ulong inSize = context.Request.SendBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; byte[] buffer = new byte[inSize]; - context.Memory.Read((ulong)inPosition, buffer); + context.Memory.Read(inPosition, buffer); using (BinaryReader inputStream = new BinaryReader(new MemoryStream(buffer))) { - result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, outputSize, out uint outConsumed, out int outSamples); + result = DecodeInterleavedInternal(inputStream, out short[] outPcmData, (long)outputSize, out uint outConsumed, out int outSamples); if (result == ResultCode.Success) { byte[] pcmDataBytes = new byte[outPcmData.Length * sizeof(short)]; Buffer.BlockCopy(outPcmData, 0, pcmDataBytes, 0, pcmDataBytes.Length); - context.Memory.Write((ulong)outputPosition, pcmDataBytes); + context.Memory.Write(outputPosition, pcmDataBytes); context.ResponseData.Write(outConsumed); context.ResponseData.Write(outSamples); diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs index f232cd3e..51d8f66c 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheDirectoryService.cs @@ -30,14 +30,14 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>) public ResultCode Read(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; byte[] data = new byte[size]; Result result = _base.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(data)); - context.Memory.Write((ulong)position, data); + context.Memory.Write(position, data); context.ResponseData.Write(entriesRead); diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs index fe7c2ffc..9354b60e 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheFileService.cs @@ -30,8 +30,8 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // Read(u64) -> (u64, buffer<bytes, 6>) public ResultCode Read(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; long offset = context.RequestData.ReadInt64(); @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator Result result = _base.Read(out long bytesRead, offset, data); - context.Memory.Write((ulong)position, data); + context.Memory.Write(position, data); context.ResponseData.Write(bytesRead); diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs index f5a6fae0..61b1f1a1 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheProgressService.cs @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator Result = 0 }; - long dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress); + ulong dcpSize = WriteDeliveryCacheProgressImpl(context, context.Request.RecvListBuff[0], deliveryCacheProgress); context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(dcpSize); Logger.Stub?.PrintStub(LogClass.ServiceBcat); @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator return ResultCode.Success; } - private long WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress) + private ulong WriteDeliveryCacheProgressImpl(ServiceCtx context, IpcRecvListBuffDesc ipcDesc, DeliveryCacheProgressImpl deliveryCacheProgress) { return MemoryHelper.Write(context.Memory, ipcDesc.Position, deliveryCacheProgress); } diff --git a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs index 8e2fb4bf..cac5f170 100644 --- a/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs +++ b/Ryujinx.HLE/HOS/Services/Bcat/ServiceCreator/IDeliveryCacheStorageService.cs @@ -46,14 +46,14 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator // EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>) public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; - long size = context.Request.ReceiveBuff[0].Size; + ulong position = context.Request.ReceiveBuff[0].Position; + ulong size = context.Request.ReceiveBuff[0].Size; byte[] data = new byte[size]; Result result = _base.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(data)); - context.Memory.Write((ulong)position, data); + context.Memory.Write(position, data); context.ResponseData.Write(count); diff --git a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index 37cc9bda..35781562 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Caps.Types; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using System; using System.IO; @@ -19,11 +18,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps public CaptureManager(Switch device) { _sdCardPath = device.FileSystem.GetSdCardPath(); - - SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder() - { - Quality = 100 - }); } public ResultCode SetShimLibraryVersion(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index b907ff35..1789122e 100644 --- a/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -26,10 +26,10 @@ namespace Ryujinx.HLE.HOS.Services.Caps ulong appletResourceUserId = context.RequestData.ReadUInt64(); ulong pidPlaceholder = context.RequestData.ReadUInt64(); - long screenshotDataPosition = context.Request.SendBuff[0].Position; - long screenshotDataSize = context.Request.SendBuff[0].Size; + ulong screenshotDataPosition = context.Request.SendBuff[0].Position; + ulong screenshotDataSize = context.Request.SendBuff[0].Size; - byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); + byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); @@ -49,16 +49,16 @@ namespace Ryujinx.HLE.HOS.Services.Caps ulong appletResourceUserId = context.RequestData.ReadUInt64(); ulong pidPlaceholder = context.RequestData.ReadUInt64(); - long applicationDataPosition = context.Request.SendBuff[0].Position; - long applicationDataSize = context.Request.SendBuff[0].Size; + ulong applicationDataPosition = context.Request.SendBuff[0].Position; + ulong applicationDataSize = context.Request.SendBuff[0].Size; - long screenshotDataPosition = context.Request.SendBuff[1].Position; - long screenshotDataSize = context.Request.SendBuff[1].Size; + ulong screenshotDataPosition = context.Request.SendBuff[1].Position; + ulong screenshotDataSize = context.Request.SendBuff[1].Size; // TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now). - byte[] applicationData = context.Memory.GetSpan((ulong)applicationDataPosition, (int)applicationDataSize).ToArray(); + byte[] applicationData = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray(); - byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); + byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); @@ -77,16 +77,16 @@ namespace Ryujinx.HLE.HOS.Services.Caps uint unknown = context.RequestData.ReadUInt32(); ulong appletResourceUserId = context.RequestData.ReadUInt64(); - long userIdListPosition = context.Request.SendBuff[0].Position; - long userIdListSize = context.Request.SendBuff[0].Size; + ulong userIdListPosition = context.Request.SendBuff[0].Position; + ulong userIdListSize = context.Request.SendBuff[0].Size; - long screenshotDataPosition = context.Request.SendBuff[1].Position; - long screenshotDataSize = context.Request.SendBuff[1].Size; + ulong screenshotDataPosition = context.Request.SendBuff[1].Position; + ulong screenshotDataSize = context.Request.SendBuff[1].Size; // TODO: Parse the UserIdList. - byte[] userIdList = context.Memory.GetSpan((ulong)userIdListPosition, (int)userIdListSize).ToArray(); + byte[] userIdList = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray(); - byte[] screenshotData = context.Memory.GetSpan((ulong)screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); + byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Application.TitleId, out ApplicationAlbumEntry applicationAlbumEntry); diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 1ae5d487..a2865e9c 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -150,12 +150,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.InvalidArgument; } - if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile)) - { - profile.OnlinePlayState = AccountState.Open; - } - - Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString(), profile.OnlinePlayState }); + context.Device.System.AccountManager.OpenUserOnlinePlay(userId); + + Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() }); return ResultCode.Success; } @@ -171,12 +168,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.InvalidArgument; } - if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile)) - { - profile.OnlinePlayState = AccountState.Closed; - } + context.Device.System.AccountManager.CloseUserOnlinePlay(userId); - Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString(), profile.OnlinePlayState }); + Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() }); return ResultCode.Success; } @@ -190,12 +184,12 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator // Pid placeholder context.RequestData.ReadInt64(); - long position = context.Request.PtrBuff[0].Position; - long size = context.Request.PtrBuff[0].Size; + ulong position = context.Request.PtrBuff[0].Position; + ulong size = context.Request.PtrBuff[0].Size; byte[] bufferContent = new byte[size]; - context.Memory.Read((ulong)position, bufferContent); + context.Memory.Read(position, bufferContent); if (uuid.IsNull) { @@ -221,9 +215,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator bool unknownBool = context.RequestData.ReadBoolean(); UserId userId = context.RequestData.ReadStruct<UserId>(); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40L); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL); - long bufferPosition = context.Request.RecvListBuff[0].Position; + ulong bufferPosition = context.Request.RecvListBuff[0].Position; if (userId.IsNull) { @@ -271,8 +265,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator */ - context.Memory.Write((ulong)bufferPosition, playHistoryRegistrationKeyBuffer); - context.Memory.Write((ulong)bufferPosition + 0x20, new byte[0x20]); // HmacHash + context.Memory.Write(bufferPosition, playHistoryRegistrationKeyBuffer); + context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash return ResultCode.Success; } @@ -287,14 +281,14 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator context.RequestData.ReadInt64(); long pid = context.Process.Pid; - long playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position; - long PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size; + ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position; + ulong PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size; - long inAppScreenName1Position = context.Request.PtrBuff[1].Position; - long inAppScreenName1Size = context.Request.PtrBuff[1].Size; + ulong inAppScreenName1Position = context.Request.PtrBuff[1].Position; + ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size; - long inAppScreenName2Position = context.Request.PtrBuff[2].Position; - long inAppScreenName2Size = context.Request.PtrBuff[2].Size; + ulong inAppScreenName2Position = context.Request.PtrBuff[2].Position; + ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size; if (userId.IsNull || inAppScreenName1Size > 0x48 || inAppScreenName2Size > 0x48) { diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs index c4d251f9..7774af23 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/FileSystemProxyHelper.cs @@ -116,12 +116,12 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy public static Result ReadFsPath(out FsPath path, ServiceCtx context, int index = 0) { - long position = context.Request.PtrBuff[index].Position; - long size = context.Request.PtrBuff[index].Size; + ulong position = context.Request.PtrBuff[index].Position; + ulong size = context.Request.PtrBuff[index].Size; byte[] pathBytes = new byte[size]; - context.Memory.Read((ulong)position, pathBytes); + context.Memory.Read(position, pathBytes); return FsPath.FromSpan(out path, pathBytes); } diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs index 014e11f4..565ddc4c 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IDirectory.cs @@ -18,15 +18,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries) public ResultCode Read(ServiceCtx context) { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferLen = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; byte[] entriesBytes = new byte[bufferLen]; Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entriesBytes); Result result = _baseDirectory.Read(out long entriesRead, entries); - context.Memory.Write((ulong)bufferPosition, entriesBytes); + context.Memory.Write(bufferPosition, entriesBytes); context.ResponseData.Write(entriesRead); return (ResultCode)result.Value; diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs index b5f342f0..681b6c17 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IFile.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf) public ResultCode Read(ServiceCtx context) { - long position = context.Request.ReceiveBuff[0].Position; + ulong position = context.Request.ReceiveBuff[0].Position; ReadOption readOption = new ReadOption(context.RequestData.ReadInt32()); context.RequestData.BaseStream.Position += 4; @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy Result result = _baseFile.Read(out long bytesRead, offset, data, readOption); - context.Memory.Write((ulong)position, data); + context.Memory.Write(position, data); context.ResponseData.Write(bytesRead); @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Write(u32 writeOption, u64 offset, u64 size, buffer<u8, 0x45, 0>) public ResultCode Write(ServiceCtx context) { - long position = context.Request.SendBuff[0].Position; + ulong position = context.Request.SendBuff[0].Position; WriteOption writeOption = new WriteOption(context.RequestData.ReadInt32()); context.RequestData.BaseStream.Position += 4; @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy byte[] data = new byte[size]; - context.Memory.Read((ulong)position, data); + context.Memory.Read(position, data); return (ResultCode)_baseFile.Write(offset, data, writeOption).Value; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs index 7889be4b..89955634 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/FileSystemProxy/IStorage.cs @@ -17,8 +17,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer public ResultCode Read(ServiceCtx context) { - long offset = context.RequestData.ReadInt64(); - long size = context.RequestData.ReadInt64(); + ulong offset = context.RequestData.ReadUInt64(); + ulong size = context.RequestData.ReadUInt64(); if (context.Request.ReceiveBuff.Count > 0) { @@ -32,9 +32,9 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy byte[] data = new byte[size]; - Result result = _baseStorage.Read(offset, data); + Result result = _baseStorage.Read((long)offset, data); - context.Memory.Write((ulong)buffDesc.Position, data); + context.Memory.Write(buffDesc.Position, data); return (ResultCode)result.Value; } diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index eaaf1fe9..fd8844c7 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs public ResultCode OpenFileSystemWithId(ServiceCtx context) { FileSystemType fileSystemType = (FileSystemType)context.RequestData.ReadInt32(); - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); string switchPath = ReadUtf8String(context); string fullPath = context.Device.FileSystem.SwitchPathToSystemPath(switchPath); @@ -337,14 +337,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64(); SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>(); - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferLen = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; byte[] infoBuffer = new byte[bufferLen]; Result result = _baseFileSystemProxy.FindSaveDataWithFilter(out long count, infoBuffer, spaceId, ref filter); - context.Memory.Write((ulong)bufferPosition, infoBuffer); + context.Memory.Write(bufferPosition, infoBuffer); context.ResponseData.Write(count); return (ResultCode)result.Value; @@ -392,11 +392,11 @@ namespace Ryujinx.HLE.HOS.Services.Fs { StorageId storageId = (StorageId)context.RequestData.ReadByte(); byte[] padding = context.RequestData.ReadBytes(7); - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); // We do a mitm here to find if the request is for an AOC. // This is because AOC can be distributed over multiple containers in the emulator. - if (context.Device.System.ContentManager.GetAocDataStorage((ulong)titleId, out LibHac.Fs.IStorage aocStorage)) + if (context.Device.System.ContentManager.GetAocDataStorage((ulong)titleId, out LibHac.Fs.IStorage aocStorage, context.Device.Configuration.FsIntegrityCheckLevel)) { Logger.Info?.Print(LogClass.Loader, $"Opened AddOnContent Data TitleID={titleId:X16}"); diff --git a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs index 7c5d5e61..d6449a2d 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/ISaveDataInfoReader.cs @@ -16,14 +16,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs // ReadSaveDataInfo() -> (u64, buffer<unknown, 6>) public ResultCode ReadSaveDataInfo(ServiceCtx context) { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferLen = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferLen = context.Request.ReceiveBuff[0].Size; byte[] infoBuffer = new byte[bufferLen]; Result result = _baseReader.Target.Read(out long readCount, infoBuffer); - context.Memory.Write((ulong)bufferPosition, infoBuffer); + context.Memory.Write(bufferPosition, infoBuffer); context.ResponseData.Write(readCount); return (ResultCode)result.Value; diff --git a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index 18a7ba11..61a12d9e 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -3,6 +3,14 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.Common.Configuration.Hid; using System.Collections.Generic; using System.Runtime.CompilerServices; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad; namespace Ryujinx.HLE.HOS.Services.Hid { @@ -12,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid private readonly ulong _hidMemoryAddress; - internal ref HidSharedMemory SharedMemory => ref _device.Memory.GetRef<HidSharedMemory>(_hidMemoryAddress); + internal ref SharedMemory SharedMemory => ref _device.Memory.GetRef<SharedMemory>(_hidMemoryAddress); internal const int SharedMemEntryCount = 17; @@ -22,40 +30,30 @@ namespace Ryujinx.HLE.HOS.Services.Hid public KeyboardDevice Keyboard; public NpadDevices Npads; - static Hid() + private static void CheckTypeSizeOrThrow<T>(int expectedSize) { - if (Unsafe.SizeOf<ShMemDebugPad>() != 0x400) - { - throw new InvalidStructLayoutException<ShMemDebugPad>(0x400); - } - if (Unsafe.SizeOf<ShMemTouchScreen>() != 0x3000) - { - throw new InvalidStructLayoutException<ShMemTouchScreen>(0x3000); - } - if (Unsafe.SizeOf<ShMemKeyboard>() != 0x400) - { - throw new InvalidStructLayoutException<ShMemKeyboard>(0x400); - } - if (Unsafe.SizeOf<ShMemMouse>() != 0x400) - { - throw new InvalidStructLayoutException<ShMemMouse>(0x400); - } - if (Unsafe.SizeOf<ShMemNpad>() != 0x5000) + if (Unsafe.SizeOf<T>() != expectedSize) { - throw new InvalidStructLayoutException<ShMemNpad>(0x5000); - } - if (Unsafe.SizeOf<HidSharedMemory>() != Horizon.HidSize) - { - throw new InvalidStructLayoutException<HidSharedMemory>(Horizon.HidSize); + throw new InvalidStructLayoutException<T>(expectedSize); } } + static Hid() + { + CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8); + CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38); + CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350); + CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8); + CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000); + CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize); + } + public Hid(in Switch device, ulong sharedHidMemoryAddress) { _device = device; _hidMemoryAddress = sharedHidMemoryAddress; - device.Memory.ZeroFill(sharedHidMemoryAddress, Horizon.HidSize); + SharedMemory = SharedMemory.Create(); } public void InitDevices() @@ -67,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Npads = new NpadDevices(_device, true); } - internal void RefreshInputConfig(List<InputConfig> inputConfig) + public void RefreshInputConfig(List<InputConfig> inputConfig) { ControllerConfig[] npadConfig = new ControllerConfig[inputConfig.Count]; @@ -80,11 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid _device.Hid.Npads.Configure(npadConfig); } - internal void RefreshInputConfigEvent(object _, ReactiveEventArgs<List<InputConfig>> args) - { - RefreshInputConfig(args.NewValue); - } - public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick) { const int stickButtonThreshold = short.MaxValue / 2; diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs index 59d6dfa3..e8bf628a 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/BaseDevice.cs @@ -12,18 +12,5 @@ namespace Ryujinx.HLE.HOS.Services.Hid _device = device; Active = active; } - - internal static int UpdateEntriesHeader(ref CommonEntriesHeader header, out int previousEntry) - { - header.NumEntries = SharedMemEntryCount; - header.MaxEntryIndex = SharedMemEntryCount - 1; - - previousEntry = (int)header.LatestEntry; - header.LatestEntry = (header.LatestEntry + 1) % SharedMemEntryCount; - - header.TimestampTicks = GetTimestampTicks(); - - return (int)header.LatestEntry; // EntryCount shouldn't overflow int - } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs index 7e708e32..e3b95390 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugPadDevice.cs @@ -1,3 +1,6 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; + namespace Ryujinx.HLE.HOS.Services.Hid { public class DebugPadDevice : BaseDevice @@ -6,20 +9,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid public void Update() { - ref ShMemDebugPad debugPad = ref _device.Hid.SharedMemory.DebugPad; + ref RingLifo<DebugPadState> lifo = ref _device.Hid.SharedMemory.DebugPad; + + ref DebugPadState previousEntry = ref lifo.GetCurrentEntryRef(); - int currentIndex = UpdateEntriesHeader(ref debugPad.Header, out int previousIndex); + DebugPadState newState = new DebugPadState(); - if (!Active) + if (Active) { - return; + // TODO: This is a debug device only present in dev environment, do we want to support it? } - ref DebugPadEntry currentEntry = ref debugPad.Entries[currentIndex]; - DebugPadEntry previousEntry = debugPad.Entries[previousIndex]; + newState.SamplingNumber = previousEntry.SamplingNumber + 1; - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; + lifo.Write(ref newState); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs index e8ed6a3e..99dc078d 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/KeyboardDevice.cs @@ -1,3 +1,7 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; +using System; + namespace Ryujinx.HLE.HOS.Services.Hid { public class KeyboardDevice : BaseDevice @@ -6,27 +10,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid public unsafe void Update(KeyboardInput keyState) { - ref ShMemKeyboard keyboard = ref _device.Hid.SharedMemory.Keyboard; - - int currentIndex = UpdateEntriesHeader(ref keyboard.Header, out int previousIndex); + ref RingLifo<KeyboardState> lifo = ref _device.Hid.SharedMemory.Keyboard; if (!Active) { + lifo.Clear(); + return; } - ref KeyboardState currentEntry = ref keyboard.Entries[currentIndex]; - KeyboardState previousEntry = keyboard.Entries[previousIndex]; + ref KeyboardState previousEntry = ref lifo.GetCurrentEntryRef(); - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; - - for (int i = 0; i < 8; ++i) + KeyboardState newState = new KeyboardState { - currentEntry.Keys[i] = (uint)keyState.Keys[i]; - } + SamplingNumber = previousEntry.SamplingNumber + 1, + }; + + keyState.Keys.AsSpan().CopyTo(newState.Keys.RawData.ToSpan()); + newState.Modifiers = (KeyboardModifier)keyState.Modifier; - currentEntry.Modifier = (ulong)keyState.Modifier; + lifo.Write(ref newState); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs index ee58a563..e07c1d20 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/MouseDevice.cs @@ -1,37 +1,35 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; + namespace Ryujinx.HLE.HOS.Services.Hid { public class MouseDevice : BaseDevice { public MouseDevice(Switch device, bool active) : base(device, active) { } - public void Update(int mouseX, int mouseY, int buttons = 0, int scrollX = 0, int scrollY = 0) + public void Update(int mouseX, int mouseY, uint buttons = 0, int scrollX = 0, int scrollY = 0) { - ref ShMemMouse mouse = ref _device.Hid.SharedMemory.Mouse; + ref RingLifo<MouseState> lifo = ref _device.Hid.SharedMemory.Mouse; - int currentIndex = UpdateEntriesHeader(ref mouse.Header, out int previousIndex); + ref MouseState previousEntry = ref lifo.GetCurrentEntryRef(); + + MouseState newState = new MouseState() + { + SamplingNumber = previousEntry.SamplingNumber + 1, + }; - if (!Active) + if (Active) { - return; + newState.Buttons = (MouseButton)buttons; + newState.X = mouseX; + newState.Y = mouseY; + newState.DeltaX = mouseX - previousEntry.DeltaX; + newState.DeltaY = mouseY - previousEntry.DeltaY; + newState.WheelDeltaX = scrollX; + newState.WheelDeltaY = scrollY; } - ref MouseState currentEntry = ref mouse.Entries[currentIndex]; - MouseState previousEntry = mouse.Entries[previousIndex]; - - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; - - currentEntry.Buttons = (ulong)buttons; - - currentEntry.Position = new MousePosition - { - X = mouseX, - Y = mouseY, - VelocityX = mouseX - previousEntry.Position.X, - VelocityY = mouseY - previousEntry.Position.Y, - ScrollVelocityX = scrollX, - ScrollVelocityY = scrollY - }; + lifo.Write(ref newState); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 2150f278..3ff7e733 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -1,16 +1,17 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Hid.Types; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad; namespace Ryujinx.HLE.HOS.Services.Hid { public class NpadDevices : BaseDevice { - private const BatteryCharge DefaultBatteryCharge = BatteryCharge.Percent100; - private const int NoMatchNotifyFrequencyMs = 2000; private int _activeCount; private long _lastNotifyTimestamp; @@ -86,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid continue; } - ControllerType currentType = _device.Hid.SharedMemory.Npads[i].Header.Type; + ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet; if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i]) { @@ -135,12 +136,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid { Remap(); - UpdateAllEntries(); + Span<bool> updated = stackalloc bool[10]; // Update configured inputs for (int i = 0; i < states.Count; ++i) { - UpdateInput(states[i]); + GamepadInput state = states[i]; + + updated[(int)state.PlayerId] = true; + + UpdateInput(state); + } + + for (int i = 0; i < updated.Length; i++) + { + if (!updated[i]) + { + UpdateDisconnectedInput((PlayerIndex)i); + } } } @@ -185,16 +198,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid private void SetupNpad(PlayerIndex player, ControllerType type) { - ref ShMemNpad controller = ref _device.Hid.SharedMemory.Npads[(int)player]; + ref NpadInternalState controller = ref _device.Hid.SharedMemory.Npads[(int)player].InternalState; - ControllerType oldType = controller.Header.Type; + ControllerType oldType = (ControllerType)controller.StyleSet; if (oldType == type) { return; // Already configured } - controller = new ShMemNpad(); // Zero it + controller = NpadInternalState.Create(); // Reset it if (type == ControllerType.None) { @@ -207,87 +220,151 @@ namespace Ryujinx.HLE.HOS.Services.Hid } // TODO: Allow customizing colors at config - NpadStateHeader defaultHeader = new NpadStateHeader - { - IsHalf = false, - SingleColorBody = NpadColor.BodyGray, - SingleColorButtons = NpadColor.ButtonGray, - LeftColorBody = NpadColor.BodyNeonBlue, - LeftColorButtons = NpadColor.ButtonGray, - RightColorBody = NpadColor.BodyNeonRed, - RightColorButtons = NpadColor.ButtonGray - }; - - controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected | - NpadSystemProperties.PowerInfo1Connected | - NpadSystemProperties.PowerInfo2Connected; - - controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge); + controller.JoyAssignmentMode = NpadJoyAssignmentMode.Dual; + controller.FullKeyColor.FullKeyBody = (uint)NpadColor.BodyGray; + controller.FullKeyColor.FullKeyButtons = (uint)NpadColor.ButtonGray; + controller.JoyColor.LeftBody = (uint)NpadColor.BodyNeonBlue; + controller.JoyColor.LeftButtons = (uint)NpadColor.ButtonGray; + controller.JoyColor.RightBody = (uint)NpadColor.BodyNeonRed; + controller.JoyColor.RightButtons = (uint)NpadColor.ButtonGray; + + controller.SystemProperties = NpadSystemProperties.IsPoweredJoyDual | + NpadSystemProperties.IsPoweredJoyLeft | + NpadSystemProperties.IsPoweredJoyRight; + + controller.BatteryLevelJoyDual = NpadBatteryLevel.Percent100; + controller.BatteryLevelJoyLeft = NpadBatteryLevel.Percent100; + controller.BatteryLevelJoyRight = NpadBatteryLevel.Percent100; switch (type) { case ControllerType.ProController: - defaultHeader.Type = ControllerType.ProController; + controller.StyleSet = NpadStyleTag.FullKey; controller.DeviceType = DeviceType.FullKey; - controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented | - NpadSystemProperties.PlusButtonCapability | - NpadSystemProperties.MinusButtonCapability; + controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented | + NpadSystemProperties.IsPlusAvailable | + NpadSystemProperties.IsMinusAvailable; break; case ControllerType.Handheld: - defaultHeader.Type = ControllerType.Handheld; + controller.StyleSet = NpadStyleTag.Handheld; controller.DeviceType = DeviceType.HandheldLeft | DeviceType.HandheldRight; - controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented | - NpadSystemProperties.PlusButtonCapability | - NpadSystemProperties.MinusButtonCapability; + controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented | + NpadSystemProperties.IsPlusAvailable | + NpadSystemProperties.IsMinusAvailable; break; case ControllerType.JoyconPair: - defaultHeader.Type = ControllerType.JoyconPair; + controller.StyleSet = NpadStyleTag.JoyDual; controller.DeviceType = DeviceType.JoyLeft | DeviceType.JoyRight; - controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented | - NpadSystemProperties.PlusButtonCapability | - NpadSystemProperties.MinusButtonCapability; + controller.SystemProperties |= NpadSystemProperties.IsAbxyButtonOriented | + NpadSystemProperties.IsPlusAvailable | + NpadSystemProperties.IsMinusAvailable; break; case ControllerType.JoyconLeft: - defaultHeader.Type = ControllerType.JoyconLeft; - defaultHeader.IsHalf = true; + controller.StyleSet = NpadStyleTag.JoyLeft; + controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single; controller.DeviceType = DeviceType.JoyLeft; - controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented | - NpadSystemProperties.MinusButtonCapability; + controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented | + NpadSystemProperties.IsMinusAvailable; break; case ControllerType.JoyconRight: - defaultHeader.Type = ControllerType.JoyconRight; - defaultHeader.IsHalf = true; + controller.StyleSet = NpadStyleTag.JoyRight; + controller.JoyAssignmentMode = NpadJoyAssignmentMode.Single; controller.DeviceType = DeviceType.JoyRight; - controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented | - NpadSystemProperties.PlusButtonCapability; + controller.SystemProperties |= NpadSystemProperties.IsSlSrButtonOriented | + NpadSystemProperties.IsPlusAvailable; break; case ControllerType.Pokeball: - defaultHeader.Type = ControllerType.Pokeball; + controller.StyleSet = NpadStyleTag.Palma; controller.DeviceType = DeviceType.Palma; break; } - controller.Header = defaultHeader; - _styleSetUpdateEvents[(int)player].ReadableEvent.Signal(); _activeCount++; Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}"); } - private static NpadLayoutsIndex ControllerTypeToNpadLayout(ControllerType controllerType) - => controllerType switch + private ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad) + { + switch (npad.StyleSet) + { + case NpadStyleTag.FullKey: + return ref npad.FullKey; + case NpadStyleTag.Handheld: + return ref npad.Handheld; + case NpadStyleTag.JoyDual: + return ref npad.JoyDual; + case NpadStyleTag.JoyLeft: + return ref npad.JoyLeft; + case NpadStyleTag.JoyRight: + return ref npad.JoyRight; + case NpadStyleTag.Palma: + return ref npad.Palma; + default: + return ref npad.SystemExt; + } + } + + private void UpdateUnusedInputIfNotEqual(ref RingLifo<NpadCommonState> currentlyUsed, ref RingLifo<NpadCommonState> possiblyUnused) + { + bool isEquals; + + unsafe + { + var aPointer = Unsafe.AsPointer(ref currentlyUsed); + var bPointer = Unsafe.AsPointer(ref possiblyUnused); + + isEquals = aPointer == bPointer; + } + + if (!isEquals) + { + NpadCommonState newState = new NpadCommonState(); + + WriteNewInputEntry(ref possiblyUnused, ref newState); + } + } + + private void WriteNewInputEntry(ref RingLifo<NpadCommonState> lifo, ref NpadCommonState state) + { + ref NpadCommonState previousEntry = ref lifo.GetCurrentEntryRef(); + + state.SamplingNumber = previousEntry.SamplingNumber + 1; + + lifo.Write(ref state); + } + + private void UpdateUnusedSixInputIfNotEqual(ref RingLifo<SixAxisSensorState> currentlyUsed, ref RingLifo<SixAxisSensorState> possiblyUnused) { - ControllerType.ProController => NpadLayoutsIndex.ProController, - ControllerType.Handheld => NpadLayoutsIndex.Handheld, - ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual, - ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft, - ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight, - ControllerType.Pokeball => NpadLayoutsIndex.Pokeball, - _ => NpadLayoutsIndex.SystemExternal - }; + bool isEquals; + + unsafe + { + var aPointer = Unsafe.AsPointer(ref currentlyUsed); + var bPointer = Unsafe.AsPointer(ref possiblyUnused); + + isEquals = aPointer == bPointer; + } + + if (!isEquals) + { + SixAxisSensorState newState = new SixAxisSensorState(); + + WriteNewSixInputEntry(ref possiblyUnused, ref newState); + } + } + + private void WriteNewSixInputEntry(ref RingLifo<SixAxisSensorState> lifo, ref SixAxisSensorState state) + { + ref SixAxisSensorState previousEntry = ref lifo.GetCurrentEntryRef(); + + state.SamplingNumber = previousEntry.SamplingNumber + 1; + + lifo.Write(ref state); + } private void UpdateInput(GamepadInput state) { @@ -296,43 +373,88 @@ namespace Ryujinx.HLE.HOS.Services.Hid return; } - ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId]; + ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState; - if (currentNpad.Header.Type == ControllerType.None) + if (currentNpad.StyleSet == NpadStyleTag.None) { return; } - ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToNpadLayout(currentNpad.Header.Type)]; - ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry]; + ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad); + + NpadCommonState newState = new NpadCommonState + { + Buttons = (NpadButton)state.Buttons, + AnalogStickL = new AnalogStickState + { + X = state.LStick.Dx, + Y = state.LStick.Dy, + }, + AnalogStickR = new AnalogStickState + { + X = state.RStick.Dx, + Y = state.RStick.Dy, + } + }; + + newState.Attributes = NpadAttribute.IsConnected; + + switch (currentNpad.StyleSet) + { + case NpadStyleTag.Handheld: + case NpadStyleTag.FullKey: + newState.Attributes |= NpadAttribute.IsWired; + break; + case NpadStyleTag.JoyDual: + newState.Attributes |= NpadAttribute.IsLeftConnected | + NpadAttribute.IsRightConnected; + break; + case NpadStyleTag.JoyLeft: + newState.Attributes |= NpadAttribute.IsLeftConnected; + break; + case NpadStyleTag.JoyRight: + newState.Attributes |= NpadAttribute.IsRightConnected; + break; + } - currentEntry.Buttons = state.Buttons; - currentEntry.LStickX = state.LStick.Dx; - currentEntry.LStickY = state.LStick.Dy; - currentEntry.RStickX = state.RStick.Dx; - currentEntry.RStickY = state.RStick.Dy; + WriteNewInputEntry(ref lifo, ref newState); // Mirror data to Default layout just in case - ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal]; - mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry; + if (!currentNpad.StyleSet.HasFlag(NpadStyleTag.SystemExt)) + { + WriteNewInputEntry(ref currentNpad.SystemExt, ref newState); + } + + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.FullKey); + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Handheld); + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyDual); + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyLeft); + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.JoyRight); + UpdateUnusedInputIfNotEqual(ref lifo, ref currentNpad.Palma); } - private static SixAxixLayoutsIndex ControllerTypeToSixAxisLayout(ControllerType controllerType) - => controllerType switch + private void UpdateDisconnectedInput(PlayerIndex index) { - ControllerType.ProController => SixAxixLayoutsIndex.ProController, - ControllerType.Handheld => SixAxixLayoutsIndex.Handheld, - ControllerType.JoyconPair => SixAxixLayoutsIndex.JoyDualLeft, - ControllerType.JoyconLeft => SixAxixLayoutsIndex.JoyLeft, - ControllerType.JoyconRight => SixAxixLayoutsIndex.JoyRight, - ControllerType.Pokeball => SixAxixLayoutsIndex.Pokeball, - _ => SixAxixLayoutsIndex.SystemExternal - }; + ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState; + + NpadCommonState newState = new NpadCommonState(); + + WriteNewInputEntry(ref currentNpad.FullKey, ref newState); + WriteNewInputEntry(ref currentNpad.Handheld, ref newState); + WriteNewInputEntry(ref currentNpad.JoyDual, ref newState); + WriteNewInputEntry(ref currentNpad.JoyLeft, ref newState); + WriteNewInputEntry(ref currentNpad.JoyRight, ref newState); + WriteNewInputEntry(ref currentNpad.Palma, ref newState); + } public void UpdateSixAxis(IList<SixAxisInput> states) { + Span<bool> updated = stackalloc bool[10]; + for (int i = 0; i < states.Count; ++i) { + updated[(int)states[i].PlayerId] = true; + if (SetSixAxisState(states[i])) { i++; @@ -345,6 +467,40 @@ namespace Ryujinx.HLE.HOS.Services.Hid SetSixAxisState(states[i], true); } } + + for (int i = 0; i < updated.Length; i++) + { + if (!updated[i]) + { + UpdateDisconnectedInputSixAxis((PlayerIndex)i); + } + } + } + + private ref RingLifo<SixAxisSensorState> GetSixAxisSensorLifo(ref NpadInternalState npad, bool isRightPair) + { + switch (npad.StyleSet) + { + case NpadStyleTag.FullKey: + return ref npad.FullKeySixAxisSensor; + case NpadStyleTag.Handheld: + return ref npad.HandheldSixAxisSensor; + case NpadStyleTag.JoyDual: + if (isRightPair) + { + return ref npad.JoyDualRightSixAxisSensor; + } + else + { + return ref npad.JoyDualSixAxisSensor; + } + case NpadStyleTag.JoyLeft: + return ref npad.JoyLeftSixAxisSensor; + case NpadStyleTag.JoyRight: + return ref npad.JoyRightSixAxisSensor; + default: + throw new NotImplementedException($"{npad.StyleSet}"); + } } private bool SetSixAxisState(SixAxisInput state, bool isRightPair = false) @@ -354,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid return false; } - ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId]; + ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId].InternalState; - if (currentNpad.Header.Type == ControllerType.None) + if (currentNpad.StyleSet == NpadStyleTag.None) { return false; } @@ -382,87 +538,57 @@ namespace Ryujinx.HLE.HOS.Services.Hid Z = state.Rotation.Z }; - ref NpadSixAxis currentLayout = ref currentNpad.Sixaxis[(int)ControllerTypeToSixAxisLayout(currentNpad.Header.Type) + (isRightPair ? 1 : 0)]; - ref SixAxisState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry]; + SixAxisSensorState newState = new SixAxisSensorState + { + Acceleration = accel, + AngularVelocity = gyro, + Angle = rotation, + Attributes = SixAxisSensorAttribute.IsConnected + }; - int previousEntryIndex = (int)(currentLayout.Header.LatestEntry == 0 ? - currentLayout.Header.MaxEntryIndex : currentLayout.Header.LatestEntry - 1); + state.Orientation.AsSpan().CopyTo(newState.Direction.ToSpan()); - ref SixAxisState previousEntry = ref currentLayout.Entries[previousEntryIndex]; + ref RingLifo<SixAxisSensorState> lifo = ref GetSixAxisSensorLifo(ref currentNpad, isRightPair); - currentEntry.Accelerometer = accel; - currentEntry.Gyroscope = gyro; - currentEntry.Rotations = rotation; + WriteNewSixInputEntry(ref lifo, ref newState); - unsafe + bool needUpdateRight = currentNpad.StyleSet == NpadStyleTag.JoyDual && !isRightPair; + + if (!isRightPair) { - for (int i = 0; i < 9; i++) - { - currentEntry.Orientation[i] = state.Orientation[i]; - } + UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.FullKeySixAxisSensor); + UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.HandheldSixAxisSensor); + UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyDualSixAxisSensor); + UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyLeftSixAxisSensor); + UpdateUnusedSixInputIfNotEqual(ref lifo, ref currentNpad.JoyRightSixAxisSensor); } - return currentNpad.Header.Type == ControllerType.JoyconPair && !isRightPair; - } - - private void UpdateAllEntries() - { - ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads; - for (int i = 0; i < controllers.Length; ++i) + if (!needUpdateRight) { - ref Array7<NpadLayout> layouts = ref controllers[i].Layouts; - for (int l = 0; l < layouts.Length; ++l) - { - ref NpadLayout currentLayout = ref layouts[l]; - int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex); + SixAxisSensorState emptyState = new SixAxisSensorState(); - ref NpadState currentEntry = ref currentLayout.Entries[currentIndex]; - NpadState previousEntry = currentLayout.Entries[previousIndex]; + emptyState.Attributes = SixAxisSensorAttribute.IsConnected; - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; - - if (controllers[i].Header.Type == ControllerType.None) - { - continue; - } - - currentEntry.ConnectionState = NpadConnectionState.ControllerStateConnected; + WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref emptyState); + } - switch (controllers[i].Header.Type) - { - case ControllerType.Handheld: - case ControllerType.ProController: - currentEntry.ConnectionState |= NpadConnectionState.ControllerStateWired; - break; - case ControllerType.JoyconPair: - currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected | - NpadConnectionState.JoyRightConnected; - break; - case ControllerType.JoyconLeft: - currentEntry.ConnectionState |= NpadConnectionState.JoyLeftConnected; - break; - case ControllerType.JoyconRight: - currentEntry.ConnectionState |= NpadConnectionState.JoyRightConnected; - break; - } - } + return needUpdateRight; + } - ref Array6<NpadSixAxis> sixaxis = ref controllers[i].Sixaxis; - for (int l = 0; l < sixaxis.Length; ++l) - { - ref NpadSixAxis currentLayout = ref sixaxis[l]; - int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex); + private void UpdateDisconnectedInputSixAxis(PlayerIndex index) + { + ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState; - ref SixAxisState currentEntry = ref currentLayout.Entries[currentIndex]; - SixAxisState previousEntry = currentLayout.Entries[previousIndex]; + SixAxisSensorState newState = new SixAxisSensorState(); - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; + newState.Attributes = SixAxisSensorAttribute.IsConnected; - currentEntry._unknown2 = 1; - } - } + WriteNewSixInputEntry(ref currentNpad.FullKeySixAxisSensor, ref newState); + WriteNewSixInputEntry(ref currentNpad.HandheldSixAxisSensor, ref newState); + WriteNewSixInputEntry(ref currentNpad.JoyDualSixAxisSensor, ref newState); + WriteNewSixInputEntry(ref currentNpad.JoyDualRightSixAxisSensor, ref newState); + WriteNewSixInputEntry(ref currentNpad.JoyLeftSixAxisSensor, ref newState); + WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs index 10c34453..432a37e3 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs @@ -1,3 +1,5 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen; using System; namespace Ryujinx.HLE.HOS.Services.Hid @@ -8,39 +10,38 @@ namespace Ryujinx.HLE.HOS.Services.Hid public void Update(params TouchPoint[] points) { - ref ShMemTouchScreen touchscreen = ref _device.Hid.SharedMemory.TouchScreen; + ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen; - int currentIndex = UpdateEntriesHeader(ref touchscreen.Header, out int previousIndex); + ref TouchScreenState previousEntry = ref lifo.GetCurrentEntryRef(); - if (!Active) + TouchScreenState newState = new TouchScreenState { - return; - } - - ref TouchScreenState currentEntry = ref touchscreen.Entries[currentIndex]; - TouchScreenState previousEntry = touchscreen.Entries[previousIndex]; - - currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1; - currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1; + SamplingNumber = previousEntry.SamplingNumber + 1 + }; - currentEntry.NumTouches = (ulong)points.Length; + if (Active) + { + newState.TouchesCount = points.Length; - int pointsLength = Math.Min(points.Length, currentEntry.Touches.Length); + int pointsLength = Math.Min(points.Length, newState.Touches.Length); - for (int i = 0; i < pointsLength; ++i) - { - TouchPoint pi = points[i]; - currentEntry.Touches[i] = new TouchScreenStateData + for (int i = 0; i < pointsLength; ++i) { - SampleTimestamp = currentEntry.SampleTimestamp, - X = pi.X, - Y = pi.Y, - TouchIndex = (uint)i, - DiameterX = pi.DiameterX, - DiameterY = pi.DiameterY, - Angle = pi.Angle - }; + TouchPoint pi = points[i]; + newState.Touches[i] = new TouchState + { + DeltaTime = newState.SamplingNumber, + X = pi.X, + Y = pi.Y, + FingerId = (uint)i, + DiameterX = pi.DiameterX, + DiameterY = pi.DiameterY, + RotationAngle = pi.Angle + }; + } } + + lifo.Write(ref newState); } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs index 26681270..be6857fb 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/KeyboardInput.cs @@ -3,6 +3,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid public struct KeyboardInput { public int Modifier; - public int[] Keys; + public ulong[] Keys; } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index e45e695f..e3c22edf 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -3,7 +3,10 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Hid.HidServer; +using Ryujinx.HLE.HOS.Services.Hid.Types; using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid { @@ -69,6 +72,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long appletResourceUserId = context.RequestData.ReadInt64(); + // Initialize entries to avoid issues with some games. + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.DebugPad.Update(); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); return ResultCode.Success; @@ -82,6 +92,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.Device.Hid.Touchscreen.Active = true; + // Initialize entries to avoid issues with some games. + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.Touchscreen.Update(); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); return ResultCode.Success; @@ -95,6 +112,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.Device.Hid.Mouse.Active = true; + // Initialize entries to avoid issues with some games. + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.Mouse.Update(0, 0); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); return ResultCode.Success; @@ -108,6 +132,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid context.Device.Hid.Keyboard.Active = true; + // Initialize entries to avoid issues with some games. + + KeyboardInput emptyInput = new KeyboardInput(); + emptyInput.Keys = new ulong[4]; + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.Keyboard.Update(emptyInput); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); return ResultCode.Success; @@ -590,25 +624,22 @@ namespace Ryujinx.HLE.HOS.Services.Hid public ResultCode SetSupportedNpadIdType(ServiceCtx context) { long appletResourceUserId = context.RequestData.ReadInt64(); - long arraySize = context.Request.PtrBuff[0].Size / 4; + ulong arrayPosition = context.Request.PtrBuff[0].Position; + ulong arraySize = context.Request.PtrBuff[0].Size; - NpadIdType[] supportedPlayerIds = new NpadIdType[arraySize]; + ReadOnlySpan<NpadIdType> supportedPlayerIds = MemoryMarshal.Cast<byte, NpadIdType>(context.Memory.GetSpan(arrayPosition, (int)arraySize)); context.Device.Hid.Npads.ClearSupportedPlayers(); - for (int i = 0; i < arraySize; ++i) + for (int i = 0; i < supportedPlayerIds.Length; ++i) { - NpadIdType id = context.Memory.Read<NpadIdType>((ulong)(context.Request.PtrBuff[0].Position + i * 4)); - - if (id >= 0) + if (supportedPlayerIds[i] >= 0) { - context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(id)); + context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i])); } - - supportedPlayerIds[i] = id; } - Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{arraySize} " + string.Join(",", supportedPlayerIds)); + Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} " + string.Join(",", supportedPlayerIds.ToArray())); return ResultCode.Success; } @@ -620,6 +651,32 @@ namespace Ryujinx.HLE.HOS.Services.Hid long appletResourceUserId = context.RequestData.ReadInt64(); context.Device.Hid.Npads.Active = true; + + // Initialize entries to avoid issues with some games. + + List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>(); + List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>(); + + for (int player = 0; player < NpadDevices.MaxControllers; player++) + { + GamepadInput gamepadInput = new GamepadInput(); + SixAxisInput sixaxisInput = new SixAxisInput(); + + gamepadInput.PlayerId = (PlayerIndex)player; + sixaxisInput.PlayerId = (PlayerIndex)player; + + sixaxisInput.Orientation = new float[9]; + + emptyGamepadInputs.Add(gamepadInput); + emptySixAxisInputs.Add(sixaxisInput); + } + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.Npads.Update(emptyGamepadInputs); + context.Device.Hid.Npads.UpdateSixAxis(emptySixAxisInputs); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); return ResultCode.Success; @@ -692,6 +749,31 @@ namespace Ryujinx.HLE.HOS.Services.Hid int revision = context.RequestData.ReadInt32(); long appletResourceUserId = context.RequestData.ReadInt64(); + // Initialize entries to avoid issues with some games. + + List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>(); + List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>(); + + for (int player = 0; player < NpadDevices.MaxControllers; player++) + { + GamepadInput gamepadInput = new GamepadInput(); + SixAxisInput sixaxisInput = new SixAxisInput(); + + gamepadInput.PlayerId = (PlayerIndex)player; + sixaxisInput.PlayerId = (PlayerIndex)player; + + sixaxisInput.Orientation = new float[9]; + + emptyGamepadInputs.Add(gamepadInput); + emptySixAxisInputs.Add(sixaxisInput); + } + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.Npads.Update(emptyGamepadInputs); + context.Device.Hid.Npads.UpdateSixAxis(emptySixAxisInputs); + } + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, revision }); return ResultCode.Success; @@ -1007,11 +1089,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid byte[] vibrationDeviceHandleBuffer = new byte[context.Request.PtrBuff[0].Size]; - context.Memory.Read((ulong)context.Request.PtrBuff[0].Position, vibrationDeviceHandleBuffer); + context.Memory.Read(context.Request.PtrBuff[0].Position, vibrationDeviceHandleBuffer); byte[] vibrationValueBuffer = new byte[context.Request.PtrBuff[1].Size]; - context.Memory.Read((ulong)context.Request.PtrBuff[1].Position, vibrationValueBuffer); + context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer); // TODO: Read all handles and values from buffer. diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs deleted file mode 100644 index 5a8d51c6..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/Boolean32.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct Boolean32 - { - private uint _value; - public static implicit operator bool(Boolean32 value) => (value._value & 1) != 0; - public static implicit operator Boolean32(bool value) => new Boolean32() { _value = value ? 1u : 0u }; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/HidVector.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs index b41bcb2e..18d9fd9c 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/HidVector.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/HidVector.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.HOS.Services.Hid +namespace Ryujinx.HLE.HOS.Services.Hid.Types { struct HidVector { diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs index 57b4b366..3c311e21 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadColor.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid { - public enum NpadColor : int + public enum NpadColor : uint { BodyGray = 0x828282, BodyNeonRed = 0xFF3C28, diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs index 5f6a68cb..1abd8468 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/NpadIdType.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid { - public enum NpadIdType + public enum NpadIdType : int { Player1 = 0, Player2 = 1, @@ -13,4 +13,4 @@ Unknown = 16, Handheld = 32 } -}
\ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadJoyHoldType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs index a6f29760..d3b51a24 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadJoyHoldType.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/NpadJoyHoldType.cs @@ -1,8 +1,8 @@ -namespace Ryujinx.HLE.HOS.Services.Hid +namespace Ryujinx.HLE.HOS.Services.Hid.Types { enum NpadJoyHoldType { Vertical, Horizontal } -}
\ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs deleted file mode 100644 index f83fdcdf..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/CommonEntriesHeader.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct CommonEntriesHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } -} - diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs deleted file mode 100644 index 3fbaa304..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct ShMemDebugPad - { - public CommonEntriesHeader Header; - public Array17<DebugPadEntry> Entries; - fixed byte _padding[0x138]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs deleted file mode 100644 index 3089fc5b..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPadEntry.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct DebugPadEntry - { - public ulong SampleTimestamp; - public ulong SampleTimestamp2; - fixed byte _unknown[0x18]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs deleted file mode 100644 index d950425d..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - // TODO: Add missing structs - unsafe struct HidSharedMemory - { - public ShMemDebugPad DebugPad; - public ShMemTouchScreen TouchScreen; - public ShMemMouse Mouse; - public ShMemKeyboard Keyboard; - public fixed byte BasicXpad[0x4 * 0x400]; - public fixed byte HomeButton[0x200]; - public fixed byte SleepButton[0x200]; - public fixed byte CaptureButton[0x200]; - public fixed byte InputDetector[0x10 * 0x80]; - public fixed byte UniquePad[0x10 * 0x400]; - public Array10<ShMemNpad> Npads; - public fixed byte Gesture[0x800]; - public fixed byte ConsoleSixAxisSensor[0x20]; - fixed byte _padding[0x3de0]; - } -} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs deleted file mode 100644 index e2c1844f..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct ShMemKeyboard - { - public CommonEntriesHeader Header; - public Array17<KeyboardState> Entries; - fixed byte _padding[0x28]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs deleted file mode 100644 index 1f54a4fd..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/KeyboardState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct KeyboardState - { - public ulong SampleTimestamp; - public ulong SampleTimestamp2; - public ulong Modifier; - public fixed uint Keys[8]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs deleted file mode 100644 index 6b99e04a..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs +++ /dev/null @@ -1,12 +0,0 @@ - -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct ShMemMouse - { - public CommonEntriesHeader Header; - public Array17<MouseState> Entries; - fixed byte _padding[0xB0]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs deleted file mode 100644 index e94c9e0c..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MousePosition.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct MousePosition - { - public int X; - public int Y; - public int VelocityX; - public int VelocityY; - public int ScrollVelocityX; - public int ScrollVelocityY; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs deleted file mode 100644 index 7856b09d..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/MouseState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct MouseState - { - public ulong SampleTimestamp; - public ulong SampleTimestamp2; - public MousePosition Position; - public ulong Buttons; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs deleted file mode 100644 index b94ab172..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/BatterCharge.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - enum BatteryCharge : int - { - Percent0 = 0, - Percent25 = 1, - Percent50 = 2, - Percent75 = 3, - Percent100 = 4 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs deleted file mode 100644 index f6d7b783..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/DeviceType.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - [Flags] - enum DeviceType : int - { - FullKey = 1 << 0, - DebugPad = 1 << 1, - HandheldLeft = 1 << 2, - HandheldRight = 1 << 3, - JoyLeft = 1 << 4, - JoyRight = 1 << 5, - Palma = 1 << 6, // Poké Ball Plus - FamicomLeft = 1 << 7, - FamicomRight = 1 << 8, - NESLeft = 1 << 9, - NESRight = 1 << 10, - HandheldFamicomLeft = 1 << 11, - HandheldFamicomRight = 1 << 12, - HandheldNESLeft = 1 << 13, - HandheldNESRight = 1 << 14, - Lucia = 1 << 15, - System = 1 << 31 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs deleted file mode 100644 index 4ef83f3d..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - // TODO: Add missing structs - unsafe struct ShMemNpad - { - public NpadStateHeader Header; - public Array7<NpadLayout> Layouts; // One for each NpadLayoutsIndex - public Array6<NpadSixAxis> Sixaxis; - public DeviceType DeviceType; - uint _padding1; - public NpadSystemProperties SystemProperties; - public uint NpadSystemButtonProperties; - public Array3<BatteryCharge> BatteryState; - public fixed byte NfcXcdDeviceHandleHeader[0x20]; - public fixed byte NfcXcdDeviceHandleState[0x20 * 2]; - public ulong Mutex; - public fixed byte NpadGcTriggerHeader[0x20]; - public fixed byte NpadGcTriggerState[0x18 * 17]; - fixed byte _padding2[0xC38]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs deleted file mode 100644 index ccc7cb8d..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadColorDescription.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - [Flags] - enum NpadColorDescription : int - { - ColorDescriptionColorsNonexistent = (1 << 1) - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs deleted file mode 100644 index 60f64fd3..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadConnectionState.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - [Flags] - enum NpadConnectionState : long - { - ControllerStateConnected = (1 << 0), - ControllerStateWired = (1 << 1), - JoyLeftConnected = (1 << 2), - JoyRightConnected = (1 << 4) - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs deleted file mode 100644 index 24c4f4d4..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct NpadLayout - { - public CommonEntriesHeader Header; - public Array17<NpadState> Entries; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs deleted file mode 100644 index c4419336..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - enum NpadLayoutsIndex : int - { - ProController = 0, - Handheld = 1, - JoyDual = 2, - JoyLeft = 3, - JoyRight = 4, - Pokeball = 5, - SystemExternal = 6 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs deleted file mode 100644 index a0a39fdc..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct NpadSixAxis - { - public CommonEntriesHeader Header; - public Array17<SixAxisState> Entries; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs deleted file mode 100644 index 60a5f9d3..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadState.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct NpadState - { - public ulong SampleTimestamp; - public ulong SampleTimestamp2; - public ControllerKeys Buttons; - public int LStickX; - public int LStickY; - public int RStickX; - public int RStickY; - public NpadConnectionState ConnectionState; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs deleted file mode 100644 index 006d4357..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadStatesHeader.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct NpadStateHeader - { - public ControllerType Type; - public Boolean32 IsHalf; - public NpadColorDescription SingleColorsDescriptor; - public NpadColor SingleColorBody; - public NpadColor SingleColorButtons; - public NpadColorDescription SplitColorsDescriptor; - public NpadColor LeftColorBody; - public NpadColor LeftColorButtons; - public NpadColor RightColorBody; - public NpadColor RightColorButtons; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs deleted file mode 100644 index 708f7da9..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSystemProperties.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - [Flags] - enum NpadSystemProperties : long - { - PowerInfo0Charging = 1 << 0, - PowerInfo1Charging = 1 << 1, - PowerInfo2Charging = 1 << 2, - PowerInfo0Connected = 1 << 3, - PowerInfo1Connected = 1 << 4, - PowerInfo2Connected = 1 << 5, - UnsupportedButtonPressedNpadSystem = 1 << 9, - UnsupportedButtonPressedNpadSystemExt = 1 << 10, - AbxyButtonOriented = 1 << 11, - SlSrButtonOriented = 1 << 12, - PlusButtonCapability = 1 << 13, - MinusButtonCapability = 1 << 14, - DirectionalButtonsSupported = 1 << 15 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs deleted file mode 100644 index a8795fc0..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisLayoutsIndex.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - enum SixAxixLayoutsIndex : int - { - ProController = 0, - Handheld = 1, - JoyDualLeft = 2, - JoyDualRight = 3, - JoyLeft = 4, - JoyRight = 5, - Pokeball = 6, - SystemExternal = 7 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs deleted file mode 100644 index 12974e7e..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/SixAxisState.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct SixAxisState - { - public ulong SampleTimestamp; - ulong _unknown1; - public ulong SampleTimestamp2; - public HidVector Accelerometer; - public HidVector Gyroscope; - public HidVector Rotations; - public fixed float Orientation[9]; - public ulong _unknown2; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs deleted file mode 100644 index 5f12295c..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - unsafe struct ShMemTouchScreen - { - public CommonEntriesHeader Header; - public Array17<TouchScreenState> Entries; - fixed byte _padding[0x3c8]; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs deleted file mode 100644 index 1c85e291..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Ryujinx.Common.Memory; - -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct TouchScreenState - { - public ulong SampleTimestamp; - public ulong SampleTimestamp2; - public ulong NumTouches; - public Array16<TouchScreenStateData> Touches; - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs deleted file mode 100644 index 4d4c48d1..00000000 --- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Hid -{ - struct TouchScreenStateData - { - public ulong SampleTimestamp; -#pragma warning disable CS0169 - uint _padding; -#pragma warning restore CS0169 - public uint TouchIndex; - public uint X; - public uint Y; - public uint DiameterX; - public uint DiameterY; - public uint Angle; -#pragma warning disable CS0169 - uint _padding2; -#pragma warning restore CS0169 - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs new file mode 100644 index 00000000..bf4b5888 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AnalogStickState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common +{ + struct AnalogStickState + { + public int X; + public int Y; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs new file mode 100644 index 00000000..45b92ba9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/AtomicStorage.cs @@ -0,0 +1,26 @@ +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common +{ + struct AtomicStorage<T> where T: unmanaged + { + public ulong SamplingNumber; + public T Object; + + public ulong ReadSamplingNumberAtomic() + { + return Interlocked.Read(ref SamplingNumber); + } + + public void SetObject(ref T obj) + { + ISampledData samplingProvider = obj as ISampledData; + + Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber); + + Thread.MemoryBarrier(); + + Object = obj; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs new file mode 100644 index 00000000..08f76747 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/ISampledData.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common +{ + interface ISampledData + { + ulong SamplingNumber { get; } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs new file mode 100644 index 00000000..615e3893 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -0,0 +1,149 @@ +using Ryujinx.Common.Memory; +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common +{ + struct RingLifo<T> where T: unmanaged + { + private const ulong MaxEntries = 17; + +#pragma warning disable CS0169 + private ulong _unused; +#pragma warning restore CS0169 +#pragma warning disable CS0414 + private ulong _bufferCount; +#pragma warning restore CS0414 + private ulong _index; + private ulong _count; + private Array17<AtomicStorage<T>> _storage; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ulong ReadCurrentIndex() + { + return Interlocked.Read(ref _index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ulong ReadCurrentCount() + { + return Interlocked.Read(ref _count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong GetNextIndexForWrite(ulong index) + { + return (index + 1) % MaxEntries; + } + + public ref AtomicStorage<T> GetCurrentAtomicEntryRef() + { + ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), 1); + + if (countAvailaible == 0) + { + _storage[0] = default; + + return ref _storage[0]; + } + + ulong index = ReadCurrentIndex(); + + while (true) + { + int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries); + + ref AtomicStorage<T> result = ref _storage[inputEntryIndex]; + + ulong samplingNumber0 = result.ReadSamplingNumberAtomic(); + ulong samplingNumber1 = result.ReadSamplingNumberAtomic(); + + if (samplingNumber0 != samplingNumber1 && (result.SamplingNumber - result.SamplingNumber) != 1) + { + ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible); + + countAvailaible = Math.Min(tempCount, 1); + index = ReadCurrentIndex(); + + continue; + } + + return ref result; + } + } + + public ref T GetCurrentEntryRef() + { + return ref GetCurrentAtomicEntryRef().Object; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan<AtomicStorage<T>> ReadEntries(uint maxCount) + { + ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount); + + if (countAvailaible == 0) + { + return ReadOnlySpan<AtomicStorage<T>>.Empty; + } + + ulong index = ReadCurrentIndex(); + + AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible]; + + for (ulong i = 0; i < countAvailaible; i++) + { + int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries); + int outputEntryIndex = (int)(countAvailaible - i - 1); + + ulong samplingNumber0 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); + result[outputEntryIndex] = _storage[inputEntryIndex]; + ulong samplingNumber1 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); + + if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1)) + { + ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible); + + countAvailaible = Math.Min(tempCount, maxCount); + index = ReadCurrentIndex(); + + i -= 1; + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Write(ref T value) + { + ulong targetIndex = GetNextIndexForWrite(ReadCurrentIndex()); + + _storage[(int)targetIndex].SetObject(ref value); + + Interlocked.Exchange(ref _index, targetIndex); + + ulong count = ReadCurrentCount(); + + if (count < (MaxEntries - 1)) + { + Interlocked.Increment(ref _count); + } + } + + public void Clear() + { + Interlocked.Exchange(ref _count, 0); + Interlocked.Exchange(ref _index, 0); + } + + public static RingLifo<T> Create() + { + return new RingLifo<T> + { + _bufferCount = MaxEntries + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs new file mode 100644 index 00000000..ec5bd3c8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad +{ + [Flags] + enum DebugPadAttribute : uint + { + None = 0, + Connected = 1 << 0 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs new file mode 100644 index 00000000..e8f28317 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadButton.cs @@ -0,0 +1,24 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad +{ + [Flags] + enum DebugPadButton : uint + { + None = 0, + A = 1 << 0, + B = 1 << 1, + X = 1 << 2, + Y = 1 << 3, + L = 1 << 4, + R = 1 << 5, + ZL = 1 << 6, + ZR = 1 << 7, + Start = 1 << 8, + Select = 1 << 9, + Left = 1 << 10, + Up = 1 << 11, + Right = 1 << 12, + Down = 1 << 13 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs new file mode 100644 index 00000000..3e1e1ad8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugPad/DebugPadState.cs @@ -0,0 +1,15 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad +{ + struct DebugPadState : ISampledData + { + public ulong SamplingNumber; + public DebugPadAttribute Attributes; + public DebugPadButton Buttons; + public AnalogStickState AnalogStickR; + public AnalogStickState AnalogStickL; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs new file mode 100644 index 00000000..22df7c79 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKey.cs @@ -0,0 +1,29 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard +{ + struct KeyboardKey + { + public Array4<ulong> RawData; + + public bool this[KeyboardKeyShift index] + { + get + { + return (RawData[(int)index / 64] & (1UL << ((int)index & 63))) != 0; + } + set + { + int arrayIndex = (int)index / 64; + ulong mask = 1UL << ((int)index & 63); + + RawData[arrayIndex] &= ~mask; + + if (value) + { + RawData[arrayIndex] |= mask; + } + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs new file mode 100644 index 00000000..01c2bb30 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardKeyShift.cs @@ -0,0 +1,138 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard +{ + enum KeyboardKeyShift + { + A = 4, + B = 5, + C = 6, + D = 7, + E = 8, + F = 9, + G = 10, + H = 11, + I = 12, + J = 13, + K = 14, + L = 15, + M = 16, + N = 17, + O = 18, + P = 19, + Q = 20, + R = 21, + S = 22, + T = 23, + U = 24, + V = 25, + W = 26, + X = 27, + Y = 28, + Z = 29, + D1 = 30, + D2 = 31, + D3 = 32, + D4 = 33, + D5 = 34, + D6 = 35, + D7 = 36, + D8 = 37, + D9 = 38, + D0 = 39, + Return = 40, + Escape = 41, + Backspace = 42, + Tab = 43, + Space = 44, + Minus = 45, + Plus = 46, + OpenBracket = 47, + CloseBracket = 48, + Pipe = 49, + Tilde = 50, + Semicolon = 51, + Quote = 52, + Backquote = 53, + Comma = 54, + Period = 55, + Slash = 56, + CapsLock = 57, + F1 = 58, + F2 = 59, + F3 = 60, + F4 = 61, + F5 = 62, + F6 = 63, + F7 = 64, + F8 = 65, + F9 = 66, + F10 = 67, + F11 = 68, + F12 = 69, + PrintScreen = 70, + ScrollLock = 71, + Pause = 72, + Insert = 73, + Home = 74, + PageUp = 75, + Delete = 76, + End = 77, + PageDown = 78, + RightArrow = 79, + LeftArrow = 80, + DownArrow = 81, + UpArrow = 82, + NumLock = 83, + NumPadDivide = 84, + NumPadMultiply = 85, + NumPadSubtract = 86, + NumPadAdd = 87, + NumPadEnter = 88, + NumPad1 = 89, + NumPad2 = 90, + NumPad3 = 91, + NumPad4 = 92, + NumPad5 = 93, + NumPad6 = 94, + NumPad7 = 95, + NumPad8 = 96, + NumPad9 = 97, + NumPad0 = 98, + NumPadDot = 99, + Backslash = 100, + Application = 101, + Power = 102, + NumPadEquals = 103, + F13 = 104, + F14 = 105, + F15 = 106, + F16 = 107, + F17 = 108, + F18 = 109, + F19 = 110, + F20 = 111, + F21 = 112, + F22 = 113, + F23 = 114, + F24 = 115, + NumPadComma = 133, + Ro = 135, + KatakanaHiragana = 136, + Yen = 137, + Henkan = 138, + Muhenkan = 139, + NumPadCommaPc98 = 140, + HangulEnglish = 144, + Hanja = 145, + Katakana = 146, + Hiragana = 147, + ZenkakuHankaku = 148, + LeftControl = 224, + LeftShift = 225, + LeftAlt = 226, + LeftGui = 227, + RightControl = 228, + RightShift = 229, + RightAlt = 230, + RightGui = 231 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs new file mode 100644 index 00000000..72d1603a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardModifier.cs @@ -0,0 +1,21 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard +{ + // TODO: This seems entirely wrong + [Flags] + enum KeyboardModifier : uint + { + None = 0, + Control = 1 << 0, + Shift = 1 << 1, + LeftAlt = 1 << 2, + RightAlt = 1 << 3, + Gui = 1 << 4, + CapsLock = 1 << 8, + ScrollLock = 1 << 9, + NumLock = 1 << 10, + Katakana = 1 << 11, + Hiragana = 1 << 12 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs new file mode 100644 index 00000000..37608506 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Keyboard/KeyboardState.cs @@ -0,0 +1,13 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard +{ + struct KeyboardState : ISampledData + { + public ulong SamplingNumber; + public KeyboardModifier Modifiers; + public KeyboardKey Keys; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs new file mode 100644 index 00000000..5ffba0d7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse +{ + [Flags] + enum MouseAttribute : uint + { + None = 0, + Transferable = 1 << 0, + IsConnected = 1 << 1 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs new file mode 100644 index 00000000..7e35140c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseButton.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse +{ + [Flags] + enum MouseButton : uint + { + None = 0, + Left = 1 << 0, + Right = 1 << 1, + Middle = 1 << 2, + Forward = 1 << 3, + Back = 1 << 4 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs new file mode 100644 index 00000000..67ad6bf1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Mouse/MouseState.cs @@ -0,0 +1,19 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse +{ + struct MouseState : ISampledData + { + public ulong SamplingNumber; + public int X; + public int Y; + public int DeltaX; + public int DeltaY; + public int WheelDeltaX; + public int WheelDeltaY; + public MouseButton Buttons; + public MouseAttribute Attributes; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs new file mode 100644 index 00000000..b0201835 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/DeviceType.cs @@ -0,0 +1,29 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum DeviceType : int + { + None = 0, + + FullKey = 1 << 0, + DebugPad = 1 << 1, + HandheldLeft = 1 << 2, + HandheldRight = 1 << 3, + JoyLeft = 1 << 4, + JoyRight = 1 << 5, + Palma = 1 << 6, + LarkHvcLeft = 1 << 7, + LarkHvcRight = 1 << 8, + LarkNesLeft = 1 << 9, + LarkNesRight = 1 << 10, + HandheldLarkHvcLeft = 1 << 11, + HandheldLarkHvcRight = 1 << 12, + HandheldLarkNesLeft = 1 << 13, + HandheldLarkNesRight = 1 << 14, + Lucia = 1 << 15, + + System = 1 << 31 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs new file mode 100644 index 00000000..0960b7bf --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum NpadAttribute : uint + { + None = 0, + IsConnected = 1 << 0, + IsWired = 1 << 1, + IsLeftConnected = 1 << 2, + IsLeftWired = 1 << 3, + IsRightConnected = 1 << 4, + IsRightWired = 1 << 5 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs new file mode 100644 index 00000000..477dfd10 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadBatteryLevel.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + enum NpadBatteryLevel : int + { + Percent0, + Percent25, + Percent50, + Percent75, + Percent100 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs new file mode 100644 index 00000000..5b3e13a7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs @@ -0,0 +1,44 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum NpadButton : ulong + { + None = 0, + A = 1 << 0, + B = 1 << 1, + X = 1 << 2, + Y = 1 << 3, + StickL = 1 << 4, + StickR = 1 << 5, + L = 1 << 6, + R = 1 << 7, + ZL = 1 << 8, + ZR = 1 << 9, + Plus = 1 << 10, + Minus = 1 << 11, + Left = 1 << 12, + Up = 1 << 13, + Right = 1 << 14, + Down = 1 << 15, + StickLLeft = 1 << 16, + StickLUp = 1 << 17, + StickLRight = 1 << 18, + StickLDown = 1 << 19, + StickRLeft = 1 << 20, + StickRUp = 1 << 21, + StickRRight = 1 << 22, + StickRDown = 1 << 23, + LeftSL = 1 << 24, + LeftSR = 1 << 25, + RightSL = 1 << 26, + RightSR = 1 << 27, + Palma = 1 << 28, + + // FIXME: Probably a button on Lark. + Unknown29 = 1 << 29, + + HandheldLeftB = 1 << 30 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs new file mode 100644 index 00000000..1e547cc8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadColorAttribute.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + enum NpadColorAttribute : uint + { + Ok, + ReadError, + NoController + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs new file mode 100644 index 00000000..eaccef80 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadCommonState.cs @@ -0,0 +1,16 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct NpadCommonState : ISampledData + { + public ulong SamplingNumber; + public NpadButton Buttons; + public AnalogStickState AnalogStickL; + public AnalogStickState AnalogStickR; + public NpadAttribute Attributes; + private uint _reserved; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs new file mode 100644 index 00000000..990eafb2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadFullKeyColorState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct NpadFullKeyColorState + { + public NpadColorAttribute Attribute; + public uint FullKeyBody; + public uint FullKeyButtons; + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs new file mode 100644 index 00000000..52668f85 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadGcTriggerState.cs @@ -0,0 +1,15 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct NpadGcTriggerState : ISampledData + { +#pragma warning disable CS0649 + public ulong SamplingNumber; + public uint TriggerL; + public uint TriggerR; +#pragma warning restore CS0649 + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs new file mode 100644 index 00000000..f225ff67 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadInternalState.cs @@ -0,0 +1,61 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct NpadInternalState + { + public NpadStyleTag StyleSet; + public NpadJoyAssignmentMode JoyAssignmentMode; + public NpadFullKeyColorState FullKeyColor; + public NpadJoyColorState JoyColor; + public RingLifo<NpadCommonState> FullKey; + public RingLifo<NpadCommonState> Handheld; + public RingLifo<NpadCommonState> JoyDual; + public RingLifo<NpadCommonState> JoyLeft; + public RingLifo<NpadCommonState> JoyRight; + public RingLifo<NpadCommonState> Palma; + public RingLifo<NpadCommonState> SystemExt; + public RingLifo<SixAxisSensorState> FullKeySixAxisSensor; + public RingLifo<SixAxisSensorState> HandheldSixAxisSensor; + public RingLifo<SixAxisSensorState> JoyDualSixAxisSensor; + public RingLifo<SixAxisSensorState> JoyDualRightSixAxisSensor; + public RingLifo<SixAxisSensorState> JoyLeftSixAxisSensor; + public RingLifo<SixAxisSensorState> JoyRightSixAxisSensor; + public DeviceType DeviceType; + private uint _reserved1; + public NpadSystemProperties SystemProperties; + public NpadSystemButtonProperties SystemButtonProperties; + public NpadBatteryLevel BatteryLevelJoyDual; + public NpadBatteryLevel BatteryLevelJoyLeft; + public NpadBatteryLevel BatteryLevelJoyRight; + public uint AppletFooterUiAttributes; + public byte AppletFooterUiType; + private unsafe fixed byte _reserved2[0x7B]; + public RingLifo<NpadGcTriggerState> GcTrigger; + public NpadLarkType LarkTypeLeftAndMain; + public NpadLarkType LarkTypeRight; + public NpadLuciaType LuciaType; + public uint Unknown43EC; + + public static NpadInternalState Create() + { + return new NpadInternalState + { + FullKey = RingLifo<NpadCommonState>.Create(), + Handheld = RingLifo<NpadCommonState>.Create(), + JoyDual = RingLifo<NpadCommonState>.Create(), + JoyLeft = RingLifo<NpadCommonState>.Create(), + JoyRight = RingLifo<NpadCommonState>.Create(), + Palma = RingLifo<NpadCommonState>.Create(), + SystemExt = RingLifo<NpadCommonState>.Create(), + FullKeySixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + HandheldSixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + JoyDualSixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + JoyDualRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + JoyLeftSixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + JoyRightSixAxisSensor = RingLifo<SixAxisSensorState>.Create(), + GcTrigger = RingLifo<NpadGcTriggerState>.Create(), + }; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs new file mode 100644 index 00000000..871c4c5a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyAssignmentMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + enum NpadJoyAssignmentMode : uint + { + Dual, + Single + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs new file mode 100644 index 00000000..3986dd5e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadJoyColorState.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct NpadJoyColorState + { + public NpadColorAttribute Attribute; + public uint LeftBody; + public uint LeftButtons; + public uint RightBody; + public uint RightButtons; + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs new file mode 100644 index 00000000..a487a911 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLarkType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + enum NpadLarkType : uint + { + Invalid, + H1, + H2, + NL, + NR + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs new file mode 100644 index 00000000..95148485 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadLuciaType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + enum NpadLuciaType + { + Invalid, + J, + E, + U + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs new file mode 100644 index 00000000..ed9e7c0d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadState.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [StructLayout(LayoutKind.Sequential, Size = 0x5000)] + struct NpadState + { + public NpadInternalState InternalState; + + public static NpadState Create() + { + return new NpadState + { + InternalState = NpadInternalState.Create() + }; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs new file mode 100644 index 00000000..f31978e2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadStyleTag.cs @@ -0,0 +1,76 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + /// <summary> + /// Nintendo pad style + /// </summary> + [Flags] + enum NpadStyleTag : uint + { + /// <summary> + /// No type. + /// </summary> + None = 0, + + /// <summary> + /// Pro controller. + /// </summary> + FullKey = 1 << 0, + + /// <summary> + /// Joy-Con controller in handheld mode. + /// </summary> + Handheld = 1 << 1, + + /// <summary> + /// Joy-Con controller in dual mode. + /// </summary> + JoyDual = 1 << 2, + + /// <summary> + /// Joy-Con left controller in single mode. + /// </summary> + JoyLeft = 1 << 3, + + /// <summary> + /// Joy-Con right controller in single mode. + /// </summary> + JoyRight = 1 << 4, + + /// <summary> + /// GameCube controller. + /// </summary> + Gc = 1 << 5, + + /// <summary> + /// Poké Ball Plus controller. + /// </summary> + Palma = 1 << 6, + + /// <summary> + /// NES and Famicom controller. + /// </summary> + Lark = 1 << 7, + + /// <summary> + /// NES and Famicom controller in handheld mode. + /// </summary> + HandheldLark = 1 << 8, + + /// <summary> + /// SNES controller. + /// </summary> + Lucia = 1 << 9, + + /// <summary> + /// Generic external controller. + /// </summary> + SystemExt = 1 << 29, + + /// <summary> + /// Generic controller. + /// </summary> + System = 1 << 30 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs new file mode 100644 index 00000000..68603271 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemButtonProperties.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum NpadSystemButtonProperties : uint + { + None = 0, + IsUnintendedHomeButtonInputProtectionEnabled = 1 << 0 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs new file mode 100644 index 00000000..13444555 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadSystemProperties.cs @@ -0,0 +1,24 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum NpadSystemProperties : ulong + { + None = 0, + + IsChargingJoyDual = 1 << 0, + IsChargingJoyLeft = 1 << 1, + IsChargingJoyRight = 1 << 2, + IsPoweredJoyDual = 1 << 3, + IsPoweredJoyLeft = 1 << 4, + IsPoweredJoyRight = 1 << 5, + IsUnsuportedButtonPressedOnNpadSystem = 1 << 9, + IsUnsuportedButtonPressedOnNpadSystemExt = 1 << 10, + IsAbxyButtonOriented = 1 << 11, + IsSlSrButtonOriented = 1 << 12, + IsPlusAvailable = 1 << 13, + IsMinusAvailable = 1 << 14, + IsDirectionalButtonsAvailable = 1 << 15 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs new file mode 100644 index 00000000..7ed46d98 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + [Flags] + enum SixAxisSensorAttribute : uint + { + None = 0, + IsConnected = 1 << 0, + IsInterpolated = 1 << 1 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs new file mode 100644 index 00000000..d024b0b0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/SixAxisSensorState.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad +{ + struct SixAxisSensorState : ISampledData + { + public ulong DeltaTime; + public ulong SamplingNumber; + public HidVector Acceleration; + public HidVector AngularVelocity; + public HidVector Angle; + public Array9<float> Direction; + public SixAxisSensorAttribute Attributes; + private uint _reserved; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs new file mode 100644 index 00000000..48acfc3f --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -0,0 +1,66 @@ +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory +{ + /// <summary> + /// Represent the shared memory shared between applications for input. + /// </summary> + [StructLayout(LayoutKind.Explicit, Size = 0x40000)] + struct SharedMemory + { + /// <summary> + /// Debug controller. + /// </summary> + [FieldOffset(0)] + public RingLifo<DebugPadState> DebugPad; + + /// <summary> + /// Touchscreen. + /// </summary> + [FieldOffset(0x400)] + public RingLifo<TouchScreenState> TouchScreen; + + /// <summary> + /// Mouse. + /// </summary> + [FieldOffset(0x3400)] + public RingLifo<MouseState> Mouse; + + /// <summary> + /// Keyboard. + /// </summary> + [FieldOffset(0x3800)] + public RingLifo<KeyboardState> Keyboard; + + /// <summary> + /// Nintendo Pads. + /// </summary> + [FieldOffset(0x9A00)] + public Array10<NpadState> Npads; + + public static SharedMemory Create() + { + SharedMemory result = new SharedMemory + { + DebugPad = RingLifo<DebugPadState>.Create(), + TouchScreen = RingLifo<TouchScreenState>.Create(), + Mouse = RingLifo<MouseState>.Create(), + Keyboard = RingLifo<KeyboardState>.Create(), + }; + + for (int i = 0; i < result.Npads.Length; i++) + { + result.Npads[i] = NpadState.Create(); + } + + return result; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs new file mode 100644 index 00000000..8a8f9cc1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen +{ + [Flags] + enum TouchAttribute : uint + { + None = 0, + Start = 1 << 0, + End = 1 << 1 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs new file mode 100644 index 00000000..8203e49b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchScreenState.cs @@ -0,0 +1,15 @@ +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen +{ + struct TouchScreenState : ISampledData + { + public ulong SamplingNumber; + public int TouchesCount; + private int _reserved; + public Array16<TouchState> Touches; + + ulong ISampledData.SamplingNumber => SamplingNumber; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs new file mode 100644 index 00000000..ba621a2b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/TouchScreen/TouchState.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen +{ + struct TouchState + { + public ulong DeltaTime; +#pragma warning disable CS0649 + public TouchAttribute Attribute; +#pragma warning restore CS0649 + public uint FingerId; + public uint X; + public uint Y; + public uint DiameterX; + public uint DiameterY; + public uint RotationAngle; +#pragma warning disable CS0169 + private uint _reserved; +#pragma warning restore CS0169 + } +} diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 69d461de..e9582c26 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services bool serviceExists = service.HipcCommands.TryGetValue(commandId, out MethodInfo processRequest); - if (ServiceConfiguration.IgnoreMissingServices || serviceExists) + if (context.Device.Configuration.IgnoreMissingServices || serviceExists) { ResultCode result = ResultCode.Success; @@ -163,7 +163,7 @@ namespace Ryujinx.HLE.HOS.Services bool serviceExists = TipcCommands.TryGetValue(commandId, out MethodInfo processRequest); - if (ServiceConfiguration.IgnoreMissingServices || serviceExists) + if (context.Device.Configuration.IgnoreMissingServices || serviceExists) { ResultCode result = ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs index 0bf6f177..3181668f 100644 --- a/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs +++ b/Ryujinx.HLE/HOS/Services/Lm/LogService/ILogger.cs @@ -19,11 +19,11 @@ namespace Ryujinx.HLE.HOS.Services.Lm.LogService private string LogImpl(ServiceCtx context) { - (long bufPos, long bufSize) = context.Request.GetBufferType0x21(); + (ulong bufPos, ulong bufSize) = context.Request.GetBufferType0x21(); byte[] logBuffer = new byte[bufSize]; - context.Memory.Read((ulong)bufPos, logBuffer); + context.Memory.Read(bufPos, logBuffer); using MemoryStream ms = new MemoryStream(logBuffer); diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs index 73f1a673..dea32f62 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs @@ -261,7 +261,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService ResultCode result = Export(data); - context.Memory.Write((ulong)outputBuffer.Position, data.ToArray()); + context.Memory.Write(outputBuffer.Position, data.ToArray()); return result; } @@ -352,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService { rawData = new byte[ipcBuff.Size]; - context.Memory.Read((ulong)ipcBuff.Position, rawData); + context.Memory.Read(ipcBuff.Position, rawData); } return new Span<byte>(rawData); @@ -367,7 +367,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService { Span<byte> rawData = MemoryMarshal.Cast<T, byte>(span); - context.Memory.Write((ulong)ipcBuff.Position, rawData); + context.Memory.Write(ipcBuff.Position, rawData); } protected abstract bool IsUpdated(SourceFlag flag); diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index 6cd643d3..bce52119 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm private static uint _uniqueId = 1; - public IRequest(ServiceCtx context) {} + public IRequest(ServiceCtx context) { } [CommandHipc(0)] // InitializeOld(u32, u32, u32) @@ -50,14 +50,14 @@ namespace Ryujinx.HLE.HOS.Services.Mm public ResultCode SetAndWaitOld(ServiceCtx context) { MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32(); - uint value = context.RequestData.ReadUInt32(); + uint frequenceHz = context.RequestData.ReadUInt32(); int timeout = context.RequestData.ReadInt32(); - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, value, timeout }); + Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, frequenceHz, timeout }); lock (_sessionListLock) { - GetSessionByType(operationType)?.SetAndWait(value, timeout); + GetSessionByType(operationType)?.SetAndWait(frequenceHz, timeout); } return ResultCode.Success; @@ -120,15 +120,15 @@ namespace Ryujinx.HLE.HOS.Services.Mm // SetAndWait(u32, u32, u32) public ResultCode SetAndWait(ServiceCtx context) { - uint id = context.RequestData.ReadUInt32(); - uint value = context.RequestData.ReadUInt32(); - int timeout = context.RequestData.ReadInt32(); + uint id = context.RequestData.ReadUInt32(); + uint frequenceHz = context.RequestData.ReadUInt32(); + int timeout = context.RequestData.ReadInt32(); - Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, value, timeout }); + Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, frequenceHz, timeout }); lock (_sessionListLock) { - GetSessionById(id)?.SetAndWait(value, timeout); + GetSessionById(id)?.SetAndWait(frequenceHz, timeout); } return ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs b/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs index cf4cdf20..2742af6c 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs @@ -2,10 +2,9 @@ { enum MultiMediaOperationType : uint { - // TODO: figure out the unknown variants. - Unknown2 = 2, - VideoDecode = 5, - VideoEncode = 6, - Unknown7 = 7 + Ram = 2, + NvEnc = 5, + NvDec = 6, + NvJpg = 7 } -} +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs index 6ffa33f4..3fbc42a9 100644 --- a/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Ncm/Lr/LocationResolverManager/ILocationResolver.cs @@ -17,10 +17,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(0)] - // ResolveProgramPath() + // ResolveProgramPath(u64 titleId) public ResultCode ResolveProgramPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); if (ResolvePath(context, titleId, NcaContentType.Program)) { @@ -33,10 +33,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(1)] - // RedirectProgramPath() + // RedirectProgramPath(u64 titleId) public ResultCode RedirectProgramPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); RedirectPath(context, titleId, 0, NcaContentType.Program); @@ -44,10 +44,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(2)] - // ResolveApplicationControlPath() + // ResolveApplicationControlPath(u64 titleId) public ResultCode ResolveApplicationControlPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); if (ResolvePath(context, titleId, NcaContentType.Control)) { @@ -60,10 +60,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(3)] - // ResolveApplicationHtmlDocumentPath() + // ResolveApplicationHtmlDocumentPath(u64 titleId) public ResultCode ResolveApplicationHtmlDocumentPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); if (ResolvePath(context, titleId, NcaContentType.Manual)) { @@ -76,10 +76,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(4)] - // ResolveDataPath() + // ResolveDataPath(u64 titleId) public ResultCode ResolveDataPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); if (ResolvePath(context, titleId, NcaContentType.Data) || ResolvePath(context, titleId, NcaContentType.PublicData)) { @@ -92,10 +92,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(5)] - // RedirectApplicationControlPath() + // RedirectApplicationControlPath(u64 titleId) public ResultCode RedirectApplicationControlPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); RedirectPath(context, titleId, 1, NcaContentType.Control); @@ -103,10 +103,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(6)] - // RedirectApplicationHtmlDocumentPath() + // RedirectApplicationHtmlDocumentPath(u64 titleId) public ResultCode RedirectApplicationHtmlDocumentPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); RedirectPath(context, titleId, 1, NcaContentType.Manual); @@ -114,10 +114,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(7)] - // ResolveApplicationLegalInformationPath() + // ResolveApplicationLegalInformationPath(u64 titleId) public ResultCode ResolveApplicationLegalInformationPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); if (ResolvePath(context, titleId, NcaContentType.Manual)) { @@ -130,10 +130,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(8)] - // RedirectApplicationLegalInformationPath() + // RedirectApplicationLegalInformationPath(u64 titleId) public ResultCode RedirectApplicationLegalInformationPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); RedirectPath(context, titleId, 1, NcaContentType.Manual); @@ -150,10 +150,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(10)] - // SetProgramNcaPath2() + // SetProgramNcaPath2(u64 titleId) public ResultCode SetProgramNcaPath2(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); RedirectPath(context, titleId, 1, NcaContentType.Program); @@ -170,10 +170,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(12)] - // DeleteProgramNcaPath() + // DeleteProgramNcaPath(u64 titleId) public ResultCode DeleteProgramNcaPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); DeleteContentPath(context, titleId, NcaContentType.Program); @@ -181,10 +181,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(13)] - // DeleteControlNcaPath() + // DeleteControlNcaPath(u64 titleId) public ResultCode DeleteControlNcaPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); DeleteContentPath(context, titleId, NcaContentType.Control); @@ -192,10 +192,10 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(14)] - // DeleteDocHtmlNcaPath() + // DeleteDocHtmlNcaPath(u64 titleId) public ResultCode DeleteDocHtmlNcaPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); DeleteContentPath(context, titleId, NcaContentType.Manual); @@ -203,17 +203,17 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager } [CommandHipc(15)] - // DeleteInfoHtmlNcaPath() + // DeleteInfoHtmlNcaPath(u64 titleId) public ResultCode DeleteInfoHtmlNcaPath(ServiceCtx context) { - long titleId = context.RequestData.ReadInt64(); + ulong titleId = context.RequestData.ReadUInt64(); DeleteContentPath(context, titleId, NcaContentType.Manual); return ResultCode.Success; } - private void RedirectPath(ServiceCtx context, long titleId, int flag, NcaContentType contentType) + private void RedirectPath(ServiceCtx context, ulong titleId, int flag, NcaContentType contentType) { string contentPath = ReadUtf8String(context); LocationEntry newLocation = new LocationEntry(contentPath, flag, titleId, contentType); @@ -221,19 +221,19 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager context.Device.System.ContentManager.RedirectLocation(newLocation, _storageId); } - private bool ResolvePath(ServiceCtx context, long titleId, NcaContentType contentType) + private bool ResolvePath(ServiceCtx context, ulong titleId, NcaContentType contentType) { ContentManager contentManager = context.Device.System.ContentManager; string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Program); if (!string.IsNullOrWhiteSpace(contentPath)) { - long position = context.Request.RecvListBuff[0].Position; - long size = context.Request.RecvListBuff[0].Size; + ulong position = context.Request.RecvListBuff[0].Position; + ulong size = context.Request.RecvListBuff[0].Size; byte[] contentPathBuffer = Encoding.UTF8.GetBytes(contentPath); - context.Memory.Write((ulong)position, contentPathBuffer); + context.Memory.Write(position, contentPathBuffer); } else { @@ -243,7 +243,7 @@ namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager return true; } - private void DeleteContentPath(ServiceCtx context, long titleId, NcaContentType contentType) + private void DeleteContentPath(ServiceCtx context, ulong titleId, NcaContentType contentType) { ContentManager contentManager = context.Device.System.ContentManager; string contentPath = contentManager.GetInstalledContentPath(titleId, _storageId, NcaContentType.Manual); diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs index dd3990ae..f241c311 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs @@ -37,12 +37,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp _appletResourceUserId = context.RequestData.ReadUInt64(); _mcuVersionData = context.RequestData.ReadUInt64(); - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; _mcuData = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, _mcuData); + context.Memory.Read(inputPosition, _mcuData); // TODO: The mcuData buffer seems to contains entries with a size of 0x40 bytes each. Usage of the data needs to be determined. @@ -93,8 +93,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.WrongArgument; } - long outputPosition = context.Request.RecvListBuff[0].Position; - long outputSize = context.Request.RecvListBuff[0].Size; + ulong outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputSize = context.Request.RecvListBuff[0].Size; if (context.Device.System.NfpDevices.Count == 0) { @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) { - context.Memory.Write((ulong)(outputPosition + (i * sizeof(long))), (uint)context.Device.System.NfpDevices[i].Handle); + context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfpDevices[i].Handle); } context.ResponseData.Write(context.Device.System.NfpDevices.Count); @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) { context.Device.System.NfpDevices[i].SignalActivate(); - Thread.Sleep(50); // NOTE: Simulate amiibo scanning delay. + Thread.Sleep(125); // NOTE: Simulate amiibo scanning delay. context.Device.System.NfpDevices[i].SignalDeactivate(); break; @@ -376,8 +376,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); @@ -397,7 +397,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { byte[] applicationArea = VirtualAmiibo.GetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId); - context.Memory.Write((ulong)outputPosition, applicationArea); + context.Memory.Write(outputPosition, applicationArea); size = (uint)applicationArea.Length; @@ -444,12 +444,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.DeviceNotFound; } - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] applicationArea = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, applicationArea); + context.Memory.Read(inputPosition, applicationArea); for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) { @@ -523,12 +523,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp uint applicationAreaId = context.RequestData.ReadUInt32(); - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] applicationArea = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, applicationArea); + context.Memory.Read(inputPosition, applicationArea); bool isCreated = false; @@ -582,9 +582,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.WrongArgument; } - long outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputPosition = context.Request.RecvListBuff[0].Position; - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(TagInfo))); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(TagInfo))); MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo))); @@ -625,7 +625,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp Uuid.CopyTo(tagInfo.Uuid.ToSpan()); - context.Memory.Write((ulong)outputPosition, tagInfo); + context.Memory.Write(outputPosition, tagInfo); resultCode = ResultCode.Success; } @@ -658,9 +658,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.WrongArgument; } - long outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputPosition = context.Request.RecvListBuff[0].Position; - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(RegisterInfo))); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(RegisterInfo))); MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo))); @@ -685,7 +685,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(context.Device.System.NfpDevices[i].AmiiboId); - context.Memory.Write((ulong)outputPosition, registerInfo); + context.Memory.Write(outputPosition, registerInfo); resultCode = ResultCode.Success; } @@ -718,9 +718,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.WrongArgument; } - long outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputPosition = context.Request.RecvListBuff[0].Position; - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(CommonInfo))); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(CommonInfo))); MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo))); @@ -745,7 +745,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId); - context.Memory.Write((ulong)outputPosition, commonInfo); + context.Memory.Write(outputPosition, commonInfo); resultCode = ResultCode.Success; } @@ -778,9 +778,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.WrongArgument; } - long outputPosition = context.Request.RecvListBuff[0].Position; + ulong outputPosition = context.Request.RecvListBuff[0].Position; - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf(typeof(ModelInfo))); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(ModelInfo))); MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo))); @@ -814,7 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber); modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber); - context.Memory.Write((ulong)outputPosition, modelInfo); + context.Memory.Write(outputPosition, modelInfo); resultCode = ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs index a1907d4f..8d99721e 100644 --- a/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs +++ b/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs @@ -11,8 +11,8 @@ namespace Ryujinx.HLE.HOS.Services.Ngct // Then it checks if ngc.t!functionality_override_enabled is enabled and if sys:set GetT is == 2. // If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values. - long bufferPosition = context.Request.PtrBuff[0].Position; - long bufferSize = context.Request.PtrBuff[0].Size; + ulong bufferPosition = context.Request.PtrBuff[0].Position; + ulong bufferSize = context.Request.PtrBuff[0].Size; bool isMatch = false; string text = ""; @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct { byte[] buffer = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, buffer); + context.Memory.Read(bufferPosition, buffer); text = Encoding.ASCII.GetString(buffer); @@ -52,10 +52,10 @@ namespace Ryujinx.HLE.HOS.Services.Ngct // Then it checks if ngc.t!functionality_override_enabled is enabled and if sys:set GetT is == 2. // If both conditions are true, it does this following code. Since we currently stub it, it's fine to don't check settings service values. - long bufferPosition = context.Request.PtrBuff[0].Position; - long bufferSize = context.Request.PtrBuff[0].Size; + ulong bufferPosition = context.Request.PtrBuff[0].Position; + ulong bufferSize = context.Request.PtrBuff[0].Size; - long bufferFilteredPosition = context.Request.RecvListBuff[0].Position; + ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position; string text = ""; string textFiltered = ""; @@ -66,13 +66,13 @@ namespace Ryujinx.HLE.HOS.Services.Ngct { textFiltered = new string('*', text.Length); - context.Memory.Write((ulong)bufferFilteredPosition, Encoding.ASCII.GetBytes(textFiltered)); + context.Memory.Write(bufferFilteredPosition, Encoding.ASCII.GetBytes(textFiltered)); } else { byte[] buffer = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, buffer); + context.Memory.Read(bufferPosition, buffer); // NOTE: Ngct use the archive 0100000000001034 which contains a words table. This is pushed on Chinese Switchs using Bcat service. // This call check if the string contains words which are in the table then returns the same string with each matched words replaced by '*'. @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct textFiltered = text = Encoding.ASCII.GetString(buffer); - context.Memory.Write((ulong)bufferFilteredPosition, buffer); + context.Memory.Write(bufferFilteredPosition, buffer); } } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 5f1f9b3a..e650879b 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -29,11 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetClientId() -> buffer<nn::nifm::ClientId, 0x1a, 4> public ResultCode GetClientId(ServiceCtx context) { - long position = context.Request.RecvListBuff[0].Position; + ulong position = context.Request.RecvListBuff[0].Position; - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(4); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(sizeof(int)); - context.Memory.Write((ulong)position, _generalServiceDetail.ClientId); + context.Memory.Write(position, _generalServiceDetail.ClientId); return ResultCode.Success; } @@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c> public ResultCode GetCurrentNetworkProfile(ServiceCtx context) { - long networkProfileDataPosition = context.Request.RecvListBuff[0].Position; + ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position; (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(); @@ -69,7 +69,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\"."); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Unsafe.SizeOf<NetworkProfileData>()); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>()); NetworkProfileData networkProfile = new NetworkProfileData { @@ -81,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService Encoding.ASCII.GetBytes("RyujinxNetwork").CopyTo(networkProfile.Name.ToSpan()); - context.Memory.Write((ulong)networkProfileDataPosition, networkProfile); + context.Memory.Write(networkProfileDataPosition, networkProfile); return ResultCode.Success; } @@ -148,10 +148,10 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService // IsAnyInternetRequestAccepted(buffer<nn::nifm::ClientId, 0x19, 4>) -> bool public ResultCode IsAnyInternetRequestAccepted(ServiceCtx context) { - long position = context.Request.PtrBuff[0].Position; - long size = context.Request.PtrBuff[0].Size; + ulong position = context.Request.PtrBuff[0].Position; + ulong size = context.Request.PtrBuff[0].Size; - int clientId = context.Memory.Read<int>((ulong)position); + int clientId = context.Memory.Read<int>(position); context.ResponseData.Write(GeneralServiceManager.Get(clientId).IsAnyInternetRequestAccepted); diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index ab10928f..d3a89178 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -12,11 +12,11 @@ byte source = (byte)context.RequestData.ReadInt64(); ulong titleId = context.RequestData.ReadUInt64(); - long position = context.Request.ReceiveBuff[0].Position; + ulong position = context.Request.ReceiveBuff[0].Position; byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); - context.Memory.Write((ulong)position, nacpData); + context.Memory.Write(position, nacpData); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs index 2ea3ee11..7ee74370 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs @@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Services.Ns // SetDefaultDeliveryTarget(pid, buffer<bytes, 5> unknown) public ResultCode SetDefaultDeliveryTarget(ServiceCtx context) { - long inBufferPosition = context.Request.SendBuff[0].Position; - long inBufferSize = context.Request.SendBuff[0].Size; + ulong inBufferPosition = context.Request.SendBuff[0].Position; + ulong inBufferSize = context.Request.SendBuff[0].Size; byte[] buffer = new byte[inBufferSize]; - context.Memory.Read((ulong)inBufferPosition, buffer); + context.Memory.Read(inBufferPosition, buffer); // NOTE: Service use the pid to call arp:r GetApplicationLaunchProperty and store it in internal field. // Then it seems to use the buffer content and compare it with a stored linked instrusive list. diff --git a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index ff57beb0..3b6965d0 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -11,11 +11,11 @@ byte source = (byte)context.RequestData.ReadInt64(); ulong titleId = context.RequestData.ReadUInt64(); - long position = context.Request.ReceiveBuff[0].Position; + ulong position = context.Request.ReceiveBuff[0].Position; byte[] nacpData = context.Device.Application.ControlData.ByteSpan.ToArray(); - context.Memory.Write((ulong)position, nacpData); + context.Memory.Write(position, nacpData); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 35bb4e6f..25279af3 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -73,8 +73,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span<byte> arguments) { - (long inputDataPosition, long inputDataSize) = context.Request.GetBufferType0x21(0); - (long outputDataPosition, long outputDataSize) = context.Request.GetBufferType0x22(0); + (ulong inputDataPosition, ulong inputDataSize) = context.Request.GetBufferType0x21(0); + (ulong outputDataPosition, ulong outputDataSize) = context.Request.GetBufferType0x22(0); NvIoctl.Direction ioctlDirection = ioctlCommand.DirectionValue; uint ioctlSize = ioctlCommand.Size; @@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv byte[] temp = new byte[inputDataSize]; - context.Memory.Read((ulong)inputDataPosition, temp); + context.Memory.Read(inputDataPosition, temp); Buffer.BlockCopy(temp, 0, outputData, 0, temp.Length); @@ -122,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv { byte[] temp = new byte[inputDataSize]; - context.Memory.Read((ulong)inputDataPosition, temp); + context.Memory.Read(inputDataPosition, temp); arguments = new Span<byte>(temp); } @@ -226,10 +226,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (errorCode == NvResult.Success) { - long pathPtr = context.Request.SendBuff[0].Position; - long pathSize = context.Request.SendBuff[0].Size; + ulong pathPtr = context.Request.SendBuff[0].Position; + ulong pathSize = context.Request.SendBuff[0].Size; - string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, pathSize); + string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, (long)pathSize); fd = Open(context, path); @@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); } } } @@ -470,13 +470,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv int fd = context.RequestData.ReadInt32(); NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>(); - (long inlineInBufferPosition, long inlineInBufferSize) = context.Request.GetBufferType0x21(1); + (ulong inlineInBufferPosition, ulong inlineInBufferSize) = context.Request.GetBufferType0x21(1); errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments); byte[] temp = new byte[inlineInBufferSize]; - context.Memory.Read((ulong)inlineInBufferPosition, temp); + context.Memory.Read(inlineInBufferPosition, temp); Span<byte> inlineInBuffer = new Span<byte>(temp); @@ -497,7 +497,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); } } } @@ -519,13 +519,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv int fd = context.RequestData.ReadInt32(); NvIoctl ioctlCommand = context.RequestData.ReadStruct<NvIoctl>(); - (long inlineOutBufferPosition, long inlineOutBufferSize) = context.Request.GetBufferType0x22(1); + (ulong inlineOutBufferPosition, ulong inlineOutBufferSize) = context.Request.GetBufferType0x22(1); errorCode = GetIoctlArgument(context, ioctlCommand, out Span<byte> arguments); byte[] temp = new byte[inlineOutBufferSize]; - context.Memory.Read((ulong)inlineOutBufferPosition, temp); + context.Memory.Read(inlineOutBufferPosition, temp); Span<byte> inlineOutBuffer = new Span<byte>(temp); @@ -546,8 +546,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv if ((ioctlCommand.DirectionValue & NvIoctl.Direction.Write) != 0) { - context.Memory.Write((ulong)context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); - context.Memory.Write((ulong)inlineOutBufferPosition, inlineOutBuffer.ToArray()); + context.Memory.Write(context.Request.GetBufferType0x22(0).Position, arguments.ToArray()); + context.Memory.Write(inlineOutBufferPosition, inlineOutBuffer.ToArray()); } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs index 1f24ab7d..44746db6 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Collections; using System.Collections.Generic; -using Ryujinx.Common; using System; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Common.Logging; @@ -198,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices { bool reachedEndOfAddresses = false; ulong targetAddress; - if(start == DefaultStart) + if (start == DefaultStart) { Logger.Debug?.Print(LogClass.ServiceNv, $"Target address set to start of the last available range: 0x{_list.Last.Value:X}."); targetAddress = _list.Last.Value; @@ -301,7 +300,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices freeAddressStartPosition = floorAddress; if (floorAddress != InvalidAddress) { - return !(gpuVa >= floorAddress && ((gpuVa + size) < _tree.Get(floorAddress))); + return !(gpuVa >= floorAddress && ((gpuVa + size) <= _tree.Get(floorAddress))); } } return true; diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs index 8313a690..4f36d401 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -117,8 +117,8 @@ namespace Ryujinx.HLE.HOS.Services.Prepo return ResultCode.InvalidState; } - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; if (inputSize == 0) { @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Prepo byte[] inputBuffer = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, inputBuffer); + context.Memory.Read(inputPosition, inputBuffer); Logger.Info?.Print(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId)); diff --git a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index c8277699..8070cf54 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro _owner = null; } - private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, long nrrAddress, long nrrSize) + private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize) { nrrInfo = null; @@ -71,12 +71,12 @@ namespace Ryujinx.HLE.HOS.Services.Ro { byte[] temp = new byte[0x20]; - _owner.CpuMemory.Read((ulong)(nrrAddress + header.HashOffset + (i * 0x20)), temp); + _owner.CpuMemory.Read(nrrAddress + header.HashOffset + (uint)(i * 0x20), temp); hashes.Add(temp); } - nrrInfo = new NrrInfo(nrrAddress, header, hashes); + nrrInfo = new NrrInfo((ulong)nrrAddress, header, hashes); return ResultCode.Success; } @@ -333,7 +333,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro process.CpuMemory.Write(roStart, relocatableObject.Ro); process.CpuMemory.Write(dataStart, relocatableObject.Data); - MemoryHelper.FillWithZeros(process.CpuMemory, (long)bssStart, (int)(bssEnd - bssStart)); + MemoryHelper.FillWithZeros(process.CpuMemory, bssStart, (int)(bssEnd - bssStart)); KernelResult result; @@ -354,7 +354,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro return process.MemoryManager.SetProcessMemoryPermission(dataStart, bssEnd - dataStart, KMemoryPermission.ReadAndWrite); } - private ResultCode RemoveNrrInfo(long nrrAddress) + private ResultCode RemoveNrrInfo(ulong nrrAddress) { foreach (NrrInfo info in _nrrInfos) { @@ -508,8 +508,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro // pid placeholder, zero context.RequestData.ReadUInt64(); - long nrrAddress = context.RequestData.ReadInt64(); - long nrrSize = context.RequestData.ReadInt64(); + ulong nrrAddress = context.RequestData.ReadUInt64(); + ulong nrrSize = context.RequestData.ReadUInt64(); if (result == ResultCode.Success) { @@ -541,7 +541,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro // pid placeholder, zero context.RequestData.ReadUInt64(); - long nrrHeapAddress = context.RequestData.ReadInt64(); + ulong nrrHeapAddress = context.RequestData.ReadUInt64(); if (result == ResultCode.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs index 8e038fcb..45c34f1c 100644 --- a/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs @@ -6,9 +6,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro { public NrrHeader Header { get; private set; } public List<byte[]> Hashes { get; private set; } - public long NrrAddress { get; private set; } + public ulong NrrAddress { get; private set; } - public NrrInfo(long nrrAddress, NrrHeader header, List<byte[]> hashes) + public NrrInfo(ulong nrrAddress, NrrHeader header, List<byte[]> hashes) { NrrAddress = nrrAddress; Header = header; diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index aaaf26e4..3449e108 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -15,11 +15,11 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false) { - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; UserId userId = byUserId ? context.RequestData.ReadStruct<UserId>() : new UserId(); @@ -35,9 +35,9 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService List<ulong> titleIds = new List<ulong>(); - for (int i = 0; i < inputSize / sizeof(ulong); i++) + for (ulong i = 0; i < inputSize / sizeof(ulong); i++) { - titleIds.Add(context.Memory.Read<ulong>((ulong)inputPosition)); + titleIds.Add(context.Memory.Read<ulong>(inputPosition)); } if (queryCapability == PlayLogQueryCapability.WhiteList) @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++) { - MemoryHelper.Write(context.Memory, outputPosition + (i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value); + MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value); } context.ResponseData.Write(filteredApplicationPlayStatistics.Count()); diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs index 1a4bc5bd..d7397f7d 100644 --- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/ISharedFontManager.cs @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl for (SharedFontType type = 0; type < SharedFontType.Count; type++) { - int offset = (int)type * 4; + uint offset = (uint)type * 4; if (!AddFontToOrderOfPriorityList(context, type, offset)) { @@ -103,27 +103,27 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl return ResultCode.Success; } - private bool AddFontToOrderOfPriorityList(ServiceCtx context, SharedFontType fontType, int offset) + private bool AddFontToOrderOfPriorityList(ServiceCtx context, SharedFontType fontType, uint offset) { - long typesPosition = context.Request.ReceiveBuff[0].Position; - long typesSize = context.Request.ReceiveBuff[0].Size; + ulong typesPosition = context.Request.ReceiveBuff[0].Position; + ulong typesSize = context.Request.ReceiveBuff[0].Size; - long offsetsPosition = context.Request.ReceiveBuff[1].Position; - long offsetsSize = context.Request.ReceiveBuff[1].Size; + ulong offsetsPosition = context.Request.ReceiveBuff[1].Position; + ulong offsetsSize = context.Request.ReceiveBuff[1].Size; - long fontSizeBufferPosition = context.Request.ReceiveBuff[2].Position; - long fontSizeBufferSize = context.Request.ReceiveBuff[2].Size; + ulong fontSizeBufferPosition = context.Request.ReceiveBuff[2].Position; + ulong fontSizeBufferSize = context.Request.ReceiveBuff[2].Size; - if ((uint)offset + 4 > (uint)typesSize || - (uint)offset + 4 > (uint)offsetsSize || - (uint)offset + 4 > (uint)fontSizeBufferSize) + if (offset + 4 > (uint)typesSize || + offset + 4 > (uint)offsetsSize || + offset + 4 > (uint)fontSizeBufferSize) { return false; } - context.Memory.Write((ulong)(typesPosition + offset), (int)fontType); - context.Memory.Write((ulong)(offsetsPosition + offset), context.Device.System.Font.GetSharedMemoryAddressOffset(fontType)); - context.Memory.Write((ulong)(fontSizeBufferPosition + offset), context.Device.System.Font.GetFontSize(fontType)); + context.Memory.Write(typesPosition + offset, (int)fontType); + context.Memory.Write(offsetsPosition + offset, context.Device.System.Font.GetSharedMemoryAddressOffset(fontType)); + context.Memory.Write(fontSizeBufferPosition + offset, context.Device.System.Font.GetFontSize(fontType)); return true; } diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs index bf638800..c9d009a9 100644 --- a/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -35,16 +35,17 @@ namespace Ryujinx.HLE.HOS.Services private readonly List<int> _sessionHandles = new List<int>(); private readonly List<int> _portHandles = new List<int>(); private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>(); - private readonly Dictionary<int, IpcService> _ports = new Dictionary<int, IpcService>(); + private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>(); public ManualResetEvent InitDone { get; } - public IpcService SmObject { get; set; } + public Func<IpcService> SmObjectFactory { get; } public string Name { get; } - public ServerBase(KernelContext context, string name) + public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null) { InitDone = new ManualResetEvent(false); Name = name; + SmObjectFactory = smObjectFactory; _context = context; const ProcessCreationFlags flags = @@ -58,10 +59,10 @@ namespace Ryujinx.HLE.HOS.Services KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop); } - private void AddPort(int serverPortHandle, IpcService obj) + private void AddPort(int serverPortHandle, Func<IpcService> objectFactory) { _portHandles.Add(serverPortHandle); - _ports.Add(serverPortHandle, obj); + _ports.Add(serverPortHandle, objectFactory); } public void AddSessionObj(KServerSession serverSession, IpcService obj) @@ -80,11 +81,11 @@ namespace Ryujinx.HLE.HOS.Services { _selfProcess = KernelStatic.GetCurrentProcess(); - if (SmObject != null) + if (SmObjectFactory != null) { _context.Syscall.ManageNamedPort("sm:", 50, out int serverPortHandle); - AddPort(serverPortHandle, SmObject); + AddPort(serverPortHandle, SmObjectFactory); InitDone.Set(); } @@ -141,7 +142,9 @@ namespace Ryujinx.HLE.HOS.Services // We got a new connection, accept the session to allow servicing future requests. if (_context.Syscall.AcceptSession(handles[signaledIndex], out int serverSessionHandle) == KernelResult.Success) { - AddSessionObj(serverSessionHandle, _ports[handles[signaledIndex]]); + IpcService obj = _ports[handles[signaledIndex]].Invoke(); + + AddSessionObj(serverSessionHandle, obj); } } @@ -180,17 +183,18 @@ namespace Ryujinx.HLE.HOS.Services { for (int i = 0; i < request.RecvListBuff.Count; i++) { - int size = BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2)); + ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan().Slice(sizesOffset + i * 2, 2)); - response.PtrBuff.Add(new IpcPtrBuffDesc((long)tempAddr, i, size)); + response.PtrBuff.Add(new IpcPtrBuffDesc(tempAddr, (uint)i, size)); - request.RecvListBuff[i] = new IpcRecvListBuffDesc((long)tempAddr, size); + request.RecvListBuff[i] = new IpcRecvListBuffDesc(tempAddr, size); - tempAddr += (ulong)size; + tempAddr += size; } } bool shouldReply = true; + bool isTipcCommunication = false; using (MemoryStream raw = new MemoryStream(request.RawData)) { @@ -269,6 +273,8 @@ namespace Ryujinx.HLE.HOS.Services // If the type is past 0xF, we are using TIPC else if (request.Type > IpcMessageType.TipcCloseSession) { + isTipcCommunication = true; + // Response type is always the same as request on TIPC. response.Type = request.Type; @@ -290,13 +296,19 @@ namespace Ryujinx.HLE.HOS.Services response.RawData = resMs.ToArray(); } + + process.CpuMemory.Write(messagePtr, response.GetBytesTipc()); } else { throw new NotImplementedException(request.Type.ToString()); } - process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48))); + if (!isTipcCommunication) + { + process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48))); + } + return shouldReply; } } diff --git a/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs b/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs deleted file mode 100644 index d73c76d9..00000000 --- a/Ryujinx.HLE/HOS/Services/ServiceConfiguration.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services -{ - public static class ServiceConfiguration - { - public static bool IgnoreMissingServices { get; set; } - } -}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs index 2932ee22..6f106789 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISettingsServer.cs @@ -190,17 +190,17 @@ namespace Ryujinx.HLE.HOS.Services.Settings break; } - context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, keyCodeMap); + context.Memory.Write(context.Request.ReceiveBuff[0].Position, keyCodeMap); if (version == 1 && context.Device.System.State.DesiredKeyboardLayout == (long)KeyboardLayout.Default) { - context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, (byte)0x01); + context.Memory.Write(context.Request.ReceiveBuff[0].Position, (byte)0x01); } return ResultCode.Success; } - public ResultCode GetAvailableLanguagesCodesImpl(ServiceCtx context, long position, long size, int maxSize) + public ResultCode GetAvailableLanguagesCodesImpl(ServiceCtx context, ulong position, ulong size, int maxSize) { int count = (int)(size / 8); @@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings for (int index = 0; index < count; index++) { - context.Memory.Write((ulong)position, SystemStateMgr.GetLanguageCode(index)); + context.Memory.Write(position, SystemStateMgr.GetLanguageCode(index)); position += 8; } diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 6bd6866d..54076b4b 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100> public ResultCode GetFirmwareVersion2(ServiceCtx context) { - long replyPos = context.Request.RecvListBuff[0].Position; + ulong replyPos = context.Request.RecvListBuff[0].Position; context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x100L); @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings if (firmwareData != null) { - context.Memory.Write((ulong)replyPos, firmwareData); + context.Memory.Write(replyPos, firmwareData); return ResultCode.Success; } @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings writer.Write(Encoding.ASCII.GetBytes(build)); - context.Memory.Write((ulong)replyPos, ms.ToArray()); + context.Memory.Write(replyPos, ms.ToArray()); } return ResultCode.Success; @@ -110,19 +110,19 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetSettingsItemValueSize(buffer<nn::settings::SettingsName, 0x19>, buffer<nn::settings::SettingsItemKey, 0x19>) -> u64 public ResultCode GetSettingsItemValueSize(ServiceCtx context) { - long classPos = context.Request.PtrBuff[0].Position; - long classSize = context.Request.PtrBuff[0].Size; + ulong classPos = context.Request.PtrBuff[0].Position; + ulong classSize = context.Request.PtrBuff[0].Size; - long namePos = context.Request.PtrBuff[1].Position; - long nameSize = context.Request.PtrBuff[1].Size; + ulong namePos = context.Request.PtrBuff[1].Position; + ulong nameSize = context.Request.PtrBuff[1].Size; byte[] classBuffer = new byte[classSize]; - context.Memory.Read((ulong)classPos, classBuffer); + context.Memory.Read(classPos, classBuffer); byte[] nameBuffer = new byte[nameSize]; - context.Memory.Read((ulong)namePos, nameBuffer); + context.Memory.Read(namePos, nameBuffer); string askedSetting = Encoding.ASCII.GetString(classBuffer).Trim('\0') + "!" + Encoding.ASCII.GetString(nameBuffer).Trim('\0'); @@ -159,22 +159,22 @@ namespace Ryujinx.HLE.HOS.Services.Settings // GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>) public ResultCode GetSettingsItemValue(ServiceCtx context) { - long classPos = context.Request.PtrBuff[0].Position; - long classSize = context.Request.PtrBuff[0].Size; + ulong classPos = context.Request.PtrBuff[0].Position; + ulong classSize = context.Request.PtrBuff[0].Size; - long namePos = context.Request.PtrBuff[1].Position; - long nameSize = context.Request.PtrBuff[1].Size; + ulong namePos = context.Request.PtrBuff[1].Position; + ulong nameSize = context.Request.PtrBuff[1].Size; - long replyPos = context.Request.ReceiveBuff[0].Position; - long replySize = context.Request.ReceiveBuff[0].Size; + ulong replyPos = context.Request.ReceiveBuff[0].Position; + ulong replySize = context.Request.ReceiveBuff[0].Size; byte[] classBuffer = new byte[classSize]; - context.Memory.Read((ulong)classPos, classBuffer); + context.Memory.Read(classPos, classBuffer); byte[] nameBuffer = new byte[nameSize]; - context.Memory.Read((ulong)namePos, nameBuffer); + context.Memory.Read(namePos, nameBuffer); string askedSetting = Encoding.ASCII.GetString(classBuffer).Trim('\0') + "!" + Encoding.ASCII.GetString(nameBuffer).Trim('\0'); @@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings if (nxSetting is string stringValue) { - if (stringValue.Length + 1 > replySize) + if ((ulong)(stringValue.Length + 1) > replySize) { Logger.Error?.Print(LogClass.ServiceSet, $"{askedSetting} String value size is too big!"); } @@ -209,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings throw new NotImplementedException(nxSetting.GetType().Name); } - context.Memory.Write((ulong)replyPos, settingBuffer); + context.Memory.Write(replyPos, settingBuffer); Logger.Debug?.Print(LogClass.ServiceSet, $"{askedSetting} set value: {nxSetting} as {nxSetting.GetType()}"); } @@ -235,8 +235,9 @@ namespace Ryujinx.HLE.HOS.Services.Settings public byte[] GetFirmwareData(Switch device) { - long titleId = 0x0100000000000809; - string contentPath = device.System.ContentManager.GetInstalledContentPath(titleId, StorageId.NandSystem, NcaContentType.Data); + const ulong SystemVersionTitleId = 0x0100000000000809; + + string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.NandSystem, NcaContentType.Data); if (string.IsNullOrWhiteSpace(contentPath)) { diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index d5dabb2d..8b1ec5b8 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -15,9 +15,9 @@ namespace Ryujinx.HLE.HOS.Services.Sm { class IUserInterface : IpcService { - private Dictionary<string, Type> _services; + private static Dictionary<string, Type> _services; - private readonly ConcurrentDictionary<string, KPort> _registeredServices; + private static readonly ConcurrentDictionary<string, KPort> _registeredServices; private readonly ServerBase _commonServer; @@ -25,16 +25,17 @@ namespace Ryujinx.HLE.HOS.Services.Sm public IUserInterface(KernelContext context) { + _commonServer = new ServerBase(context, "CommonServer"); + } + + static IUserInterface() + { _registeredServices = new ConcurrentDictionary<string, KPort>(); _services = Assembly.GetExecutingAssembly().GetTypes() .SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true) .Select(service => (((ServiceAttribute)service).Name, type))) .ToDictionary(service => service.Name, service => service.type); - - TrySetServer(new ServerBase(context, "SmServer") { SmObject = this }); - - _commonServer = new ServerBase(context, "CommonServer"); } [CommandHipc(0)] @@ -47,9 +48,16 @@ namespace Ryujinx.HLE.HOS.Services.Sm return ResultCode.Success; } - [CommandHipc(1)] [CommandTipc(1)] // 12.0.0+ // GetService(ServiceName name) -> handle<move, session> + public ResultCode GetServiceTipc(ServiceCtx context) + { + context.Response.HandleDesc = IpcHandleDesc.MakeMove(0); + + return GetService(context); + } + + [CommandHipc(1)] public ResultCode GetService(ServiceCtx context) { if (!_isInitialized) @@ -90,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm } else { - if (ServiceConfiguration.IgnoreMissingServices) + if (context.Device.Configuration.IgnoreMissingServices) { Logger.Warning?.Print(LogClass.Service, $"Missing service {name} ignored"); } @@ -142,6 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Sm { if (!_isInitialized) { + context.Response.HandleDesc = IpcHandleDesc.MakeMove(0); + return ResultCode.NotInitialized; } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index 1246c31b..63f639cb 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -197,28 +197,28 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, _sockets.Count - 1); } - private IPEndPoint ParseSockAddr(ServiceCtx context, long bufferPosition, long bufferSize) + private IPEndPoint ParseSockAddr(ServiceCtx context, ulong bufferPosition, ulong bufferSize) { - int size = context.Memory.Read<byte>((ulong)bufferPosition); - int family = context.Memory.Read<byte>((ulong)bufferPosition + 1); - int port = BinaryPrimitives.ReverseEndianness(context.Memory.Read<ushort>((ulong)bufferPosition + 2)); + int size = context.Memory.Read<byte>(bufferPosition); + int family = context.Memory.Read<byte>(bufferPosition + 1); + int port = BinaryPrimitives.ReverseEndianness(context.Memory.Read<ushort>(bufferPosition + 2)); byte[] rawIp = new byte[4]; - context.Memory.Read((ulong)bufferPosition + 4, rawIp); + context.Memory.Read(bufferPosition + 4, rawIp); return new IPEndPoint(new IPAddress(rawIp), port); } - private void WriteSockAddr(ServiceCtx context, long bufferPosition, IPEndPoint endPoint) + private void WriteSockAddr(ServiceCtx context, ulong bufferPosition, IPEndPoint endPoint) { - context.Memory.Write((ulong)bufferPosition, (byte)0); - context.Memory.Write((ulong)bufferPosition + 1, (byte)endPoint.AddressFamily); - context.Memory.Write((ulong)bufferPosition + 2, BinaryPrimitives.ReverseEndianness((ushort)endPoint.Port)); - context.Memory.Write((ulong)bufferPosition + 4, endPoint.Address.GetAddressBytes()); + context.Memory.Write(bufferPosition, (byte)0); + context.Memory.Write(bufferPosition + 1, (byte)endPoint.AddressFamily); + context.Memory.Write(bufferPosition + 2, BinaryPrimitives.ReverseEndianness((ushort)endPoint.Port)); + context.Memory.Write(bufferPosition + 4, endPoint.Address.GetAddressBytes()); } - private void WriteSockAddr(ServiceCtx context, long bufferPosition, BsdSocket socket, bool isRemote) + private void WriteSockAddr(ServiceCtx context, ulong bufferPosition, BsdSocket socket, bool isRemote) { IPEndPoint endPoint = (isRemote ? socket.Handle.RemoteEndPoint : socket.Handle.LocalEndPoint) as IPEndPoint; @@ -282,13 +282,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd // Open(u32 flags, array<unknown, 0x21> path) -> (i32 ret, u32 bsd_errno) public ResultCode Open(ServiceCtx context) { - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); int flags = context.RequestData.ReadInt32(); byte[] rawPath = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, rawPath); + context.Memory.Read(bufferPosition, rawPath); string path = Encoding.ASCII.GetString(rawPath); @@ -317,10 +317,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int fdsCount = context.RequestData.ReadInt32(); int timeout = context.RequestData.ReadInt32(); - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); - if (timeout < -1 || fdsCount < 0 || (fdsCount * 8) > bufferSize) + if (timeout < -1 || fdsCount < 0 || (ulong)(fdsCount * 8) > bufferSize) { return WriteBsdResult(context, -1, LinuxError.EINVAL); } @@ -329,7 +329,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd for (int i = 0; i < fdsCount; i++) { - int socketFd = context.Memory.Read<int>((ulong)(bufferPosition + i * 8)); + int socketFd = context.Memory.Read<int>(bufferPosition + (ulong)i * 8); BsdSocket socket = RetrieveSocket(socketFd); @@ -337,8 +337,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { return WriteBsdResult(context, -1, LinuxError.EBADF);} - PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>((ulong)(bufferPosition + i * 8 + 4)); - PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>((ulong)(bufferPosition + i * 8 + 6)); + PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>(bufferPosition + (ulong)i * 8 + 4); + PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.Read<short>(bufferPosition + (ulong)i * 8 + 6); events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents); } @@ -413,8 +413,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd for (int i = 0; i < fdsCount; i++) { PollEvent Event = events[i]; - context.Memory.Write((ulong)(bufferPosition + i * 8), Event.SocketFd); - context.Memory.Write((ulong)(bufferPosition + i * 8 + 4), (short)Event.InputEvents); + context.Memory.Write(bufferPosition + (ulong)i * 8, Event.SocketFd); + context.Memory.Write(bufferPosition + (ulong)i * 8 + 4, (short)Event.InputEvents); PollEvent.EventTypeMask outputEvents = 0; @@ -443,7 +443,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd outputEvents |= PollEvent.EventTypeMask.Output; } - context.Memory.Write((ulong)(bufferPosition + i * 8 + 6), (short)outputEvents); + context.Memory.Write(bufferPosition + (ulong)i * 8 + 6, (short)outputEvents); } return WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS); @@ -467,7 +467,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int socketFd = context.RequestData.ReadInt32(); SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); - (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); + (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -489,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd result = socket.Handle.Receive(receivedBuffer, socketFlags); errno = SetResultErrno(socket.Handle, result); - context.Memory.Write((ulong)receivePosition, receivedBuffer); + context.Memory.Write(receivePosition, receivedBuffer); } catch (SocketException exception) { @@ -507,8 +507,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int socketFd = context.RequestData.ReadInt32(); SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); - (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); - (long sockAddrOutPosition, long sockAddrOutSize) = context.Request.GetBufferType0x22(1); + (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(); + (ulong sockAddrOutPosition, ulong sockAddrOutSize) = context.Request.GetBufferType0x22(1); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -532,7 +532,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd result = socket.Handle.ReceiveFrom(receivedBuffer, receivedBuffer.Length, socketFlags, ref endPoint); errno = SetResultErrno(socket.Handle, result); - context.Memory.Write((ulong)receivePosition, receivedBuffer); + context.Memory.Write(receivePosition, receivedBuffer); WriteSockAddr(context, sockAddrOutPosition, (IPEndPoint)endPoint); } catch (SocketException exception) @@ -551,7 +551,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int socketFd = context.RequestData.ReadInt32(); SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); - (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); + (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -569,7 +569,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd byte[] sendBuffer = new byte[sendSize]; - context.Memory.Read((ulong)sendPosition, sendBuffer); + context.Memory.Read(sendPosition, sendBuffer); try { @@ -593,8 +593,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int socketFd = context.RequestData.ReadInt32(); SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); - (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(1); + (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(1); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -612,7 +612,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd byte[] sendBuffer = new byte[sendSize]; - context.Memory.Read((ulong)sendPosition, sendBuffer); + context.Memory.Read(sendPosition, sendBuffer); EndPoint endPoint = ParseSockAddr(context, bufferPosition, bufferSize); @@ -637,7 +637,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -692,7 +692,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -751,7 +751,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -774,7 +774,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -799,7 +799,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int level = context.RequestData.ReadInt32(); int optionName = context.RequestData.ReadInt32(); - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -866,10 +866,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd case BsdIoctl.AtMark: errno = LinuxError.SUCCESS; - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); // FIXME: OOB not implemented. - context.Memory.Write((ulong)bufferPosition, 0); + context.Memory.Write(bufferPosition, 0); break; default: @@ -917,7 +917,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd return WriteBsdResult(context, result, errno); } - private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize) + private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, ulong optionValuePosition, ulong optionValueSize) { try { @@ -938,13 +938,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd case SocketOptionName.Type: case SocketOptionName.Linger: socket.Handle.GetSocketOption(SocketOptionLevel.Socket, optionName, optionValue); - context.Memory.Write((ulong)optionValuePosition, optionValue); + context.Memory.Write(optionValuePosition, optionValue); return LinuxError.SUCCESS; case (SocketOptionName)0x200: socket.Handle.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue); - context.Memory.Write((ulong)optionValuePosition, optionValue); + context.Memory.Write(optionValuePosition, optionValue); return LinuxError.SUCCESS; @@ -960,7 +960,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } - private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize) + private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, ulong optionValuePosition, ulong optionValueSize) { try { @@ -1013,7 +1013,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd int level = context.RequestData.ReadInt32(); int optionName = context.RequestData.ReadInt32(); - (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -1105,7 +1105,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); + (ulong sendPosition, ulong sendSize) = context.Request.GetBufferType0x21(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -1115,7 +1115,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { byte[] sendBuffer = new byte[sendSize]; - context.Memory.Read((ulong)sendPosition, sendBuffer); + context.Memory.Read(sendPosition, sendBuffer); try { @@ -1137,7 +1137,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { int socketFd = context.RequestData.ReadInt32(); - (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); + (ulong receivePosition, ulong receiveLength) = context.Request.GetBufferType0x22(); LinuxError errno = LinuxError.EBADF; BsdSocket socket = RetrieveSocket(socketFd); @@ -1151,7 +1151,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { result = socket.Handle.Receive(receivedBuffer); errno = SetResultErrno(socket.Handle, result); - context.Memory.Write((ulong)receivePosition, receivedBuffer); + context.Memory.Write(receivePosition, receivedBuffer); } catch (SocketException exception) { diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs index f6b83eaa..f58953d8 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs @@ -3,6 +3,8 @@ using Ryujinx.Cpu; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Services.Settings; using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager; +using Ryujinx.HLE.HOS.Services.Sockets.Nsd.Types; +using System.Runtime.InteropServices; using System.Text; namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd @@ -11,8 +13,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd [Service("nsd:u")] // Max sessions: 20 class IManager : IpcService { - private NsdSettings _nsdSettings; - private FqdnResolver _fqdnResolver; + private readonly NsdSettings _nsdSettings; + private readonly FqdnResolver _fqdnResolver; private bool _isInitialized = false; @@ -20,12 +22,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd { // TODO: Load nsd settings through the savedata 0x80000000000000B0 (nsdsave:/). - NxSettings.Settings.TryGetValue("nsd!test_mode", out object testMode); + if (!NxSettings.Settings.TryGetValue("nsd!test_mode", out object testMode)) + { + // return ResultCode.InvalidSettingsValue; + } + + if (!NxSettings.Settings.TryGetValue("nsd!environment_identifier", out object environmentIdentifier)) + { + // return ResultCode.InvalidSettingsValue; + } _nsdSettings = new NsdSettings { Initialized = true, - TestMode = (bool)testMode + TestMode = (bool)testMode, + Environment = (string)environmentIdentifier }; _fqdnResolver = new FqdnResolver(_nsdSettings); @@ -33,37 +44,35 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd _isInitialized = true; } + [CommandHipc(5)] // 11.0.0+ + // GetSettingUrl() -> buffer<unknown<0x100>, 0x16> + public ResultCode GetSettingUrl(ServiceCtx context) + { + throw new ServiceNotImplementedException(this, context, false); + } + [CommandHipc(10)] // GetSettingName() -> buffer<unknown<0x100>, 0x16> public ResultCode GetSettingName(ServiceCtx context) { - (long outputPosition, long outputSize) = context.Request.GetBufferType0x22(); - - ResultCode result = _fqdnResolver.GetSettingName(context, out string settingName); - - if (result == ResultCode.Success) - { - byte[] settingNameBuffer = Encoding.UTF8.GetBytes(settingName + '\0'); - - context.Memory.Write((ulong)outputPosition, settingNameBuffer); - } - - return result; + throw new ServiceNotImplementedException(this, context, false); } [CommandHipc(11)] - // GetEnvironmentIdentifier() -> buffer<unknown<8>, 0x16> + // GetEnvironmentIdentifier() -> buffer<bytes<8> environment_identifier, 0x16> public ResultCode GetEnvironmentIdentifier(ServiceCtx context) { - (long outputPosition, long outputSize) = context.Request.GetBufferType0x22(); + (ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(); - ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(context, out string identifier); + MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); + + ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(out string identifier); if (result == ResultCode.Success) { - byte[] identifierBuffer = Encoding.UTF8.GetBytes(identifier + '\0'); + byte[] identifierBuffer = Encoding.UTF8.GetBytes(identifier); - context.Memory.Write((ulong)outputPosition, identifierBuffer); + context.Memory.Write(outputPosition, identifierBuffer); } return result; @@ -122,23 +131,35 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd throw new ServiceNotImplementedException(this, context, false); } - [CommandHipc(15)] - // Unknown(bytes<1>) - public ResultCode Unknown(ServiceCtx context) + [CommandHipc(15)] // 4.0.0+ + // SetChangeEnvironmentIdentifierDisabled(bytes<1>) + public ResultCode SetChangeEnvironmentIdentifierDisabled(ServiceCtx context) { - throw new ServiceNotImplementedException(this, context, false); + byte disabled = context.RequestData.ReadByte(); + + // TODO: When sys:set service calls will be implemented + /* + if (((nn::settings::detail::GetServiceDiscoveryControlSettings() ^ disabled) & 1) != 0 ) + { + nn::settings::detail::SetServiceDiscoveryControlSettings(disabled & 1); + } + */ + + Logger.Stub?.PrintStub(LogClass.ServiceNsd, new { disabled }); + + return ResultCode.Success; } [CommandHipc(20)] // Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16> public ResultCode Resolve(ServiceCtx context) { - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; ResultCode result = _fqdnResolver.ResolveEx(context, out _, out string resolvedAddress); - if (resolvedAddress.Length > outputSize) + if ((ulong)resolvedAddress.Length > outputSize) { return ResultCode.InvalidArgument; } @@ -147,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - context.Memory.Write((ulong)outputPosition, resolvedAddressBuffer); + context.Memory.Write(outputPosition, resolvedAddressBuffer); return result; } @@ -156,12 +177,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd // ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>) public ResultCode ResolveEx(ServiceCtx context) { - long outputPosition = context.Request.ReceiveBuff[0].Position; - long outputSize = context.Request.ReceiveBuff[0].Size; + ulong outputPosition = context.Request.ReceiveBuff[0].Position; + ulong outputSize = context.Request.ReceiveBuff[0].Size; ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress); - if (resolvedAddress.Length > outputSize) + if ((ulong)resolvedAddress.Length > outputSize) { return ResultCode.InvalidArgument; } @@ -170,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - context.Memory.Write((ulong)outputPosition, resolvedAddressBuffer); + context.Memory.Write(outputPosition, resolvedAddressBuffer); context.ResponseData.Write((int)errorCode); @@ -226,6 +247,24 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd throw new ServiceNotImplementedException(this, context, false); } + [CommandHipc(51)] // 9.0.0+ + // WriteTestParameter(buffer<?>) + public ResultCode WriteTestParameter(ServiceCtx context) + { + // TODO: Write test parameter through the savedata 0x80000000000000B0 (nsdsave:/test_parameter). + + throw new ServiceNotImplementedException(this, context, false); + } + + [CommandHipc(52)] // 9.0.0+ + // ReadTestParameter() -> buffer<?> + public ResultCode ReadTestParameter(ServiceCtx context) + { + // TODO: Read test parameter through the savedata 0x80000000000000B0 (nsdsave:/test_parameter). + + throw new ServiceNotImplementedException(this, context, false); + } + [CommandHipc(60)] // ReadSaveDataFromFsForTest() -> buffer<unknown<0x12bf0>, 0x16> public ResultCode ReadSaveDataFromFsForTest(ServiceCtx context) @@ -235,8 +274,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd return ResultCode.ServiceNotInitialized; } - // TODO: Call nn::nsd::detail::fs::ReadSaveDataWithOffset() at offset 0 to write the - // whole savedata inside the buffer. + // TODO: Read the savedata 0x80000000000000B0 (nsdsave:/file) and write it inside the buffer. Logger.Stub?.PrintStub(LogClass.ServiceNsd); @@ -247,38 +285,114 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd // WriteSaveDataToFsForTest(buffer<unknown<0x12bf0>, 0x15>) public ResultCode WriteSaveDataToFsForTest(ServiceCtx context) { - // NOTE: Stubbed in system module. - - if (_isInitialized) + if (!_isInitialized) { - return ResultCode.NotImplemented; + return ResultCode.ServiceNotInitialized; } - else + + // TODO: When sys:set service calls will be implemented + /* + if (nn::settings::detail::GetSettingsItemValueSize("nsd", "test_mode") != 1) + { + return ResultCode.InvalidSettingsValue; + } + */ + + if (!_nsdSettings.TestMode) { - return ResultCode.ServiceNotInitialized; + return ResultCode.InvalidSettingsValue; } + + // TODO: Write the buffer inside the savedata 0x80000000000000B0 (nsdsave:/file). + + Logger.Stub?.PrintStub(LogClass.ServiceNsd); + + return ResultCode.Success; } [CommandHipc(62)] // DeleteSaveDataOfFsForTest() public ResultCode DeleteSaveDataOfFsForTest(ServiceCtx context) { - // NOTE: Stubbed in system module. - - if (_isInitialized) + if (!_isInitialized) { - return ResultCode.NotImplemented; + return ResultCode.ServiceNotInitialized; } - else + + // TODO: When sys:set service calls will be implemented + /* + if (nn::settings::detail::GetSettingsItemValueSize("nsd", "test_mode") != 1) + { + return ResultCode.InvalidSettingsValue; + } + */ + + if (!_nsdSettings.TestMode) { - return ResultCode.ServiceNotInitialized; + return ResultCode.InvalidSettingsValue; } + + // TODO: Delete the savedata 0x80000000000000B0. + + Logger.Stub?.PrintStub(LogClass.ServiceNsd); + + return ResultCode.Success; } - [CommandHipc(63)] + [CommandHipc(63)] // 4.0.0+ // IsChangeEnvironmentIdentifierDisabled() -> bytes<1> public ResultCode IsChangeEnvironmentIdentifierDisabled(ServiceCtx context) { + // TODO: When sys:set service calls will be implemented use nn::settings::detail::GetServiceDiscoveryControlSettings() + + bool disabled = false; + + context.ResponseData.Write(disabled); + + Logger.Stub?.PrintStub(LogClass.ServiceNsd, new { disabled }); + + return ResultCode.Success; + } + + [CommandHipc(100)] // 10.0.0+ + // GetApplicationServerEnvironmentType() -> bytes<1> + public ResultCode GetApplicationServerEnvironmentType(ServiceCtx context) + { + // TODO: Mount the savedata 0x80000000000000B0 (nsdsave:/test_parameter) and returns the environment type stored inside if the mount succeed. + // Returns ResultCode.NullOutputObject if failed. + + ResultCode result = _fqdnResolver.GetEnvironmentIdentifier(out string identifier); + + if (result != ResultCode.Success) + { + return result; + } + + byte environmentType = identifier.Substring(0, 2) switch + { + "lp" => (byte)ApplicationServerEnvironmentType.Lp, + "sd" => (byte)ApplicationServerEnvironmentType.Sd, + "sp" => (byte)ApplicationServerEnvironmentType.Sp, + "dp" => (byte)ApplicationServerEnvironmentType.Dp, + _ => (byte)ApplicationServerEnvironmentType.None + }; + + context.ResponseData.Write(environmentType); + + return ResultCode.Success; + } + + [CommandHipc(101)] // 10.0.0+ + // SetApplicationServerEnvironmentType(bytes<1>) + public ResultCode SetApplicationServerEnvironmentType(ServiceCtx context) + { + throw new ServiceNotImplementedException(this, context, false); + } + + [CommandHipc(102)] // 10.0.0+ + // DeleteApplicationServerEnvironmentType() + public ResultCode DeleteApplicationServerEnvironmentType(ServiceCtx context) + { throw new ServiceNotImplementedException(this, context, false); } } diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs index 6bdf06ad..2e23f3fb 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs @@ -13,38 +13,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager _nsdSettings = nsdSettings; } - public ResultCode GetSettingName(ServiceCtx context, out string settingName) + public ResultCode GetEnvironmentIdentifier(out string identifier) { if (_nsdSettings.TestMode) { - settingName = ""; + identifier = "err"; - return ResultCode.NotImplemented; - } - else - { - settingName = ""; - - if (true) // TODO: Determine field (struct + 0x2C) - { - settingName = _nsdSettings.Environment; - - return ResultCode.Success; - } - -#pragma warning disable CS0162 - return ResultCode.NullOutputObject; -#pragma warning restore CS0162 - } - } - - public ResultCode GetEnvironmentIdentifier(ServiceCtx context, out string identifier) - { - if (_nsdSettings.TestMode) - { - identifier = "rre"; - - return ResultCode.NotImplemented; + return ResultCode.InvalidSettingsValue; } else { @@ -56,7 +31,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager public ResultCode Resolve(ServiceCtx context, string address, out string resolvedAddress) { - if (address != "api.sect.srv.nintendo.net" || address != "conntest.nintendowifi.net") + if (address == "api.sect.srv.nintendo.net" || + address == "ctest.cdn.nintendo.net" || + address == "ctest.cdn.n.nintendoswitch.cn" || + address == "unknown.dummy.nintendo.net") + { + resolvedAddress = address; + } + else { // TODO: Load Environment from the savedata. address = address.Replace("%", _nsdSettings.Environment); @@ -87,22 +69,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager _ => address, }; } - else - { - resolvedAddress = address; - } return ResultCode.Success; } public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress) { - long inputPosition = context.Request.SendBuff[0].Position; - long inputSize = context.Request.SendBuff[0].Size; + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; byte[] addressBuffer = new byte[inputSize]; - context.Memory.Read((ulong)inputPosition, addressBuffer); + context.Memory.Read(inputPosition, addressBuffer); string address = Encoding.UTF8.GetString(addressBuffer).TrimEnd('\0'); diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs index 0e636f9a..993fbe8a 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/ResultCode.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd Success = 0, - NotImplemented = ( 1 << ErrorCodeShift) | ModuleId, + InvalidSettingsValue = ( 1 << ErrorCodeShift) | ModuleId, InvalidObject1 = ( 3 << ErrorCodeShift) | ModuleId, InvalidObject2 = ( 4 << ErrorCodeShift) | ModuleId, NullOutputObject = ( 5 << ErrorCodeShift) | ModuleId, diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs new file mode 100644 index 00000000..150bdab4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/ApplicationServerEnvironmentType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Types +{ + enum ApplicationServerEnvironmentType : byte + { + None, + Lp, + Sd, + Sp, + Dp + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs index 2b31cb1d..0a72fa87 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Types/NsdSettings.cs @@ -4,6 +4,6 @@ { public bool Initialized; public bool TestMode; - public string Environment = "lp1"; // or "dd1" if devkit. + public string Environment; } }
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index f86a27dd..2baeb1ef 100644 --- a/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -24,8 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode SetDnsAddressesPrivateRequest(ServiceCtx context) { uint cancelHandleRequest = context.RequestData.ReadUInt32(); - long bufferPosition = context.Request.SendBuff[0].Position; - long bufferSize = context.Request.SendBuff[0].Size; + ulong bufferPosition = context.Request.SendBuff[0].Position; + ulong bufferSize = context.Request.SendBuff[0].Size; // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest }); @@ -38,8 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres public ResultCode GetDnsAddressPrivateRequest(ServiceCtx context) { uint cancelHandleRequest = context.RequestData.ReadUInt32(); - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake of completeness. Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { cancelHandleRequest }); @@ -51,11 +51,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByNameRequest(u8, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>) public ResultCode GetHostByNameRequest(ServiceCtx context) { - long inputBufferPosition = context.Request.SendBuff[0].Position; - long inputBufferSize = context.Request.SendBuff[0].Size; + ulong inputBufferPosition = context.Request.SendBuff[0].Position; + ulong inputBufferSize = context.Request.SendBuff[0].Size; - long outputBufferPosition = context.Request.ReceiveBuff[0].Position; - long outputBufferSize = context.Request.ReceiveBuff[0].Size; + ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position; + ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0); } @@ -64,11 +64,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByAddrRequest(u32, u32, u32, u64, pid, buffer<unknown, 5, 0>) -> (u32, u32, u32, buffer<unknown, 6, 0>) public ResultCode GetHostByAddrRequest(ServiceCtx context) { - long inputBufferPosition = context.Request.SendBuff[0].Position; - long inputBufferSize = context.Request.SendBuff[0].Size; + ulong inputBufferPosition = context.Request.SendBuff[0].Position; + ulong inputBufferSize = context.Request.SendBuff[0].Size; - long outputBufferPosition = context.Request.ReceiveBuff[0].Position; - long outputBufferSize = context.Request.ReceiveBuff[0].Size; + ulong outputBufferPosition = context.Request.ReceiveBuff[0].Position; + ulong outputBufferSize = context.Request.ReceiveBuff[0].Size; return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, 0, 0); } @@ -90,12 +90,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres _ => (errorCode <= NetDbError.Internal) ? "Resolver internal error" : "Unknown resolver error" }; - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; - if (errorString.Length + 1 <= bufferSize) + if ((ulong)(errorString.Length + 1) <= bufferSize) { - context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0')); + context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0')); resultCode = ResultCode.Success; } @@ -135,12 +135,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres _ => "Success" }; - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; - if (errorString.Length + 1 <= bufferSize) + if ((ulong)(errorString.Length + 1) <= bufferSize) { - context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0')); + context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(errorString + '\0')); resultCode = ResultCode.Success; } @@ -152,8 +152,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetAddrInfoRequest(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints) -> (i32 ret, u32 bsd_errno, u32 packed_addrinfo_size, buffer<packed_addrinfo, 6, 0> response) public ResultCode GetAddrInfoRequest(ServiceCtx context) { - long responseBufferPosition = context.Request.ReceiveBuff[0].Position; - long responseBufferSize = context.Request.ReceiveBuff[0].Size; + ulong responseBufferPosition = context.Request.ReceiveBuff[0].Position; + ulong responseBufferSize = context.Request.ReceiveBuff[0].Size; return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, 0, 0); } @@ -188,9 +188,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByNameRequestWithOptions(u8, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByNameRequestWithOptions(ServiceCtx context) { - (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21(); - (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22(); - (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21(); + (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); + (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); + (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetHostByNameRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize); } @@ -199,9 +199,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetHostByAddrRequestWithOptions(u32, u32, u32, u64, pid, buffer<unknown, 21, 0>, buffer<unknown, 21, 0>) -> (u32, u32, u32, buffer<unknown, 22, 0>) public ResultCode GetHostByAddrRequestWithOptions(ServiceCtx context) { - (long inputBufferPosition, long inputBufferSize) = context.Request.GetBufferType0x21(); - (long outputBufferPosition, long outputBufferSize) = context.Request.GetBufferType0x22(); - (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21(); + (ulong inputBufferPosition, ulong inputBufferSize) = context.Request.GetBufferType0x21(); + (ulong outputBufferPosition, ulong outputBufferSize) = context.Request.GetBufferType0x22(); + (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetHostByAddrRequestImpl(context, inputBufferPosition, inputBufferSize, outputBufferPosition, outputBufferSize, optionsBufferPosition, optionsBufferSize); } @@ -210,17 +210,17 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // GetAddrInfoRequestWithOptions(bool enable_nsd_resolve, u32, u64 pid_placeholder, pid, buffer<i8, 5, 0> host, buffer<i8, 5, 0> service, buffer<packed_addrinfo, 5, 0> hints, buffer<unknown, 21, 0>) -> (i32 ret, u32 bsd_errno, u32 unknown, u32 packed_addrinfo_size, buffer<packed_addrinfo, 22, 0> response) public ResultCode GetAddrInfoRequestWithOptions(ServiceCtx context) { - (long responseBufferPosition, long responseBufferSize) = context.Request.GetBufferType0x22(); - (long optionsBufferPosition, long optionsBufferSize) = context.Request.GetBufferType0x21(); + (ulong responseBufferPosition, ulong responseBufferSize) = context.Request.GetBufferType0x22(); + (ulong optionsBufferPosition, ulong optionsBufferSize) = context.Request.GetBufferType0x21(); return GetAddrInfoRequestImpl(context, responseBufferPosition, responseBufferSize, optionsBufferPosition, optionsBufferSize); } - private ResultCode GetHostByNameRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize) + private ResultCode GetHostByNameRequestImpl(ServiceCtx context, ulong inputBufferPosition, ulong inputBufferSize, ulong outputBufferPosition, ulong outputBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize) { byte[] rawName = new byte[inputBufferSize]; - context.Memory.Read((ulong)inputBufferPosition, rawName); + context.Memory.Read(inputBufferPosition, rawName); string name = Encoding.ASCII.GetString(rawName).TrimEnd('\0'); @@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres NetDbError netDbErrorCode = NetDbError.Success; GaiError errno = GaiError.Overflow; - long serializedSize = 0; + ulong serializedSize = 0; if (name.Length <= byte.MaxValue) { @@ -294,11 +294,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.Success; } - private ResultCode GetHostByAddrRequestImpl(ServiceCtx context, long inputBufferPosition, long inputBufferSize, long outputBufferPosition, long outputBufferSize, long optionsBufferPosition, long optionsBufferSize) + private ResultCode GetHostByAddrRequestImpl(ServiceCtx context, ulong inputBufferPosition, ulong inputBufferSize, ulong outputBufferPosition, ulong outputBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize) { byte[] rawIp = new byte[inputBufferSize]; - context.Memory.Read((ulong)inputBufferPosition, rawIp); + context.Memory.Read(inputBufferPosition, rawIp); // TODO: Use params. uint socketLength = context.RequestData.ReadUInt32(); @@ -315,7 +315,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres NetDbError netDbErrorCode = NetDbError.Success; GaiError errno = GaiError.AddressFamily; - long serializedSize = 0; + ulong serializedSize = 0; if (rawIp.Length == 4) { @@ -349,59 +349,59 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres return ResultCode.Success; } - private long SerializeHostEntries(ServiceCtx context, long outputBufferPosition, long outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null) + private ulong SerializeHostEntries(ServiceCtx context, ulong outputBufferPosition, ulong outputBufferSize, IPHostEntry hostEntry, IEnumerable<IPAddress> addresses = null) { - long originalBufferPosition = outputBufferPosition; - long bufferPosition = originalBufferPosition; + ulong originalBufferPosition = outputBufferPosition; + ulong bufferPosition = originalBufferPosition; string hostName = hostEntry.HostName + '\0'; // h_name - context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(hostName)); - bufferPosition += hostName.Length; + context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(hostName)); + bufferPosition += (ulong)hostName.Length; // h_aliases list size - context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(hostEntry.Aliases.Length)); - bufferPosition += 4; + context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness(hostEntry.Aliases.Length)); + bufferPosition += sizeof(int); // Actual aliases foreach (string alias in hostEntry.Aliases) { - context.Memory.Write((ulong)bufferPosition, Encoding.ASCII.GetBytes(alias + '\0')); - bufferPosition += alias.Length + 1; + context.Memory.Write(bufferPosition, Encoding.ASCII.GetBytes(alias + '\0')); + bufferPosition += (ulong)(alias.Length + 1); } // h_addrtype but it's a short (also only support IPv4) - context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)AddressFamily.InterNetwork)); - bufferPosition += 2; + context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness((short)AddressFamily.InterNetwork)); + bufferPosition += sizeof(short); // h_length but it's a short - context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness((short)4)); - bufferPosition += 2; + context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness((short)4)); + bufferPosition += sizeof(short); // Ip address count, we can only support ipv4 (blame Nintendo) - context.Memory.Write((ulong)bufferPosition, addresses != null ? BinaryPrimitives.ReverseEndianness(addresses.Count()) : 0); - bufferPosition += 4; + context.Memory.Write(bufferPosition, addresses != null ? BinaryPrimitives.ReverseEndianness(addresses.Count()) : 0); + bufferPosition += sizeof(int); if (addresses != null) { foreach (IPAddress ip in addresses) { - context.Memory.Write((ulong)bufferPosition, BinaryPrimitives.ReverseEndianness(BitConverter.ToInt32(ip.GetAddressBytes(), 0))); - bufferPosition += 4; + context.Memory.Write(bufferPosition, BinaryPrimitives.ReverseEndianness(BitConverter.ToInt32(ip.GetAddressBytes(), 0))); + bufferPosition += sizeof(int); } } return bufferPosition - originalBufferPosition; } - private ResultCode GetAddrInfoRequestImpl(ServiceCtx context, long responseBufferPosition, long responseBufferSize, long optionsBufferPosition, long optionsBufferSize) + private ResultCode GetAddrInfoRequestImpl(ServiceCtx context, ulong responseBufferPosition, ulong responseBufferSize, ulong optionsBufferPosition, ulong optionsBufferSize) { bool enableNsdResolve = (context.RequestData.ReadInt32() & 1) != 0; uint cancelHandle = context.RequestData.ReadUInt32(); - string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size); - string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, context.Request.SendBuff[1].Size); + string host = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[0].Position, (long)context.Request.SendBuff[0].Size); + string service = MemoryHelper.ReadAsciiString(context.Memory, context.Request.SendBuff[1].Position, (long)context.Request.SendBuff[1].Size); // NOTE: We ignore hints for now. DeserializeAddrInfos(context.Memory, (ulong)context.Request.SendBuff[2].Position, (ulong)context.Request.SendBuff[2].Size); @@ -500,7 +500,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres } } - private ulong SerializeAddrInfos(ServiceCtx context, long responseBufferPosition, long responseBufferSize, IPHostEntry hostEntry, int port) + private ulong SerializeAddrInfos(ServiceCtx context, ulong responseBufferPosition, ulong responseBufferSize, IPHostEntry hostEntry, int port) { ulong originalBufferPosition = (ulong)responseBufferPosition; ulong bufferPosition = originalBufferPosition; @@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres // Termination zero value. context.Memory.Write(bufferPosition, 0); - bufferPosition += 4; + bufferPosition += sizeof(int); return bufferPosition - originalBufferPosition; } diff --git a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs index 3d6be4d8..62e118d9 100644 --- a/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Spl/IRandomInterface.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Spl _rng.GetBytes(randomBytes); - context.Memory.Write((ulong)context.Request.ReceiveBuff[0].Position, randomBytes); + context.Memory.Write(context.Request.ReceiveBuff[0].Position, randomBytes); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs index 9d1ffe85..24f3d066 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs @@ -26,12 +26,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // SetHostName(buffer<bytes, 5>) public ResultCode SetHostName(ServiceCtx context) { - long hostNameDataPosition = context.Request.SendBuff[0].Position; - long hostNameDataSize = context.Request.SendBuff[0].Size; + ulong hostNameDataPosition = context.Request.SendBuff[0].Position; + ulong hostNameDataSize = context.Request.SendBuff[0].Size; byte[] hostNameData = new byte[hostNameDataSize]; - context.Memory.Read((ulong)hostNameDataPosition, hostNameData); + context.Memory.Read(hostNameDataPosition, hostNameData); string hostName = Encoding.ASCII.GetString(hostNameData).Trim('\0'); @@ -75,12 +75,12 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // Write(buffer<bytes, 5>) -> u32 public ResultCode Write(ServiceCtx context) { - long inputDataPosition = context.Request.SendBuff[0].Position; - long inputDataSize = context.Request.SendBuff[0].Size; + ulong inputDataPosition = context.Request.SendBuff[0].Position; + ulong inputDataSize = context.Request.SendBuff[0].Size; byte[] data = new byte[inputDataSize]; - context.Memory.Read((ulong)inputDataPosition, data); + context.Memory.Read(inputDataPosition, data); // NOTE: Tell the guest everything is transferred. uint transferredSize = (uint)inputDataSize; diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs index ec4b5112..718af2cb 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs @@ -41,8 +41,8 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService { CertificateFormat certificateFormat = (CertificateFormat)context.RequestData.ReadUInt32(); - long certificateDataPosition = context.Request.SendBuff[0].Position; - long certificateDataSize = context.Request.SendBuff[0].Size; + ulong certificateDataPosition = context.Request.SendBuff[0].Position; + ulong certificateDataSize = context.Request.SendBuff[0].Size; context.ResponseData.Write(_serverCertificateId++); @@ -55,15 +55,15 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService // ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId public ResultCode ImportClientPki(ServiceCtx context) { - long certificateDataPosition = context.Request.SendBuff[0].Position; - long certificateDataSize = context.Request.SendBuff[0].Size; + ulong certificateDataPosition = context.Request.SendBuff[0].Position; + ulong certificateDataSize = context.Request.SendBuff[0].Size; - long asciiPasswordDataPosition = context.Request.SendBuff[1].Position; - long asciiPasswordDataSize = context.Request.SendBuff[1].Size; + ulong asciiPasswordDataPosition = context.Request.SendBuff[1].Position; + ulong asciiPasswordDataSize = context.Request.SendBuff[1].Size; byte[] asciiPasswordData = new byte[asciiPasswordDataSize]; - context.Memory.Read((ulong)asciiPasswordDataPosition, asciiPasswordData); + context.Memory.Read(asciiPasswordDataPosition, asciiPasswordData); string asciiPassword = Encoding.ASCII.GetString(asciiPasswordData).Trim('\0'); diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 011ab6ed..c3dcbee7 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -18,11 +18,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger uint code = context.RequestData.ReadUInt32(); uint flags = context.RequestData.ReadUInt32(); - ulong dataPos = (ulong)context.Request.SendBuff[0].Position; - ulong dataSize = (ulong)context.Request.SendBuff[0].Size; + ulong dataPos = context.Request.SendBuff[0].Position; + ulong dataSize = context.Request.SendBuff[0].Size; - long replyPos = context.Request.ReceiveBuff[0].Position; - long replySize = context.Request.ReceiveBuff[0].Size; + ulong replyPos = context.Request.ReceiveBuff[0].Position; + ulong replySize = context.Request.ReceiveBuff[0].Size; ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (result == ResultCode.Success) { - context.Memory.Write((ulong)replyPos, outputParcel); + context.Memory.Write(replyPos, outputParcel); } return result; @@ -78,10 +78,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger uint code = context.RequestData.ReadUInt32(); uint flags = context.RequestData.ReadUInt32(); - (long dataPos, long dataSize) = context.Request.GetBufferType0x21(); - (long replyPos, long replySize) = context.Request.GetBufferType0x22(); + (ulong dataPos, ulong dataSize) = context.Request.GetBufferType0x21(); + (ulong replyPos, ulong replySize) = context.Request.GetBufferType0x22(); - ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan((ulong)dataPos, (int)dataSize); + ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize); Span<byte> outputParcel = new Span<byte>(new byte[replySize]); @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (result == ResultCode.Success) { - context.Memory.Write((ulong)replyPos, outputParcel); + context.Memory.Write(replyPos, outputParcel); } return result; diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 556e6a3b..9d8e526f 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Configuration; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -351,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX); bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY); - AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; + AspectRatio aspectRatio = _device.Configuration.AspectRatio; bool isStretched = aspectRatio == AspectRatio.Stretched; ImageCrop crop = new ImageCrop( diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs index 52530025..534af457 100644 --- a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs @@ -246,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { byte type = context.RequestData.ReadByte(); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf<ClockSnapshot>()); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>()); ResultCode result = _timeManager.StandardUserSystemClock.GetClockContext(context.Thread, out SystemClockContext userContext); @@ -274,7 +274,7 @@ namespace Ryujinx.HLE.HOS.Services.Time { byte type = context.RequestData.ReadByte(); - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Marshal.SizeOf<ClockSnapshot>()); + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ClockSnapshot>()); context.RequestData.BaseStream.Position += 7; @@ -354,6 +354,7 @@ namespace Ryujinx.HLE.HOS.Services.Time clockSnapshot.IsAutomaticCorrectionEnabled = _timeManager.StandardUserSystemClock.IsAutomaticCorrectionEnabled(); clockSnapshot.UserContext = userContext; clockSnapshot.NetworkContext = networkContext; + clockSnapshot.SteadyClockTimePoint = currentTimePoint; ResultCode result = _timeManager.TimeZone.Manager.GetDeviceLocationName(out string deviceLocationName); @@ -404,11 +405,11 @@ namespace Ryujinx.HLE.HOS.Services.Time private ClockSnapshot ReadClockSnapshotFromBuffer(ServiceCtx context, IpcPtrBuffDesc ipcDesc) { - Debug.Assert(ipcDesc.Size == Marshal.SizeOf<ClockSnapshot>()); + Debug.Assert(ipcDesc.Size == (ulong)Marshal.SizeOf<ClockSnapshot>()); byte[] temp = new byte[ipcDesc.Size]; - context.Memory.Read((ulong)ipcDesc.Position, temp); + context.Memory.Read(ipcDesc.Position, temp); using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(temp))) { diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs index 9b6c59f6..8b4cd27c 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs @@ -121,11 +121,11 @@ namespace Ryujinx.HLE.HOS.Services.Time uint totalLocationNameCount = context.RequestData.ReadUInt32(); UInt128 timeZoneRuleVersion = context.RequestData.ReadStruct<UInt128>(); - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); byte[] temp = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, temp); + context.Memory.Read(bufferPosition, temp); using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs index 5f35412d..da806ab6 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -51,9 +51,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>) public ResultCode LoadLocationNameList(ServiceCtx context) { - uint index = context.RequestData.ReadUInt32(); - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + uint index = context.RequestData.ReadUInt32(); + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; ResultCode errorCode = _timeZoneContentManager.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24); @@ -70,8 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.LocationNameTooLong; } - context.Memory.Write((ulong)bufferPosition + offset, Encoding.ASCII.GetBytes(locationName)); - MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding); + context.Memory.Write(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName)); + MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + (ulong)locationName.Length, padding); offset += 0x24; } @@ -86,8 +86,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16> public ResultCode LoadTimeZoneRule(ServiceCtx context) { - long bufferPosition = context.Request.ReceiveBuff[0].Position; - long bufferSize = context.Request.ReceiveBuff[0].Size; + ulong bufferPosition = context.Request.ReceiveBuff[0].Position; + ulong bufferSize = context.Request.ReceiveBuff[0].Size; if (bufferSize != 0x4000) { diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs index 659e1331..d0ed9379 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService return ResultCode.PermissionDenied; } - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0'); @@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService byte[] temp = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, temp); + context.Memory.Read(bufferPosition, temp); using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) { @@ -145,10 +145,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16> public ResultCode ParseTimeZoneBinary(ServiceCtx context) { - (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(); - long timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position; - long timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; + ulong timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position; + ulong timeZoneRuleBufferSize = context.Request.ReceiveBuff[0].Size; if (timeZoneRuleBufferSize != 0x4000) { @@ -162,7 +162,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService byte[] temp = new byte[bufferSize]; - context.Memory.Read((ulong)bufferPosition, temp); + context.Memory.Read(bufferPosition, temp); using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) { @@ -188,9 +188,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) public ResultCode ToCalendarTime(ServiceCtx context) { - long posixTime = context.RequestData.ReadInt64(); - long bufferPosition = context.Request.SendBuff[0].Position; - long bufferSize = context.Request.SendBuff[0].Size; + long posixTime = context.RequestData.ReadInt64(); + ulong bufferPosition = context.Request.SendBuff[0].Position; + ulong bufferSize = context.Request.SendBuff[0].Size; if (bufferSize != 0x4000) { @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar); - if (resultCode == 0) + if (resultCode == ResultCode.Success) { context.ResponseData.WriteStruct(calendar); } @@ -232,8 +232,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>) public ResultCode ToPosixTime(ServiceCtx context) { - long inBufferPosition = context.Request.SendBuff[0].Position; - long inBufferSize = context.Request.SendBuff[0].Size; + ulong inBufferPosition = context.Request.SendBuff[0].Position; + ulong inBufferSize = context.Request.SendBuff[0].Size; CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>(); @@ -249,12 +249,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime); - if (resultCode == 0) + if (resultCode == ResultCode.Success) { - long outBufferPosition = context.Request.RecvListBuff[0].Position; - long outBufferSize = context.Request.RecvListBuff[0].Size; + ulong outBufferPosition = context.Request.RecvListBuff[0].Position; + ulong outBufferSize = context.Request.RecvListBuff[0].Size; - context.Memory.Write((ulong)outBufferPosition, posixTime); + context.Memory.Write(outBufferPosition, posixTime); context.ResponseData.Write(1); } @@ -269,12 +269,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime); - if (resultCode == 0) + if (resultCode == ResultCode.Success) { - long outBufferPosition = context.Request.RecvListBuff[0].Position; - long outBufferSize = context.Request.RecvListBuff[0].Size; + ulong outBufferPosition = context.Request.RecvListBuff[0].Position; + ulong outBufferSize = context.Request.RecvListBuff[0].Size; - context.Memory.Write((ulong)outBufferPosition, posixTime); + context.Memory.Write(outBufferPosition, posixTime); // There could be only one result on one calendar as leap seconds aren't supported. context.ResponseData.Write(1); diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index b66ffc3f..963ea9fd 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -5,7 +5,6 @@ using LibHac.Fs.Fsa; using LibHac.FsSystem; using LibHac.FsSystem.NcaUtils; using Ryujinx.Common.Logging; -using Ryujinx.Configuration; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem.Content; @@ -47,10 +46,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone InitializeLocationNameCache(); } - public string SanityCheckDeviceLocationName() + public string SanityCheckDeviceLocationName(string locationName) { - string locationName = ConfigurationState.Instance.System.TimeZone; - if (IsLocationNameValid(locationName)) { return locationName; @@ -58,8 +55,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone Logger.Warning?.Print(LogClass.ServiceTime, $"Invalid device TimeZone {locationName}, switching back to UTC"); - ConfigurationState.Instance.System.TimeZone.Value = "UTC"; - return "UTC"; } @@ -69,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone SteadyClockTimePoint timeZoneUpdatedTimePoint = timeManager.StandardSteadyClock.GetCurrentTimePoint(null); - string deviceLocationName = SanityCheckDeviceLocationName(); + string deviceLocationName = SanityCheckDeviceLocationName(device.Configuration.TimeZone); ResultCode result = GetTimeZoneBinary(deviceLocationName, out Stream timeZoneBinaryStream, out LocalStorage ncaFile); diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 37e603dc..5f161bee 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -61,16 +61,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // ListDisplays() -> (u64, buffer<nn::vi::DisplayInfo, 6>) public ResultCode ListDisplays(ServiceCtx context) { - long recBuffPtr = context.Request.ReceiveBuff[0].Position; + ulong recBuffPtr = context.Request.ReceiveBuff[0].Position; MemoryHelper.FillWithZeros(context.Memory, recBuffPtr, 0x60); // Add only the default display to buffer - context.Memory.Write((ulong)recBuffPtr, Encoding.ASCII.GetBytes("Default")); - context.Memory.Write((ulong)recBuffPtr + 0x40, 0x1L); - context.Memory.Write((ulong)recBuffPtr + 0x48, 0x1L); - context.Memory.Write((ulong)recBuffPtr + 0x50, 1280L); - context.Memory.Write((ulong)recBuffPtr + 0x58, 720L); + context.Memory.Write(recBuffPtr, Encoding.ASCII.GetBytes("Default")); + context.Memory.Write(recBuffPtr + 0x40, 0x1UL); + context.Memory.Write(recBuffPtr + 0x48, 0x1UL); + context.Memory.Write(recBuffPtr + 0x50, 1280UL); + context.Memory.Write(recBuffPtr + 0x58, 720UL); context.ResponseData.Write(1L); @@ -120,9 +120,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // TODO: support multi display. byte[] displayName = context.RequestData.ReadBytes(0x40); - long layerId = context.RequestData.ReadInt64(); - long userId = context.RequestData.ReadInt64(); - long parcelPtr = context.Request.ReceiveBuff[0].Position; + long layerId = context.RequestData.ReadInt64(); + long userId = context.RequestData.ReadInt64(); + ulong parcelPtr = context.Request.ReceiveBuff[0].Position; IBinder producer = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId); @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService ReadOnlySpan<byte> parcelData = parcel.Finish(); - context.Memory.Write((ulong)parcelPtr, parcelData); + context.Memory.Write(parcelPtr, parcelData); context.ResponseData.Write((long)parcelData.Length); @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService long layerFlags = context.RequestData.ReadInt64(); long displayId = context.RequestData.ReadInt64(); - long parcelPtr = context.Request.ReceiveBuff[0].Position; + ulong parcelPtr = context.Request.ReceiveBuff[0].Position; // TODO: support multi display. Display disp = _displays.GetData<Display>((int)displayId); @@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService ReadOnlySpan<byte> parcelData = parcel.Finish(); - context.Memory.Write((ulong)parcelPtr, parcelData); + context.Memory.Write(parcelPtr, parcelData); context.ResponseData.Write(layerId); context.ResponseData.Write((long)parcelData.Length); @@ -250,11 +250,11 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // The size of the layer buffer should be an aligned multiple of width * height // because it was created using GetIndirectLayerImageRequiredMemoryInfo as a guide. - long layerBuffPosition = context.Request.ReceiveBuff[0].Position; - long layerBuffSize = context.Request.ReceiveBuff[0].Size; + ulong layerBuffPosition = context.Request.ReceiveBuff[0].Position; + ulong layerBuffSize = context.Request.ReceiveBuff[0].Size; // Fill the layer with zeros. - context.Memory.Fill((ulong)layerBuffPosition, (ulong)layerBuffSize, 0x00); + context.Memory.Fill(layerBuffPosition, layerBuffSize, 0x00); Logger.Stub?.PrintStub(LogClass.ServiceVi); diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index b88bdbfe..ee359dda 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,22 +1,14 @@ -using LibHac.FsSystem; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; -using Ryujinx.Common; -using Ryujinx.Configuration; -using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Host1x; using Ryujinx.Graphics.Nvdec; using Ryujinx.Graphics.Vic; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services; -using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices; -using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Memory; using System; @@ -24,69 +16,60 @@ namespace Ryujinx.HLE { public class Switch : IDisposable { - private MemoryConfiguration _memoryConfiguration; + public HLEConfiguration Configuration { get; } - public IHardwareDeviceDriver AudioDeviceDriver { get; private set; } + public IHardwareDeviceDriver AudioDeviceDriver { get; } - internal MemoryBlock Memory { get; private set; } + internal MemoryBlock Memory { get; } - public GpuContext Gpu { get; private set; } + public GpuContext Gpu { get; } - internal NvMemoryAllocator MemoryAllocator { get; private set; } + internal NvMemoryAllocator MemoryAllocator { get; } internal Host1xDevice Host1x { get; } - public VirtualFileSystem FileSystem { get; private set; } + public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem; - public Horizon System { get; private set; } + public Horizon System { get; } public ApplicationLoader Application { get; } - public PerformanceStatistics Statistics { get; private set; } + public PerformanceStatistics Statistics { get; } - public UserChannelPersistence UserChannelPersistence { get; } + public Hid Hid { get; } - public Hid Hid { get; private set; } + public TamperMachine TamperMachine { get; } - public TamperMachine TamperMachine { get; private set; } - - public IHostUiHandler UiHandler { get; set; } + public IHostUiHandler UiHandler { get; } public bool EnableDeviceVsync { get; set; } = true; - public Switch( - VirtualFileSystem fileSystem, - ContentManager contentManager, - AccountManager accountManager, - UserChannelPersistence userChannelPersistence, - IRenderer renderer, - IHardwareDeviceDriver audioDeviceDriver, - MemoryConfiguration memoryConfiguration) + public Switch(HLEConfiguration configuration) { - if (renderer == null) + if (configuration.GpuRenderer == null) { - throw new ArgumentNullException(nameof(renderer)); + throw new ArgumentNullException(nameof(configuration.GpuRenderer)); } - if (audioDeviceDriver == null) + if (configuration.AudioDeviceDriver == null) { - throw new ArgumentNullException(nameof(audioDeviceDriver)); + throw new ArgumentNullException(nameof(configuration.AudioDeviceDriver)); } - if (userChannelPersistence == null) + if (configuration.UserChannelPersistence== null) { - throw new ArgumentNullException(nameof(userChannelPersistence)); + throw new ArgumentNullException(nameof(configuration.UserChannelPersistence)); } - UserChannelPersistence = userChannelPersistence; + Configuration = configuration; - _memoryConfiguration = memoryConfiguration; + UiHandler = configuration.HostUiHandler; - AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(audioDeviceDriver); + AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(configuration.AudioDeviceDriver); - Memory = new MemoryBlock(memoryConfiguration.ToDramSize()); + Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize()); - Gpu = new GpuContext(renderer); + Gpu = new GpuContext(configuration.GpuRenderer); MemoryAllocator = new NvMemoryAllocator(); @@ -111,9 +94,7 @@ namespace Ryujinx.HLE } }; - FileSystem = fileSystem; - - System = new Horizon(this, contentManager, accountManager, memoryConfiguration); + System = new Horizon(this); System.InitializeServices(); Statistics = new PerformanceStatistics(); @@ -121,45 +102,30 @@ namespace Ryujinx.HLE Hid = new Hid(this, System.HidBaseAddress); Hid.InitDevices(); - Application = new ApplicationLoader(this, fileSystem, contentManager); + Application = new ApplicationLoader(this); TamperMachine = new TamperMachine(); + + Initialize(); } - public void Initialize() + private void Initialize() { - System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value); + System.State.SetLanguage(Configuration.SystemLanguage); - System.State.SetRegion((RegionCode)ConfigurationState.Instance.System.Region.Value); + System.State.SetRegion(Configuration.Region); - EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync; + EnableDeviceVsync = Configuration.EnableVsync; - System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode; + System.State.DockedMode = Configuration.EnableDockedMode; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; - System.EnablePtc = ConfigurationState.Instance.System.EnablePtc; + System.EnablePtc = Configuration.EnablePtc; - System.FsIntegrityCheckLevel = GetIntegrityCheckLevel(); + System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; - System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode; - - ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices; - ConfigurationState.Instance.System.IgnoreMissingServices.Event += (object _, ReactiveEventArgs<bool> args) => - { - ServiceConfiguration.IgnoreMissingServices = args.NewValue; - }; - - // Configure controllers - Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value); - ConfigurationState.Instance.Hid.InputConfig.Event += Hid.RefreshInputConfigEvent; - } - - public static IntegrityCheckLevel GetIntegrityCheckLevel() - { - return ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; + System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; } public void LoadCart(string exeFsDir, string romFsFile = null) @@ -223,8 +189,6 @@ namespace Ryujinx.HLE { if (disposing) { - ConfigurationState.Instance.Hid.InputConfig.Event -= Hid.RefreshInputConfigEvent; - System.Dispose(); Host1x.Dispose(); AudioDeviceDriver.Dispose(); diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs index 4399da21..0259a4fd 100644 --- a/Ryujinx.HLE/Utilities/StringUtils.cs +++ b/Ryujinx.HLE/Utilities/StringUtils.cs @@ -53,14 +53,14 @@ namespace Ryujinx.HLE.Utilities public static string ReadUtf8String(ServiceCtx context, int index = 0) { - long position = context.Request.PtrBuff[index].Position; - long size = context.Request.PtrBuff[index].Size; + ulong position = context.Request.PtrBuff[index].Position; + ulong size = context.Request.PtrBuff[index].Size; using (MemoryStream ms = new MemoryStream()) { while (size-- > 0) { - byte value = context.Memory.Read<byte>((ulong)position++); + byte value = context.Memory.Read<byte>(position++); if (value == 0) { @@ -86,8 +86,8 @@ namespace Ryujinx.HLE.Utilities public static string ReadUtf8StringSend(ServiceCtx context, int index = 0) { - long position = context.Request.SendBuff[index].Position; - long size = context.Request.SendBuff[index].Size; + ulong position = context.Request.SendBuff[index].Position; + ulong size = context.Request.SendBuff[index].Size; using (MemoryStream ms = new MemoryStream()) { diff --git a/Ryujinx.HLE/Utilities/StructReader.cs b/Ryujinx.HLE/Utilities/StructReader.cs index 3bde1c77..81e758a8 100644 --- a/Ryujinx.HLE/Utilities/StructReader.cs +++ b/Ryujinx.HLE/Utilities/StructReader.cs @@ -1,5 +1,6 @@ using Ryujinx.Cpu; using Ryujinx.Memory; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.Utilities @@ -8,39 +9,30 @@ namespace Ryujinx.HLE.Utilities { private IVirtualMemoryManager _memory; - public long Position { get; private set; } + public ulong Position { get; private set; } - public StructReader(IVirtualMemoryManager memory, long position) + public StructReader(IVirtualMemoryManager memory, ulong position) { _memory = memory; Position = position; } - public T Read<T>() where T : struct + public T Read<T>() where T : unmanaged { T value = MemoryHelper.Read<T>(_memory, Position); - Position += Marshal.SizeOf<T>(); + Position += (uint)Marshal.SizeOf<T>(); return value; } - public T[] Read<T>(int size) where T : struct + public ReadOnlySpan<T> Read<T>(int size) where T : unmanaged { - int structSize = Marshal.SizeOf<T>(); + ReadOnlySpan<byte> data = _memory.GetSpan(Position, size); - int count = size / structSize; + Position += (uint)size; - T[] output = new T[count]; - - for (int index = 0; index < count; index++) - { - output[index] = MemoryHelper.Read<T>(_memory, Position); - - Position += structSize; - } - - return output; + return MemoryMarshal.Cast<byte, T>(data); } } } diff --git a/Ryujinx.HLE/Utilities/StructWriter.cs b/Ryujinx.HLE/Utilities/StructWriter.cs index 6b1e0838..39644db5 100644 --- a/Ryujinx.HLE/Utilities/StructWriter.cs +++ b/Ryujinx.HLE/Utilities/StructWriter.cs @@ -8,9 +8,9 @@ namespace Ryujinx.HLE.Utilities { private IVirtualMemoryManager _memory; - public long Position { get; private set; } + public ulong Position { get; private set; } - public StructWriter(IVirtualMemoryManager memory, long position) + public StructWriter(IVirtualMemoryManager memory, ulong position) { _memory = memory; Position = position; @@ -20,10 +20,10 @@ namespace Ryujinx.HLE.Utilities { MemoryHelper.Write(_memory, Position, value); - Position += Marshal.SizeOf<T>(); + Position += (ulong)Marshal.SizeOf<T>(); } - public void SkipBytes(long count) + public void SkipBytes(ulong count) { Position += count; } diff --git a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj index cee18996..2d61dfb8 100644 --- a/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj +++ b/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj @@ -6,11 +6,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="ppy.SDL2-CS" Version="1.0.225-alpha" /> - </ItemGroup> - - <ItemGroup> <ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" /> + <ProjectReference Include="..\Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj" /> </ItemGroup> </Project> diff --git a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 62098383..927d7fe6 100644 --- a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.SDL2.Common; +using System; using System.Collections.Generic; using static SDL2.SDL; diff --git a/Ryujinx.Input/HLE/NpadController.cs b/Ryujinx.Input/HLE/NpadController.cs index d3553d64..e1a8e2d7 100644 --- a/Ryujinx.Input/HLE/NpadController.cs +++ b/Ryujinx.Input/HLE/NpadController.cs @@ -456,14 +456,14 @@ namespace Ryujinx.Input.HLE KeyboardInput hidKeyboard = new KeyboardInput { Modifier = 0, - Keys = new int[0x8] + Keys = new ulong[0x4] }; foreach (HLEKeyboardMappingEntry entry in KeyMapping) { - int value = keyboardState.IsPressed(entry.TargetKey) ? 1 : 0; + ulong value = keyboardState.IsPressed(entry.TargetKey) ? 1UL : 0UL; - hidKeyboard.Keys[entry.Target / 0x20] |= (value << (entry.Target % 0x20)); + hidKeyboard.Keys[entry.Target / 0x40] |= (value << (entry.Target % 0x40)); } foreach (HLEKeyboardMappingEntry entry in KeyModifierMapping) diff --git a/Ryujinx.Input/HLE/NpadManager.cs b/Ryujinx.Input/HLE/NpadManager.cs index fdb87f9b..abb820b0 100644 --- a/Ryujinx.Input/HLE/NpadManager.cs +++ b/Ryujinx.Input/HLE/NpadManager.cs @@ -1,8 +1,6 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Configuration; -using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Hid; using System; using System.Collections.Generic; @@ -10,6 +8,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client; +using Switch = Ryujinx.HLE.Switch; namespace Ryujinx.Input.HLE { @@ -31,30 +30,41 @@ namespace Ryujinx.Input.HLE private bool _isDisposed; private List<InputConfig> _inputConfig; + private bool _enableKeyboard; + private Switch _device; public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver) { _controllers = new NpadController[MaxControllers]; - _cemuHookClient = new CemuHookClient(); + _cemuHookClient = new CemuHookClient(this); _keyboardDriver = keyboardDriver; _gamepadDriver = gamepadDriver; - _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value; + _inputConfig = new List<InputConfig>(); + _enableKeyboard = false; _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; } + private void RefreshInputConfigForHLE() + { + lock (_lock) + { + _device.Hid.RefreshInputConfig(_inputConfig); + } + } + private void HandleOnGamepadDisconnected(string obj) { // Force input reload - ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value); + ReloadConfiguration(_inputConfig, _enableKeyboard); } private void HandleOnGamepadConnected(string id) { // Force input reload - ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value); + ReloadConfiguration(_inputConfig, _enableKeyboard); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -83,7 +93,7 @@ namespace Ryujinx.Input.HLE } } - public void ReloadConfiguration(List<InputConfig> inputConfig) + public void ReloadConfiguration(List<InputConfig> inputConfig, bool enableKeyboard) { lock (_lock) { @@ -110,10 +120,9 @@ namespace Ryujinx.Input.HLE } _inputConfig = inputConfig; + _enableKeyboard = enableKeyboard; - // Enforce an update of the property that will be updated by HLE. - // TODO: Move that in the input manager maybe? - ConfigurationState.Instance.Hid.InputConfig.Value = inputConfig; + _device.Hid.RefreshInputConfig(inputConfig); } } @@ -133,13 +142,23 @@ namespace Ryujinx.Input.HLE } } - public void Update(Hid hleHid, TamperMachine tamperMachine) + public void Initialize(Switch device, List<InputConfig> inputConfig, bool enableKeyboard) + { + _device = device; + _device.Configuration.RefreshInputConfig = RefreshInputConfigForHLE; + + ReloadConfiguration(inputConfig, enableKeyboard); + } + + public void Update() { lock (_lock) { List<GamepadInput> hleInputStates = new List<GamepadInput>(); List<SixAxisInput> hleMotionStates = new List<SixAxisInput>(NpadDevices.MaxControllers); + KeyboardInput? hleKeyboardInput = null; + foreach (InputConfig inputConfig in _inputConfig) { GamepadInput inputState = default; @@ -157,9 +176,14 @@ namespace Ryujinx.Input.HLE inputState = controller.GetHLEInputState(); - inputState.Buttons |= hleHid.UpdateStickButtons(inputState.LStick, inputState.RStick); + inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick); motionState = controller.GetHLEMotionState(); + + if (_enableKeyboard) + { + hleKeyboardInput = controller.GetHLEKeyboardInput(); + } } else { @@ -172,21 +196,25 @@ namespace Ryujinx.Input.HLE hleInputStates.Add(inputState); hleMotionStates.Add(motionState); + } - if (ConfigurationState.Instance.Hid.EnableKeyboard) - { - KeyboardInput? hleKeyboardInput = controller.GetHLEKeyboardInput(); + _device.Hid.Npads.Update(hleInputStates); + _device.Hid.Npads.UpdateSixAxis(hleMotionStates); - if (hleKeyboardInput.HasValue) - { - hleHid.Keyboard.Update(hleKeyboardInput.Value); - } - } + if (hleKeyboardInput.HasValue) + { + _device.Hid.Keyboard.Update(hleKeyboardInput.Value); } - hleHid.Npads.Update(hleInputStates); - hleHid.Npads.UpdateSixAxis(hleMotionStates); - tamperMachine.UpdateInput(hleInputStates); + _device.TamperMachine.UpdateInput(hleInputStates); + } + } + + internal InputConfig GetPlayerInputConfigByIndex(int index) + { + lock (_lock) + { + return _inputConfig.Find(x => x.PlayerIndex == (Ryujinx.Common.Configuration.Hid.PlayerIndex)index); } } diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs index 395bd0b3..214e23f0 100644 --- a/Ryujinx.Input/Motion/CemuHook/Client.cs +++ b/Ryujinx.Input/Motion/CemuHook/Client.cs @@ -4,7 +4,7 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Logging; -using Ryujinx.Configuration; +using Ryujinx.Input.HLE; using Ryujinx.Input.Motion.CemuHook.Protocol; using System; using System.Collections.Generic; @@ -29,12 +29,14 @@ namespace Ryujinx.Input.Motion.CemuHook private readonly bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length]; private readonly long[] _clientRetryTimer = new long[Enum.GetValues(typeof(PlayerIndex)).Length]; + private NpadManager _npadManager; - public Client() + public Client(NpadManager npadManager) { - _hosts = new Dictionary<int, IPEndPoint>(); - _motionData = new Dictionary<int, Dictionary<int, MotionInput>>(); - _clients = new Dictionary<int, UdpClient>(); + _npadManager = npadManager; + _hosts = new Dictionary<int, IPEndPoint>(); + _motionData = new Dictionary<int, Dictionary<int, MotionInput>>(); + _clients = new Dictionary<int, UdpClient>(); CloseClients(); } @@ -323,7 +325,7 @@ namespace Ryujinx.Input.Motion.CemuHook ulong timestamp = inputData.MotionTimestamp; - InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == (PlayerIndex)clientId); + InputConfig config = _npadManager.GetPlayerInputConfigByIndex(clientId); lock (_motionData) { diff --git a/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj new file mode 100644 index 00000000..a35f8743 --- /dev/null +++ b/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj @@ -0,0 +1,15 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="ppy.SDL2-CS" Version="1.0.225-alpha" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> + </ItemGroup> + +</Project> diff --git a/Ryujinx.Input.SDL2/SDL2Driver.cs b/Ryujinx.SDL2.Common/SDL2Driver.cs index f77bb1d5..edd634ee 100644 --- a/Ryujinx.Input.SDL2/SDL2Driver.cs +++ b/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -4,9 +4,9 @@ using System.IO; using System.Threading; using static SDL2.SDL; -namespace Ryujinx.Input.SDL2 +namespace Ryujinx.SDL2.Common { - class SDL2Driver : IDisposable + public class SDL2Driver : IDisposable { private static SDL2Driver _instance; @@ -25,7 +25,7 @@ namespace Ryujinx.Input.SDL2 } } - private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK; + private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO; private bool _isRunning; private uint _refereceCount; diff --git a/Ryujinx.sln b/Ryujinx.sln index bd00f000..f4eec573 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -59,9 +59,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Open EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SoundIo", "Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj", "{716364DE-B988-41A6-BAB4-327964266ECC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input", "Ryujinx.Input\Ryujinx.Input.csproj", "{C16F112F-38C3-40BC-9F5F-4791112063D6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Input.SDL2", "Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj", "{DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.SDL2.Common", "Ryujinx.SDL2.Common\Ryujinx.SDL2.Common.csproj", "{2D5D3A1D-5730-4648-B0AB-06C53CB910C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Audio.Backends.SDL2", "Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj", "{D99A395A-8569-4DB0-B336-900647890052}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -177,6 +181,14 @@ Global {DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFAB6F2D-B9BF-4AFF-B22B-7684A328EBA3}.Release|Any CPU.Build.0 = Release|Any CPU + {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D5D3A1D-5730-4648-B0AB-06C53CB910C0}.Release|Any CPU.Build.0 = Release|Any CPU + {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D99A395A-8569-4DB0-B336-900647890052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D99A395A-8569-4DB0-B336-900647890052}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json index 29460614..cf21656a 100644 --- a/Ryujinx/Config.json +++ b/Ryujinx/Config.json @@ -1,5 +1,6 @@ { "version": 24, + "enable_file_log": true, "res_scale": 1, "res_scale_custom": 1, "max_anisotropy": -1, @@ -14,7 +15,6 @@ "logging_enable_fs_access_log": false, "logging_filtered_classes": [], "logging_graphics_debug_level": "None", - "enable_file_log": true, "system_language": "AmericanEnglish", "system_region": "USA", "system_time_zone": "UTC", diff --git a/Ryujinx.Common/Configuration/AudioBackend.cs b/Ryujinx/Configuration/AudioBackend.cs index 28233354..e42df039 100644 --- a/Ryujinx.Common/Configuration/AudioBackend.cs +++ b/Ryujinx/Configuration/AudioBackend.cs @@ -4,6 +4,7 @@ { Dummy, OpenAl, - SoundIo + SoundIo, + SDL2 } }
\ No newline at end of file diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx/Configuration/ConfigurationFileFormat.cs index 1d47051a..be9c6864 100644 --- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs +++ b/Ryujinx/Configuration/ConfigurationFileFormat.cs @@ -14,11 +14,16 @@ namespace Ryujinx.Configuration /// <summary> /// The current version of the file format /// </summary> - public const int CurrentVersion = 24; + public const int CurrentVersion = 25; public int Version { get; set; } /// <summary> + /// Enables or disables logging to a file on disk + /// </summary> + public bool EnableFileLog { get; set; } + + /// <summary> /// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead. /// </summary> public int ResScale { get; set; } @@ -88,10 +93,6 @@ namespace Ryujinx.Configuration /// </summary> public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; } - /// <summary> - /// Enables or disables logging to a file on disk - /// </summary> - public bool EnableFileLog { get; set; } /// <summary> /// Change System Language diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx/Configuration/ConfigurationState.cs index 7063110f..9ea5c282 100644 --- a/Ryujinx.Common/Configuration/ConfigurationState.cs +++ b/Ryujinx/Configuration/ConfigurationState.cs @@ -408,6 +408,7 @@ namespace Ryujinx.Configuration ConfigurationFileFormat configurationFile = new ConfigurationFileFormat { Version = ConfigurationFileFormat.CurrentVersion, + EnableFileLog = Logger.EnableFileLog, ResScale = Graphics.ResScale, ResScaleCustom = Graphics.ResScaleCustom, MaxAnisotropy = Graphics.MaxAnisotropy, @@ -422,7 +423,6 @@ namespace Ryujinx.Configuration LoggingEnableFsAccessLog = Logger.EnableFsAccessLog, LoggingFilteredClasses = Logger.FilteredClasses, LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel, - EnableFileLog = Logger.EnableFileLog, SystemLanguage = System.Language, SystemRegion = System.Region, SystemTimeZone = System.TimeZone, @@ -474,6 +474,7 @@ namespace Ryujinx.Configuration public void LoadDefault() { + Logger.EnableFileLog.Value = true; Graphics.ResScale.Value = 1; Graphics.ResScaleCustom.Value = 1.0f; Graphics.MaxAnisotropy.Value = -1.0f; @@ -488,7 +489,6 @@ namespace Ryujinx.Configuration Logger.EnableFsAccessLog.Value = false; Logger.FilteredClasses.Value = Array.Empty<LogClass>(); Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None; - Logger.EnableFileLog.Value = true; System.Language.Value = Language.AmericanEnglish; System.Region.Value = Region.USA; System.TimeZone.Value = "UTC"; @@ -803,6 +803,14 @@ namespace Ryujinx.Configuration configurationFileUpdated = true; } + if (configurationFileFormat.Version < 25) + { + Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 25."); + + configurationFileUpdated = true; + } + + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; @@ -817,7 +825,6 @@ namespace Ryujinx.Configuration Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog; Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses; Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel; - Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; System.Language.Value = configurationFileFormat.SystemLanguage; System.Region.Value = configurationFileFormat.SystemRegion; System.TimeZone.Value = configurationFileFormat.SystemTimeZone; diff --git a/Ryujinx.Common/Configuration/LoggerModule.cs b/Ryujinx/Configuration/LoggerModule.cs index 20c0fb46..44631ea0 100644 --- a/Ryujinx.Common/Configuration/LoggerModule.cs +++ b/Ryujinx/Configuration/LoggerModule.cs @@ -1,7 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using System; -using System.IO; namespace Ryujinx.Configuration { diff --git a/Ryujinx.Common/Configuration/System/Language.cs b/Ryujinx/Configuration/System/Language.cs index d3af296b..d3af296b 100644 --- a/Ryujinx.Common/Configuration/System/Language.cs +++ b/Ryujinx/Configuration/System/Language.cs diff --git a/Ryujinx.Common/Configuration/System/Region.cs b/Ryujinx/Configuration/System/Region.cs index 54b1c36f..54b1c36f 100644 --- a/Ryujinx.Common/Configuration/System/Region.cs +++ b/Ryujinx/Configuration/System/Region.cs diff --git a/Ryujinx.Common/Configuration/Ui/ColumnSort.cs b/Ryujinx/Configuration/Ui/ColumnSort.cs index fd8b5da1..fd8b5da1 100644 --- a/Ryujinx.Common/Configuration/Ui/ColumnSort.cs +++ b/Ryujinx/Configuration/Ui/ColumnSort.cs diff --git a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs b/Ryujinx/Configuration/Ui/GuiColumns.cs index de4f7369..de4f7369 100644 --- a/Ryujinx.Common/Configuration/Ui/GuiColumns.cs +++ b/Ryujinx/Configuration/Ui/GuiColumns.cs diff --git a/Ryujinx/Input/GTK3/GTK3MappingHelper.cs b/Ryujinx/Input/GTK3/GTK3MappingHelper.cs index 5e68aa40..c6be528e 100644 --- a/Ryujinx/Input/GTK3/GTK3MappingHelper.cs +++ b/Ryujinx/Input/GTK3/GTK3MappingHelper.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Input.GTK3 GtkKey.F27, GtkKey.F28, GtkKey.F29, - GtkKey.F29, + GtkKey.F30, GtkKey.F31, GtkKey.F32, GtkKey.F33, @@ -128,6 +128,7 @@ namespace Ryujinx.Input.GTK3 GtkKey.Key_8, GtkKey.Key_9, GtkKey.grave, + GtkKey.grave, GtkKey.minus, GtkKey.plus, GtkKey.bracketleft, @@ -138,7 +139,6 @@ namespace Ryujinx.Input.GTK3 GtkKey.period, GtkKey.slash, GtkKey.backslash, - GtkKey.backslash, // NOTE: invalid GtkKey.blank, diff --git a/Ryujinx/Modules/DiscordIntegrationModule.cs b/Ryujinx/Modules/DiscordIntegrationModule.cs index 3a91cf53..d890a4c8 100644 --- a/Ryujinx/Modules/DiscordIntegrationModule.cs +++ b/Ryujinx/Modules/DiscordIntegrationModule.cs @@ -2,41 +2,48 @@ using Ryujinx.Common; using Ryujinx.Configuration; using System; -using System.Linq; namespace Ryujinx.Modules { static class DiscordIntegrationModule { - private static DiscordRpcClient _discordClient; - - private const string LargeDescription = "Ryujinx is a Nintendo Switch emulator."; + private const string Description = "A simple, experimental Nintendo Switch emulator."; + private const string CliendId = "568815339807309834"; - public static RichPresence DiscordPresence { get; private set; } + private static DiscordRpcClient _discordClient; + private static RichPresence _discordPresenceMain; public static void Initialize() { - DiscordPresence = new RichPresence + _discordPresenceMain = new RichPresence { Assets = new Assets { LargeImageKey = "ryujinx", - LargeImageText = LargeDescription + LargeImageText = Description }, Details = "Main Menu", State = "Idling", - Timestamps = new Timestamps(DateTime.UtcNow) + Timestamps = Timestamps.Now, + Buttons = new Button[] + { + new Button() + { + Label = "Website", + Url = "https://ryujinx.org/" + } + } }; ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; } - private static void Update(object sender, ReactiveEventArgs<bool> e) + private static void Update(object sender, ReactiveEventArgs<bool> evnt) { - if (e.OldValue != e.NewValue) + if (evnt.OldValue != evnt.NewValue) { // If the integration was active, disable it and unload everything - if (e.OldValue) + if (evnt.OldValue) { _discordClient?.Dispose(); @@ -44,130 +51,49 @@ namespace Ryujinx.Modules } // If we need to activate it and the client isn't active, initialize it - if (e.NewValue && _discordClient == null) + if (evnt.NewValue && _discordClient == null) { - _discordClient = new DiscordRpcClient("568815339807309834"); + _discordClient = new DiscordRpcClient(CliendId); _discordClient.Initialize(); - _discordClient.SetPresence(DiscordPresence); + _discordClient.SetPresence(_discordPresenceMain); } } } public static void SwitchToPlayingState(string titleId, string titleName) { - if (SupportedTitles.Contains(titleId)) - { - DiscordPresence.Assets.LargeImageKey = titleId; - } - - string state = titleId; - - if (state == null) - { - state = "Ryujinx"; - } - else - { - state = state.ToUpper(); - } - - string details = "Idling"; - - if (titleName != null) + _discordClient?.SetPresence(new RichPresence { - 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); + Assets = new Assets + { + LargeImageKey = "game", + LargeImageText = titleName, + SmallImageKey = "ryujinx", + SmallImageText = Description, + }, + Details = $"Playing {titleName}", + State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), + Timestamps = Timestamps.Now, + Buttons = new Button[] + { + new Button() + { + Label = "Website", + Url = "https://ryujinx.org/" + } + } + }); } public static void SwitchToMainMenu() { - DiscordPresence.Details = "Main Menu"; - DiscordPresence.State = "Idling"; - DiscordPresence.Assets.LargeImageKey = "ryujinx"; - DiscordPresence.Assets.LargeImageText = LargeDescription; - DiscordPresence.Assets.SmallImageKey = null; - DiscordPresence.Assets.SmallImageText = null; - DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow); - - _discordClient?.SetPresence(DiscordPresence); + _discordClient?.SetPresence(_discordPresenceMain); } public static void Exit() { _discordClient?.Dispose(); } - - private static readonly string[] SupportedTitles = - { - "0100000000010000", // Super Mario Odyssey™ - "01000b900d8b0000", // Cadence of Hyrule – Crypt of the NecroDancer Featuring The Legend of Zelda - "01000d200ac0c000", // Bud Spencer & Terence Hill - Slaps And Beans - "01000d700be88000", // My Girlfriend is a Mermaid!? - "01000dc007e90000", // Sparkle Unleashed - "01000e2003fa0000", // MIGHTY GUNVOLT BURST - "0100225000fee000", // Blaster Master Zero - "010028d0045ce000", // Sparkle 2 - "01002b30028f6000", // Celeste - "01002fc00c6d0000", // Witch Thief - "010034e005c9c000", // Code of Princess EX - "010036b0034e4000", // Super Mario Party™ - "01003d200baa2000", // Pokémon Mystery Dungeon™: Rescue Team DX - "01004f8006a78000", // Super Meat Boy - "010051f00ac5e000", // SEGA AGES Sonic The Hedgehog - "010055d009f78000", // Fire Emblem™: Three Houses - "010056e00853a000", // A Hat in Time - "0100574009f9e000", // 嘘つき姫と盲目王子 - "01005d700e742000", // DOOM 64 - "0100628004bce000", // Nights of Azure 2: Bride of the New Moon - "0100633007d48000", // Hollow Knight - "010065500b218000", // メモリーズオフ -Innocent Fille- - "010068f00aa78000", // FINAL FANTASY XV POCKET EDITION HD - "01006bb00c6f0000", // The Legend of Zelda™: Link’s Awakening - "01006f8002326000", // Animal Crossing™: New Horizons - "01006a800016e000", // Super Smash Bros.™ Ultimate - "010072800cbe8000", // PC Building Simulator - "01007300020fa000", // ASTRAL CHAIN - "01007330027ee000", // Ultra Street Fighter® II: The Final Challengers - "0100749009844000", // 20XX - "01007a4008486000", // Enchanting Mahjong Match - "01007ef00011e000", // The Legend of Zelda™: Breath of the Wild - "010080b00ad66000", // Undertale - "010082400bcc6000", // Untitled Goose Game - "01008db008c2c000", // Pokémon™ Shield - "010094e00b52e000", // Capcom Beat 'Em Up Bundle - "01009aa000faa000", // Sonic Mania - "01009b90006dc000", // Super Mario Maker™ 2 - "01009cc00c97c000", // DEAD OR ALIVE Xtreme 3 Scarlet 基本無料版 - "0100ea80032ea000", // New Super Mario Bros.™ U Deluxe - "0100a4200a284000", // LUMINES REMASTERED - "0100a5c00d162000", // Cuphead - "0100abf008968000", // Pokémon™ Sword - "0100ae000aebc000", // Angels of Death - "0100b3f000be2000", // Pokkén Tournament™ DX - "0100bc2004ff4000", // Owlboy - "0100cf3007578000", // Atari Flashback Classics - "0100d5d00c6be000", // Our World Is Ended. - "0100d6b00cd88000", // YUMENIKKI -DREAM DIARY- - "0100d870045b6000", // Nintendo Entertainment System™ - Nintendo Switch Online - "0100e0c00adac000", // SENRAN KAGURA Reflexions - "0100e46006708000", // Terraria - "0100e7200b272000", // Lanota - "0100e9f00b882000", // null - "0100eab00605c000", // Poly Bridge - "0100efd00a4fa000", // Shantae and the Pirate's Curse - "0100f6a00a684000", // ひぐらしのなく頃に奉 - "0100f9f00c696000", // Crash™ Team Racing Nitro-Fueled - "051337133769a000", // RGB-Seizure - }; } }
\ No newline at end of file diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index b1fe41a7..9600f9dc 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -1,4 +1,5 @@ using ARMeilleure.Translation.PTC; +using FFmpeg.AutoGen; using Gtk; using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; @@ -9,6 +10,7 @@ using Ryujinx.Configuration; using Ryujinx.Modules; using Ryujinx.Ui; using Ryujinx.Ui.Widgets; +using SixLabors.ImageSharp.Formats.Jpeg; using System; using System.IO; using System.Reflection; @@ -76,6 +78,9 @@ namespace Ryujinx if (OperatingSystem.IsLinux()) { XInitThreads(); + + // Configure FFmpeg search path + ffmpeg.RootPath = "/lib"; } string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); @@ -98,6 +103,12 @@ namespace Ryujinx // Initialize Discord integration. DiscordIntegrationModule.Initialize(); + // Sets ImageSharp Jpeg Encoder Quality. + SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder() + { + Quality = 100 + }); + string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json"); diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 59edc919..1cd9595b 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -12,13 +12,13 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="DiscordRichPresence" Version="1.0.166" /> + <PackageReference Include="DiscordRichPresence" Version="1.0.175" /> <PackageReference Include="GtkSharp" Version="3.22.25.128" /> <PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> - <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.3.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> + <PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="4.4.0-build7" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="OpenTK.Graphics" Version="4.5.0" /> - <PackageReference Include="SPB" Version="0.0.2" /> + <PackageReference Include="SPB" Version="0.0.3-build15" /> <PackageReference Include="SharpZipLib" Version="1.3.0" /> </ItemGroup> @@ -26,6 +26,7 @@ <ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" /> <ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" /> <ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" /> + <ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" /> <ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" /> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" /> diff --git a/Ryujinx/Ui/Applet/GtkHostUiHandler.cs b/Ryujinx/Ui/Applet/GtkHostUiHandler.cs index 804a1a27..c227ebd3 100644 --- a/Ryujinx/Ui/Applet/GtkHostUiHandler.cs +++ b/Ryujinx/Ui/Applet/GtkHostUiHandler.cs @@ -131,8 +131,8 @@ namespace Ryujinx.Ui.Applet public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value) { - device.UserChannelPersistence.ExecuteProgram(kind, value); - ((MainWindow)_parent).GlRendererWidget?.Exit(); + device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); + ((MainWindow)_parent).RendererWidget?.Exit(); } public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 99c4698a..4c3f8ce4 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -1,10 +1,10 @@ using ARMeilleure.Translation; using ARMeilleure.Translation.PTC; using Gdk; +using Gtk; using OpenTK.Graphics.OpenGL; using Ryujinx.Common; using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; using Ryujinx.Configuration; using Ryujinx.Graphics.OpenGL; using Ryujinx.HLE.HOS.Services.Hid; @@ -13,9 +13,14 @@ using Ryujinx.Input.HLE; using Ryujinx.Ui.Widgets; using SPB.Graphics; using SPB.Graphics.OpenGL; +using SPB.Platform; +using SPB.Platform.GLX; +using SPB.Platform.WGL; +using SPB.Windowing; using System; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Threading; using Key = Ryujinx.Input.Key; @@ -24,606 +29,123 @@ namespace Ryujinx.Ui { using Switch = HLE.Switch; - public class GlRenderer : GLWidget + public class GlRenderer : RendererWidgetBase { - private const int SwitchPanelWidth = 1280; - private const int SwitchPanelHeight = 720; - private const int TargetFps = 60; - - public ManualResetEvent WaitEvent { get; set; } - public NpadManager NpadManager { get; } - - public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent; - - private bool _isActive; - private bool _isStopped; - private bool _isFocused; - - private double _mouseX; - private double _mouseY; - private bool _mousePressed; - - private bool _toggleFullscreen; - private bool _toggleDockedMode; - - private readonly long _ticksPerFrame; - - private long _ticks = 0; - - private readonly Stopwatch _chrono; - - private readonly Switch _device; - - private Renderer _renderer; - - private KeyboardHotkeyState _prevHotkeyState; - private GraphicsDebugLevel _glLogLevel; - private readonly ManualResetEvent _exitEvent; - - // Hide Cursor - const int CursorHideIdleTime = 8; // seconds - private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); - private long _lastCursorMoveTime; - private bool _hideCursorOnIdle; - private InputManager _inputManager; - private IKeyboard _keyboardInterface; + private bool _initializedOpenGL; - public GlRenderer(Switch device, InputManager inputManager, GraphicsDebugLevel glLogLevel) - : base (GetGraphicsMode(), - 3, 3, - glLogLevel == GraphicsDebugLevel.None - ? OpenGLContextFlags.Compat - : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug) - { - _inputManager = inputManager; - NpadManager = _inputManager.CreateNpadManager(); - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - - NpadManager.ReloadConfiguration(ConfigurationState.Instance.Hid.InputConfig.Value.ToList()); - - WaitEvent = new ManualResetEvent(false); - - _device = device; - - Initialized += GLRenderer_Initialized; - Destroyed += GLRenderer_Destroyed; - ShuttingDown += GLRenderer_ShuttingDown; - - Initialize(); - - _chrono = new Stopwatch(); - - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - - AddEvents((int)(EventMask.ButtonPressMask - | EventMask.ButtonReleaseMask - | EventMask.PointerMotionMask - | EventMask.KeyPressMask - | EventMask.KeyReleaseMask)); - - Shown += Renderer_Shown; + private OpenGLContextBase _openGLContext; + private SwappableNativeWindowBase _nativeWindow; + public GlRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) + { _glLogLevel = glLogLevel; - - _exitEvent = new ManualResetEvent(false); - - _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - - ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged; } - private void HideCursorStateChanged(object sender, ReactiveEventArgs<bool> state) + protected override bool OnDrawn(Cairo.Context cr) { - Gtk.Application.Invoke(delegate + if (!_initializedOpenGL) { - _hideCursorOnIdle = state.NewValue; - - if (_hideCursorOnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - else - { - Window.Cursor = null; - } - }); - } - - private static FramebufferFormat GetGraphicsMode() - { - return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default; - } - - private void GLRenderer_ShuttingDown(object sender, EventArgs args) - { - _device.DisposeGpu(); - NpadManager.Dispose(); - } - - private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args) - { - _isFocused = false; - } - - private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args) - { - _isFocused = true; - } - - private void GLRenderer_Destroyed(object sender, EventArgs e) - { - ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; - - NpadManager.Dispose(); - Dispose(); - } + IntializeOpenGL(); + } - protected void Renderer_Shown(object sender, EventArgs e) - { - _isFocused = this.ParentWindow.State.HasFlag(Gdk.WindowState.Focused); + return true; } - public void HandleScreenState(KeyboardStateSnapshot keyboard) + private void IntializeOpenGL() { - bool toggleFullscreen = keyboard.IsPressed(Key.F11) - || ((keyboard.IsPressed(Key.AltLeft) - || keyboard.IsPressed(Key.AltRight)) - && keyboard.IsPressed(Key.Enter)) - || keyboard.IsPressed(Key.Escape); - - bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); - - if (toggleFullscreen != _toggleFullscreen) - { - if (toggleFullscreen) - { - if (fullScreenToggled) - { - ParentWindow.Unfullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(true); - } - else - { - if (keyboard.IsPressed(Key.Escape)) - { - if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - Exit(); - } - } - else - { - ParentWindow.Fullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(false); - } - } - } - } + _nativeWindow = RetrieveNativeWindow(); - _toggleFullscreen = toggleFullscreen; + Window.EnsureNative(); - bool toggleDockedMode = keyboard.IsPressed(Key.F9); - - if (toggleDockedMode != _toggleDockedMode) - { - if (toggleDockedMode) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = - !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - } - - _toggleDockedMode = toggleDockedMode; - - if (_hideCursorOnIdle) - { - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; - } - } + _openGLContext = PlatformHelper.CreateOpenGLContext(GetGraphicsMode(), 3, 3, _glLogLevel == GraphicsDebugLevel.None ? OpenGLContextFlags.Compat : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug); + _openGLContext.Initialize(_nativeWindow); + _openGLContext.MakeCurrent(_nativeWindow); - private void GLRenderer_Initialized(object sender, EventArgs e) - { // Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread. - OpenGLContext.MakeCurrent(null); + _openGLContext.MakeCurrent(null); WaitEvent.Set(); - } - - protected override bool OnConfigureEvent(EventConfigure evnt) - { - bool result = base.OnConfigureEvent(evnt); - - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - _renderer.Window.SetSize(evnt.Width * monitor.ScaleFactor, evnt.Height * monitor.ScaleFactor); - - return result; - } - - public void Start() - { - _chrono.Restart(); - _isActive = true; - - Gtk.Window parent = this.Toplevel as Gtk.Window; - - parent.FocusInEvent += Parent_FocusInEvent; - parent.FocusOutEvent += Parent_FocusOutEvent; - - Gtk.Application.Invoke(delegate - { - parent.Present(); - - string titleNameSection = string.IsNullOrWhiteSpace(_device.Application.TitleName) ? string.Empty - : $" - {_device.Application.TitleName}"; - - string titleVersionSection = string.IsNullOrWhiteSpace(_device.Application.DisplayVersion) ? string.Empty - : $" v{_device.Application.DisplayVersion}"; - - string titleIdSection = string.IsNullOrWhiteSpace(_device.Application.TitleIdText) ? string.Empty - : $" ({_device.Application.TitleIdText.ToUpper()})"; - - string titleArchSection = _device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; - - parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; - }); - - Thread renderLoopThread = new Thread(Render) - { - Name = "GUI.RenderLoop" - }; - renderLoopThread.Start(); - - Thread nvStutterWorkaround = new Thread(NVStutterWorkaround) - { - Name = "GUI.NVStutterWorkaround" - }; - nvStutterWorkaround.Start(); - - MainLoop(); - - renderLoopThread.Join(); - nvStutterWorkaround.Join(); - - Exit(); + _initializedOpenGL = true; } - private void NVStutterWorkaround() + private SwappableNativeWindowBase RetrieveNativeWindow() { - while (_isActive) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones. - // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity. - // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ. - // This creates a new thread every second or so. - // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics. - // This is a little over budget on a frame time of 16ms, so creates a large stutter. - // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread. - - // TODO: This should be removed when the issue with the GateThread is resolved. + IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - ThreadPool.QueueUserWorkItem((state) => { }); - Thread.Sleep(300); + return new WGLWindow(new NativeHandle(windowHandle)); } - } - - protected override bool OnButtonPressEvent(EventButton evnt) - { - _mouseX = evnt.X; - _mouseY = evnt.Y; - - if (evnt.Button == 1) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - _mousePressed = true; - } - - return false; - } + IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); + IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - protected override bool OnButtonReleaseEvent(EventButton evnt) - { - if (evnt.Button == 1) - { - _mousePressed = false; - } - - return false; - } - - protected override bool OnMotionNotifyEvent(EventMotion evnt) - { - if (evnt.Device.InputSource == InputSource.Mouse) - { - _mouseX = evnt.X; - _mouseY = evnt.Y; - } - - if (_hideCursorOnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - - return false; - } - - protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumHeight = SwitchPanelHeight; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumHeight = 480; - } - - naturalHeight = minimumHeight; - } - - protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumWidth = SwitchPanelWidth; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumWidth = 854; + return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); } - naturalWidth = minimumWidth; + throw new NotImplementedException(); } - public void Exit() - { - NpadManager?.Dispose(); - - if (_isStopped) - { - return; - } + [DllImport("libgdk-3-0.dll")] + private static extern IntPtr gdk_win32_window_get_handle(IntPtr d); - _isStopped = true; - _isActive = false; + [DllImport("libgdk-3.so.0")] + private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - _exitEvent.WaitOne(); - _exitEvent.Dispose(); - } + [DllImport("libgdk-3.so.0")] + private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - public void Initialize() + private static FramebufferFormat GetGraphicsMode() { - if (!(_device.Gpu.Renderer is Renderer)) - { - throw new NotSupportedException($"GPU renderer must be an OpenGL renderer when using {typeof(Renderer).Name}!"); - } - - _renderer = (Renderer)_device.Gpu.Renderer; + return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default; } - public void Render() + public override void InitializeRenderer() { // First take exclusivity on the OpenGL context. - _renderer.InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(OpenGLContext)); - - Gtk.Window parent = Toplevel as Gtk.Window; - parent.Present(); + ((Renderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); - OpenGLContext.MakeCurrent(NativeWindow); + _openGLContext.MakeCurrent(_nativeWindow); - _device.Gpu.Renderer.Initialize(_glLogLevel); - - // Make sure the first frame is not transparent. GL.ClearColor(0, 0, 0, 1.0f); GL.Clear(ClearBufferMask.ColorBufferBit); SwapBuffers(); - - _device.Gpu.InitializeShaderCache(); - Translator.IsReadyForTranslation.Set(); - - while (_isActive) - { - if (_isStopped) - { - return; - } - - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (_device.WaitFifo()) - { - _device.Statistics.RecordFifoStart(); - _device.ProcessFrame(); - _device.Statistics.RecordFifoEnd(); - } - - while (_device.ConsumeFrameAvailable()) - { - _device.PresentFrame(SwapBuffers); - } - - if (_ticks >= _ticksPerFrame) - { - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld"; - float scale = Graphics.Gpu.GraphicsConfig.ResScale; - if (scale != 1) - { - dockedMode += $" ({scale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - _device.EnableDeviceVsync, - dockedMode, - ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - $"Game: {_device.Statistics.GetGameFrameRate():00.00} FPS", - $"FIFO: {_device.Statistics.GetFifoPercent():0.00} %", - $"GPU: {_renderer.GpuVendor}")); - - _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); - } - } } - public void SwapBuffers() + public override void SwapBuffers() { - NativeWindow.SwapBuffers(); + _nativeWindow.SwapBuffers(); } - public void MainLoop() + public override string GetGpuVendorName() { - while (_isActive) - { - UpdateFrame(); - - // Polling becomes expensive if it's not slept - Thread.Sleep(1); - } - - _exitEvent.Set(); + return ((Renderer)Renderer).GpuVendor; } - private bool UpdateFrame() + protected override void Dispose(bool disposing) { - if (!_isActive) - { - return true; - } - - if (_isStopped) - { - return false; - } - - if (_isFocused) - { - Gtk.Application.Invoke(delegate - { - KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot(); - - HandleScreenState(keyboard); - - if (keyboard.IsPressed(Key.Delete)) - { - if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) - { - Ptc.Continue(); - } - } - }); - } - - NpadManager.Update(_device.Hid, _device.TamperMachine); - - if(_isFocused) - { - KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync)) - { - _device.EnableDeviceVsync = !_device.EnableDeviceVsync; - } - - _prevHotkeyState = currentHotkeyState; - } - - //Touchscreen - bool hasTouch = false; - - // Get screen touch position from left mouse click - // OpenTK always captures mouse events, even if out of focus, so check if window is focused. - if (_isFocused && _mousePressed) - { - float aspectWidth = SwitchPanelHeight * ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat(); - - int screenWidth = AllocatedWidth; - int screenHeight = AllocatedHeight; - - if (AllocatedWidth > AllocatedHeight * aspectWidth / SwitchPanelHeight) - { - screenWidth = (int)(AllocatedHeight * aspectWidth) / SwitchPanelHeight; - } - else - { - screenHeight = (AllocatedWidth * SwitchPanelHeight) / (int)aspectWidth; - } - - int startX = (AllocatedWidth - screenWidth) >> 1; - int startY = (AllocatedHeight - screenHeight) >> 1; - - int endX = startX + screenWidth; - int endY = startY + screenHeight; - - - if (_mouseX >= startX && - _mouseY >= startY && - _mouseX < endX && - _mouseY < endY) - { - int screenMouseX = (int)_mouseX - startX; - int screenMouseY = (int)_mouseY - startY; - - int mX = (screenMouseX * (int)aspectWidth) / screenWidth; - int mY = (screenMouseY * SwitchPanelHeight) / screenHeight; - - TouchPoint currentPoint = new TouchPoint - { - X = (uint)mX, - Y = (uint)mY, - - // Placeholder values till more data is acquired - DiameterX = 10, - DiameterY = 10, - Angle = 90 - }; - - hasTouch = true; - - _device.Hid.Touchscreen.Update(currentPoint); - } - } - - if (!hasTouch) + // Try to bind the OpenGL context before calling the shutdown event + try { - _device.Hid.Touchscreen.Update(); + _openGLContext?.MakeCurrent(_nativeWindow); } + catch (Exception) { } - _device.Hid.DebugPad.Update(); - - return true; - } - - [Flags] - private enum KeyboardHotkeyState - { - None, - ToggleVSync - } - - private KeyboardHotkeyState GetHotkeyState() - { - KeyboardHotkeyState state = KeyboardHotkeyState.None; + Device.DisposeGpu(); + NpadManager.Dispose(); - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + // Unbind context and destroy everything + try { - state |= KeyboardHotkeyState.ToggleVSync; + _openGLContext?.MakeCurrent(null); } + catch (Exception) { } - return state; + _openGLContext.Dispose(); } } } diff --git a/Ryujinx/Ui/GLWidget.cs b/Ryujinx/Ui/GLWidget.cs deleted file mode 100644 index a465aeef..00000000 --- a/Ryujinx/Ui/GLWidget.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Gtk; -using SPB.Graphics; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Platform.GLX; -using SPB.Platform.WGL; -using SPB.Windowing; -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; - -namespace Ryujinx.Ui -{ - [ToolboxItem(true)] - public class GLWidget : DrawingArea - { - private bool _initialized; - - public event EventHandler Initialized; - public event EventHandler ShuttingDown; - - public OpenGLContextBase OpenGLContext { get; private set; } - public NativeWindowBase NativeWindow { get; private set; } - - public FramebufferFormat FramebufferFormat { get; } - public int GLVersionMajor { get; } - public int GLVersionMinor { get; } - public OpenGLContextFlags ContextFlags { get; } - - public bool DirectRendering { get; } - public OpenGLContextBase SharedContext { get; } - - public GLWidget(FramebufferFormat framebufferFormat, int major, int minor, OpenGLContextFlags flags = OpenGLContextFlags.Default, bool directRendering = true, OpenGLContextBase sharedContext = null) - { - FramebufferFormat = framebufferFormat; - GLVersionMajor = major; - GLVersionMinor = minor; - ContextFlags = flags; - DirectRendering = directRendering; - SharedContext = sharedContext; - } - - protected override bool OnDrawn(Cairo.Context cr) - { - if (!_initialized) - { - Intialize(); - } - - return true; - } - - private NativeWindowBase RetrieveNativeWindow() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - - return new WGLWindow(new NativeHandle(windowHandle)); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - - return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); - } - - throw new NotImplementedException(); - } - - [DllImport("libgdk-3-0.dll")] - private static extern IntPtr gdk_win32_window_get_handle(IntPtr d); - - [DllImport("libgdk-3.so.0")] - private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [DllImport("libgdk-3.so.0")] - private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - - private void Intialize() - { - NativeWindow = RetrieveNativeWindow(); - - Window.EnsureNative(); - - OpenGLContext = PlatformHelper.CreateOpenGLContext(FramebufferFormat, GLVersionMajor, GLVersionMinor, ContextFlags, DirectRendering, SharedContext); - - OpenGLContext.Initialize(NativeWindow); - OpenGLContext.MakeCurrent(NativeWindow); - - _initialized = true; - - Initialized?.Invoke(this, EventArgs.Empty); - } - - protected override void Dispose(bool disposing) - { - // Try to bind the OpenGL context before calling the shutdown event - try - { - OpenGLContext?.MakeCurrent(NativeWindow); - } - catch (Exception) { } - - ShuttingDown?.Invoke(this, EventArgs.Empty); - - // Unbind context and destroy everything - try - { - OpenGLContext?.MakeCurrent(null); - } - catch (Exception) { } - - OpenGLContext.Dispose(); - } - } -} diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 433d23dc..1eef7554 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -2,11 +2,14 @@ using ARMeilleure.Translation; using ARMeilleure.Translation.PTC; using Gtk; using LibHac.Common; +using LibHac.FsSystem; using LibHac.Ns; using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.OpenAL; +using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Audio.Integration; +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.System; @@ -17,6 +20,7 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.SystemState; using Ryujinx.Input.GTK3; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; @@ -67,9 +71,11 @@ namespace Ryujinx.Ui private string _lastScannedAmiiboId = ""; private bool _lastScannedAmiiboShowAll = false; - public GlRenderer GlRendererWidget; + public RendererWidgetBase RendererWidget; public InputManager InputManager; + private static bool UseVulkan = false; + #pragma warning disable CS0169, CS0649, IDE0044 [GUI] public MenuItem ExitMenuItem; @@ -78,6 +84,7 @@ namespace Ryujinx.Ui [GUI] Box _footerBox; [GUI] Box _statusBar; [GUI] MenuItem _optionMenu; + [GUI] MenuItem _manageUserProfiles; [GUI] MenuItem _actionMenu; [GUI] MenuItem _stopEmulation; [GUI] MenuItem _simulateWakeUpMessage; @@ -140,7 +147,7 @@ namespace Ryujinx.Ui // Instanciate HLE objects. _virtualFileSystem = VirtualFileSystem.CreateInstance(); _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(); + _accountManager = new AccountManager(_virtualFileSystem); _userChannelPersistence = new UserChannelPersistence(); // Instanciate GUI objects. @@ -155,11 +162,16 @@ namespace Ryujinx.Ui _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; _actionMenu.StateChanged += ActionMenu_StateChanged; + _optionMenu.StateChanged += OptionMenu_StateChanged; _gameTable.ButtonReleaseEvent += Row_Clicked; _fullScreen.Activated += FullScreen_Toggled; - GlRenderer.StatusUpdatedEvent += Update_StatusBar; + RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar; + + ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; + ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; + ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; if (ConfigurationState.Instance.Ui.StartFullscreen) { @@ -231,6 +243,30 @@ namespace Ryujinx.Ui InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver()); } + private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs<bool> args) + { + if (_emulationContext != null) + { + _emulationContext.Configuration.IgnoreMissingServices = args.NewValue; + } + } + + private void UpdateAspectRatioState(object sender, ReactiveEventArgs<AspectRatio> args) + { + if (_emulationContext != null) + { + _emulationContext.Configuration.AspectRatio = args.NewValue; + } + } + + private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e) + { + if (_emulationContext != null) + { + _emulationContext.System.ChangeDockedModeState(e.NewValue); + } + } + private void WindowStateEvent_Changed(object o, WindowStateEventArgs args) { _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen"; @@ -310,10 +346,31 @@ namespace Ryujinx.Ui { _virtualFileSystem.Reload(); - IRenderer renderer = new Renderer(); + IRenderer renderer; + + if (UseVulkan) + { + throw new NotImplementedException(); + } + else + { + renderer = new Renderer(); + } + IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver(); - if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo) + if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2) + { + if (SDL2HardwareDeviceDriver.IsSupported) + { + deviceDriver = new SDL2HardwareDeviceDriver(); + } + else + { + Logger.Warning?.Print(LogClass.Audio, "SDL2 audio is not supported, falling back to dummy audio out."); + } + } + else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo) { if (SoundIoHardwareDeviceDriver.IsSupported) { @@ -354,19 +411,29 @@ namespace Ryujinx.Ui ? HLE.MemoryConfiguration.MemoryConfiguration6GB : HLE.MemoryConfiguration.MemoryConfiguration4GB; - _emulationContext = new HLE.Switch( - _virtualFileSystem, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - deviceDriver, - memoryConfiguration) - { - UiHandler = _uiHandler - }; - - _emulationContext.Initialize(); + IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; + + HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(_virtualFileSystem, + _contentManager, + _accountManager, + _userChannelPersistence, + renderer, + deviceDriver, + memoryConfiguration, + _uiHandler, + (SystemLanguage)ConfigurationState.Instance.System.Language.Value, + (RegionCode)ConfigurationState.Instance.System.Region.Value, + ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.System.EnableDockedMode, + ConfigurationState.Instance.System.EnablePtc, + fsIntegrityCheckLevel, + ConfigurationState.Instance.System.FsGlobalAccessLogMode, + ConfigurationState.Instance.System.SystemTimeOffset, + ConfigurationState.Instance.System.TimeZone, + ConfigurationState.Instance.System.IgnoreMissingServices, + ConfigurationState.Instance.Graphics.AspectRatio); + + _emulationContext = new HLE.Switch(configuration); } private void SetupProgressUiHandlers() @@ -480,6 +547,10 @@ namespace Ryujinx.Ui Logger.RestartTime(); + RendererWidget = CreateRendererWidget(); + + SwitchToRenderWidget(); + InitializeSwitchInstance(); UpdateGraphicsConfig(); @@ -505,6 +576,8 @@ namespace Ryujinx.Ui UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); + SwitchToGameTable(); + RendererWidget.Dispose(); return; } @@ -515,6 +588,8 @@ namespace Ryujinx.Ui UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); + SwitchToGameTable(); + RendererWidget.Dispose(); return; } @@ -536,6 +611,8 @@ namespace Ryujinx.Ui UserErrorDialog.CreateUserErrorDialog(userError); _emulationContext.Dispose(); + SwitchToGameTable(); + RendererWidget.Dispose(); return; } @@ -598,6 +675,7 @@ namespace Ryujinx.Ui Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); _emulationContext.Dispose(); + RendererWidget.Dispose(); return; } @@ -638,91 +716,108 @@ namespace Ryujinx.Ui } } - private void CreateGameWindow() + private RendererWidgetBase CreateRendererWidget() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (UseVulkan) { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); + return new VKRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); } + else + { + return new GlRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); + } + } - DisplaySleep.Prevent(); + private void SwitchToRenderWidget() + { + _viewBox.Remove(_gameTableWindow); + RendererWidget.Expand = true; + _viewBox.Child = RendererWidget; - GlRendererWidget = new GlRenderer(_emulationContext, InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); + RendererWidget.ShowAll(); + EditFooterForGameRenderer(); - Application.Invoke(delegate + if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) + { + ToggleExtraWidgets(false); + } + else if (ConfigurationState.Instance.Ui.StartFullscreen.Value) { - _viewBox.Remove(_gameTableWindow); - GlRendererWidget.Expand = true; - _viewBox.Child = GlRendererWidget; + FullScreen_Toggled(null, null); + } + } - GlRendererWidget.ShowAll(); - EditFooterForGameRenderer(); + private void SwitchToGameTable() + { + if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) + { + ToggleExtraWidgets(true); + } - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(false); - } - else if (ConfigurationState.Instance.Ui.StartFullscreen.Value) - { - FullScreen_Toggled(null, null); - } - }); + RendererWidget.Exit(); + + if (RendererWidget.Window != Window && RendererWidget.Window != null) + { + RendererWidget.Window.Dispose(); + } - GlRendererWidget.WaitEvent.WaitOne(); + RendererWidget.Dispose(); - GlRendererWidget.Start(); + _windowsMultimediaTimerResolution?.Dispose(); + _windowsMultimediaTimerResolution = null; + DisplaySleep.Restore(); - Ptc.Close(); - PtcProfiler.Stop(); + _viewBox.Remove(RendererWidget); + _viewBox.Add(_gameTableWindow); - _emulationContext.Dispose(); - _deviceExitStatus.Set(); + _gameTableWindow.Expand = true; - // NOTE: Everything that is here will not be executed when you close the UI. - Application.Invoke(delegate - { - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(true); - } + Window.Title = $"Ryujinx {Program.Version}"; - GlRendererWidget.Exit(); + _emulationContext = null; + _gameLoaded = false; + RendererWidget = null; - if (GlRendererWidget.Window != Window && GlRendererWidget.Window != null) - { - GlRendererWidget.Window.Dispose(); - } + DiscordIntegrationModule.SwitchToMainMenu(); + + RecreateFooterForMenu(); - GlRendererWidget.Dispose(); + UpdateColumns(); + UpdateGameTable(); - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - DisplaySleep.Restore(); + Task.Run(RefreshFirmwareLabel); + Task.Run(HandleRelaunch); - _viewBox.Remove(GlRendererWidget); - _viewBox.Add(_gameTableWindow); + _actionMenu.Sensitive = false; + _firmwareInstallFile.Sensitive = true; + _firmwareInstallDirectory.Sensitive = true; + } - _gameTableWindow.Expand = true; + private void CreateGameWindow() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); + } - Window.Title = $"Ryujinx {Program.Version}"; + DisplaySleep.Prevent(); - _emulationContext = null; - _gameLoaded = false; - GlRendererWidget = null; + RendererWidget.Initialize(_emulationContext); - DiscordIntegrationModule.SwitchToMainMenu(); + RendererWidget.WaitEvent.WaitOne(); - RecreateFooterForMenu(); + RendererWidget.Start(); - UpdateColumns(); - UpdateGameTable(); + Ptc.Close(); + PtcProfiler.Stop(); - Task.Run(RefreshFirmwareLabel); - Task.Run(HandleRelaunch); + _emulationContext.Dispose(); + _deviceExitStatus.Set(); - _actionMenu.Sensitive = false; - _firmwareInstallFile.Sensitive = true; - _firmwareInstallDirectory.Sensitive = true; + // NOTE: Everything that is here will not be executed when you close the UI. + Application.Invoke(delegate + { + SwitchToGameTable(); }); } @@ -740,7 +835,7 @@ namespace Ryujinx.Ui public void ToggleExtraWidgets(bool show) { - if (GlRendererWidget != null) + if (RendererWidget != null) { if (show) { @@ -799,14 +894,14 @@ namespace Ryujinx.Ui { UpdateGameMetadata(_emulationContext.Application.TitleIdText); - if (GlRendererWidget != null) + if (RendererWidget != null) { // We tell the widget that we are exiting. - GlRendererWidget.Exit(); + RendererWidget.Exit(); // Wait for the other thread to dispose the HLE context before exiting. _deviceExitStatus.WaitOne(); - GlRendererWidget.Dispose(); + RendererWidget.Dispose(); } } @@ -1025,7 +1120,7 @@ namespace Ryujinx.Ui private void StopEmulation_Pressed(object sender, EventArgs args) { - GlRendererWidget?.Exit(); + RendererWidget?.Exit(); } private void Installer_File_Pressed(object o, EventArgs args) @@ -1192,6 +1287,11 @@ namespace Ryujinx.Ui SaveConfig(); } + private void OptionMenu_StateChanged(object o, StateChangedArgs args) + { + _manageUserProfiles.Sensitive = _emulationContext == null; + } + private void Settings_Pressed(object sender, EventArgs args) { SettingsWindow settingsWindow = new SettingsWindow(this, _virtualFileSystem, _contentManager); @@ -1200,6 +1300,14 @@ namespace Ryujinx.Ui settingsWindow.Show(); } + private void ManageUserProfiles_Pressed(object sender, EventArgs args) + { + UserProfilesManagerWindow userProfilesManagerWindow = new UserProfilesManagerWindow(_accountManager, _contentManager, _virtualFileSystem); + + userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor)); + userProfilesManagerWindow.Show(); + } + private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args) { if (_emulationContext != null) diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index beeed265..129b768e 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -248,6 +248,16 @@ <signal name="activate" handler="Settings_Pressed" swapped="no"/> </object> </child> + <child> + <object class="GtkMenuItem" id="_manageUserProfiles"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">Open User Profiles Manager window</property> + <property name="label" translatable="yes">Manage User Profiles</property> + <property name="use_underline">True</property> + <signal name="activate" handler="ManageUserProfiles_Pressed" swapped="no"/> + </object> + </child> </object> </child> </object> diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs new file mode 100644 index 00000000..00882ba0 --- /dev/null +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -0,0 +1,591 @@ +using ARMeilleure.Translation; +using ARMeilleure.Translation.PTC; +using Gdk; +using Gtk; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Configuration; +using Ryujinx.Graphics.GAL; +using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.Input; +using Ryujinx.Input.HLE; +using Ryujinx.Ui.Widgets; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace Ryujinx.Ui +{ + using Key = Input.Key; + using Switch = HLE.Switch; + + public abstract class RendererWidgetBase : DrawingArea + { + private const int SwitchPanelWidth = 1280; + private const int SwitchPanelHeight = 720; + private const int TargetFps = 60; + + public ManualResetEvent WaitEvent { get; set; } + public NpadManager NpadManager { get; } + public Switch Device { get; private set; } + public IRenderer Renderer { get; private set; } + + public static event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent; + + private bool _isActive; + private bool _isStopped; + private bool _isFocused; + + private double _mouseX; + private double _mouseY; + private bool _mousePressed; + + private bool _toggleFullscreen; + private bool _toggleDockedMode; + + private readonly long _ticksPerFrame; + + private long _ticks = 0; + + private readonly Stopwatch _chrono; + + private KeyboardHotkeyState _prevHotkeyState; + + private readonly ManualResetEvent _exitEvent; + + // Hide Cursor + const int CursorHideIdleTime = 8; // seconds + private static readonly Cursor _invisibleCursor = new Cursor(Display.Default, CursorType.BlankCursor); + private long _lastCursorMoveTime; + private bool _hideCursorOnIdle; + private InputManager _inputManager; + private IKeyboard _keyboardInterface; + private GraphicsDebugLevel _glLogLevel; + private string _gpuVendorName; + + private int _windowHeight; + private int _windowWidth; + + public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel) + { + _inputManager = inputManager; + NpadManager = _inputManager.CreateNpadManager(); + _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); + + WaitEvent = new ManualResetEvent(false); + + _glLogLevel = glLogLevel; + + Destroyed += Renderer_Destroyed; + + _chrono = new Stopwatch(); + + _ticksPerFrame = Stopwatch.Frequency / TargetFps; + + AddEvents((int)(EventMask.ButtonPressMask + | EventMask.ButtonReleaseMask + | EventMask.PointerMotionMask + | EventMask.KeyPressMask + | EventMask.KeyReleaseMask)); + + Shown += Renderer_Shown; + + _exitEvent = new ManualResetEvent(false); + + _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + + ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged; + } + + public abstract void InitializeRenderer(); + + public abstract void SwapBuffers(); + + public abstract string GetGpuVendorName(); + + private void HideCursorStateChanged(object sender, ReactiveEventArgs<bool> state) + { + Gtk.Application.Invoke(delegate + { + _hideCursorOnIdle = state.NewValue; + + if (_hideCursorOnIdle) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + } + else + { + Window.Cursor = null; + } + }); + } + + private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args) + { + _isFocused = false; + } + + private void Parent_FocusInEvent(object o, Gtk.FocusInEventArgs args) + { + _isFocused = true; + } + + private void Renderer_Destroyed(object sender, EventArgs e) + { + ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged; + + NpadManager.Dispose(); + Dispose(); + } + + private void Renderer_Shown(object sender, EventArgs e) + { + _isFocused = ParentWindow.State.HasFlag(Gdk.WindowState.Focused); + } + + protected override bool OnButtonPressEvent(EventButton evnt) + { + _mouseX = evnt.X; + _mouseY = evnt.Y; + + if (evnt.Button == 1) + { + _mousePressed = true; + } + + return false; + } + + protected override bool OnButtonReleaseEvent(EventButton evnt) + { + if (evnt.Button == 1) + { + _mousePressed = false; + } + + return false; + } + + protected override bool OnMotionNotifyEvent(EventMotion evnt) + { + if (evnt.Device.InputSource == InputSource.Mouse) + { + _mouseX = evnt.X; + _mouseY = evnt.Y; + } + + if (_hideCursorOnIdle) + { + _lastCursorMoveTime = Stopwatch.GetTimestamp(); + } + + return false; + } + + protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight) + { + Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); + + // If the monitor is at least 1080p, use the Switch panel size as minimal size. + if (monitor.Geometry.Height >= 1080) + { + minimumHeight = SwitchPanelHeight; + } + // Otherwise, we default minimal size to 480p 16:9. + else + { + minimumHeight = 480; + } + + naturalHeight = minimumHeight; + } + + protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth) + { + Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); + + // If the monitor is at least 1080p, use the Switch panel size as minimal size. + if (monitor.Geometry.Height >= 1080) + { + minimumWidth = SwitchPanelWidth; + } + // Otherwise, we default minimal size to 480p 16:9. + else + { + minimumWidth = 854; + } + + naturalWidth = minimumWidth; + } + + protected override bool OnConfigureEvent(EventConfigure evnt) + { + bool result = base.OnConfigureEvent(evnt); + + Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); + + _windowWidth = evnt.Width * monitor.ScaleFactor; + _windowHeight = evnt.Height * monitor.ScaleFactor; + + Renderer?.Window.SetSize(_windowWidth, _windowHeight); + + return result; + } + + private void HandleScreenState(KeyboardStateSnapshot keyboard) + { + bool toggleFullscreen = keyboard.IsPressed(Key.F11) + || ((keyboard.IsPressed(Key.AltLeft) + || keyboard.IsPressed(Key.AltRight)) + && keyboard.IsPressed(Key.Enter)) + || keyboard.IsPressed(Key.Escape); + + bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); + + if (toggleFullscreen != _toggleFullscreen) + { + if (toggleFullscreen) + { + if (fullScreenToggled) + { + ParentWindow.Unfullscreen(); + (Toplevel as MainWindow)?.ToggleExtraWidgets(true); + } + else + { + if (keyboard.IsPressed(Key.Escape)) + { + if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) + { + Exit(); + } + } + else + { + ParentWindow.Fullscreen(); + (Toplevel as MainWindow)?.ToggleExtraWidgets(false); + } + } + } + } + + _toggleFullscreen = toggleFullscreen; + + bool toggleDockedMode = keyboard.IsPressed(Key.F9); + + if (toggleDockedMode != _toggleDockedMode) + { + if (toggleDockedMode) + { + ConfigurationState.Instance.System.EnableDockedMode.Value = + !ConfigurationState.Instance.System.EnableDockedMode.Value; + } + } + + _toggleDockedMode = toggleDockedMode; + + if (_hideCursorOnIdle) + { + long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; + Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; + } + } + + public void Initialize(Switch device) + { + Device = device; + Renderer = Device.Gpu.Renderer; + Renderer?.Window.SetSize(_windowWidth, _windowHeight); + + NpadManager.Initialize(device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard); + } + + public void Render() + { + Gtk.Window parent = Toplevel as Gtk.Window; + parent.Present(); + + InitializeRenderer(); + + Device.Gpu.Renderer.Initialize(_glLogLevel); + + _gpuVendorName = GetGpuVendorName(); + + Device.Gpu.InitializeShaderCache(); + Translator.IsReadyForTranslation.Set(); + + while (_isActive) + { + if (_isStopped) + { + return; + } + + _ticks += _chrono.ElapsedTicks; + + _chrono.Restart(); + + if (Device.WaitFifo()) + { + Device.Statistics.RecordFifoStart(); + Device.ProcessFrame(); + Device.Statistics.RecordFifoEnd(); + } + + while (Device.ConsumeFrameAvailable()) + { + Device.PresentFrame(SwapBuffers); + } + + if (_ticks >= _ticksPerFrame) + { + string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld"; + float scale = Graphics.Gpu.GraphicsConfig.ResScale; + if (scale != 1) + { + dockedMode += $" ({scale}x)"; + } + + StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( + Device.EnableDeviceVsync, + dockedMode, + ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), + $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS", + $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", + $"GPU: {_gpuVendorName}")); + + _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); + } + } + } + + public void Start() + { + _chrono.Restart(); + + _isActive = true; + + Gtk.Window parent = this.Toplevel as Gtk.Window; + + parent.FocusInEvent += Parent_FocusInEvent; + parent.FocusOutEvent += Parent_FocusOutEvent; + + Application.Invoke(delegate + { + parent.Present(); + + string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty + : $" - {Device.Application.TitleName}"; + + string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty + : $" v{Device.Application.DisplayVersion}"; + + string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty + : $" ({Device.Application.TitleIdText.ToUpper()})"; + + string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)"; + + parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; + }); + + Thread renderLoopThread = new Thread(Render) + { + Name = "GUI.RenderLoop" + }; + renderLoopThread.Start(); + + Thread nvStutterWorkaround = new Thread(NVStutterWorkaround) + { + Name = "GUI.NVStutterWorkaround" + }; + nvStutterWorkaround.Start(); + + MainLoop(); + + renderLoopThread.Join(); + nvStutterWorkaround.Join(); + + Exit(); + } + + public void Exit() + { + NpadManager?.Dispose(); + + if (_isStopped) + { + return; + } + + _isStopped = true; + _isActive = false; + + _exitEvent.WaitOne(); + _exitEvent.Dispose(); + } + + private void NVStutterWorkaround() + { + while (_isActive) + { + // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones. + // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity. + // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ. + // This creates a new thread every second or so. + // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics. + // This is a little over budget on a frame time of 16ms, so creates a large stutter. + // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread. + + // TODO: This should be removed when the issue with the GateThread is resolved. + + ThreadPool.QueueUserWorkItem((state) => { }); + Thread.Sleep(300); + } + } + + public void MainLoop() + { + while (_isActive) + { + UpdateFrame(); + + // Polling becomes expensive if it's not slept + Thread.Sleep(1); + } + + _exitEvent.Set(); + } + + private bool UpdateFrame() + { + if (!_isActive) + { + return true; + } + + if (_isStopped) + { + return false; + } + + if (_isFocused) + { + Gtk.Application.Invoke(delegate + { + KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot(); + + HandleScreenState(keyboard); + + if (keyboard.IsPressed(Key.Delete)) + { + if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) + { + Ptc.Continue(); + } + } + }); + } + + NpadManager.Update(); + + if (_isFocused) + { + KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); + + if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) && + !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync)) + { + Device.EnableDeviceVsync = !Device.EnableDeviceVsync; + } + + _prevHotkeyState = currentHotkeyState; + } + + // Touchscreen + bool hasTouch = false; + + // Get screen touch position from left mouse click + // OpenTK always captures mouse events, even if out of focus, so check if window is focused. + if (_isFocused && _mousePressed) + { + float aspectWidth = SwitchPanelHeight * ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat(); + + int screenWidth = AllocatedWidth; + int screenHeight = AllocatedHeight; + + if (AllocatedWidth > AllocatedHeight * aspectWidth / SwitchPanelHeight) + { + screenWidth = (int)(AllocatedHeight * aspectWidth) / SwitchPanelHeight; + } + else + { + screenHeight = (AllocatedWidth * SwitchPanelHeight) / (int)aspectWidth; + } + + int startX = (AllocatedWidth - screenWidth) >> 1; + int startY = (AllocatedHeight - screenHeight) >> 1; + + int endX = startX + screenWidth; + int endY = startY + screenHeight; + + if (_mouseX >= startX && + _mouseY >= startY && + _mouseX < endX && + _mouseY < endY) + { + int screenMouseX = (int)_mouseX - startX; + int screenMouseY = (int)_mouseY - startY; + + int mX = (screenMouseX * (int)aspectWidth) / screenWidth; + int mY = (screenMouseY * SwitchPanelHeight) / screenHeight; + + TouchPoint currentPoint = new TouchPoint + { + X = (uint)mX, + Y = (uint)mY, + + // Placeholder values till more data is acquired + DiameterX = 10, + DiameterY = 10, + Angle = 90 + }; + + hasTouch = true; + + Device.Hid.Touchscreen.Update(currentPoint); + } + } + + if (!hasTouch) + { + Device.Hid.Touchscreen.Update(); + } + + Device.Hid.DebugPad.Update(); + + return true; + } + + + [Flags] + private enum KeyboardHotkeyState + { + None, + ToggleVSync + } + + private KeyboardHotkeyState GetHotkeyState() + { + KeyboardHotkeyState state = KeyboardHotkeyState.None; + + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + { + state |= KeyboardHotkeyState.ToggleVSync; + } + + return state; + } + } +} diff --git a/Ryujinx/Ui/SPBOpenGLContext.cs b/Ryujinx/Ui/SPBOpenGLContext.cs index c2b5d638..e1a315c9 100644 --- a/Ryujinx/Ui/SPBOpenGLContext.cs +++ b/Ryujinx/Ui/SPBOpenGLContext.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Ui public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext) { OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext); - NativeWindowBase window = PlatformHelper.CreateWindow(FramebufferFormat.Default, 0, 0, 100, 100); + NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); context.Initialize(window); context.MakeCurrent(window); diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs new file mode 100644 index 00000000..7b01f709 --- /dev/null +++ b/Ryujinx/Ui/VKRenderer.cs @@ -0,0 +1,80 @@ +using Gdk; +using Gtk; +using Ryujinx.Common.Configuration; +using Ryujinx.Input.HLE; +using SPB.Graphics.Vulkan; +using SPB.Platform.Win32; +using SPB.Platform.X11; +using SPB.Windowing; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Ui +{ + public class VKRenderer : RendererWidgetBase + { + public NativeWindowBase NativeWindow { get; private set; } + + public VKRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } + + private NativeWindowBase RetrieveNativeWindow() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); + + return new SimpleWin32Window(new NativeHandle(windowHandle)); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); + IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); + + return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); + } + + throw new NotImplementedException(); + } + + [DllImport("libgdk-3-0.dll")] + private static extern IntPtr gdk_win32_window_get_handle(IntPtr d); + + [DllImport("libgdk-3.so.0")] + private static extern IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); + + [DllImport("libgdk-3.so.0")] + private static extern IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); + + protected override bool OnConfigureEvent(EventConfigure evnt) + { + if (NativeWindow == null) + { + NativeWindow = RetrieveNativeWindow(); + + WaitEvent.Set(); + } + + return base.OnConfigureEvent(evnt); + } + + public unsafe IntPtr CreateWindowSurface(IntPtr instance) + { + return VulkanHelper.CreateWindowSurface(instance, NativeWindow); + } + + public override void InitializeRenderer() { } + + public override void SwapBuffers() { } + + public override string GetGpuVendorName() + { + return "Vulkan (Unknown)"; + } + + protected override void Dispose(bool disposing) + { + Device.DisposeGpu(); + NpadManager.Dispose(); + } + } +} diff --git a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs index 4fdc666a..eb3150ce 100644 --- a/Ryujinx/Ui/Widgets/GameTableContextMenu.cs +++ b/Ryujinx/Ui/Widgets/GameTableContextMenu.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Ui.Widgets Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); } - Uid user = new Uid(1, 0); // TODO: Remove Hardcoded value. + Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); result = EnsureApplicationSaveData(_virtualFileSystem.FsClient, out _, new LibHac.Ncm.ApplicationId(titleId), ref control, ref user); diff --git a/Ryujinx/Ui/Widgets/GtkDialog.cs b/Ryujinx/Ui/Widgets/GtkDialog.cs index 3d19724d..83f6bb2b 100644 --- a/Ryujinx/Ui/Widgets/GtkDialog.cs +++ b/Ryujinx/Ui/Widgets/GtkDialog.cs @@ -1,6 +1,7 @@ using Gtk; using System.Reflection; using Ryujinx.Common.Logging; +using System.Collections.Generic; namespace Ryujinx.Ui.Widgets { @@ -76,6 +77,34 @@ namespace Ryujinx.Ui.Widgets return response == ResponseType.Yes; } + internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary<int, string> buttons, MessageType messageType = MessageType.Other) + { + GtkDialog gtkDialog = new GtkDialog(title, mainText, secondaryText, messageType, ButtonsType.None); + + foreach (var button in buttons) + { + gtkDialog.AddButton(button.Value, button.Key); + } + + return (ResponseType)gtkDialog.Run(); + } + + internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax) + { + GtkInputDialog gtkDialog = new GtkInputDialog(parent, title, mainText, inputMax); + ResponseType response = (ResponseType)gtkDialog.Run(); + string responseText = gtkDialog.InputEntry.Text.TrimEnd(); + + gtkDialog.Dispose(); + + if (response == ResponseType.Ok) + { + return responseText; + } + + return ""; + } + internal static bool CreateExitDialog() { return CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to close Ryujinx?", "All unsaved data will be lost!"); diff --git a/Ryujinx/Ui/Widgets/GtkInputDialog.cs b/Ryujinx/Ui/Widgets/GtkInputDialog.cs new file mode 100644 index 00000000..21b34937 --- /dev/null +++ b/Ryujinx/Ui/Widgets/GtkInputDialog.cs @@ -0,0 +1,37 @@ +using Gtk; + +namespace Ryujinx.Ui.Widgets +{ + public class GtkInputDialog : MessageDialog + { + public Entry InputEntry { get; } + + public GtkInputDialog(Window parent, string title, string mainText, uint inputMax) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel, null) + { + SetDefaultSize(300, 0); + + Title = title; + + Label mainTextLabel = new Label + { + Text = mainText + }; + + InputEntry = new Entry + { + MaxLength = (int)inputMax + }; + + Label inputMaxTextLabel = new Label + { + Text = $"(Max length: {inputMax})" + }; + + ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); + ((Box)MessageArea).PackStart(InputEntry, true, true, 5); + ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0); + + ShowAll(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs new file mode 100644 index 00000000..52e03d30 --- /dev/null +++ b/Ryujinx/Ui/Windows/AvatarWindow.cs @@ -0,0 +1,289 @@ +using Gtk; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.FsSystem.NcaUtils; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +using Image = SixLabors.ImageSharp.Image; + +namespace Ryujinx.Ui.Windows +{ + public class AvatarWindow : Window + { + public byte[] SelectedProfileImage; + public bool NewUser; + + private static Dictionary<string, byte[]> _avatarDict = new Dictionary<string, byte[]>(); + + private ListStore _listStore; + private IconView _iconView; + private Button _setBackgroungColorButton; + private Gdk.RGBA _backgroundColor; + + public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar") + { + Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png"); + + CanFocus = false; + Resizable = false; + Modal = true; + TypeHint = Gdk.WindowTypeHint.Dialog; + + SetDefaultSize(740, 400); + SetPosition(WindowPosition.Center); + + VBox vbox = new VBox(false, 0); + Add(vbox); + + ScrolledWindow scrolledWindow = new ScrolledWindow + { + ShadowType = ShadowType.EtchedIn + }; + scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic); + + HBox hbox = new HBox(false, 0); + + Button chooseButton = new Button() + { + Label = "Choose", + CanFocus = true, + ReceivesDefault = true + }; + chooseButton.Clicked += ChooseButton_Pressed; + + _setBackgroungColorButton = new Button() + { + Label = "Set Background Color", + CanFocus = true + }; + _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed; + + _backgroundColor.Red = 1; + _backgroundColor.Green = 1; + _backgroundColor.Blue = 1; + _backgroundColor.Alpha = 1; + + Button closeButton = new Button() + { + Label = "Close", + CanFocus = true + }; + closeButton.Clicked += CloseButton_Pressed; + + vbox.PackStart(scrolledWindow, true, true, 0); + hbox.PackStart(chooseButton, true, true, 0); + hbox.PackStart(_setBackgroungColorButton, true, true, 0); + hbox.PackStart(closeButton, true, true, 0); + vbox.PackStart(hbox, false, false, 0); + + _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf)); + _listStore.SetSortColumnId(0, SortType.Ascending); + + _iconView = new IconView(_listStore); + _iconView.ItemWidth = 64; + _iconView.ItemPadding = 10; + _iconView.PixbufColumn = 1; + + _iconView.SelectionChanged += IconView_SelectionChanged; + + scrolledWindow.Add(_iconView); + + _iconView.GrabFocus(); + + ProcessAvatars(); + + ShowAll(); + } + + public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem) + { + if (_avatarDict.Count > 0) + { + return; + } + + string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.NandSystem, NcaContentType.Data); + string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath); + + if (!string.IsNullOrWhiteSpace(avatarPath)) + { + using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) + { + Nca nca = new Nca(virtualFileSystem.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + foreach (var item in romfs.EnumerateEntries()) + { + // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. + + if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) + { + romfs.OpenFile(out IFile file, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + using (MemoryStream stream = new MemoryStream()) + using (MemoryStream streamPng = new MemoryStream()) + { + file.AsStream().CopyTo(stream); + + stream.Position = 0; + + Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256); + + avatarImage.SaveAsPng(streamPng); + + _avatarDict.Add(item.FullPath, streamPng.ToArray()); + } + } + } + } + } + } + + private void ProcessAvatars() + { + _listStore.Clear(); + + foreach (var avatar in _avatarDict) + { + _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96)); + } + + _iconView.SelectPath(new TreePath(new int[] { 0 })); + } + + private byte[] ProcessImage(byte[] data) + { + using (MemoryStream streamJpg = new MemoryStream()) + { + Image avatarImage = Image.Load(data, new PngDecoder()); + + avatarImage.Mutate(x => x.BackgroundColor(new Rgba32((byte)(_backgroundColor.Red * 255), + (byte)(_backgroundColor.Green * 255), + (byte)(_backgroundColor.Blue * 255), + (byte)(_backgroundColor.Alpha * 255)))); + avatarImage.SaveAsJpeg(streamJpg); + + return streamJpg.ToArray(); + } + } + + private void CloseButton_Pressed(object sender, EventArgs e) + { + SelectedProfileImage = null; + + Close(); + } + + private void IconView_SelectionChanged(object sender, EventArgs e) + { + if (_iconView.SelectedItems.Length > 0) + { + _listStore.GetIter(out TreeIter iter, _iconView.SelectedItems[0]); + + SelectedProfileImage = ProcessImage(_avatarDict[(string)_listStore.GetValue(iter, 0)]); + } + } + + private void SetBackgroungColorButton_Pressed(object sender, EventArgs e) + { + using (ColorChooserDialog colorChooserDialog = new ColorChooserDialog("Set Background Color", this)) + { + colorChooserDialog.UseAlpha = false; + colorChooserDialog.Rgba = _backgroundColor; + + if (colorChooserDialog.Run() == (int)ResponseType.Ok) + { + _backgroundColor = colorChooserDialog.Rgba; + + ProcessAvatars(); + } + + colorChooserDialog.Hide(); + } + } + + private void ChooseButton_Pressed(object sender, EventArgs e) + { + Close(); + } + + private static byte[] DecompressYaz0(Stream stream) + { + using (BinaryReader reader = new BinaryReader(stream)) + { + reader.ReadInt32(); // Magic + + uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); + + reader.ReadInt64(); // Padding + + byte[] input = new byte[stream.Length - stream.Position]; + stream.Read(input, 0, input.Length); + + long inputOffset = 0; + + byte[] output = new byte[decodedLength]; + long outputOffset = 0; + + ushort mask = 0; + byte header = 0; + + while (outputOffset < decodedLength) + { + if ((mask >>= 1) == 0) + { + header = input[inputOffset++]; + mask = 0x80; + } + + if ((header & mask) > 0) + { + if (outputOffset == output.Length) + { + break; + } + + output[outputOffset++] = input[inputOffset++]; + } + else + { + byte byte1 = input[inputOffset++]; + byte byte2 = input[inputOffset++]; + + int dist = ((byte1 & 0xF) << 8) | byte2; + int position = (int)outputOffset - (dist + 1); + + int length = byte1 >> 4; + if (length == 0) + { + length = input[inputOffset++] + 0x12; + } + else + { + length += 2; + } + + while (length-- > 0) + { + output[outputOffset++] = output[position++]; + } + } + } + + return output; + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs index 6e876ad9..0a3deec1 100644 --- a/Ryujinx/Ui/Windows/ControllerWindow.cs +++ b/Ryujinx/Ui/Windows/ControllerWindow.cs @@ -187,9 +187,9 @@ namespace Ryujinx.Ui.Windows mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - if (_mainWindow.GlRendererWidget != null) + if (_mainWindow.RendererWidget != null) { - _mainWindow.GlRendererWidget.NpadManager.BlockInputUpdates(); + _mainWindow.RendererWidget.NpadManager.BlockInputUpdates(); } } @@ -219,9 +219,9 @@ namespace Ryujinx.Ui.Windows _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; - if (_mainWindow.GlRendererWidget != null) + if (_mainWindow.RendererWidget != null) { - _mainWindow.GlRendererWidget.NpadManager.UnblockInputUpdates(); + _mainWindow.RendererWidget.NpadManager.UnblockInputUpdates(); } _selectedGamepad?.Dispose(); @@ -1141,9 +1141,9 @@ namespace Ryujinx.Ui.Windows } } - if (_mainWindow.GlRendererWidget != null) + if (_mainWindow.RendererWidget != null) { - _mainWindow.GlRendererWidget.NpadManager.ReloadConfiguration(newConfig); + _mainWindow.RendererWidget.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard); } // Atomically replace and signal input change. diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs index be9dc271..43fea4e2 100644 --- a/Ryujinx/Ui/Windows/SettingsWindow.cs +++ b/Ryujinx/Ui/Windows/SettingsWindow.cs @@ -1,5 +1,6 @@ using Gtk; using Ryujinx.Audio.Backends.OpenAL; +using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -262,7 +263,7 @@ namespace Ryujinx.Ui.Windows } _systemTimeZoneEntry.WidthChars = Math.Max(20, maxLocationLength + 1); // Ensure minimum Entry width - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(); + _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); _systemTimeZoneCompletion.MatchFunc = TimeZoneMatchFunc; @@ -302,6 +303,7 @@ namespace Ryujinx.Ui.Windows TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo); + TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); _audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore); @@ -316,6 +318,9 @@ namespace Ryujinx.Ui.Windows case AudioBackend.SoundIo: _audioBackendSelect.SetActiveIter(soundIoIter); break; + case AudioBackend.SDL2: + _audioBackendSelect.SetActiveIter(sdl2Iter); + break; case AudioBackend.Dummy: _audioBackendSelect.SetActiveIter(dummyIter); break; @@ -328,11 +333,13 @@ namespace Ryujinx.Ui.Windows bool openAlIsSupported = false; bool soundIoIsSupported = false; + bool sdl2IsSupported = false; Task.Run(() => { openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; soundIoIsSupported = SoundIoHardwareDeviceDriver.IsSupported; + sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; }); // This function runs whenever the dropdown is opened @@ -342,6 +349,7 @@ namespace Ryujinx.Ui.Windows { AudioBackend.OpenAl => openAlIsSupported, AudioBackend.SoundIo => soundIoIsSupported, + AudioBackend.SDL2 => sdl2IsSupported, AudioBackend.Dummy => true, _ => throw new ArgumentOutOfRangeException() }; @@ -454,7 +462,7 @@ namespace Ryujinx.Ui.Windows { if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text)) { - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(); + _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); } } diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs new file mode 100644 index 00000000..70291290 --- /dev/null +++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.Designer.cs @@ -0,0 +1,255 @@ +using Gtk; +using Pango; + +namespace Ryujinx.Ui.Windows +{ + public partial class UserProfilesManagerWindow : Window + { + private Box _mainBox; + private Label _selectedLabel; + private Box _selectedUserBox; + private Image _selectedUserImage; + private VBox _selectedUserInfoBox; + private Entry _selectedUserNameEntry; + private Label _selectedUserIdLabel; + private VBox _selectedUserButtonsBox; + private Button _saveProfileNameButton; + private Button _changeProfileImageButton; + private Box _usersTreeViewBox; + private Label _availableUsersLabel; + private ScrolledWindow _usersTreeViewWindow; + private ListStore _tableStore; + private TreeView _usersTreeView; + private Box _bottomBox; + private Button _addButton; + private Button _deleteButton; + private Button _closeButton; + + private void InitializeComponent() + { + +#pragma warning disable CS0612 + + // + // UserProfilesManagerWindow + // + CanFocus = false; + Resizable = false; + Modal = true; + WindowPosition = WindowPosition.Center; + DefaultWidth = 620; + DefaultHeight = 548; + TypeHint = Gdk.WindowTypeHint.Dialog; + + // + // _mainBox + // + _mainBox = new Box(Orientation.Vertical, 0); + + // + // _selectedLabel + // + _selectedLabel = new Label("Selected User Profile:") + { + Margin = 15, + Attributes = new AttrList() + }; + _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); + + // + // _viewBox + // + _usersTreeViewBox = new Box(Orientation.Vertical, 0); + + // + // _SelectedUserBox + // + _selectedUserBox = new Box(Orientation.Horizontal, 0) + { + MarginLeft = 30 + }; + + // + // _selectedUserImage + // + _selectedUserImage = new Image(); + + // + // _selectedUserInfoBox + // + _selectedUserInfoBox = new VBox(true, 0); + + // + // _selectedUserNameEntry + // + _selectedUserNameEntry = new Entry("") + { + MarginLeft = 15, + MaxLength = (int)MaxProfileNameLength + }; + _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent; + + // + // _selectedUserIdLabel + // + _selectedUserIdLabel = new Label("") + { + MarginTop = 15, + MarginLeft = 15 + }; + + // + // _selectedUserButtonsBox + // + _selectedUserButtonsBox = new VBox() + { + MarginRight = 30 + }; + + // + // _saveProfileNameButton + // + _saveProfileNameButton = new Button() + { + Label = "Save Profile Name", + CanFocus = true, + ReceivesDefault = true, + Sensitive = false + }; + _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; + + // + // _changeProfileImageButton + // + _changeProfileImageButton = new Button() + { + Label = "Change Profile Image", + CanFocus = true, + ReceivesDefault = true, + MarginTop = 10 + }; + _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; + + // + // _availableUsersLabel + // + _availableUsersLabel = new Label("Available User Profiles:") + { + Margin = 15, + Attributes = new AttrList() + }; + _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); + + // + // _usersTreeViewWindow + // + _usersTreeViewWindow = new ScrolledWindow() + { + ShadowType = ShadowType.In, + CanFocus = true, + Expand = true, + MarginLeft = 30, + MarginRight = 30, + MarginBottom = 15 + }; + + // + // _tableStore + // + _tableStore = new ListStore(typeof(bool), typeof(Gdk.Pixbuf), typeof(string), typeof(Gdk.RGBA)); + + // + // _usersTreeView + // + _usersTreeView = new TreeView(_tableStore) + { + HoverSelection = true, + HeadersVisible = false, + }; + _usersTreeView.RowActivated += UsersTreeView_Activated; + + // + // _bottomBox + // + _bottomBox = new Box(Orientation.Horizontal, 0) + { + MarginLeft = 30, + MarginRight = 30, + MarginBottom = 15 + }; + + // + // _addButton + // + _addButton = new Button() + { + Label = "Add New Profile", + CanFocus = true, + ReceivesDefault = true, + HeightRequest = 35 + }; + _addButton.Clicked += AddButton_Pressed; + + // + // _deleteButton + // + _deleteButton = new Button() + { + Label = "Delete Selected Profile", + CanFocus = true, + ReceivesDefault = true, + HeightRequest = 35, + MarginLeft = 10 + }; + _deleteButton.Clicked += DeleteButton_Pressed; + + // + // _closeButton + // + _closeButton = new Button() + { + Label = "Close", + CanFocus = true, + ReceivesDefault = true, + HeightRequest = 35, + WidthRequest = 80 + }; + _closeButton.Clicked += CloseButton_Pressed; + +#pragma warning restore CS0612 + + ShowComponent(); + } + + private void ShowComponent() + { + _usersTreeViewWindow.Add(_usersTreeView); + + _usersTreeViewBox.Add(_usersTreeViewWindow); + + _bottomBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _addButton }, false, false, 0); + _bottomBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _deleteButton }, false, false, 0); + _bottomBox.PackEnd(new Gtk.Alignment(1, 0, 0, 0) { _closeButton }, false, false, 0); + + _selectedUserInfoBox.Add(_selectedUserNameEntry); + _selectedUserInfoBox.Add(_selectedUserIdLabel); + + _selectedUserButtonsBox.Add(_saveProfileNameButton); + _selectedUserButtonsBox.Add(_changeProfileImageButton); + + _selectedUserBox.Add(_selectedUserImage); + _selectedUserBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _selectedUserInfoBox }, true, true, 0); + _selectedUserBox.Add(_selectedUserButtonsBox); + + _mainBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _selectedLabel }, false, false, 0); + _mainBox.PackStart(_selectedUserBox, false, true, 0); + _mainBox.PackStart(new Gtk.Alignment(-1, 0, 0, 0) { _availableUsersLabel }, false, false, 0); + _mainBox.Add(_usersTreeViewBox); + _mainBox.Add(_bottomBox); + + Add(_mainBox); + + ShowAll(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs new file mode 100644 index 00000000..6a4788b1 --- /dev/null +++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs @@ -0,0 +1,327 @@ +using Gtk; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.FileSystem.Content; +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.Ui.Widgets; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +using Image = SixLabors.ImageSharp.Image; +using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; + +namespace Ryujinx.Ui.Windows +{ + public partial class UserProfilesManagerWindow : Window + { + private const uint MaxProfileNameLength = 0x20; + + private readonly AccountManager _accountManager; + private readonly ContentManager _contentManager; + + private byte[] _bufferImageProfile; + private string _tempNewProfileName; + + private Gdk.RGBA _selectedColor; + + private ManualResetEvent _avatarsPreloadingEvent = new ManualResetEvent(false); + + public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") + { + Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png"); + + InitializeComponent(); + + _selectedColor.Red = 0.212; + _selectedColor.Green = 0.843; + _selectedColor.Blue = 0.718; + _selectedColor.Alpha = 1; + + _accountManager = accountManager; + _contentManager = contentManager; + + CellRendererToggle userSelectedToggle = new CellRendererToggle(); + userSelectedToggle.Toggled += UserSelectedToggle_Toggled; + + // NOTE: Uncomment following line when multiple selection of user profiles is supported. + //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); + _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); + _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); + + _tableStore.SetSortColumnId(0, SortType.Descending); + + RefreshList(); + + if (_contentManager.GetCurrentFirmwareVersion() != null) + { + Task.Run(() => + { + AvatarWindow.PreloadAvatars(contentManager, virtualFileSystem); + _avatarsPreloadingEvent.Set(); + }); + } + } + + public void RefreshList() + { + _tableStore.Clear(); + + foreach (UserProfile userProfile in _accountManager.GetAllUsers()) + { + _tableStore.AppendValues(userProfile.AccountState == AccountState.Open, new Gdk.Pixbuf(userProfile.Image, 96, 96), $"{userProfile.Name}\n{userProfile.UserId}", Gdk.RGBA.Zero); + + if (userProfile.AccountState == AccountState.Open) + { + _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); + _selectedUserIdLabel.Text = userProfile.UserId.ToString(); + _selectedUserNameEntry.Text = userProfile.Name; + + _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId; + + _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); + _tableStore.SetValue(firstIter, 3, _selectedColor); + } + } + } + + // + // Events + // + + private void UsersTreeView_Activated(object o, RowActivatedArgs args) + { + SelectUserTreeView(); + } + + private void UserSelectedToggle_Toggled(object o, ToggledArgs args) + { + SelectUserTreeView(); + } + + private void SelectUserTreeView() + { + // Get selected item informations. + _usersTreeView.Selection.GetSelected(out TreeIter selectedIter); + + Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1); + + string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0]; + string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; + + // Unselect the first user. + _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); + _tableStore.SetValue(firstIter, 0, false); + _tableStore.SetValue(firstIter, 3, Gdk.RGBA.Zero); + + // Set new informations. + _tableStore.SetValue(selectedIter, 0, true); + + _selectedUserImage.Pixbuf = userPicture; + _selectedUserNameEntry.Text = userName; + _selectedUserIdLabel.Text = userId; + _saveProfileNameButton.Sensitive = false; + + // Open the selected one. + _accountManager.OpenUser(new UserId(userId)); + + _deleteButton.Sensitive = userId != AccountManager.DefaultUserId.ToString(); + + _tableStore.SetValue(selectedIter, 3, _selectedColor); + } + + private void SelectedUserNameEntry_KeyReleaseEvent(object o, KeyReleaseEventArgs args) + { + if (_saveProfileNameButton.Sensitive == false) + { + _saveProfileNameButton.Sensitive = true; + } + } + + private void AddButton_Pressed(object sender, EventArgs e) + { + _tempNewProfileName = GtkDialog.CreateInputDialog(this, "Choose the Profile Name", "Please Enter a Profile Name", MaxProfileNameLength); + + if (_tempNewProfileName != "") + { + SelectProfileImage(true); + + if (_bufferImageProfile != null) + { + AddUser(); + } + } + } + + private void DeleteButton_Pressed(object sender, EventArgs e) + { + if (GtkDialog.CreateChoiceDialog("Delete User Profile", "Are you sure you want to delete the profile ?", "Deleting this profile will also delete all associated save data.")) + { + _accountManager.DeleteUser(GetSelectedUserId()); + + RefreshList(); + } + } + + private void EditProfileNameButton_Pressed(object sender, EventArgs e) + { + _saveProfileNameButton.Sensitive = false; + + _accountManager.SetUserName(GetSelectedUserId(), _selectedUserNameEntry.Text); + + RefreshList(); + } + + private void ProcessProfileImage(byte[] buffer) + { + using (Image image = Image.Load(buffer)) + { + image.Mutate(x => x.Resize(256, 256)); + + using (MemoryStream streamJpg = new MemoryStream()) + { + image.SaveAsJpeg(streamJpg); + + _bufferImageProfile = streamJpg.ToArray(); + } + } + } + + private void ProfileImageFileChooser() + { + FileChooserDialog fileChooser = new FileChooserDialog("Import Custom Profile Image", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Import", ResponseType.Accept) + { + SelectMultiple = false, + Filter = new FileFilter() + }; + + fileChooser.SetPosition(WindowPosition.Center); + fileChooser.Filter.AddPattern("*.jpg"); + fileChooser.Filter.AddPattern("*.jpeg"); + fileChooser.Filter.AddPattern("*.png"); + fileChooser.Filter.AddPattern("*.bmp"); + + if (fileChooser.Run() == (int)ResponseType.Accept) + { + ProcessProfileImage(File.ReadAllBytes(fileChooser.Filename)); + } + + fileChooser.Dispose(); + } + + private void SelectProfileImage(bool newUser = false) + { + if (_contentManager.GetCurrentFirmwareVersion() == null) + { + ProfileImageFileChooser(); + } + else + { + Dictionary<int, string> buttons = new Dictionary<int, string>() + { + { 0, "Import Image File" }, + { 1, "Select Firmware Avatar" } + }; + + ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", + "Choose a Profile Image", + "You may import a custom profile image, or select an avatar from the system firmware.", + buttons, MessageType.Question); + + if (responseDialog == 0) + { + ProfileImageFileChooser(); + } + else if (responseDialog == (ResponseType)1) + { + AvatarWindow avatarWindow = new AvatarWindow() + { + NewUser = newUser + }; + + avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; + + avatarWindow.SetSizeRequest((int)(avatarWindow.DefaultWidth * Program.WindowScaleFactor), (int)(avatarWindow.DefaultHeight * Program.WindowScaleFactor)); + avatarWindow.Show(); + } + } + } + + private void ChangeProfileImageButton_Pressed(object sender, EventArgs e) + { + if (_contentManager.GetCurrentFirmwareVersion() != null) + { + _avatarsPreloadingEvent.WaitOne(); + } + + SelectProfileImage(); + + if (_bufferImageProfile != null) + { + SetUserImage(); + } + } + + private void AvatarWindow_DeleteEvent(object sender, DeleteEventArgs args) + { + _bufferImageProfile = ((AvatarWindow)sender).SelectedProfileImage; + + if (_bufferImageProfile != null) + { + if (((AvatarWindow)sender).NewUser) + { + AddUser(); + } + else + { + SetUserImage(); + } + } + } + + private void AddUser() + { + _accountManager.AddUser(_tempNewProfileName, _bufferImageProfile); + + _bufferImageProfile = null; + _tempNewProfileName = ""; + + RefreshList(); + } + + private void SetUserImage() + { + _accountManager.SetUserImage(GetSelectedUserId(), _bufferImageProfile); + + _bufferImageProfile = null; + + RefreshList(); + } + + private UserId GetSelectedUserId() + { + if (_usersTreeView.Model.GetIterFirst(out TreeIter iter)) + { + do + { + if ((bool)_tableStore.GetValue(iter, 0)) + { + break; + } + } + while (_usersTreeView.Model.IterNext(ref iter)); + } + + return new UserId(_tableStore.GetValue(iter, 2).ToString().Split("\n")[1]); + } + + private void CloseButton_Pressed(object sender, EventArgs e) + { + Close(); + } + } +}
\ No newline at end of file |
