diff --git a/Ryujinx.Core/Logging/LogClass.cs b/Ryujinx.Core/Logging/LogClass.cs index 0d9801ff33..d26855d006 100644 --- a/Ryujinx.Core/Logging/LogClass.cs +++ b/Ryujinx.Core/Logging/LogClass.cs @@ -23,6 +23,7 @@ namespace Ryujinx.Core.Logging ServiceHid, ServiceLm, ServiceMm, + ServiceNfp, ServiceNifm, ServiceNs, ServiceNv, @@ -32,6 +33,7 @@ namespace Ryujinx.Core.Logging ServiceSet, ServiceSfdnsres, ServiceSm, + ServiceSsl, ServiceSss, ServiceTime, ServiceVi diff --git a/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs b/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs index 7b09b5f0cd..84fb62bdf7 100644 --- a/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs @@ -16,6 +16,7 @@ namespace Ryujinx.Core.OsHle.Services.Acc { { 0, GetUserCount }, { 3, ListOpenUsers }, + { 4, GetLastOpenedUser }, { 5, GetProfile }, { 100, InitializeApplicationInfo }, { 101, GetBaasAccountManagerForApplication } @@ -38,6 +39,16 @@ namespace Ryujinx.Core.OsHle.Services.Acc return 0; } + public long GetLastOpenedUser(ServiceCtx Context) + { + Context.ResponseData.Write(0L); + Context.ResponseData.Write(0L); + + Context.Ns.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + + return 0; + } + public long GetProfile(ServiceCtx Context) { MakeObject(Context, new IProfile()); diff --git a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs index 73ec7fac31..1ba0ff3219 100644 --- a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs @@ -15,13 +15,15 @@ namespace Ryujinx.Core.OsHle.Services.Am { m_Commands = new Dictionary() { - { 1, PopLaunchParameter }, - { 20, EnsureSaveData }, - { 21, GetDesiredLanguage }, - { 22, SetTerminateResult }, - { 23, GetDisplayVersion }, - { 40, NotifyRunning }, - { 50, GetPseudoDeviceId } + { 1, PopLaunchParameter }, + { 20, EnsureSaveData }, + { 21, GetDesiredLanguage }, + { 22, SetTerminateResult }, + { 23, GetDisplayVersion }, + { 40, NotifyRunning }, + { 50, GetPseudoDeviceId }, + { 66, InitializeGamePlayRecording }, + { 67, SetGamePlayRecordingState } }; } @@ -99,6 +101,22 @@ namespace Ryujinx.Core.OsHle.Services.Am return 0; } + public long InitializeGamePlayRecording(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long SetGamePlayRecordingState(ServiceCtx Context) + { + int State = Context.RequestData.ReadInt32(); + + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + private byte[] MakeLaunchParams() { //Size needs to be at least 0x88 bytes otherwise application errors. diff --git a/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletAccessor.cs b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletAccessor.cs new file mode 100644 index 0000000000..d386e917f6 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletAccessor.cs @@ -0,0 +1,92 @@ +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; +using System.IO; + +namespace Ryujinx.Core.OsHle.Services.Am +{ + class ILibraryAppletAccessor : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private KEvent StateChangedEvent; + + public ILibraryAppletAccessor() + { + m_Commands = new Dictionary() + { + { 0, GetAppletStateChangedEvent }, + { 10, Start }, + { 30, GetResult }, + { 100, PushInData }, + { 101, PopOutData } + }; + + StateChangedEvent = new KEvent(); + } + + private const uint LaunchParamsMagic = 0xc79497ca; + + public long GetAppletStateChangedEvent(ServiceCtx Context) + { + StateChangedEvent.WaitEvent.Set(); + + int Handle = Context.Process.HandleTable.OpenHandle(StateChangedEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long Start(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long GetResult(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long PushInData(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long PopOutData(ServiceCtx Context) + { + MakeObject(Context, new IStorage(MakeLaunchParams())); + + return 0; + } + + private byte[] MakeLaunchParams() + { + //Size needs to be at least 0x88 bytes otherwise application errors. + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + MS.SetLength(0x88); + + Writer.Write(LaunchParamsMagic); + Writer.Write(1); //IsAccountSelected? Only lower 8 bits actually used. + Writer.Write(1L); //User Id Low (note: User Id needs to be != 0) + Writer.Write(0L); //User Id High + + return MS.ToArray(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs index 7b3e12cc6a..06d2888806 100644 --- a/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs +++ b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs @@ -1,5 +1,6 @@ using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; +using System.IO; namespace Ryujinx.Core.OsHle.Services.Am { @@ -13,8 +14,43 @@ namespace Ryujinx.Core.OsHle.Services.Am { m_Commands = new Dictionary() { - //... + { 0, CreateLibraryApplet }, + { 10, CreateStorage } }; } + + private const uint LaunchParamsMagic = 0xc79497ca; + + public long CreateLibraryApplet(ServiceCtx Context) + { + MakeObject(Context, new ILibraryAppletAccessor()); + + return 0; + } + + public long CreateStorage(ServiceCtx Context) + { + MakeObject(Context, new IStorage(MakeLaunchParams())); + + return 0; + } + + private byte[] MakeLaunchParams() + { + //Size needs to be at least 0x88 bytes otherwise application errors. + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + MS.SetLength(0x88); + + Writer.Write(LaunchParamsMagic); + Writer.Write(1); //IsAccountSelected? Only lower 8 bits actually used. + Writer.Write(1L); //User Id Low (note: User Id needs to be != 0) + Writer.Write(0L); //User Id High + + return MS.ToArray(); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs index 41027df076..3fd5c7fc4d 100644 --- a/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs +++ b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs @@ -1,4 +1,5 @@ using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -10,11 +11,14 @@ namespace Ryujinx.Core.OsHle.Services.Am public override IReadOnlyDictionary Commands => m_Commands; + private KEvent LaunchableEvent; + public ISelfController() { m_Commands = new Dictionary() { { 1, LockExit }, + { 9, GetLibraryAppletLaunchableEvent }, { 10, SetScreenShotPermission }, { 11, SetOperationModeChangedNotification }, { 12, SetPerformanceModeChangedNotification }, @@ -23,6 +27,8 @@ namespace Ryujinx.Core.OsHle.Services.Am { 16, SetOutOfFocusSuspendingEnabled }, { 50, SetHandlesRequestToDisplay } }; + + LaunchableEvent = new KEvent(); } public long LockExit(ServiceCtx Context) @@ -30,6 +36,19 @@ namespace Ryujinx.Core.OsHle.Services.Am return 0; } + public long GetLibraryAppletLaunchableEvent(ServiceCtx Context) + { + LaunchableEvent.WaitEvent.Set(); + + int Handle = Context.Process.HandleTable.OpenHandle(LaunchableEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + public long SetScreenShotPermission(ServiceCtx Context) { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; diff --git a/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs index 12336df2ec..5efc49931f 100644 --- a/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; @@ -18,6 +19,7 @@ namespace Ryujinx.Core.OsHle.Services.Am m_Commands = new Dictionary() { { 0, GetSize }, + { 10, Write }, { 11, Read } }; @@ -31,6 +33,13 @@ namespace Ryujinx.Core.OsHle.Services.Am return 0; } + public long Write(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + public long Read(ServiceCtx Context) { long ReadPosition = Context.RequestData.ReadInt64(); diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs index 1eb61d29f5..8ec517e06f 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs @@ -19,11 +19,17 @@ namespace Ryujinx.Core.OsHle.Services.Aud { m_Commands = new Dictionary() { - { 0, ListAudioDeviceName }, - { 1, SetAudioDeviceOutputVolume }, - { 3, GetActiveAudioDeviceName }, - { 4, QueryAudioDeviceSystemEvent }, - { 5, GetActiveChannelCount } + { 0, ListAudioDeviceName }, + { 1, SetAudioDeviceOutputVolume }, + { 3, GetActiveAudioDeviceName }, + { 4, QueryAudioDeviceSystemEvent }, + { 5, GetActiveChannelCount }, + { 6, ListAudioDeviceNameAuto }, + { 7, SetAudioDeviceOutputVolumeAuto }, + { 8, GetAudioDeviceOutputVolumeAuto }, + { 10, GetActiveAudioDeviceNameAuto }, + { 11, QueryAudioDeviceInputEvent }, + { 12, QueryAudioDeviceOutputEvent } }; SystemEvent = new KEvent(); @@ -118,5 +124,103 @@ namespace Ryujinx.Core.OsHle.Services.Aud return 0; } + + public long ListAudioDeviceNameAuto(ServiceCtx Context) + { + string[] DeviceNames = SystemStateMgr.AudioOutputs; + + Context.ResponseData.Write(DeviceNames.Length); + + long Position = Context.Request.RecvListBuff[0].Position; + long Size = Context.Request.RecvListBuff[0].Size; + + long BasePosition = Position; + + foreach (string Name in DeviceNames) + { + byte[] Buffer = Encoding.UTF8.GetBytes(Name + '\0'); + + if ((Position - BasePosition) + Buffer.Length > Size) + { + Context.Ns.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + + break; + } + + AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer); + + Position += Buffer.Length; + } + + return 0; + } + + public long SetAudioDeviceOutputVolumeAuto(ServiceCtx Context) + { + float Volume = Context.RequestData.ReadSingle(); + + long Position = Context.Request.SendBuff[0].Position; + long Size = Context.Request.SendBuff[0].Size; + + byte[] DeviceNameBuffer = AMemoryHelper.ReadBytes(Context.Memory, Position, Size); + + string DeviceName = Encoding.UTF8.GetString(DeviceNameBuffer); + + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } + + public long GetAudioDeviceOutputVolumeAuto(ServiceCtx Context) + { + Context.ResponseData.Write(100); + + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } + + public long GetActiveAudioDeviceNameAuto(ServiceCtx Context) + { + string Name = Context.Ns.Os.SystemState.ActiveAudioOutput; + + long Position = Context.Request.RecvListBuff[0].Position; + long Size = Context.Request.RecvListBuff[0].Size; + + byte[] DeviceNameBuffer = Encoding.UTF8.GetBytes(Name + '\0'); + + if ((ulong)DeviceNameBuffer.Length <= (ulong)Size) + { + AMemoryHelper.WriteBytes(Context.Memory, Position, DeviceNameBuffer); + } + else + { + Context.Ns.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + } + + return 0; + } + + public long QueryAudioDeviceInputEvent(ServiceCtx Context) + { + int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } + + public long QueryAudioDeviceOutputEvent(ServiceCtx Context) + { + int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } } } diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs index 29c5c68f34..eb16ff43e0 100644 --- a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs @@ -1,3 +1,4 @@ +using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -15,6 +16,7 @@ namespace Ryujinx.Core.OsHle.Services.FspSrv { { 1, SetCurrentProcess }, { 18, OpenSdCardFileSystem }, + { 22, CreateSaveData }, { 51, OpenSaveDataFileSystem }, { 200, OpenDataStorageByCurrentProcess }, { 203, OpenPatchDataStorageByCurrentProcess }, @@ -34,6 +36,13 @@ namespace Ryujinx.Core.OsHle.Services.FspSrv return 0; } + public long CreateSaveData(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceFs, "Stubbed."); + + return 0; + } + public long OpenSaveDataFileSystem(ServiceCtx Context) { MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath())); diff --git a/Ryujinx.Core/OsHle/Services/Nfp/IUser.cs b/Ryujinx.Core/OsHle/Services/Nfp/IUser.cs new file mode 100644 index 0000000000..199d4e151b --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nfp/IUser.cs @@ -0,0 +1,28 @@ +using Ryujinx.Core.Logging; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.Services.Nfp +{ + class IUser : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IUser() + { + m_Commands = new Dictionary() + { + { 0, Initialize } + }; + } + + public long Initialize(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nfp/IUserManager.cs b/Ryujinx.Core/OsHle/Services/Nfp/IUserManager.cs new file mode 100644 index 0000000000..662987d74a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nfp/IUserManager.cs @@ -0,0 +1,27 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.Services.Nfp +{ + class IUserManager : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IUserManager() + { + m_Commands = new Dictionary() + { + { 0, GetUserInterface } + }; + } + + public long GetUserInterface(ServiceCtx Context) + { + MakeObject(Context, new IUser()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs b/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs index 2129ce43a9..b2fe0f9762 100644 --- a/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs +++ b/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs @@ -13,7 +13,8 @@ namespace Ryujinx.Core.OsHle.Services.Nifm { m_Commands = new Dictionary() { - { 4, CreateGeneralServiceOld } + { 4, CreateGeneralServiceOld }, + { 5, CreateGeneralService } }; } @@ -23,5 +24,12 @@ namespace Ryujinx.Core.OsHle.Services.Nifm return 0; } + + public long CreateGeneralService(ServiceCtx Context) + { + MakeObject(Context, new IGeneralService()); + + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs index 07dde173e2..05301d1eaf 100644 --- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs +++ b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs @@ -8,6 +8,7 @@ using Ryujinx.Core.OsHle.Services.Friend; using Ryujinx.Core.OsHle.Services.FspSrv; using Ryujinx.Core.OsHle.Services.Hid; using Ryujinx.Core.OsHle.Services.Lm; +using Ryujinx.Core.OsHle.Services.Nfp; using Ryujinx.Core.OsHle.Services.Ns; using Ryujinx.Core.OsHle.Services.Nv; using Ryujinx.Core.OsHle.Services.Pctl; @@ -79,6 +80,9 @@ namespace Ryujinx.Core.OsHle.Services case "lm": return new ILogService(); + case "nfp:user": + return new IUserManager(); + case "nifm:u": return new Nifm.IStaticService(); diff --git a/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs b/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs index 825e336391..3dab451549 100644 --- a/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs +++ b/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs @@ -1,3 +1,4 @@ +using Ryujinx.Core.Logging; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -13,8 +14,17 @@ namespace Ryujinx.Core.OsHle.Services.Ssl { m_Commands = new Dictionary() { - //... + { 5, SetInterfaceVersion } }; } + + public long SetInterfaceVersion(ServiceCtx Context) + { + int Version = Context.RequestData.ReadInt32(); + + Context.Ns.Log.PrintStub(LogClass.ServiceSsl, "Stubbed."); + + return 0; + } } } \ No newline at end of file