Start LibraryAppletAccessor Impl

This commit is contained in:
Isaac Marovitz 2024-02-15 22:15:29 -05:00
parent 0873c86c58
commit 242eb17fee
No known key found for this signature in database
GPG key ID: 97250B2B09A132E1
10 changed files with 466 additions and 12 deletions

View file

@ -0,0 +1,121 @@
using Ryujinx.Horizon.Sdk.Am;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Horizon.Am.Ipc
{
internal class AppletFifo<T> : IAppletFifo<T>
{
private readonly ConcurrentQueue<T> _dataQueue;
public event EventHandler DataAvailable;
public bool IsSynchronized
{
get { return ((ICollection)_dataQueue).IsSynchronized; }
}
public object SyncRoot
{
get { return ((ICollection)_dataQueue).SyncRoot; }
}
public int Count
{
get { return _dataQueue.Count; }
}
public AppletFifo()
{
_dataQueue = new ConcurrentQueue<T>();
}
public void Push(T item)
{
_dataQueue.Enqueue(item);
DataAvailable?.Invoke(this, null);
}
public bool TryAdd(T item)
{
try
{
this.Push(item);
return true;
}
catch
{
return false;
}
}
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 bool TryTake(out T item)
{
return this.TryPop(out item);
}
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 void CopyTo(Array array, int index)
{
this.CopyTo((T[])array, index);
}
public IEnumerator<T> GetEnumerator()
{
return _dataQueue.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _dataQueue.GetEnumerator();
}
}
}

View file

@ -0,0 +1,204 @@
using Ryujinx.Common.Logging;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Am;
using Ryujinx.Horizon.Sdk.Am.Controllers;
using Ryujinx.Horizon.Sdk.Am.Storage;
using Ryujinx.Horizon.Sdk.OsTypes;
using Ryujinx.Horizon.Sdk.Sf;
using System;
namespace Ryujinx.Horizon.Am.Ipc.Controllers
{
partial class LibraryAppletAccessor : ILibraryAppletAccessor
{
private readonly AppletSession _normalSession;
private readonly AppletSession _interactiveSession;
private SystemEventType _stateChangedEvent;
private SystemEventType _normalOutDataEvent;
private SystemEventType _interactiveOutDataEvent;
public LibraryAppletAccessor(AppletId appletId)
{
Os.CreateSystemEvent(out _stateChangedEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
Os.CreateSystemEvent(out _normalOutDataEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
Os.CreateSystemEvent(out _interactiveOutDataEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
_normalSession = new AppletSession();
_interactiveSession = new AppletSession();
_normalSession.DataAvailable += OnNormalOutData;
_interactiveSession.DataAvailable += OnInteractiveOutData;
}
private void OnAppletStateChanged(object sender, EventArgs e)
{
Os.SignalSystemEvent(ref _stateChangedEvent);
}
private void OnNormalOutData(object sender, EventArgs e)
{
Os.SignalSystemEvent(ref _normalOutDataEvent);
}
private void OnInteractiveOutData(object sender, EventArgs e)
{
Os.SignalSystemEvent(ref _interactiveOutDataEvent);
}
[CmifCommand(0)]
public Result GetAppletStateChangedEvent([CopyHandle] out int arg0)
{
arg0 = Os.GetReadableHandleOfSystemEvent(ref _stateChangedEvent);
return Result.Success;
}
[CmifCommand(1)]
public Result IsCompleted(out bool arg0)
{
arg0 = false;
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(10)]
public Result Start()
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(20)]
public Result RequestExit()
{
Os.SignalSystemEvent(ref _stateChangedEvent);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(25)]
public Result Terminate()
{
throw new System.NotImplementedException();
}
[CmifCommand(30)]
public Result GetResult()
{
throw new System.NotImplementedException();
}
[CmifCommand(50)]
public Result SetOutOfFocusApplicationSuspendingEnabled(bool arg0)
{
throw new System.NotImplementedException();
}
[CmifCommand(60)]
public Result PresetLibraryAppletGpuTimeSliceZero()
{
// NOTE: This call reset two internal fields to 0 and one internal field to "true".
// It seems to be used only with software keyboard inline.
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(100)]
public Result PushInData(IStorage data)
{
var storage = new Storage.Storage(data);
_normalSession.Push(storage.Data);
return Result.Success;
}
[CmifCommand(101)]
public Result PopOutData(out IStorage data)
{
if (_interactiveSession.TryPop(out byte[] bytes))
{
data = new Storage.Storage(bytes);
return Result.Success;
}
data = new Storage.Storage([]);
return AmResult.NotAvailable;
}
[CmifCommand(102)]
public Result PushExtraStorage(IStorage data)
{
throw new System.NotImplementedException();
}
[CmifCommand(103)]
public Result PushInteractiveInData(IStorage data)
{
var storage = new Storage.Storage(data);
_interactiveSession.Push(storage.Data);
return Result.Success;
}
[CmifCommand(104)]
public Result PopInteractiveOutData(out IStorage arg0)
{
throw new System.NotImplementedException();
}
[CmifCommand(105)]
public Result GetPopOutDataEvent(out int handle)
{
handle = Os.GetReadableHandleOfSystemEvent(ref _normalOutDataEvent);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(106)]
public Result GetPopInteractiveOutDataEvent(out int handle)
{
handle = Os.GetReadableHandleOfSystemEvent(ref _interactiveOutDataEvent);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return Result.Success;
}
[CmifCommand(110)]
public Result NeedsToExitProcess(out bool arg0)
{
arg0 = false;
return AmResult.Stubbed;
}
[CmifCommand(120)]
public Result GetLibraryAppletInfo(out LibraryAppletInfo arg0)
{
throw new System.NotImplementedException();
}
[CmifCommand(150)]
public Result RequestForAppletToGetForeground()
{
return AmResult.Stubbed;
}
[CmifCommand(160)]
public Result GetIndirectLayerConsumerHandle(out ulong arg0, ulong arg1, ulong pid)
{
throw new System.NotImplementedException();
}
}
}

View file

@ -1,6 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Am.Controllers;
using Ryujinx.Horizon.Sdk.Am.Storage;
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Am.Ipc.Controllers
@ -8,7 +9,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
partial class LibraryAppletCreator : ILibraryAppletCreator
{
[CmifCommand(0)]
public Result CreateLibraryApplet()
public Result CreateLibraryApplet(out ILibraryAppletAccessor arg0, uint arg1, uint arg2)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -16,7 +17,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
}
[CmifCommand(1)]
public Result TerminateAllLibraryApplets()
public Result TerminateAllLibraryApplets(out bool arg0)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -32,7 +33,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
}
[CmifCommand(10)]
public Result CreateStorage()
public Result CreateStorage(out IStorage arg0, long arg1)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -40,7 +41,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
}
[CmifCommand(11)]
public Result CreateTransferMemoryStorage()
public Result CreateTransferMemoryStorage(out IStorage arg0, int arg1, long arg2, bool arg3)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -48,7 +49,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
}
[CmifCommand(12)]
public Result CreateHandleStorage()
public Result CreateHandleStorage(out IStorage arg0, int arg1, long arg2)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);

View file

@ -15,6 +15,12 @@ namespace Ryujinx.Horizon.Am.Ipc.Storage
Data = data;
}
// TODO: Something for this...
public Storage(IStorage handle)
{
}
[CmifCommand(0)]
public Result Open(out IStorageAccessor storageAccessor)
{

View file

@ -5,7 +5,8 @@ namespace Ryujinx.Horizon.Sdk.Am
[StructLayout(LayoutKind.Sequential, Size = 0x4)]
public struct AppletProcessLaunchReason
{
// TODO: Better way to rep single bit flag
public bool flag;
public byte Flag;
public ushort Unknown1;
public byte Unknown2;
}
}

View file

@ -0,0 +1,77 @@
using Ryujinx.Horizon.Am.Ipc;
using System;
namespace Ryujinx.Horizon.Sdk.Am
{
internal class AppletSession
{
private readonly IAppletFifo<byte[]> _inputData;
private readonly IAppletFifo<byte[]> _outputData;
public event EventHandler DataAvailable;
public int Length
{
get { return _inputData.Count; }
}
public AppletSession()
{
_inputData = new AppletFifo<byte[]>();
_outputData = new AppletFifo<byte[]>();
}
public AppletSession(IAppletFifo<byte[]> inputData, IAppletFifo<byte[]> outputData)
{
_inputData = inputData;
_outputData = outputData;
_inputData.DataAvailable += OnDataAvailable;
}
private void OnDataAvailable(object sender, EventArgs e)
{
DataAvailable?.Invoke(this, null);
}
public void Push(byte[] item)
{
if (!this.TryPush(item))
{
// TODO(jduncanator): Throw a proper exception
throw new InvalidOperationException();
}
}
public bool TryPush(byte[] item)
{
return _outputData.TryAdd(item);
}
public byte[] Pop()
{
if (this.TryPop(out byte[] item))
{
return item;
}
throw new InvalidOperationException("Input data empty.");
}
public bool TryPop(out byte[] item)
{
return _inputData.TryTake(out item);
}
/// <summary>
/// This returns an AppletSession that can be used at the
/// other end of the pipe. Pushing data into this new session
/// will put it in the first session's input buffer, and vice
/// versa.
/// </summary>
public AppletSession GetConsumer()
{
return new AppletSession(this._outputData, this._inputData);
}
}
}

View file

@ -0,0 +1,22 @@
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Am.Storage;
namespace Ryujinx.Horizon.Sdk.Am.Controllers
{
interface ILibraryAppletAccessor : IAppletAccessor
{
Result SetOutOfFocusApplicationSuspendingEnabled(bool arg0);
Result PresetLibraryAppletGpuTimeSliceZero();
Result PushInData(IStorage arg0);
Result PopOutData(out IStorage data);
Result PushExtraStorage(IStorage data);
Result PushInteractiveInData(IStorage data);
Result PopInteractiveOutData(out IStorage arg0);
Result GetPopOutDataEvent(out int handle);
Result GetPopInteractiveOutDataEvent(out int handle);
Result NeedsToExitProcess(out bool arg0);
Result GetLibraryAppletInfo(out LibraryAppletInfo arg0);
Result RequestForAppletToGetForeground();
Result GetIndirectLayerConsumerHandle(out ulong arg0, ulong arg1, ulong pid);
}
}

View file

@ -1,15 +1,16 @@
using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Am.Storage;
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Sdk.Am.Controllers
{
interface ILibraryAppletCreator : IServiceObject
{
Result CreateLibraryApplet();
Result CreateLibraryApplet(out ILibraryAppletAccessor arg0, uint arg1, uint arg2);
Result TerminateAllLibraryApplets();
Result AreAnyLibraryAppletsLeft();
Result CreateStorage();
Result CreateTransferMemoryStorage();
Result CreateHandleStorage();
Result AreAnyLibraryAppletsLeft(out bool arg0);
Result CreateStorage(out IStorage arg0, long arg1);
Result CreateTransferMemoryStorage(out IStorage arg0, int arg1, long arg2, bool arg3);
Result CreateHandleStorage(out IStorage arg0, int arg1, long arg2);
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Horizon.Sdk.Am
{
interface IAppletFifo<T> : IProducerConsumerCollection<T>
{
event EventHandler DataAvailable;
}
}

View file

@ -0,0 +1,11 @@
using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Am
{
[StructLayout(LayoutKind.Sequential, Size = 0x8)]
struct LibraryAppletInfo
{
public AppletId AppletId;
public LibraryAppletMode LibraryAppletMode;
}
}