diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index c2d2f55e..30e3409d 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -37,6 +37,7 @@ namespace Ryujinx.Common.Logging ServiceLdn, ServiceLdr, ServiceLm, + ServiceMii, ServiceMm, ServiceNfc, ServiceNfp, diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index 3034d107..a4ed7c37 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -45,10 +45,11 @@ namespace Ryujinx.HLE.HOS public class Horizon : IDisposable { - internal const int HidSize = 0x40000; - internal const int FontSize = 0x1100000; - internal const int IirsSize = 0x8000; - internal const int TimeSize = 0x1000; + internal const int HidSize = 0x40000; + internal const int FontSize = 0x1100000; + internal const int IirsSize = 0x8000; + internal const int TimeSize = 0x1000; + internal const int AppletCaptureBufferSize = 0x384000; internal KernelContext KernelContext { get; } @@ -82,6 +83,9 @@ namespace Ryujinx.HLE.HOS internal KSharedMemory HidSharedMem { get; private set; } internal KSharedMemory FontSharedMem { get; private set; } internal KSharedMemory IirsSharedMem { get; private set; } + + internal KTransferMemory AppletCaptureBufferTransfer { get; private set; } + internal SharedFontManager Font { get; private set; } internal AccountManager AccountManager { get; private set; } @@ -129,25 +133,29 @@ namespace Ryujinx.HLE.HOS // region used that is used is Application, so we can use the other ones for anything. KMemoryRegionManager region = KernelContext.MemoryManager.MemoryRegions[(int)MemoryRegion.NvServices]; - ulong hidPa = region.Address; - ulong fontPa = region.Address + HidSize; - ulong iirsPa = region.Address + HidSize + FontSize; - ulong timePa = region.Address + HidSize + FontSize + IirsSize; + ulong hidPa = region.Address; + ulong fontPa = region.Address + HidSize; + ulong iirsPa = region.Address + HidSize + FontSize; + ulong timePa = region.Address + HidSize + FontSize + IirsSize; + ulong appletCaptureBufferPa = region.Address + HidSize + FontSize + IirsSize + TimeSize; - KPageList hidPageList = new KPageList(); - KPageList fontPageList = new KPageList(); - KPageList iirsPageList = new KPageList(); - KPageList timePageList = new KPageList(); + KPageList hidPageList = new KPageList(); + KPageList fontPageList = new KPageList(); + KPageList iirsPageList = new KPageList(); + KPageList timePageList = new KPageList(); + KPageList appletCaptureBufferPageList = new KPageList(); - hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize); + hidPageList.AddRange(hidPa, HidSize / KPageTableBase.PageSize); fontPageList.AddRange(fontPa, FontSize / KPageTableBase.PageSize); iirsPageList.AddRange(iirsPa, IirsSize / KPageTableBase.PageSize); timePageList.AddRange(timePa, TimeSize / KPageTableBase.PageSize); + appletCaptureBufferPageList.AddRange(appletCaptureBufferPa, AppletCaptureBufferSize / KPageTableBase.PageSize); var hidStorage = new SharedMemoryStorage(KernelContext, hidPageList); var fontStorage = new SharedMemoryStorage(KernelContext, fontPageList); var iirsStorage = new SharedMemoryStorage(KernelContext, iirsPageList); var timeStorage = new SharedMemoryStorage(KernelContext, timePageList); + var appletCaptureBufferStorage = new SharedMemoryStorage(KernelContext, appletCaptureBufferPageList); HidStorage = hidStorage; @@ -159,6 +167,8 @@ namespace Ryujinx.HLE.HOS TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, timeStorage, TimeSize); + AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage); + AppletState = new AppletStateMgr(this); AppletState.SetFocus(true); diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs new file mode 100644 index 00000000..b85a39e5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs @@ -0,0 +1,105 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy; +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService +{ + class ILibraryAppletProxy : IpcService + { + private readonly long _pid; + + public ILibraryAppletProxy(long pid) + { + _pid = pid; + } + + [CommandHipc(0)] + // GetCommonStateGetter() -> object + public ResultCode GetCommonStateGetter(ServiceCtx context) + { + MakeObject(context, new ICommonStateGetter(context)); + + return ResultCode.Success; + } + + [CommandHipc(1)] + // GetSelfController() -> object + public ResultCode GetSelfController(ServiceCtx context) + { + MakeObject(context, new ISelfController(context, _pid)); + + return ResultCode.Success; + } + + [CommandHipc(2)] + // GetWindowController() -> object + public ResultCode GetWindowController(ServiceCtx context) + { + MakeObject(context, new IWindowController(_pid)); + + return ResultCode.Success; + } + + [CommandHipc(3)] + // GetAudioController() -> object + public ResultCode GetAudioController(ServiceCtx context) + { + MakeObject(context, new IAudioController()); + + return ResultCode.Success; + } + + [CommandHipc(4)] + // GetDisplayController() -> object + public ResultCode GetDisplayController(ServiceCtx context) + { + MakeObject(context, new IDisplayController(context)); + + return ResultCode.Success; + } + + [CommandHipc(10)] + // GetProcessWindingController() -> object + public ResultCode GetProcessWindingController(ServiceCtx context) + { + MakeObject(context, new IProcessWindingController()); + + return ResultCode.Success; + } + + [CommandHipc(11)] + // GetLibraryAppletCreator() -> object + public ResultCode GetLibraryAppletCreator(ServiceCtx context) + { + MakeObject(context, new ILibraryAppletCreator()); + + return ResultCode.Success; + } + + [CommandHipc(20)] + // OpenLibraryAppletSelfAccessor() -> object + public ResultCode OpenLibraryAppletSelfAccessor(ServiceCtx context) + { + MakeObject(context, new ILibraryAppletSelfAccessor(context)); + + return ResultCode.Success; + } + + [CommandHipc(21)] + // GetAppletCommonFunctions() -> object + public ResultCode GetAppletCommonFunctions(ServiceCtx context) + { + MakeObject(context, new IAppletCommonFunctions()); + + return ResultCode.Success; + } + + [CommandHipc(1000)] + // GetDebugFunctions() -> object + public ResultCode GetDebugFunctions(ServiceCtx context) + { + MakeObject(context, new IDebugFunctions()); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index 7017488d..4c94f9b6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetSelfController() -> object public ResultCode GetSelfController(ServiceCtx context) { - MakeObject(context, new ISelfController(context.Device.System, _pid)); + MakeObject(context, new ISelfController(context, _pid)); return ResultCode.Success; } @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetDisplayController() -> object public ResultCode GetDisplayController(ServiceCtx context) { - MakeObject(context, new IDisplayController()); + MakeObject(context, new IDisplayController(context)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs new file mode 100644 index 00000000..69967c56 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/AppletStandalone.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy +{ + class AppletStandalone + { + public AppletId AppletId; + public LibraryAppletMode LibraryAppletMode; + public Queue InputData; + + public AppletStandalone() + { + InputData = new Queue(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs new file mode 100644 index 00000000..00081e1b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -0,0 +1,78 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy +{ + class ILibraryAppletSelfAccessor : IpcService + { + private AppletStandalone _appletStandalone = new AppletStandalone(); + + public ILibraryAppletSelfAccessor(ServiceCtx context) + { + if (context.Device.Application.TitleId == 0x0100000000001009) + { + // Create MiiEdit data. + _appletStandalone = new AppletStandalone() + { + AppletId = AppletId.MiiEdit, + LibraryAppletMode = LibraryAppletMode.AllForeground + }; + + byte[] miiEditInputData = new byte[0x100]; + miiEditInputData[0] = 0x03; // Hardcoded unknown value. + + _appletStandalone.InputData.Enqueue(miiEditInputData); + } + else + { + throw new NotImplementedException($"{context.Device.Application.TitleId} applet is not implemented."); + } + } + + [CommandHipc(0)] + // PopInData() -> object + public ResultCode PopInData(ServiceCtx context) + { + byte[] appletData = _appletStandalone.InputData.Dequeue(); + + if (appletData.Length == 0) + { + return ResultCode.NotAvailable; + } + + MakeObject(context, new IStorage(appletData)); + + return ResultCode.Success; + } + + [CommandHipc(11)] + // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo + public ResultCode GetLibraryAppletInfo(ServiceCtx context) + { + LibraryAppletInfo libraryAppletInfo = new LibraryAppletInfo() + { + AppletId = _appletStandalone.AppletId, + LibraryAppletMode = _appletStandalone.LibraryAppletMode + }; + + context.ResponseData.WriteStruct(libraryAppletInfo); + + return ResultCode.Success; + } + + [CommandHipc(14)] + // GetCallerAppletIdentityInfo() -> nn::am::service::AppletIdentityInfo + public ResultCode GetCallerAppletIdentityInfo(ServiceCtx context) + { + AppletIdentifyInfo appletIdentifyInfo = new AppletIdentifyInfo() + { + AppletId = AppletId.QLaunch, + TitleId = 0x0100000000001000 + }; + + context.ResponseData.WriteStruct(appletIdentifyInfo); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs new file mode 100644 index 00000000..e6c16734 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/IProcessWindingController.cs @@ -0,0 +1,24 @@ +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy +{ + class IProcessWindingController : IpcService + { + public IProcessWindingController() { } + + [CommandHipc(0)] + // GetLaunchReason() -> nn::am::service::AppletProcessLaunchReason + public ResultCode GetLaunchReason(ServiceCtx context) + { + // NOTE: Flag is set by using an internal field. + AppletProcessLaunchReason appletProcessLaunchReason = new AppletProcessLaunchReason() + { + Flag = 0 + }; + + context.ResponseData.WriteStruct(appletProcessLaunchReason); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs new file mode 100644 index 00000000..c42202b8 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IAppletCommonFunctions.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy +{ + class IAppletCommonFunctions : IpcService + { + public IAppletCommonFunctions() { } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 9e47f05b..4c862dd6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -2,6 +2,8 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Settings.Types; +using Ryujinx.HLE.HOS.SystemState; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -241,6 +243,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return (ResultCode)_apmSystemManagerServer.GetCurrentPerformanceConfiguration(context); } + [CommandHipc(300)] // 9.0.0+ + // GetSettingsPlatformRegion() -> u8 + public ResultCode GetSettingsPlatformRegion(ServiceCtx context) + { + PlatformRegion platformRegion = context.Device.System.State.DesiredRegionCode == (uint)RegionCode.China ? PlatformRegion.China : PlatformRegion.Global; + + // FIXME: Call set:sys GetPlatformRegion + context.ResponseData.Write((byte)platformRegion); + + return ResultCode.Success; + } + [CommandHipc(900)] // 11.0.0+ // SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled() public ResultCode SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs index 2b04dbb5..d7816de9 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs @@ -1,7 +1,106 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Memory; +using System; + namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy { class IDisplayController : IpcService { - public IDisplayController() { } + private KTransferMemory _transferMem; + private bool _lastApplicationCaptureBufferAcquired; + private bool _callerAppletCaptureBufferAcquired; + + public IDisplayController(ServiceCtx context) + { + _transferMem = context.Device.System.AppletCaptureBufferTransfer; + } + + [CommandHipc(8)] // 2.0.0+ + // TakeScreenShotOfOwnLayer(b8, s32) + public ResultCode TakeScreenShotOfOwnLayer(ServiceCtx context) + { + bool unknown1 = context.RequestData.ReadBoolean(); + int unknown2 = context.RequestData.ReadInt32(); + + Logger.Stub?.PrintStub(LogClass.ServiceAm, new { unknown1, unknown2 }); + + return ResultCode.Success; + } + + [CommandHipc(11)] + // ReleaseLastApplicationCaptureBuffer() + public ResultCode ReleaseLastApplicationCaptureBuffer(ServiceCtx context) + { + if (!_lastApplicationCaptureBufferAcquired) + { + return ResultCode.BufferNotAcquired; + } + + _lastApplicationCaptureBufferAcquired = false; + + return ResultCode.Success; + } + + [CommandHipc(15)] + // ReleaseCallerAppletCaptureBuffer() + public ResultCode ReleaseCallerAppletCaptureBuffer(ServiceCtx context) + { + if (!_callerAppletCaptureBufferAcquired) + { + return ResultCode.BufferNotAcquired; + } + + _callerAppletCaptureBufferAcquired = false; + + return ResultCode.Success; + } + + [CommandHipc(16)] + // AcquireLastApplicationCaptureBufferEx() -> (b8, handle) + public ResultCode AcquireLastApplicationCaptureBufferEx(ServiceCtx context) + { + if (_lastApplicationCaptureBufferAcquired) + { + return ResultCode.BufferAlreadyAcquired; + } + + if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + _lastApplicationCaptureBufferAcquired = true; + + context.ResponseData.Write(_lastApplicationCaptureBufferAcquired); + + return ResultCode.Success; + } + + [CommandHipc(18)] + // AcquireCallerAppletCaptureBufferEx() -> (b8, handle) + public ResultCode AcquireCallerAppletCaptureBufferEx(ServiceCtx context) + { + if (_callerAppletCaptureBufferAcquired) + { + return ResultCode.BufferAlreadyAcquired; + } + + if (context.Process.HandleTable.GenerateHandle(_transferMem, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + _callerAppletCaptureBufferAcquired = true; + + context.ResponseData.Write(_callerAppletCaptureBufferAcquired); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index e9af55a0..0ecbd4a0 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -35,9 +35,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private uint _screenShotImageOrientation = 0; private uint _idleTimeDetectionExtension = 0; - public ISelfController(Horizon system, long pid) + public ISelfController(ServiceCtx context, long pid) { - _libraryAppletLaunchableEvent = new KEvent(system.KernelContext); + _libraryAppletLaunchableEvent = new KEvent(context.Device.System.KernelContext); _pid = pid; } @@ -225,6 +225,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandHipc(41)] // 4.0.0+ + // IsSystemBufferSharingEnabled() + public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context) + { + // NOTE: Service checks a private field and return an error if the SystemBufferSharing is disabled. + + return ResultCode.NotImplemented; + } + [CommandHipc(44)] // 10.0.0+ // CreateManagedDisplaySeparableLayer() -> (u64, u64) public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index 2f5e86ba..926234f5 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -15,5 +15,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } + + [CommandHipc(200)] + [CommandHipc(201)] // 3.0.0+ + // OpenLibraryAppletProxy(u64, pid, handle) -> object + public ResultCode OpenLibraryAppletProxy(ServiceCtx context) + { + MakeObject(context, new ILibraryAppletProxy(context.Request.HandleDesc.PId)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs new file mode 100644 index 00000000..17a485ab --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletIdentityInfo.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + struct AppletIdentifyInfo + { + public AppletId AppletId; + public uint Padding; + public ulong TitleId; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs new file mode 100644 index 00000000..6c528337 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletProcessLaunchReason.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + [StructLayout(LayoutKind.Sequential, Size = 0x4)] + struct AppletProcessLaunchReason + { + public byte Flag; + public ushort Unknown1; + public byte Unknown2; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs new file mode 100644 index 00000000..fc1c11e4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletInfo.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + struct LibraryAppletInfo + { + public AppletId AppletId; + public LibraryAppletMode LibraryAppletMode; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs new file mode 100644 index 00000000..6b9a2284 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/LibraryAppletMode.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + [Flags] + enum LibraryAppletMode : uint + { + AllForeground, + PartialForeground, + NoUi, + PartialForegroundWithIndirectDisplay, + AllForegroundInitiallyHidden + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index 8b67cece..44477922 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetSelfController() -> object public ResultCode GetSelfController(ServiceCtx context) { - MakeObject(context, new ISelfController(context.Device.System, _pid)); + MakeObject(context, new ISelfController(context, _pid)); return ResultCode.Success; } @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetDisplayController() -> object public ResultCode GetDisplayController(ServiceCtx context) { - MakeObject(context, new IDisplayController()); + MakeObject(context, new IDisplayController(context)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs index 422462e9..5cafff67 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs @@ -14,6 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Am ObjectInvalid = (500 << ErrorCodeShift) | ModuleId, IStorageInUse = (502 << ErrorCodeShift) | ModuleId, OutOfBounds = (503 << ErrorCodeShift) | ModuleId, + BufferNotAcquired = (504 << ErrorCodeShift) | ModuleId, + BufferAlreadyAcquired = (505 << ErrorCodeShift) | ModuleId, InvalidParameters = (506 << ErrorCodeShift) | ModuleId, OpenedAsWrongType = (511 << ErrorCodeShift) | ModuleId, UnbalancedFatalSection = (512 << ErrorCodeShift) | ModuleId, diff --git a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index fd8844c7..bd07c103 100644 --- a/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -499,6 +499,15 @@ namespace Ryujinx.HLE.HOS.Services.Fs return (ResultCode)result.Value; } + [CommandHipc(1003)] + // DisableAutoSaveDataCreation() + public ResultCode DisableAutoSaveDataCreation(ServiceCtx context) + { + // NOTE: This call does nothing in original service. + + return ResultCode.Success; + } + [CommandHipc(1004)] // SetGlobalAccessLogMode(u32 mode) public ResultCode SetGlobalAccessLogMode(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Mii/Helper.cs b/Ryujinx.HLE/HOS/Services/Mii/Helper.cs index d5fa98ea..47debd59 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/Helper.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/Helper.cs @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii public static UInt128 GetDeviceId() { // FIXME: call set:sys GetMiiAuthorId - return new UInt128(0, 1); + return new UInt128("5279754d69694e780000000000000000"); // RyuMiiNx } public static ReadOnlySpan Ver3FacelineColorTable => new byte[] { 0, 1, 2, 3, 4, 5 }; diff --git a/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs index a9f76902..1792bbb7 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/IImageDatabaseService.cs @@ -1,8 +1,41 @@ -namespace Ryujinx.HLE.HOS.Services.Mii +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Mii { [Service("miiimg")] // 5.0.0+ class IImageDatabaseService : IpcService { + private uint _imageCount; + private bool _isDirty; + public IImageDatabaseService(ServiceCtx context) { } + + [CommandHipc(0)] + // Initialize(b8) -> b8 + public ResultCode Initialize(ServiceCtx context) + { + // TODO: Service uses MiiImage:/database.dat if true, seems to use hardcoded data if false. + bool useHardcodedData = context.RequestData.ReadBoolean(); + + _imageCount = 0; + _isDirty = false; + + context.ResponseData.Write(_isDirty); + + Logger.Stub?.PrintStub(LogClass.ServiceMii, new { useHardcodedData }); + + return ResultCode.Success; + } + + [CommandHipc(11)] + // GetCount() -> u32 + public ResultCode GetCount(ServiceCtx context) + { + context.ResponseData.Write(_imageCount); + + Logger.Stub?.PrintStub(LogClass.ServiceMii); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs b/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs index 4491f2c8..0bf15a7f 100644 --- a/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs +++ b/Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs @@ -101,6 +101,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii // Ensure we have valid data in the database _database.Format(); + // TODO: Unmount is currently not implemented properly at dispose, implement that and decrement MountCounter. + MountCounter = 0; + MountSave(); } @@ -151,8 +154,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii } } - - return result; } @@ -183,6 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii if (result.IsSuccess()) { result = _filesystemClient.GetFileSize(out long fileSize, handle); + if (result.IsSuccess()) { if (fileSize == Unsafe.SizeOf()) diff --git a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs index 66b768ee..1a437289 100644 --- a/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Settings/ISystemSettingsServer.cs @@ -7,6 +7,7 @@ using LibHac.FsSystem.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; using System; using System.IO; using System.Text; @@ -271,6 +272,20 @@ namespace Ryujinx.HLE.HOS.Services.Settings return ResultCode.Success; } + [CommandHipc(90)] + // GetMiiAuthorId() -> nn::util::Uuid + public ResultCode GetMiiAuthorId(ServiceCtx context) + { + // NOTE: If miiAuthorId is null ResultCode.NullMiiAuthorIdBuffer is returned. + // Doesn't occur in our case. + + UInt128 miiAuthorId = Mii.Helper.GetDeviceId(); + + miiAuthorId.Write(context.ResponseData); + + return ResultCode.Success; + } + public byte[] GetFirmwareData(Switch device) { const ulong SystemVersionTitleId = 0x0100000000000809; diff --git a/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs b/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs new file mode 100644 index 00000000..b8ef8e8e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Settings/Types/PlatformRegion.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Settings.Types +{ + enum PlatformRegion + { + Global = 1, + China = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 2a6f327b..f0b95f55 100644 --- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -55,11 +55,11 @@ namespace Ryujinx.HLE.HOS.SystemState DesiredTitleLanguage = language switch { - SystemLanguage.Taiwanese or - SystemLanguage.TraditionalChinese => TitleLanguage.Taiwanese, + SystemLanguage.Taiwanese => TitleLanguage.Taiwanese, + SystemLanguage.TraditionalChinese or SystemLanguage.Chinese or - SystemLanguage.SimplifiedChinese => TitleLanguage.Chinese, - _ => Enum.Parse(Enum.GetName(typeof(SystemLanguage), language)), + SystemLanguage.SimplifiedChinese => TitleLanguage.Chinese, + _ => Enum.Parse(Enum.GetName(typeof(SystemLanguage), language)), }; } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 9eb6f303..113ac639 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -3,6 +3,7 @@ using ARMeilleure.Translation.PTC; using Gtk; using LibHac.Common; using LibHac.FsSystem; +using LibHac.FsSystem.NcaUtils; using LibHac.Ns; using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.OpenAL; @@ -87,6 +88,10 @@ namespace Ryujinx.Ui [GUI] Box _statusBar; [GUI] MenuItem _optionMenu; [GUI] MenuItem _manageUserProfiles; + [GUI] MenuItem _fileMenu; + [GUI] MenuItem _loadApplicationFile; + [GUI] MenuItem _loadApplicationFolder; + [GUI] MenuItem _appletMenu; [GUI] MenuItem _actionMenu; [GUI] MenuItem _stopEmulation; [GUI] MenuItem _simulateWakeUpMessage; @@ -165,6 +170,7 @@ namespace Ryujinx.Ui _applicationLibrary.ApplicationAdded += Application_Added; _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; + _fileMenu.StateChanged += FileMenu_StateChanged; _actionMenu.StateChanged += ActionMenu_StateChanged; _optionMenu.StateChanged += OptionMenu_StateChanged; @@ -575,7 +581,15 @@ namespace Ryujinx.Ui SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - bool isDirectory = Directory.Exists(path); + bool isDirectory = Directory.Exists(path); + bool isFirmwareTitle = false; + + if (path.StartsWith("@SystemContent")) + { + path = _virtualFileSystem.SwitchPathToSystemPath(path); + + isFirmwareTitle = true; + } if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) { @@ -636,7 +650,13 @@ namespace Ryujinx.Ui Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - if (Directory.Exists(path)) + if (isFirmwareTitle) + { + Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); + + _emulationContext.LoadNca(path); + } + else if (Directory.Exists(path)) { string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); @@ -1100,6 +1120,20 @@ namespace Ryujinx.Ui } } + private void FileMenu_StateChanged(object o, StateChangedArgs args) + { + _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3; + _loadApplicationFile.Sensitive = _emulationContext == null; + _loadApplicationFolder.Sensitive = _emulationContext == null; + } + + private void Load_Mii_Edit_Applet(object sender, EventArgs args) + { + string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.NandSystem, NcaContentType.Program); + + LoadApplication(contentPath); + } + private void Open_Ryu_Folder(object sender, EventArgs args) { OpenHelper.OpenFolder(AppDataManager.BaseDirPath); @@ -1217,6 +1251,15 @@ namespace Ryujinx.Ui GtkDialog.CreateInfoDialog(dialogTitle, message); Logger.Info?.Print(LogClass.Application, message); + + // Purge Applet Cache. + + DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); + + if (miiEditorCacheFolder.Exists) + { + miiEditorCacheFolder.Delete(true); + } }); } catch (Exception ex) diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 129b768e..e974d878 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -19,7 +19,7 @@ True False - + True False File @@ -29,7 +29,7 @@ True False - + True False Open a file chooser to chose a switch compatible file to load @@ -39,7 +39,7 @@ - + True False Open a file chooser to chose a switch compatible, unpacked application to load @@ -48,6 +48,30 @@ + + + True + False + Load Applet + True + + + True + False + + + True + False + Open Mii Editor Applet in Standalone mode + Mii Editor + True + + + + + + + True