Make Cmif commands in HLE use virtual method invocation instead of reflection

# Conflicts:
#	src/Ryujinx.HLE/HOS/Services/IpcService.cs
This commit is contained in:
Aaron Robinson
2025-11-19 18:31:35 -06:00
parent 19de0d0db6
commit 8e1e2d69df
133 changed files with 298 additions and 239 deletions

View File

@@ -7,7 +7,7 @@
<Target Name="PostBuildTarget" AfterTargets="AfterBuild">
<Message Text="Running Validation Project" Importance="high" />
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\"
<Exec WorkingDirectory="$(OutDir)"
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true"
Condition="'$(RuntimeIdentifier)' == ''"

View File

@@ -0,0 +1,118 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using System.Collections.Immutable;
using System.Threading;
namespace Ryujinx.HLE.Generators
{
[Generator]
public class IpcCommandGenerator : IIncrementalGenerator
{
private sealed class CommandData
{
public required string Namespace { get; init; }
public required string TypeName { get; init; }
public required string MethodName { get; init; }
public required ImmutableArray<int> CommandIds { get; init; }
}
private sealed class ServiceData
{
public required string Namespace { get; init; }
public required string TypeName { get; init; }
public required ImmutableArray<CommandData> CmifCommands { get; init; }
public required ImmutableArray<CommandData> TipcCommands { get; init; }
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var predicate = (SyntaxNode node, CancellationToken _) => node is MethodDeclarationSyntax;
var transform = (GeneratorAttributeSyntaxContext ctx, CancellationToken _) =>
{
var target = (IMethodSymbol)ctx.TargetSymbol;
return new CommandData
{
Namespace = target.ContainingType.ContainingNamespace?.ToDisplayString(),
TypeName = target.ContainingType.Name,
MethodName = target.Name,
CommandIds = ctx.Attributes.Select(attr => (int)attr.ConstructorArguments[0].Value!).ToImmutableArray(),
};
};
var cmifCommands =
context.SyntaxProvider.ForAttributeWithMetadataName("Ryujinx.HLE.HOS.Services.CommandCmifAttribute",
predicate,
transform
);
var tipcCommands =
context.SyntaxProvider.ForAttributeWithMetadataName("Ryujinx.HLE.HOS.Services.CommandTipcAttribute",
predicate,
transform
);
var allCommands = cmifCommands.Collect().Combine(tipcCommands.Collect());
var types = allCommands.SelectMany((commands, _) =>
{
var cmif = commands.Left.ToLookup(c => (c.Namespace, c.TypeName));
var tipc = commands.Right.ToLookup(c => (c.Namespace, c.TypeName));
var builder = ImmutableArray.CreateBuilder<ServiceData>();
foreach (var type in cmif.Select(c => c.Key).Union(tipc.Select(t => t.Key)))
{
builder.Add(new ServiceData
{
Namespace = type.Namespace,
TypeName = type.TypeName,
CmifCommands = cmif.Contains(type) ? cmif[type].ToImmutableArray() : [],
TipcCommands = tipc.Contains(type) ? tipc[type].ToImmutableArray() : [],
});
}
return builder.DrainToImmutable();
});
context.RegisterSourceOutput(types, (ctx, data) =>
{
var generator = new CodeGenerator();
generator.AppendLine("using Ryujinx.HLE.HOS;");
generator.AppendLine("using RC = global::Ryujinx.HLE.HOS.ResultCode;");
generator.EnterScope($"namespace {data.Namespace}");
generator.EnterScope($"partial class {data.TypeName}");
generator.EnterScope("protected override RC InvokeCmifMethod(int id, ServiceCtx context)");
generator.EnterScope("switch (id)");
foreach (var command in data.CmifCommands)
{
generator.AppendLine($"case {string.Join(" or ", command.CommandIds)}:");
generator.IncreaseIndentation();
generator.AppendLine($"LogInvoke(\"{command.MethodName}\");");
generator.AppendLine($"return (RC){command.MethodName}(context);");
generator.DecreaseIndentation();
}
generator.AppendLine("default: return base.InvokeCmifMethod(id, context);");
generator.LeaveScope();
generator.LeaveScope();
generator.EnterScope("public override int CmifCommandIdByMethodName(string name)");
generator.EnterScope("return name switch");
foreach (var command in data.CmifCommands)
{
// just return the first command with this name
generator.AppendLine($"\"{command.MethodName}\" => {command.CommandIds[0]},");
}
generator.AppendLine("_ => base.CmifCommandIdByMethodName(name),");
generator.LeaveScope(";");
generator.LeaveScope();
generator.LeaveScope();
generator.LeaveScope();
ctx.AddSource($"{data.Namespace}.{data.TypeName}.g.cs", generator.ToString());
});
}
}
}

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace Ryujinx.HLE.Exceptions
@@ -17,22 +18,22 @@ namespace Ryujinx.HLE.Exceptions
public IpcService Service { get; }
public ServiceCtx Context { get; }
public IpcMessage Request { get; }
private string MethodName { get; }
public ServiceNotImplementedException(IpcService service, ServiceCtx context)
: this(service, context, "The service call is not implemented.") { }
public ServiceNotImplementedException(IpcService service, ServiceCtx context, string message) : base(message)
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) : base(message, inner)
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
@@ -47,25 +48,12 @@ namespace Ryujinx.HLE.Exceptions
{
StringBuilder sb = new();
// Print the IPC command details (service name, command ID, and handler)
(Type callingType, MethodBase callingMethod) = WalkStackTrace(new StackTrace(this));
var commandId = Request.Type > IpcMessageType.TipcCloseSession
? -1 // TODO: tipc name
: Service.CmifCommandIdByMethodName(MethodName);
if (callingType != null && callingMethod != null)
{
// If the type is past 0xF, we are using TIPC
IReadOnlyDictionary<int, MethodInfo> ipcCommands = Request.Type > IpcMessageType.TipcCloseSession ? Service.TipcCommands : Service.CmifCommands;
// Find the handler for the method called
KeyValuePair<int, MethodInfo> ipcHandler = ipcCommands.FirstOrDefault(x => x.Value == callingMethod);
int ipcCommandId = ipcHandler.Key;
MethodInfo ipcMethod = ipcHandler.Value;
if (ipcMethod != null)
{
sb.AppendLine($"Service Command: {Service.GetType().FullName}: {ipcCommandId} ({ipcMethod.Name})");
sb.AppendLine();
}
}
sb.AppendLine($"Service Command: {Service.GetType().FullName}: {commandId} ({MethodName})");
sb.AppendLine();
sb.AppendLine("Guest Stack Trace:");
sb.AppendLine(Context.Thread.GetGuestStackTrace());
@@ -137,26 +125,5 @@ namespace Ryujinx.HLE.Exceptions
return sb.ToString();
}
private static (Type, MethodBase) WalkStackTrace(StackTrace trace)
{
int i = 0;
StackFrame frame;
// Find the IIpcService method that threw this exception
while ((frame = trace.GetFrame(i++)) != null)
{
MethodBase method = frame.GetMethod();
Type declType = method.DeclaringType;
if (typeof(IpcService).IsAssignableFrom(declType))
{
return (declType, method);
}
}
return (null, null);
}
}
}

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
{
class IManagerForApplication : IpcService
partial class IManagerForApplication : IpcService
{
private readonly ManagerServer _managerServer;

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
{
class IManagerForSystemService : IpcService
partial class IManagerForSystemService : IpcService
{
private readonly ManagerServer _managerServer;

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
{
class IProfile : IpcService
partial class IProfile : IpcService
{
private readonly ProfileServer _profileServer;

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
{
class IProfileEditor : IpcService
partial class IProfileEditor : IpcService
{
private readonly ProfileServer _profileServer;

View File

@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
[Service("acc:su", AccountServiceFlag.Administrator)] // Max Sessions: 8
class IAccountServiceForAdministrator : IpcService
partial class IAccountServiceForAdministrator : IpcService
{
private readonly ApplicationServiceServer _applicationServiceServer;

View File

@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Services.Arp;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
[Service("acc:u0", AccountServiceFlag.Application)] // Max Sessions: 4
class IAccountServiceForApplication : IpcService
partial class IAccountServiceForApplication : IpcService
{
private readonly ApplicationServiceServer _applicationServiceServer;

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
[Service("acc:u1", AccountServiceFlag.SystemService)] // Max Sessions: 16
class IAccountServiceForSystemService : IpcService
partial class IAccountServiceForSystemService : IpcService
{
private readonly ApplicationServiceServer _applicationServiceServer;

View File

@@ -5,7 +5,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
class IAsyncContext : IpcService
partial class IAsyncContext : IpcService
{
protected AsyncExecution AsyncExecution;

View File

@@ -2,7 +2,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
class IAsyncNetworkServiceLicenseKindContext : IAsyncContext
partial class IAsyncNetworkServiceLicenseKindContext : IAsyncContext
{
private readonly NetworkServiceLicenseKind? _serviceLicenseKind;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemA
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
{
class ILibraryAppletProxy : IpcService
partial class ILibraryAppletProxy : IpcService
{
private readonly ulong _pid;

View File

@@ -2,7 +2,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemA
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
{
class ISystemAppletProxy : IpcService
partial class ISystemAppletProxy : IpcService
{
private readonly ulong _pid;

View File

@@ -9,7 +9,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator
{
class ILibraryAppletAccessor : DisposableIpcService
partial class ILibraryAppletAccessor : DisposableIpcService
{
private readonly KernelContext _kernelContext;

View File

@@ -3,7 +3,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
{
class ILibraryAppletSelfAccessor : IpcService
partial class ILibraryAppletSelfAccessor : IpcService
{
private readonly AppletStandalone _appletStandalone = new();

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
{
class IProcessWindingController : IpcService
partial class IProcessWindingController : IpcService
{
public IProcessWindingController() { }

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class IAudioController : IpcService
partial class IAudioController : IpcService
{
public IAudioController() { }

View File

@@ -10,7 +10,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class ICommonStateGetter : DisposableIpcService
partial class ICommonStateGetter : DisposableIpcService
{
private readonly ServiceCtx _context;

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class IDisplayController : IpcService
partial class IDisplayController : IpcService
{
private readonly KTransferMemory _transferMem;
private bool _lastApplicationCaptureBufferAcquired;

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class IHomeMenuFunctions : IpcService
partial class IHomeMenuFunctions : IpcService
{
private readonly KEvent _channelEvent;
private int _channelEventHandle;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Library
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class ILibraryAppletCreator : IpcService
partial class ILibraryAppletCreator : IpcService
{
public ILibraryAppletCreator() { }

View File

@@ -8,7 +8,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class ISelfController : IpcService
partial class ISelfController : IpcService
{
private readonly ulong _pid;

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class IWindowController : IpcService
partial class IWindowController : IpcService
{
private readonly ulong _pid;

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
{
[Service("appletAE")]
class IAllSystemAppletProxiesService : IpcService
partial class IAllSystemAppletProxiesService : IpcService
{
public IAllSystemAppletProxiesService(ServiceCtx context) { }

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
{
class IStorage : IpcService
partial class IStorage : IpcService
{
public bool IsReadOnly { get; private set; }
public byte[] Data { get; private set; }

View File

@@ -2,7 +2,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
{
class IStorageAccessor : IpcService
partial class IStorageAccessor : IpcService
{
private readonly IStorage _storage;

View File

@@ -22,7 +22,7 @@ using ApplicationId = LibHac.Ncm.ApplicationId;
namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy
{
class IApplicationFunctions : IpcService
partial class IApplicationFunctions : IpcService
{
private long _defaultSaveDataSize = 200000000;
private long _defaultJournalSaveDataSize = 200000000;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationPr
namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
{
class IApplicationProxy : IpcService
partial class IApplicationProxy : IpcService
{
private readonly ulong _pid;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService;
namespace Ryujinx.HLE.HOS.Services.Am
{
[Service("appletOE")]
class IApplicationProxyService : IpcService
partial class IApplicationProxyService : IpcService
{
public IApplicationProxyService(ServiceCtx context) { }

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Apm
{
abstract class IManager : IpcService
abstract partial class IManager : IpcService
{
public IManager(ServiceCtx context) { }

View File

@@ -3,7 +3,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm
// NOTE: This service doesnt exist anymore after firmware 7.0.1. But some outdated homebrew still uses it.
[Service("apm:p")] // 1.0.0-7.0.1
class IManagerPrivileged : IpcService
partial class IManagerPrivileged : IpcService
{
public IManagerPrivileged(ServiceCtx context) { }

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Apm
{
abstract class ISession : IpcService
abstract partial class ISession : IpcService
{
public ISession(ServiceCtx context) { }

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Apm
{
abstract class ISystemManager : IpcService
abstract partial class ISystemManager : IpcService
{
public ISystemManager(ServiceCtx context) { }

View File

@@ -8,7 +8,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Bluetooth
{
[Service("btdrv")]
class IBluetoothDriver : IpcService
partial class IBluetoothDriver : IpcService
{
#pragma warning disable CS0414, IDE0052 // Remove unread private member
private string _unknownLowEnergy;

View File

@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Services.Settings;
namespace Ryujinx.HLE.HOS.Services.Bluetooth
{
[Service("bt")]
class IBluetoothUser : IpcService
partial class IBluetoothUser : IpcService
{
public IBluetoothUser(ServiceCtx context) { }

View File

@@ -5,7 +5,7 @@ using Ryujinx.Horizon.Common;
namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser
{
class IBtmUserCore : IpcService
partial class IBtmUserCore : IpcService
{
public KEvent _bleScanEvent;
public int _bleScanEventHandle;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.BluetoothManager.BtmUser;
namespace Ryujinx.HLE.HOS.Services.BluetoothManager
{
[Service("btm:u")] // 5.0.0+
class IBtmUser : IpcService
partial class IBtmUser : IpcService
{
public IBtmUser(ServiceCtx context) { }

View File

@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Services.Caps.Types;
namespace Ryujinx.HLE.HOS.Services.Caps
{
[Service("caps:u")]
class IAlbumApplicationService : IpcService
partial class IAlbumApplicationService : IpcService
{
public IAlbumApplicationService(ServiceCtx context) { }

View File

@@ -1,7 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Caps
{
[Service("caps:c")]
class IAlbumControlService : IpcService
partial class IAlbumControlService : IpcService
{
public IAlbumControlService(ServiceCtx context) { }

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Caps.Types;
namespace Ryujinx.HLE.HOS.Services.Caps
{
[Service("caps:su")] // 6.0.0+
class IScreenShotApplicationService : IpcService
partial class IScreenShotApplicationService : IpcService
{
public IScreenShotApplicationService(ServiceCtx context) { }

View File

@@ -4,7 +4,7 @@ using Ryujinx.Horizon.Common;
namespace Ryujinx.HLE.HOS.Services.Ectx
{
class IContextRegistrar : DisposableIpcService
partial class IContextRegistrar : DisposableIpcService
{
public IContextRegistrar(ServiceCtx context) { }

View File

@@ -1,7 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Ectx
{
[Service("ectx:aw")] // 11.0.0+
class IWriterForApplication : IpcService
partial class IWriterForApplication : IpcService
{
public IWriterForApplication(ServiceCtx context) { }

View File

@@ -7,7 +7,7 @@ using System.Text;
namespace Ryujinx.HLE.HOS.Services.Fatal
{
[Service("fatal:u")]
class IService : IpcService
partial class IService : IpcService
{
public IService(ServiceCtx context) { }

View File

@@ -5,7 +5,7 @@ using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IDirectory : DisposableIpcService
partial class IDirectory : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IDirectory> _baseDirectory;

View File

@@ -7,7 +7,7 @@ using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IFile : DisposableIpcService
partial class IFile : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IFile> _baseFile;

View File

@@ -7,7 +7,7 @@ using Path = LibHac.FsSrv.Sf.Path;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IFileSystem : DisposableIpcService
partial class IFileSystem : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IFileSystem> _fileSystem;

View File

@@ -10,7 +10,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
class IStorage : DisposableIpcService
partial class IStorage : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IStorage> _baseStorage;

View File

@@ -5,7 +5,7 @@ using GameCardHandle = System.UInt32;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class IDeviceOperator : DisposableIpcService
partial class IDeviceOperator : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IDeviceOperator> _baseOperator;

View File

@@ -25,7 +25,7 @@ using IStorage = LibHac.FsSrv.Sf.IStorage;
namespace Ryujinx.HLE.HOS.Services.Fs
{
[Service("fsp-srv")]
class IFileSystemProxy : DisposableIpcService
partial class IFileSystemProxy : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.IFileSystemProxy> _baseFileSystemProxy;
private ulong _pid;

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class IMultiCommitManager : DisposableIpcService // 6.0.0+
partial class IMultiCommitManager : DisposableIpcService // 6.0.0+
{
private SharedRef<LibHac.FsSrv.Sf.IMultiCommitManager> _baseCommitManager;

View File

@@ -5,7 +5,7 @@ using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Services.Fs
{
class ISaveDataInfoReader : DisposableIpcService
partial class ISaveDataInfoReader : DisposableIpcService
{
private SharedRef<LibHac.FsSrv.Sf.ISaveDataInfoReader> _baseReader;

View File

@@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
{
class IActiveApplicationDeviceList : IpcService
partial class IActiveApplicationDeviceList : IpcService
{
public IActiveApplicationDeviceList() { }

View File

@@ -5,7 +5,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
{
class IAppletResource : IpcService
partial class IAppletResource : IpcService
{
private readonly KSharedMemory _hidSharedMem;
private int _hidSharedMemHandle;

View File

@@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Hid
{
[Service("hid")]
class IHidServer : IpcService
partial class IHidServer : IpcService
{
private readonly KEvent _xpadIdEvent;
private readonly KEvent _palmaOperationCompleteEvent;

View File

@@ -5,7 +5,7 @@ using Ryujinx.HLE.HOS.Services.Hid.Types;
namespace Ryujinx.HLE.HOS.Services.Hid
{
[Service("hid:sys")]
class IHidSystemServer : IpcService
partial class IHidSystemServer : IpcService
{
public IHidSystemServer(ServiceCtx context) { }

View File

@@ -4,7 +4,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Hid
{
[Service("hidbus")]
class IHidbusServer : IpcService
partial class IHidbusServer : IpcService
{
public IHidbusServer(ServiceCtx context) { }

View File

@@ -9,7 +9,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Hid.Irs
{
[Service("irs")]
class IIrSensorServer : IpcService
partial class IIrSensorServer : IpcService
{
private int _irsensorSharedMemoryHandle = 0;

View File

@@ -13,8 +13,6 @@ namespace Ryujinx.HLE.HOS.Services
{
abstract partial class IpcService
{
public IReadOnlyDictionary<int, MethodInfo> CmifCommands { get; }
public IReadOnlyDictionary<int, MethodInfo> TipcCommands { get; }
public ServerBase Server { get; private set; }
@@ -24,30 +22,11 @@ namespace Ryujinx.HLE.HOS.Services
private int _selfId;
private bool _isDomain;
// cache array so we don't recreate it all the time
private object[] _parameters = [null];
public IpcService(ServerBase server = null, bool registerTipc = false)
public IpcService(ServerBase server = null)
{
Stopwatch sw = Stopwatch.StartNew();
CmifCommands = GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
.SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandCmifAttribute>()
.Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
sw.Stop();
Logger.Debug?.Print(
LogClass.Emulation,
$"{CmifCommands.Count} Cmif commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).",
GetType().AsPrettyString()
);
if (registerTipc)
if (true)
{
sw.Start();
var sw = Stopwatch.StartNew();
TipcCommands = GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
@@ -88,6 +67,29 @@ namespace Ryujinx.HLE.HOS.Services
_isDomain = false;
}
protected virtual ResultCode InvokeCmifMethod(int id, ServiceCtx context)
{
if (!context.Device.Configuration.IgnoreMissingServices)
{
string dbgMessage = $"{this.GetType().FullName}: {id}";
throw new ServiceNotImplementedException(this, context, dbgMessage);
}
string serviceName = (this is not DummyService dummyService)
? this.GetType().FullName
: dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {id} ignored");
return ResultCode.Success;
}
protected void LogInvoke(string name)
=> Logger.Trace?.Print(LogClass.KernelIpc, $"{this.GetType().Name}: {name}");
public virtual int CmifCommandIdByMethodName(string name) => -1;
public void CallCmifMethod(ServiceCtx context)
{
IpcService service = this;
@@ -138,52 +140,26 @@ namespace Ryujinx.HLE.HOS.Services
#pragma warning restore IDE0059
int commandId = (int)context.RequestData.ReadInt64();
bool serviceExists = service.CmifCommands.TryGetValue(commandId, out MethodInfo processRequest);
context.ResponseData.BaseStream.Seek(_isDomain ? 0x20 : 0x10, SeekOrigin.Begin);
if (context.Device.Configuration.IgnoreMissingServices || serviceExists)
ResultCode result = service.InvokeCmifMethod(commandId, context);
if (_isDomain)
{
ResultCode result = ResultCode.Success;
context.ResponseData.BaseStream.Seek(_isDomain ? 0x20 : 0x10, SeekOrigin.Begin);
if (serviceExists)
foreach (int id in context.Response.ObjectIds)
{
Logger.Trace?.Print(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Name}");
_parameters[0] = context;
result = (ResultCode)processRequest.Invoke(service, _parameters);
}
else
{
string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
context.ResponseData.Write(id);
}
if (_isDomain)
{
foreach (int id in context.Response.ObjectIds)
{
context.ResponseData.Write(id);
}
context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
context.ResponseData.Write(context.Response.ObjectIds.Count);
}
context.ResponseData.BaseStream.Seek(_isDomain ? 0x10 : 0, SeekOrigin.Begin);
context.ResponseData.Write(IpcMagic.Sfco);
context.ResponseData.Write((long)result);
context.ResponseData.Write(context.Response.ObjectIds.Count);
}
else
{
string dbgMessage = $"{service.GetType().FullName}: {commandId}";
throw new ServiceNotImplementedException(service, context, dbgMessage);
}
context.ResponseData.BaseStream.Seek(_isDomain ? 0x10 : 0, SeekOrigin.Begin);
context.ResponseData.Write(IpcMagic.Sfco);
context.ResponseData.Write((long)result);
}
public void CallTipcMethod(ServiceCtx context)
@@ -202,9 +178,7 @@ namespace Ryujinx.HLE.HOS.Services
{
Logger.Debug?.Print(LogClass.KernelIpc, $"{GetType().Name}: {processRequest.Name}");
_parameters[0] = context;
result = (ResultCode)processRequest.Invoke(this, _parameters);
result = (ResultCode)processRequest.Invoke(this, [context]);
}
else
{

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator;
namespace Ryujinx.HLE.HOS.Services.Ldn
{
[Service("ldn:u")]
class IUserServiceCreator : IpcService
partial class IUserServiceCreator : IpcService
{
public IUserServiceCreator(ServiceCtx context) : base(context.Device.System.LdnServer) { }

View File

@@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
{
[Service("lp2p:app")] // 9.0.0+
[Service("lp2p:sys")] // 9.0.0+
class IServiceCreator : IpcService
partial class IServiceCreator : IpcService
{
public IServiceCreator(ServiceCtx context) { }

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
{
class ISfService : IpcService
partial class ISfService : IpcService
{
public ISfService(ServiceCtx context) { }

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
{
class ISfServiceMonitor : IpcService
partial class ISfServiceMonitor : IpcService
{
private readonly KEvent _stateChangeEvent;
private readonly KEvent _jointEvent;

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
class IClientProcessMonitor : DisposableIpcService
partial class IClientProcessMonitor : DisposableIpcService
{
public IClientProcessMonitor(ServiceCtx context) { }

View File

@@ -21,7 +21,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
class IUserLocalCommunicationService : IpcService, IDisposable
partial class IUserLocalCommunicationService : IpcService, IDisposable
{
public INetworkClient NetworkClient { get; private set; }

View File

@@ -3,7 +3,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Mii
{
[Service("miiimg")] // 5.0.0+
class IImageDatabaseService : IpcService
partial class IImageDatabaseService : IpcService
{
private uint _imageCount;
private bool _isDirty;

View File

@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
{
[Service("mii:e", true)]
[Service("mii:u", false)]
class IStaticService : IpcService
partial class IStaticService : IpcService
{
private readonly DatabaseImpl _databaseImpl;

View File

@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{
abstract class IDatabaseService : IpcService
abstract partial class IDatabaseService : IpcService
{
[CommandCmif(0)]
// IsUpdated(SourceFlag flag) -> bool

View File

@@ -6,7 +6,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
namespace Ryujinx.HLE.HOS.Services.Mnpp
{
[Service("mnpp:app")] // 13.0.0+
class IServiceForApplication : IpcService
partial class IServiceForApplication : IpcService
{
public IServiceForApplication(ServiceCtx context) { }

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager;
namespace Ryujinx.HLE.HOS.Services.Ncm.Lr
{
[Service("lr")]
class ILocationResolverManager : IpcService
partial class ILocationResolverManager : IpcService
{
public ILocationResolverManager(ServiceCtx context) { }

View File

@@ -6,7 +6,7 @@ using static Ryujinx.HLE.Utilities.StringUtils;
namespace Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager
{
class ILocationResolver : IpcService
partial class ILocationResolver : IpcService
{
private readonly StorageId _storageId;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.NfcManager;
namespace Ryujinx.HLE.HOS.Services.Nfc
{
[Service("nfc:sys")]
class ISystemManager : IpcService
partial class ISystemManager : IpcService
{
public ISystemManager(ServiceCtx context) { }

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.NfcManager;
namespace Ryujinx.HLE.HOS.Services.Nfc
{
[Service("nfc:user")]
class IUserManager : IpcService
partial class IUserManager : IpcService
{
public IUserManager(ServiceCtx context) { }

View File

@@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
{
class INfc : IpcService
partial class INfc : IpcService
{
private readonly NfcPermissionLevel _permissionLevel;
private State _state;

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
[Service("nfp:dbg")]
class IAmManager : IpcService
partial class IAmManager : IpcService
{
public IAmManager(ServiceCtx context) { }

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
[Service("nfp:sys")]
class ISystemManager : IpcService
partial class ISystemManager : IpcService
{
public ISystemManager(ServiceCtx context) { }

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
[Service("nfp:user")]
class IUserManager : IpcService
partial class IUserManager : IpcService
{
public IUserManager(ServiceCtx context) { }

View File

@@ -16,7 +16,7 @@ using System.Threading.Tasks;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{
class INfp : IpcService
partial class INfp : IpcService
{
#pragma warning disable IDE0052 // Remove unread private member
private ulong _appletResourceUserId;

View File

@@ -1,7 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Ngct
{
[Service("ngct:u")] // 9.0.0+
class IService : IpcService
partial class IService : IpcService
{
public IService(ServiceCtx context) { }

View File

@@ -1,7 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Ngct
{
[Service("ngct:s")] // 9.0.0+
class IServiceWithManagementApi : IpcService
partial class IServiceWithManagementApi : IpcService
{
public IServiceWithManagementApi(ServiceCtx context) { }

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm
[Service("nifm:a")] // Max sessions: 2
[Service("nifm:s")] // Max sessions: 16
[Service("nifm:u")] // Max sessions: 5
class IStaticService : IpcService
partial class IStaticService : IpcService
{
public IStaticService(ServiceCtx context) { }

View File

@@ -10,7 +10,7 @@ using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
class IGeneralService : DisposableIpcService
partial class IGeneralService : DisposableIpcService
{
private readonly GeneralServiceDetail _generalServiceDetail;

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
class IRequest : IpcService
partial class IRequest : IpcService
{
private enum RequestState
{

View File

@@ -3,7 +3,7 @@ using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceA
namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface
{
class IShopServiceAccessServer : IpcService
partial class IShopServiceAccessServer : IpcService
{
public IShopServiceAccessServer() { }

View File

@@ -6,7 +6,7 @@ using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface;
namespace Ryujinx.HLE.HOS.Services.Nim
{
[Service("nim:eca")] // 5.0.0+
class IShopServiceAccessServerInterface : IpcService
partial class IShopServiceAccessServerInterface : IpcService
{
public IShopServiceAccessServerInterface(ServiceCtx context) { }

View File

@@ -7,7 +7,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer
{
class IShopServiceAccessor : IpcService
partial class IShopServiceAccessor : IpcService
{
private readonly KEvent _event;

View File

@@ -4,7 +4,7 @@ using Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService;
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc
{
[Service("ntc")]
class IStaticService : IpcService
partial class IStaticService : IpcService
{
public IStaticService(ServiceCtx context) { }

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService
{
class IEnsureNetworkClockAvailabilityService : IpcService
partial class IEnsureNetworkClockAvailabilityService : IpcService
{
private readonly KEvent _finishNotificationEvent;
private ResultCode _taskResultCode;

View File

@@ -8,7 +8,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
{
[Service("aoc:u")]
class IAddOnContentManager : IpcService
partial class IAddOnContentManager : IpcService
{
private readonly KEvent _addOnContentListChangedEvent;
private int _addOnContentListChangedEventHandle;

View File

@@ -6,7 +6,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
{
class IPurchaseEventManager : IpcService
partial class IPurchaseEventManager : IpcService
{
private readonly KEvent _purchasedEvent;

View File

@@ -4,7 +4,7 @@ using Ryujinx.Common.Utilities;
namespace Ryujinx.HLE.HOS.Services.Ns
{
[Service("ns:am")]
class IApplicationManagerInterface : IpcService
partial class IApplicationManagerInterface : IpcService
{
public IApplicationManagerInterface(ServiceCtx context) { }

View File

@@ -3,7 +3,7 @@ using LibHac.Ns;
namespace Ryujinx.HLE.HOS.Services.Ns
{
class IReadOnlyApplicationControlDataInterface : IpcService
partial class IReadOnlyApplicationControlDataInterface : IpcService
{
public IReadOnlyApplicationControlDataInterface(ServiceCtx context) { }

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns
[Service("ns:rid")]
[Service("ns:rt")]
[Service("ns:web")]
class IServiceGetterInterface : IpcService
partial class IServiceGetterInterface : IpcService
{
public IServiceGetterInterface(ServiceCtx context) { }

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
[Service("nvdrv:a")]
[Service("nvdrv:s")]
[Service("nvdrv:t")]
class INvDrvServices : IpcService
partial class INvDrvServices : IpcService
{
private static readonly List<string> _deviceFileDebugRegistry =
[

View File

@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Olsc
{
[Service("olsc:u")] // 10.0.0+
class IOlscServiceForApplication : IpcService
partial class IOlscServiceForApplication : IpcService
{
private bool _initialized;
private Dictionary<UserId, bool> _saveDataBackupSettingDatabase;

View File

@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl
[Service("pctl:a", 0x83BE)]
[Service("pctl:r", 0x8040)]
[Service("pctl:s", 0x838E)]
class IParentalControlServiceFactory : IpcService
partial class IParentalControlServiceFactory : IpcService
{
private readonly int _permissionFlag;

View File

@@ -5,7 +5,7 @@ using static LibHac.Ns.ApplicationControlProperty;
namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
{
class IParentalControlService : IpcService
partial class IParentalControlService : IpcService
{
private readonly ulong _pid;
private readonly int _permissionFlag;

View File

@@ -3,7 +3,7 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Pcv.Bpc
{
[Service("bpc:r")] // 1.0.0 - 8.1.0
class IRtcManager : IpcService
partial class IRtcManager : IpcService
{
public IRtcManager(ServiceCtx context) { }

View File

@@ -4,7 +4,7 @@ using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst.ClkrstManager
{
class IClkrstSession : IpcService
partial class IClkrstSession : IpcService
{
private readonly DeviceCode _deviceCode;
#pragma warning disable IDE0052 // Remove unread private member

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Clkrst
{
[Service("clkrst")] // 8.0.0+
[Service("clkrst:i")] // 8.0.0+
class IClkrstManager : IpcService
partial class IClkrstManager : IpcService
{
private int _moduleStateTableEventHandle = 0;

Some files were not shown because too many files have changed in this diff Show More