diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs index c4ec001bf..9ad80a58c 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.MainThread.cs @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Debugger Logger.Notice.Print(LogClass.GdbStub, "NACK received!"); continue; case '\x03': - _messages.Add(StatelessMessage.BreakIn); + _messages.Add(Message.BreakIn); break; case '$': string cmd = string.Empty; @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.Debugger } else { - _messages.Add(StatelessMessage.SendNack); + _messages.Add(Message.SendNack); } break; diff --git a/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs index 4e2cdc237..69d0b15fe 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.MessageHandler.cs @@ -14,16 +14,16 @@ namespace Ryujinx.HLE.Debugger { switch (_messages.Take()) { - case StatelessMessage { Type: MessageType.BreakIn }: + case Message { Type: MessageType.BreakIn }: Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); _commands.Interrupt(); break; - case StatelessMessage { Type: MessageType.SendNack }: + case Message { Type: MessageType.SendNack }: _writeStream.WriteByte((byte)'-'); break; - case StatelessMessage { Type: MessageType.Kill }: + case Message { Type: MessageType.Kill }: return; case CommandMessage { Command: { } cmd }: diff --git a/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs index ef1d7f394..f63817ef3 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.Rcmd.cs @@ -1,13 +1,49 @@ +using Gommon; +using JetBrains.Annotations; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using System; +using System.Collections.Generic; +using System.Linq; using System.Text; namespace Ryujinx.HLE.Debugger { public partial class Debugger { + static Debugger() + { + _rcmdDelegates.Add(["help"], + _ => _rcmdDelegates.Keys + .Where(x => !x[0].Equals("help")) + .Select(x => x.JoinToString('\n')) + .JoinToString('\n') + '\n' + ); + _rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo()); + _rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace()); + _rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters()); + _rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump()); + } + + private static readonly Dictionary> _rcmdDelegates = new(); + + public static Func FindRcmdDelegate(string command) + { + Func searchResult = _ => $"Unknown command: {command}\n"; + + foreach ((string[] names, Func dlg) in _rcmdDelegates) + { + if (names.ContainsIgnoreCase(command.Trim())) + { + searchResult = dlg; + break; + } + } + + return searchResult; + } + public string GetStackTrace() { if (GThread == null) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 6a5da60ee..567e97071 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -4,10 +4,8 @@ using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using System; using System.Collections.Concurrent; -using System.IO; using System.Linq; using System.Net.Sockets; -using System.Text; using System.Threading; using IExecutionContext = Ryujinx.Cpu.IExecutionContext; @@ -20,7 +18,7 @@ namespace Ryujinx.HLE.Debugger public ushort GdbStubPort { get; private set; } - private readonly BlockingCollection _messages = new(1); + private readonly BlockingCollection _messages = new(1); private readonly Thread _debuggerThread; private readonly Thread _messageHandlerThread; @@ -59,8 +57,8 @@ namespace Ryujinx.HLE.Debugger internal KThread[] GetThreads() => DebugProcess.ThreadUids.Select(DebugProcess.GetThread).ToArray(); - internal bool IsProcess32Bit => DebugProcess.GetThread(GThread.Value).Context.IsAarch32; - + internal bool IsProcess32Bit => DebugProcess.GetThread(GThread ?? DebugProcess.ThreadUids.First()).Context.IsAarch32; + internal bool WriteRegister(IExecutionContext ctx, int registerId, StringStream ss) => IsProcess32Bit ? ctx.WriteRegister32(registerId, ss) @@ -89,7 +87,7 @@ namespace Ryujinx.HLE.Debugger _readStream?.Close(); _writeStream?.Close(); _debuggerThread.Join(); - _messages.Add(StatelessMessage.Kill); + _messages.Add(Message.Kill); _messageHandlerThread.Join(); _messages.Dispose(); _breakHandlerEvent.Dispose(); diff --git a/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs b/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs index a07dafd1c..84f4dd7e6 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/CommandProcessor.cs @@ -20,6 +20,9 @@ namespace Ryujinx.HLE.Debugger.Gdb Commands = commands; } + public void ReplyHex(string data) => Reply(Helpers.ToHex(data)); + public void ReplyHex(byte[] data) => Reply(Helpers.ToHex(data)); + public void Reply(string cmd) { Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}"); @@ -146,6 +149,12 @@ namespace Ryujinx.HLE.Debugger.Gdb break; } + if (ss.ConsumeRemaining("Attached")) + { + Reply("1"); + break; + } + if (ss.ConsumeRemaining("ProcessInfo")) { Reply( @@ -191,11 +200,10 @@ namespace Ryujinx.HLE.Debugger.Gdb break; } - Reply(Helpers.ToHex( - DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value)) - ? "Paused" - : "Running" - ) + ReplyHex( + DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value)) + ? "Paused" + : "Running" ); break; diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs index 0220e259f..a7cefce2e 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Commands.cs @@ -83,12 +83,13 @@ namespace Ryujinx.HLE.Debugger.Gdb } Debugger.DebugProcess.DebugContinue(); + Processor.ReplyOK(); } internal void Detach() { Debugger.BreakpointManager.ClearAll(); - Continue(null); + Continue(null); // Continue() will call ReplyError/ReplyOK for us. } internal void ReadRegisters() @@ -196,7 +197,7 @@ namespace Ryujinx.HLE.Debugger.Gdb { byte[] data = new byte[len]; Debugger.DebugProcess.CpuMemory.Read(addr, data); - Processor.Reply(Helpers.ToHex(data)); + Processor.ReplyHex(data); } catch (InvalidMemoryRegionException) { @@ -421,17 +422,9 @@ namespace Ryujinx.HLE.Debugger.Gdb string command = Helpers.FromHex(hexCommand); Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}"); - string response = command.Trim().ToLowerInvariant() switch - { - "help" => "backtrace\nbt\nregisters\nreg\nget info\nminidump\n", - "get info" => Debugger.GetProcessInfo(), - "backtrace" or "bt" => Debugger.GetStackTrace(), - "registers" or "reg" => Debugger.GetRegisters(), - "minidump" => Debugger.GetMinidump(), - _ => $"Unknown command: {command}\n" - }; + Func rcmd = Debugger.FindRcmdDelegate(command); - Processor.Reply(Helpers.ToHex(response)); + Processor.ReplyHex(rcmd(Debugger)); } catch (Exception e) { diff --git a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs index 31203b62b..7d3083b31 100644 --- a/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs +++ b/src/Ryujinx.HLE/Debugger/Gdb/Registers.cs @@ -20,8 +20,8 @@ namespace Ryujinx.HLE.Debugger.Gdb 32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)), 33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)), >= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()), - 66 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpsr)), - 67 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpcr)), + 66 => Helpers.ToHex(BitConverter.GetBytes(state.Fpsr)), + 67 => Helpers.ToHex(BitConverter.GetBytes(state.Fpcr)), _ => null }; diff --git a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs index a632cea33..6d0cf3029 100644 --- a/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs +++ b/src/Ryujinx.HLE/Debugger/IDebuggableProcess.cs @@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Debugger { internal interface IDebuggableProcess { + IVirtualMemoryManager CpuMemory { get; } + ulong[] ThreadUids { get; } + DebugState DebugState { get; } + void DebugStop(); void DebugContinue(); void DebugContinue(KThread thread); @@ -13,9 +17,6 @@ namespace Ryujinx.HLE.Debugger KThread GetThread(ulong threadUid); bool IsThreadPaused(KThread thread); public void DebugInterruptHandler(IExecutionContext ctx); - IVirtualMemoryManager CpuMemory { get; } - ulong[] ThreadUids { get; } - DebugState DebugState { get; } void InvalidateCacheRegion(ulong address, ulong size); } } diff --git a/src/Ryujinx.HLE/Debugger/IMessage.cs b/src/Ryujinx.HLE/Debugger/Message.cs similarity index 56% rename from src/Ryujinx.HLE/Debugger/IMessage.cs rename to src/Ryujinx.HLE/Debugger/Message.cs index 9877bcd5e..c5f9e9554 100644 --- a/src/Ryujinx.HLE/Debugger/IMessage.cs +++ b/src/Ryujinx.HLE/Debugger/Message.cs @@ -2,11 +2,6 @@ using Ryujinx.Cpu; namespace Ryujinx.HLE.Debugger { - /// - /// Marker interface for debugger messages. - /// - interface IMessage; - public enum MessageType { Kill, @@ -14,14 +9,19 @@ namespace Ryujinx.HLE.Debugger SendNack } - record struct StatelessMessage(MessageType Type) : IMessage + record struct Message(MessageType Type) : Message.IMarker { - public static StatelessMessage Kill => new(MessageType.Kill); - public static StatelessMessage BreakIn => new(MessageType.BreakIn); - public static StatelessMessage SendNack => new(MessageType.SendNack); + /// + /// Marker interface for debugger messages. + /// + internal interface IMarker; + + public static Message Kill => new(MessageType.Kill); + public static Message BreakIn => new(MessageType.BreakIn); + public static Message SendNack => new(MessageType.SendNack); } - struct CommandMessage : IMessage + struct CommandMessage : Message.IMarker { public readonly string Command; @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Debugger } } - public class ThreadBreakMessage : IMessage + public class ThreadBreakMessage : Message.IMarker { public IExecutionContext Context { get; } public ulong Address { get; } diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs index 3cb46d204..a00514cb6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs @@ -38,6 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService } [CommandCmif(3)] + // LoadIdTokenCacheDeprecated() -> (u32 id_token_cache_size, buffer) + public ResultCode LoadIdTokenCacheDeprecated(ServiceCtx context) + { + return _managerServer.LoadIdTokenCache(context); + } + + [CommandCmif(4)] // 19.0.0+ // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) public ResultCode LoadIdTokenCache(ServiceCtx context) { diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs index 8510837b2..40c73c439 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs @@ -38,6 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService } [CommandCmif(3)] + // LoadIdTokenCacheDeprecated() -> (u32 id_token_cache_size, buffer) + public ResultCode LoadIdTokenCacheDeprecated(ServiceCtx context) + { + return _managerServer.LoadIdTokenCache(context); + } + + [CommandCmif(4)] // 19.0.0+ // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) public ResultCode LoadIdTokenCache(ServiceCtx context) { diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs index 092d0215a..5b4ae7e0d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -83,23 +83,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan> ReadEntries(uint maxCount) { - ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount); + ulong countAvailable = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount); - if (countAvailaible == 0) + if (countAvailable == 0) { return ReadOnlySpan>.Empty; } ulong index = ReadCurrentIndex(); - AtomicStorage[] result = new AtomicStorage[countAvailaible]; + AtomicStorage[] result = new AtomicStorage[countAvailable]; Span> storageSpan = _storage.AsSpan(); - for (ulong i = 0; i < countAvailaible; i++) + for (ulong i = 0; i < countAvailable; i++) { - int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries); - int outputEntryIndex = (int)(countAvailaible - i - 1); + int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailable + i) % MaxEntries); + int outputEntryIndex = (int)(countAvailable - i - 1); ulong samplingNumber0 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic(); result[outputEntryIndex] = storageSpan[inputEntryIndex]; @@ -107,9 +107,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1)) { - ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible); + ulong tempCount = Math.Min(ReadCurrentCount(), countAvailable); - countAvailaible = Math.Min(tempCount, maxCount); + countAvailable = Math.Min(tempCount, maxCount); index = ReadCurrentIndex(); i -= 1; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs index 633d5f739..3e3a226ae 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs @@ -15,5 +15,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn return ResultCode.Success; } + + [CommandCmif(1)] // 18.0.0+ + // CreateClientProcessMonitor() -> object + public ResultCode CreateClientProcessMonitor(ServiceCtx context) + { + MakeObject(context, new IClientProcessMonitor(context)); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IClientProcessMonitor.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IClientProcessMonitor.cs new file mode 100644 index 000000000..349d51a3f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IClientProcessMonitor.cs @@ -0,0 +1,20 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator +{ + class IClientProcessMonitor : DisposableIpcService + { + public IClientProcessMonitor(ServiceCtx context) { } + + [CommandCmif(0)] // 18.0.0+ + // RegisterClient(u64 pid_placeholder, pid) + public ResultCode RegisterClient(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + protected override void Dispose(bool isDisposing) { } + } +} diff --git a/src/Ryujinx.HLE/UI/Input/NpadReader.cs b/src/Ryujinx.HLE/UI/Input/NpadReader.cs index bb6d309ea..8d26cf440 100644 --- a/src/Ryujinx.HLE/UI/Input/NpadReader.cs +++ b/src/Ryujinx.HLE/UI/Input/NpadReader.cs @@ -59,18 +59,18 @@ namespace Ryujinx.HLE.UI.Input } } - public void Update(bool supressEvents = false) + public void Update(bool suppressEvents = false) { int npadsCount = _device.Hid.SharedMemory.Npads.Length; // Process each input individually. for (int npadIndex = 0; npadIndex < npadsCount; npadIndex++) { - UpdateNpad(npadIndex, supressEvents); + UpdateNpad(npadIndex, suppressEvents); } } - private void UpdateNpad(int npadIndex, bool supressEvents) + private void UpdateNpad(int npadIndex, bool suppressEvents) { const int MaxEntries = 1024; @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.UI.Input break; } - if (!supressEvents) + if (!suppressEvents) { ProcessNpadButtons(npadIndex, entry.Object.Buttons); }