using Ryujinx.Common; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; namespace Ryujinx.HLE.Exceptions { [Serializable] internal class ServiceNotImplementedException : Exception { public IpcService Service { get; } public ServiceCtx Context { get; } public IpcMessage Request { get; } private string MethodName { get; } public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message = "The service call is not implemented.", [CallerMemberName] string methodName = null) : base(message) { Service = service; Context = context; Request = context.Request; MethodName = methodName; } public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message, Exception inner, [CallerMemberName] string methodName = null) : base(message, inner) { Service = service; Context = context; Request = context.Request; MethodName = methodName; } public override string Message { get { return base.Message + Environment.NewLine + Environment.NewLine + BuildMessage(); } } private string BuildMessage() { StringBuilder sb = new(); int commandId = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommandIdByMethodName(MethodName) : Service.CmifCommandIdByMethodName(MethodName); sb.AppendLine($"Service Command: {Service.GetType().FullName}: {commandId} ({MethodName})"); sb.AppendLine(); sb.AppendLine("Guest Stack Trace:"); sb.AppendLine(Context.Thread.GetGuestStackTrace()); // Print buffer information if (Request.PtrBuff.Count > 0 || Request.SendBuff.Count > 0 || Request.ReceiveBuff.Count > 0 || Request.ExchangeBuff.Count > 0 || Request.RecvListBuff.Count > 0) { sb.AppendLine("Buffer Information:"); if (Request.PtrBuff.Count > 0) { sb.AppendLine("\tPtrBuff:"); foreach (IpcPtrBuffDesc buff in Request.PtrBuff) { sb.AppendLine($"\t[{buff.Index}] Position: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); } } if (Request.SendBuff.Count > 0) { sb.AppendLine("\tSendBuff:"); foreach (IpcBuffDesc buff in Request.SendBuff) { sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); } } if (Request.ReceiveBuff.Count > 0) { sb.AppendLine("\tReceiveBuff:"); foreach (IpcBuffDesc buff in Request.ReceiveBuff) { sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); } } if (Request.ExchangeBuff.Count > 0) { sb.AppendLine("\tExchangeBuff:"); foreach (IpcBuffDesc buff in Request.ExchangeBuff) { sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); } } if (Request.RecvListBuff.Count > 0) { sb.AppendLine("\tRecvListBuff:"); foreach (IpcRecvListBuffDesc buff in Request.RecvListBuff) { sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); } } sb.AppendLine(); } sb.AppendLine("Raw Request Data:"); sb.Append(HexUtils.HexTable(Request.RawData)); return sb.ToString(); } } }