/ src / Ryujinx.HLE / Exceptions / ServiceNotImplementedException.cs
ServiceNotImplementedException.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.HLE.HOS;
  3  using Ryujinx.HLE.HOS.Ipc;
  4  using Ryujinx.HLE.HOS.Services;
  5  using System;
  6  using System.Diagnostics;
  7  using System.Linq;
  8  using System.Reflection;
  9  using System.Runtime.Serialization;
 10  using System.Text;
 11  
 12  namespace Ryujinx.HLE.Exceptions
 13  {
 14      [Serializable]
 15      internal class ServiceNotImplementedException : Exception
 16      {
 17          public IpcService Service { get; }
 18          public ServiceCtx Context { get; }
 19          public IpcMessage Request { get; }
 20  
 21          public ServiceNotImplementedException(IpcService service, ServiceCtx context)
 22              : this(service, context, "The service call is not implemented.") { }
 23  
 24          public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message) : base(message)
 25          {
 26              Service = service;
 27              Context = context;
 28              Request = context.Request;
 29          }
 30  
 31          public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message, Exception inner) : base(message, inner)
 32          {
 33              Service = service;
 34              Context = context;
 35              Request = context.Request;
 36          }
 37  
 38          public override string Message
 39          {
 40              get
 41              {
 42                  return base.Message + Environment.NewLine + Environment.NewLine + BuildMessage();
 43              }
 44          }
 45  
 46          private string BuildMessage()
 47          {
 48              StringBuilder sb = new();
 49  
 50              // Print the IPC command details (service name, command ID, and handler)
 51              (Type callingType, MethodBase callingMethod) = WalkStackTrace(new StackTrace(this));
 52  
 53              if (callingType != null && callingMethod != null)
 54              {
 55                  // If the type is past 0xF, we are using TIPC
 56                  var ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.CmifCommands;
 57  
 58                  // Find the handler for the method called
 59                  var ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
 60                  var ipcCommandId = ipcHandler.Key;
 61                  var ipcMethod = ipcHandler.Value;
 62  
 63                  if (ipcMethod != null)
 64                  {
 65                      sb.AppendLine($"Service Command: {Service.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})");
 66                      sb.AppendLine();
 67                  }
 68              }
 69  
 70              sb.AppendLine("Guest Stack Trace:");
 71              sb.AppendLine(Context.Thread.GetGuestStackTrace());
 72  
 73              // Print buffer information
 74              if (Request.PtrBuff.Count > 0 ||
 75                  Request.SendBuff.Count > 0 ||
 76                  Request.ReceiveBuff.Count > 0 ||
 77                  Request.ExchangeBuff.Count > 0 ||
 78                  Request.RecvListBuff.Count > 0)
 79              {
 80                  sb.AppendLine("Buffer Information:");
 81  
 82                  if (Request.PtrBuff.Count > 0)
 83                  {
 84                      sb.AppendLine("\tPtrBuff:");
 85  
 86                      foreach (var buff in Request.PtrBuff)
 87                      {
 88                          sb.AppendLine($"\t[{buff.Index}] Position: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}");
 89                      }
 90                  }
 91  
 92                  if (Request.SendBuff.Count > 0)
 93                  {
 94                      sb.AppendLine("\tSendBuff:");
 95  
 96                      foreach (var buff in Request.SendBuff)
 97                      {
 98                          sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
 99                      }
100                  }
101  
102                  if (Request.ReceiveBuff.Count > 0)
103                  {
104                      sb.AppendLine("\tReceiveBuff:");
105  
106                      foreach (var buff in Request.ReceiveBuff)
107                      {
108                          sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
109                      }
110                  }
111  
112                  if (Request.ExchangeBuff.Count > 0)
113                  {
114                      sb.AppendLine("\tExchangeBuff:");
115  
116                      foreach (var buff in Request.ExchangeBuff)
117                      {
118                          sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}");
119                      }
120                  }
121  
122                  if (Request.RecvListBuff.Count > 0)
123                  {
124                      sb.AppendLine("\tRecvListBuff:");
125  
126                      foreach (var buff in Request.RecvListBuff)
127                      {
128                          sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}");
129                      }
130                  }
131  
132                  sb.AppendLine();
133              }
134  
135              sb.AppendLine("Raw Request Data:");
136              sb.Append(HexUtils.HexTable(Request.RawData));
137  
138              return sb.ToString();
139          }
140  
141          private static (Type, MethodBase) WalkStackTrace(StackTrace trace)
142          {
143              int i = 0;
144  
145              StackFrame frame;
146  
147              // Find the IIpcService method that threw this exception
148              while ((frame = trace.GetFrame(i++)) != null)
149              {
150                  var method = frame.GetMethod();
151                  var declType = method.DeclaringType;
152  
153                  if (typeof(IpcService).IsAssignableFrom(declType))
154                  {
155                      return (declType, method);
156                  }
157              }
158  
159              return (null, null);
160          }
161      }
162  }