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
This commit is contained in:
parent
7c111a3567
commit
35e5612766
8 changed files with 245 additions and 14 deletions
29
Ryujinx.HLE/HOS/Applets/AppletManager.cs
Normal file
29
Ryujinx.HLE/HOS/Applets/AppletManager.cs
Normal file
|
@ -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<AppletId, Type> _appletMapping;
|
||||
|
||||
static AppletManager()
|
||||
{
|
||||
_appletMapping = new Dictionary<AppletId, Type>
|
||||
{
|
||||
{ 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.");
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.HLE/HOS/Applets/IApplet.cs
Normal file
13
Ryujinx.HLE/HOS/Applets/IApplet.cs
Normal file
|
@ -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<byte[]> inData, AppletFifo<byte[]> outData);
|
||||
ResultCode GetResult();
|
||||
}
|
||||
}
|
55
Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
Normal file
55
Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
Normal file
|
@ -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<byte[]> _inputData;
|
||||
private AppletFifo<byte[]> _outputData;
|
||||
|
||||
public event EventHandler AppletStateChanged;
|
||||
|
||||
public PlayerSelectApplet(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public ResultCode Start(AppletFifo<byte[]> inData, AppletFifo<byte[]> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
enum PlayerSelectResult : ulong
|
||||
{
|
||||
Success = 0,
|
||||
Failure = 2
|
||||
}
|
||||
}
|
|
@ -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<byte[]> _inData;
|
||||
private AppletFifo<byte[]> _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<byte[]>();
|
||||
_outData = new AppletFifo<byte[]>();
|
||||
|
||||
_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<nn::am::service::IStorage>)
|
||||
public ResultCode PushInData(ServiceCtx context)
|
||||
{
|
||||
Logger.PrintStub(LogClass.ServiceAm);
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
|
||||
_inData.Push(data.Data);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -65,7 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||
// PopOutData() -> object<nn::am::service::IStorage>
|
||||
public ResultCode PopOutData(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IStorage(StorageHelper.MakeLaunchParams()));
|
||||
byte[] data = _outData.Pop();
|
||||
|
||||
MakeObject(context, new IStorage(data));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
|
|
@ -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<nn::am::service::ILibraryAppletAccessor>
|
||||
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;
|
||||
}
|
||||
|
|
79
Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs
Normal file
79
Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs
Normal file
|
@ -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<T> : IEnumerable<T>
|
||||
{
|
||||
private ConcurrentQueue<T> _dataQueue;
|
||||
|
||||
public int Count => _dataQueue.Count;
|
||||
|
||||
public AppletFifo()
|
||||
{
|
||||
_dataQueue = new ConcurrentQueue<T>();
|
||||
}
|
||||
|
||||
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<T> GetEnumerator()
|
||||
{
|
||||
return _dataQueue.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _dataQueue.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs
Normal file
27
Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
Reference in a new issue