From 35e561276648ecfd828cfa4883d95f26959100c6 Mon Sep 17 00:00:00 2001 From: jduncanator <1518948+jduncanator@users.noreply.github.com> Date: Thu, 14 Nov 2019 16:18:44 +1100 Subject: [PATCH] Implement a rudimentary applets system (#804) * Implement Player Select applet * Initialize the Horizon system reference * Tidy up namespaces * Resolve nits * Resolve nits * Rename stack to queue * Implement an applet FIFO * Remove debugging log * Log applet creation events * Reorganise AppletFifo * More reorganisation * Final changes --- Ryujinx.HLE/HOS/Applets/AppletManager.cs | 29 +++++++ Ryujinx.HLE/HOS/Applets/IApplet.cs | 13 +++ .../PlayerSelect/PlayerSelectApplet.cs | 55 +++++++++++++ .../PlayerSelect/PlayerSelectResult.cs | 8 ++ .../ILibraryAppletAccessor.cs | 42 +++++++--- .../ILibraryAppletCreator.cs | 6 +- .../HOS/Services/Am/AppletAE/AppletFifo.cs | 79 +++++++++++++++++++ .../Services/Am/AppletAE/Types/AppletId.cs | 27 +++++++ 8 files changed, 245 insertions(+), 14 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Applets/AppletManager.cs create mode 100644 Ryujinx.HLE/HOS/Applets/IApplet.cs create mode 100644 Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs create mode 100644 Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs create mode 100644 Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs create mode 100644 Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs diff --git a/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/Ryujinx.HLE/HOS/Applets/AppletManager.cs new file mode 100644 index 000000000..e5426cd75 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Applets +{ + static class AppletManager + { + private static Dictionary _appletMapping; + + static AppletManager() + { + _appletMapping = new Dictionary + { + { AppletId.PlayerSelect, typeof(PlayerSelectApplet) } + }; + } + + public static IApplet Create(AppletId applet, Horizon system) + { + if (_appletMapping.TryGetValue(applet, out Type appletClass)) + { + return (IApplet)Activator.CreateInstance(appletClass, system); + } + + throw new NotImplementedException($"{applet} applet is not implemented."); + } + } +} diff --git a/Ryujinx.HLE/HOS/Applets/IApplet.cs b/Ryujinx.HLE/HOS/Applets/IApplet.cs new file mode 100644 index 000000000..aa248bf59 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/IApplet.cs @@ -0,0 +1,13 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; + +namespace Ryujinx.HLE.HOS.Applets +{ + interface IApplet + { + event EventHandler AppletStateChanged; + + ResultCode Start(AppletFifo inData, AppletFifo outData); + ResultCode GetResult(); + } +} diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs new file mode 100644 index 000000000..7658c6db1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -0,0 +1,55 @@ +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; +using System.IO; + +namespace Ryujinx.HLE.HOS.Applets +{ + internal class PlayerSelectApplet : IApplet + { + private Horizon _system; + + private AppletFifo _inputData; + private AppletFifo _outputData; + + public event EventHandler AppletStateChanged; + + public PlayerSelectApplet(Horizon system) + { + _system = system; + } + + public ResultCode Start(AppletFifo inData, AppletFifo outData) + { + _inputData = inData; + _outputData = outData; + + // TODO(jduncanator): Parse PlayerSelectConfig from input data + _outputData.Push(BuildResponse()); + + AppletStateChanged?.Invoke(this, null); + + return ResultCode.Success; + } + + public ResultCode GetResult() + { + return ResultCode.Success; + } + + private byte[] BuildResponse() + { + UserProfile currentUser = _system.State.Account.LastOpenedUser; + + using (MemoryStream stream = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write((ulong)PlayerSelectResult.Success); + + currentUser.UserId.Write(writer); + + return stream.ToArray(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs new file mode 100644 index 000000000..682e094ed --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Applets +{ + enum PlayerSelectResult : ulong + { + Success = 0, + Failure = 2 + } +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 9d8e2a96f..8c4d10084 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -1,19 +1,37 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator { class ILibraryAppletAccessor : IpcService { + private IApplet _applet; + + private AppletFifo _inData; + private AppletFifo _outData; + private KEvent _stateChangedEvent; - public ILibraryAppletAccessor(Horizon system) + public ILibraryAppletAccessor(AppletId appletId, Horizon system) { _stateChangedEvent = new KEvent(system); + + _applet = AppletManager.Create(appletId, system); + _inData = new AppletFifo(); + _outData = new AppletFifo(); + + _applet.AppletStateChanged += OnAppletStateChanged; + + Logger.PrintInfo(LogClass.ServiceAm, $"Applet '{appletId}' created."); + } + + private void OnAppletStateChanged(object sender, EventArgs e) + { + _stateChangedEvent.ReadableEvent.Signal(); } [Command(0)] @@ -29,8 +47,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - Logger.PrintStub(LogClass.ServiceAm); - return ResultCode.Success; } @@ -38,25 +54,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // Start() public ResultCode Start(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); - - return ResultCode.Success; + return (ResultCode)_applet.Start(_inData, _outData); } [Command(30)] // GetResult() public ResultCode GetResult(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); - - return ResultCode.Success; + return (ResultCode)_applet.GetResult(); } [Command(100)] // PushInData(object) public ResultCode PushInData(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); + IStorage data = GetObject(context, 0); + + _inData.Push(data.Data); return ResultCode.Success; } @@ -65,9 +79,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // PopOutData() -> object public ResultCode PopOutData(ServiceCtx context) { - MakeObject(context, new IStorage(StorageHelper.MakeLaunchParams())); + byte[] data = _outData.Pop(); + MakeObject(context, new IStorage(data)); + return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index 8b0b225bd..094ed3050 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -10,7 +11,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // CreateLibraryApplet(u32, u32) -> object public ResultCode CreateLibraryApplet(ServiceCtx context) { - MakeObject(context, new ILibraryAppletAccessor(context.Device.System)); + AppletId appletId = (AppletId)context.RequestData.ReadInt32(); + int libraryAppletMode = context.RequestData.ReadInt32(); + + MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs new file mode 100644 index 000000000..2391ba5e2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + internal class AppletFifo : IEnumerable + { + private ConcurrentQueue _dataQueue; + + public int Count => _dataQueue.Count; + + public AppletFifo() + { + _dataQueue = new ConcurrentQueue(); + } + + public void Push(T item) + { + _dataQueue.Enqueue(item); + } + + public T Pop() + { + if (_dataQueue.TryDequeue(out T result)) + { + return result; + } + + throw new InvalidOperationException("FIFO empty."); + } + + public bool TryPop(out T result) + { + return _dataQueue.TryDequeue(out result); + } + + public T Peek() + { + if (_dataQueue.TryPeek(out T result)) + { + return result; + } + + throw new InvalidOperationException("FIFO empty."); + } + + public bool TryPeek(out T result) + { + return _dataQueue.TryPeek(out result); + } + + public void Clear() + { + _dataQueue.Clear(); + } + + public T[] ToArray() + { + return _dataQueue.ToArray(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + _dataQueue.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _dataQueue.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _dataQueue.GetEnumerator(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs new file mode 100644 index 000000000..917f68658 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + enum AppletId + { + Application = 0x01, + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + PlayerSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + LibAppletWeb = 0x13, + LibAppletShop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + LibAppletOff = 0x17, + LibAppletWhitelisted = 0x18, + LibAppletAuth = 0x19, + MyPage = 0x1A + } +}