forked from Mirror/Ryujinx
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.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator
|
||||||
{
|
{
|
||||||
class ILibraryAppletAccessor : IpcService
|
class ILibraryAppletAccessor : IpcService
|
||||||
{
|
{
|
||||||
|
private IApplet _applet;
|
||||||
|
|
||||||
|
private AppletFifo<byte[]> _inData;
|
||||||
|
private AppletFifo<byte[]> _outData;
|
||||||
|
|
||||||
private KEvent _stateChangedEvent;
|
private KEvent _stateChangedEvent;
|
||||||
|
|
||||||
public ILibraryAppletAccessor(Horizon system)
|
public ILibraryAppletAccessor(AppletId appletId, Horizon system)
|
||||||
{
|
{
|
||||||
_stateChangedEvent = new KEvent(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)]
|
[Command(0)]
|
||||||
|
@ -29,8 +47,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
||||||
|
|
||||||
Logger.PrintStub(LogClass.ServiceAm);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,25 +54,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
||||||
// Start()
|
// Start()
|
||||||
public ResultCode Start(ServiceCtx context)
|
public ResultCode Start(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Logger.PrintStub(LogClass.ServiceAm);
|
return (ResultCode)_applet.Start(_inData, _outData);
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(30)]
|
[Command(30)]
|
||||||
// GetResult()
|
// GetResult()
|
||||||
public ResultCode GetResult(ServiceCtx context)
|
public ResultCode GetResult(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Logger.PrintStub(LogClass.ServiceAm);
|
return (ResultCode)_applet.GetResult();
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(100)]
|
[Command(100)]
|
||||||
// PushInData(object<nn::am::service::IStorage>)
|
// PushInData(object<nn::am::service::IStorage>)
|
||||||
public ResultCode PushInData(ServiceCtx context)
|
public ResultCode PushInData(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Logger.PrintStub(LogClass.ServiceAm);
|
IStorage data = GetObject<IStorage>(context, 0);
|
||||||
|
|
||||||
|
_inData.Push(data.Data);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
||||||
// PopOutData() -> object<nn::am::service::IStorage>
|
// PopOutData() -> object<nn::am::service::IStorage>
|
||||||
public ResultCode PopOutData(ServiceCtx context)
|
public ResultCode PopOutData(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new IStorage(StorageHelper.MakeLaunchParams()));
|
byte[] data = _outData.Pop();
|
||||||
|
|
||||||
|
MakeObject(context, new IStorage(data));
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
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>
|
// CreateLibraryApplet(u32, u32) -> object<nn::am::service::ILibraryAppletAccessor>
|
||||||
public ResultCode CreateLibraryApplet(ServiceCtx context)
|
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;
|
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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue