feat: implement real applets
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
This commit is contained in:
parent
c218305bc0
commit
3b568b61ba
@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
||||
{
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public const int MaxTrackedRanges = 16;
|
||||
public const int MaxTrackedRanges = 256;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
|
@ -25,9 +25,6 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
case AppletId.LibAppletShop:
|
||||
case AppletId.LibAppletOff:
|
||||
return new BrowserApplet();
|
||||
case AppletId.MiiEdit:
|
||||
Logger.Warning?.Print(LogClass.Application, $"Please use the MiiEdit inside File/Open Applet");
|
||||
return new DummyApplet(system);
|
||||
case AppletId.Cabinet:
|
||||
return new CabinetApplet(system);
|
||||
}
|
||||
|
@ -576,6 +576,35 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLaunchedAsReal(ulong pid)
|
||||
{
|
||||
RealApplet applet = null;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_applets.TryGetValue(pid, out applet))
|
||||
{
|
||||
if (_applets.Count > 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (applet==_homeMenu||applet==_overlayDisp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAnyApplets()
|
||||
{
|
||||
return _applets.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ButtonPressTracker
|
||||
|
@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||
{
|
||||
class KBufferDescriptorTable
|
||||
{
|
||||
private const int MaxInternalBuffersCount = 8;
|
||||
private const int MaxInternalBuffersCount = 256;
|
||||
|
||||
private readonly List<KBufferDescriptor> _sendBufferDescriptors;
|
||||
private readonly List<KBufferDescriptor> _receiveBufferDescriptors;
|
||||
|
@ -88,8 +88,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
|
||||
// OpenLibraryAppletSelfAccessor() -> object<nn::am::service::ILibraryAppletSelfAccessor>
|
||||
public ResultCode OpenLibraryAppletSelfAccessor(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ILibraryAppletSelfAccessor(context));
|
||||
|
||||
if (context.Device.System.WindowSystem.IsLaunchedAsReal(_pid))
|
||||
{
|
||||
MakeObject(context, new ILibraryRealAppletSelfAccessor(context, _pid));
|
||||
}
|
||||
else
|
||||
{
|
||||
MakeObject(context, new ILibraryAppletSelfAccessor(context));
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,323 @@
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator
|
||||
{
|
||||
class ILibraryRealAppletAccessor : DisposableIpcService
|
||||
{
|
||||
private readonly KernelContext _kernelContext;
|
||||
private readonly ulong _callerPid;
|
||||
|
||||
private readonly RealAppletId _appletId;
|
||||
private RealApplet _applet;
|
||||
|
||||
private readonly AppletChannel _inChannel;
|
||||
private readonly AppletChannel _outChannel;
|
||||
private readonly AppletChannel _interactiveInChannel;
|
||||
private readonly AppletChannel _interactiveOutChannel;
|
||||
private readonly AppletChannel _contextChannel;
|
||||
|
||||
private readonly KEvent _stateChangedEvent;
|
||||
private readonly KEvent _normalOutDataEvent;
|
||||
private readonly KEvent _interactiveOutDataEvent;
|
||||
|
||||
private int _stateChangedEventHandle;
|
||||
private int _normalOutDataEventHandle;
|
||||
private int _interactiveOutDataEventHandle;
|
||||
|
||||
private int _indirectLayerHandle;
|
||||
|
||||
private ResultCode StartAppletProcess(Horizon system)
|
||||
{
|
||||
// TODO: use ns
|
||||
var programId = RealApplet.GetProgramIdFromAppletId(_appletId);
|
||||
|
||||
string contentPath = system.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (contentPath.Length == 0)
|
||||
{
|
||||
return ResultCode.AppletLaunchFailed;
|
||||
}
|
||||
|
||||
if (contentPath.StartsWith("@SystemContent"))
|
||||
{
|
||||
contentPath = FileSystem.VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||
}
|
||||
|
||||
if (!system.Device.Processes.LoadNca(contentPath, out var Process))
|
||||
{
|
||||
return ResultCode.AppletLaunchFailed;
|
||||
}
|
||||
|
||||
_applet = system.WindowSystem.TrackProcess(Process.ProcessId, _callerPid, false);
|
||||
_applet.AppletStateChanged += OnAppletStateChanged;
|
||||
|
||||
_applet.AppletState.LaunchableEvent.ReadableEvent.Signal();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ILibraryRealAppletAccessor(RealAppletId appletId, Horizon system, ulong callerPid)
|
||||
{
|
||||
_kernelContext = system.KernelContext;
|
||||
|
||||
_stateChangedEvent = new KEvent(system.KernelContext);
|
||||
_normalOutDataEvent = new KEvent(system.KernelContext);
|
||||
_interactiveOutDataEvent = new KEvent(system.KernelContext);
|
||||
|
||||
_callerPid = callerPid;
|
||||
_appletId = appletId;
|
||||
|
||||
_inChannel = new AppletChannel();
|
||||
_outChannel = new AppletChannel();
|
||||
_interactiveInChannel = new AppletChannel();
|
||||
_interactiveOutChannel = new AppletChannel();
|
||||
_contextChannel = new AppletChannel();
|
||||
|
||||
_outChannel.DataAvailable += OnNormalOutData;
|
||||
_interactiveOutChannel.DataAvailable += OnInteractiveOutData;
|
||||
}
|
||||
|
||||
private void OnAppletStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
_stateChangedEvent.WritableEvent.Signal();
|
||||
}
|
||||
|
||||
private void OnNormalOutData(object sender, EventArgs e)
|
||||
{
|
||||
_normalOutDataEvent.WritableEvent.Signal();
|
||||
}
|
||||
|
||||
private void OnInteractiveOutData(object sender, EventArgs e)
|
||||
{
|
||||
_interactiveOutDataEvent.WritableEvent.Signal();
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetAppletStateChangedEvent() -> handle<copy>
|
||||
public ResultCode GetAppletStateChangedEvent(ServiceCtx context)
|
||||
{
|
||||
if (_stateChangedEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangedEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10)]
|
||||
// Start()
|
||||
public ResultCode Start(ServiceCtx context)
|
||||
{
|
||||
var result = StartAppletProcess(context.Device.System);
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return (ResultCode)_applet.Start(_inChannel, _outChannel, _interactiveInChannel, _interactiveOutChannel, _contextChannel);
|
||||
}
|
||||
|
||||
[CommandCmif(20)]
|
||||
// RequestExit()
|
||||
public ResultCode RequestExit(ServiceCtx context)
|
||||
{
|
||||
_applet.ProcessHandle.SetActivity(false);
|
||||
_applet.AppletState.OnExitRequested();
|
||||
_applet?.ProcessHandle.Terminate();
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(25)]
|
||||
// Terminate()
|
||||
public ResultCode Terminate(ServiceCtx context)
|
||||
{
|
||||
_applet.ProcessHandle.SetActivity(false);
|
||||
_applet.AppletState.OnExitRequested();
|
||||
_applet?.ProcessHandle.Terminate();
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(30)]
|
||||
// GetResult()
|
||||
public ResultCode GetResult(ServiceCtx context)
|
||||
{
|
||||
if (_applet == null)
|
||||
{
|
||||
return ResultCode.LibraryAppletTerminated;
|
||||
}
|
||||
|
||||
return (ResultCode)_applet.GetResult();
|
||||
}
|
||||
|
||||
[CommandCmif(60)]
|
||||
// PresetLibraryAppletGpuTimeSliceZero()
|
||||
public ResultCode PresetLibraryAppletGpuTimeSliceZero(ServiceCtx context)
|
||||
{
|
||||
// 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.
|
||||
// Since we doesn't support applets for now, it's fine to stub it.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(100)]
|
||||
// PushInData(object<nn::am::service::IStorage>)
|
||||
public ResultCode PushInData(ServiceCtx context)
|
||||
{
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
|
||||
_inChannel.PushData(data.Data);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(101)]
|
||||
// PopOutData() -> object<nn::am::service::IStorage>
|
||||
public ResultCode PopOutData(ServiceCtx context)
|
||||
{
|
||||
if (_outChannel.TryPopData(out byte[] data))
|
||||
{
|
||||
MakeObject(context, new IStorage(data));
|
||||
|
||||
_normalOutDataEvent.WritableEvent.Clear();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
[CommandCmif(103)]
|
||||
// PushInteractiveInData(object<nn::am::service::IStorage>)
|
||||
public ResultCode PushInteractiveInData(ServiceCtx context)
|
||||
{
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
|
||||
_interactiveInChannel.PushData(data.Data);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(104)]
|
||||
// PopInteractiveOutData() -> object<nn::am::service::IStorage>
|
||||
public ResultCode PopInteractiveOutData(ServiceCtx context)
|
||||
{
|
||||
if (_interactiveOutChannel.TryPopData(out byte[] data))
|
||||
{
|
||||
MakeObject(context, new IStorage(data));
|
||||
|
||||
_interactiveOutDataEvent.WritableEvent.Clear();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
[CommandCmif(105)]
|
||||
// GetPopOutDataEvent() -> handle<copy>
|
||||
public ResultCode GetPopOutDataEvent(ServiceCtx context)
|
||||
{
|
||||
if (_normalOutDataEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_normalOutDataEvent.ReadableEvent, out _normalOutDataEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_normalOutDataEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(106)]
|
||||
// GetPopInteractiveOutDataEvent() -> handle<copy>
|
||||
public ResultCode GetPopInteractiveOutDataEvent(ServiceCtx context)
|
||||
{
|
||||
if (_interactiveOutDataEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_interactiveOutDataEvent.ReadableEvent, out _interactiveOutDataEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_interactiveOutDataEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(110)]
|
||||
// NeedsToExitProcess()
|
||||
public ResultCode NeedsToExitProcess(ServiceCtx context)
|
||||
{
|
||||
return ResultCode.Stubbed;
|
||||
}
|
||||
|
||||
[CommandCmif(150)]
|
||||
// RequestForAppletToGetForeground()
|
||||
public ResultCode RequestForAppletToGetForeground(ServiceCtx context)
|
||||
{
|
||||
return ResultCode.Stubbed;
|
||||
}
|
||||
|
||||
[CommandCmif(160)] // 2.0.0+
|
||||
// GetIndirectLayerConsumerHandle() -> u64 indirect_layer_consumer_handle
|
||||
public ResultCode GetIndirectLayerConsumerHandle(ServiceCtx context)
|
||||
{
|
||||
if (_applet == null)
|
||||
{
|
||||
return ResultCode.LibraryAppletTerminated;
|
||||
}
|
||||
|
||||
_indirectLayerHandle = _applet.AppletState.IndirectLayerHandles.Add(_applet);
|
||||
|
||||
context.ResponseData.Write((ulong)_indirectLayerHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
if (_stateChangedEventHandle != 0)
|
||||
{
|
||||
_kernelContext.Syscall.CloseHandle(_stateChangedEventHandle);
|
||||
}
|
||||
|
||||
if (_normalOutDataEventHandle != 0)
|
||||
{
|
||||
_kernelContext.Syscall.CloseHandle(_normalOutDataEventHandle);
|
||||
}
|
||||
|
||||
if (_interactiveOutDataEventHandle != 0)
|
||||
{
|
||||
_kernelContext.Syscall.CloseHandle(_interactiveOutDataEventHandle);
|
||||
}
|
||||
|
||||
_applet?.AppletState.IndirectLayerHandles.Delete(_indirectLayerHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
|
||||
{
|
||||
class ILibraryRealAppletSelfAccessor : DisposableIpcService
|
||||
{
|
||||
private readonly KernelContext _kernelContext;
|
||||
private readonly RealApplet _applet;
|
||||
|
||||
private readonly KEvent _normalInDataEvent;
|
||||
private readonly KEvent _interactiveInDataEvent;
|
||||
private int _normalInDataEventHandle;
|
||||
private int _interactiveInDataEventHandle;
|
||||
|
||||
public ILibraryRealAppletSelfAccessor(ServiceCtx context, ulong pid)
|
||||
{
|
||||
var system = context.Device.System;
|
||||
|
||||
_kernelContext = system.KernelContext;
|
||||
_applet = system.WindowSystem.GetByAruId(pid);
|
||||
_normalInDataEvent = new KEvent(system.KernelContext);
|
||||
_interactiveInDataEvent = new KEvent(system.KernelContext);
|
||||
|
||||
_applet.InChannel.DataAvailable += OnNormalInData;
|
||||
_applet.InteractiveInChannel.DataAvailable += OnInteractiveInData;
|
||||
}
|
||||
|
||||
private void OnNormalInData(object sender, EventArgs e)
|
||||
{
|
||||
_normalInDataEvent.WritableEvent.Signal();
|
||||
}
|
||||
|
||||
private void OnInteractiveInData(object sender, EventArgs e)
|
||||
{
|
||||
_interactiveInDataEvent.WritableEvent.Signal();
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// PopInData() -> object<nn::am::service::IStorage>
|
||||
public ResultCode PopInData(ServiceCtx context)
|
||||
{
|
||||
byte[] appletData;
|
||||
|
||||
if (!_applet.InChannel.TryPopData(out appletData))
|
||||
{
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
if (appletData.Length == 0)
|
||||
{
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
MakeObject(context, new IStorage(appletData));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
public ResultCode PushOutData(ServiceCtx context)
|
||||
{
|
||||
if (_applet != null)
|
||||
{
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
_applet.OutChannel.PushData(data.Data);
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
public ResultCode PopInteractiveInData(ServiceCtx context)
|
||||
{
|
||||
byte[] appletData;
|
||||
|
||||
if (!_applet.InteractiveInChannel.TryPopData(out appletData))
|
||||
{
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
if (appletData.Length == 0)
|
||||
{
|
||||
return ResultCode.NotAvailable;
|
||||
}
|
||||
|
||||
MakeObject(context, new IStorage(appletData));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
public ResultCode PushInteractiveOutData(ServiceCtx context)
|
||||
{
|
||||
if (_applet != null)
|
||||
{
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
_applet.InteractiveOutChannel.PushData(data.Data);
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(5)]
|
||||
public ResultCode GetPopInDataEvent(ServiceCtx context)
|
||||
{
|
||||
if (_normalInDataEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_normalInDataEvent.ReadableEvent, out _normalInDataEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_normalInDataEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(6)]
|
||||
public ResultCode GetPopInteractiveInDataEvent(ServiceCtx context)
|
||||
{
|
||||
if (_interactiveInDataEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_interactiveInDataEvent.ReadableEvent, out _interactiveInDataEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_interactiveInDataEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(10)]
|
||||
public ResultCode ExitProcessAndReturn(ServiceCtx context)
|
||||
{
|
||||
_applet.ProcessHandle.Terminate();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(11)]
|
||||
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
||||
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
||||
{
|
||||
LibraryAppletInfo libraryAppletInfo = new();
|
||||
|
||||
libraryAppletInfo.AppletId = (AppletId)_applet.AppletId;
|
||||
libraryAppletInfo.LibraryAppletMode = _applet.LibraryAppletMode;
|
||||
|
||||
context.ResponseData.WriteStruct(libraryAppletInfo);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(12)]
|
||||
// GetMainAppletIdentityInfo() -> nn::am::service::AppletIdentityInfo
|
||||
public ResultCode GetMainAppletIdentityInfo(ServiceCtx context)
|
||||
{
|
||||
AppletIdentifyInfo appletIdentifyInfo = new()
|
||||
{
|
||||
AppletId = AppletId.QLaunch,
|
||||
TitleId = 0x0100000000001000,
|
||||
};
|
||||
|
||||
context.ResponseData.WriteStruct(appletIdentifyInfo);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(13)]
|
||||
// CanUseApplicationCore() -> bool
|
||||
public ResultCode CanUseApplicationCore(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(14)]
|
||||
// GetCallerAppletIdentityInfo() -> nn::am::service::AppletIdentityInfo
|
||||
public ResultCode GetCallerAppletIdentityInfo(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.WriteStruct(GetCallerIdentity(_applet));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(30)]
|
||||
// UnpopInData(nn::am::service::IStorage)
|
||||
public ResultCode UnpopInData(ServiceCtx context)
|
||||
{
|
||||
IStorage data = GetObject<IStorage>(context, 0);
|
||||
|
||||
_applet.InChannel.InsertFrontData(data.Data);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(50)]
|
||||
// ReportVisibleError(nn::err::ErrorCode)
|
||||
public ResultCode ReportVisibleError(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(150)]
|
||||
// ShouldSetGpuTimeSliceManually() -> bool
|
||||
public ResultCode ShouldSetGpuTimeSliceManually(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.ResponseData.Write(false);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(160)]
|
||||
// GetLibraryAppletInfoEx() -> u64 usually 0
|
||||
public ResultCode GetLibraryAppletInfoEx(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.ResponseData.Write((ulong)0);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private static AppletIdentifyInfo GetCallerIdentity(RealApplet applet)
|
||||
{
|
||||
if (applet.CallerApplet != null)
|
||||
{
|
||||
return new AppletIdentifyInfo
|
||||
{
|
||||
AppletId = (AppletId)applet.CallerApplet.AppletId,
|
||||
TitleId = applet.CallerApplet.ProcessHandle.TitleId,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AppletIdentifyInfo
|
||||
{
|
||||
AppletId = AppletId.QLaunch,
|
||||
TitleId = 0x0100000000001000
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
{
|
||||
if (_normalInDataEventHandle != 0)
|
||||
{
|
||||
_kernelContext.Syscall.CloseHandle(_normalInDataEventHandle);
|
||||
}
|
||||
|
||||
if (_interactiveInDataEventHandle != 0)
|
||||
{
|
||||
_kernelContext.Syscall.CloseHandle(_interactiveInDataEventHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -124,6 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7)]
|
||||
[CommandCmif(24)]
|
||||
[CommandCmif(26)]
|
||||
|
||||
|
@ -22,7 +22,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
int libraryAppletMode = context.RequestData.ReadInt32();
|
||||
#pragma warning restore IDE0059
|
||||
|
||||
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System, _pid));
|
||||
if (ShouldBeReal(context.Device.System,appletId))
|
||||
{
|
||||
RealAppletId realAppletId = (RealAppletId)appletId;
|
||||
MakeObject(context, new ILibraryRealAppletAccessor(realAppletId, context.Device.System, _pid));
|
||||
}
|
||||
else
|
||||
{
|
||||
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System, _pid));
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@ -95,5 +103,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public bool ShouldBeReal(Horizon horizon, AppletId appletId)
|
||||
{
|
||||
return horizon.Device.Configuration.HostUIHandler.IsAppletReal((RealAppletId)appletId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
|
||||
namespace Ryujinx.HLE.UI
|
||||
{
|
||||
@ -71,5 +72,10 @@ namespace Ryujinx.HLE.UI
|
||||
/// Gets the UI theme and returns true if is dark mode.
|
||||
/// </summary>
|
||||
bool IsDarkMode();
|
||||
|
||||
/// <summary>
|
||||
/// Gets weather or not the applet is real in the system
|
||||
/// </summary>
|
||||
bool IsAppletReal(RealAppletId appletId);
|
||||
}
|
||||
}
|
||||
|
@ -5647,6 +5647,31 @@
|
||||
"zh_TW": "日誌"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabRealApplets",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Real Applets",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabLoggingLogging",
|
||||
"Translations": {
|
||||
|
@ -150,6 +150,24 @@ namespace Ryujinx.Headless
|
||||
if (NeedsOverride(nameof(IgnoreControllerApplet)))
|
||||
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
|
||||
|
||||
if (NeedsOverride(nameof(MissingAppletsAsReal)))
|
||||
MissingAppletsAsReal = configurationState.System.MissingAppletsAsReal;
|
||||
|
||||
if (NeedsOverride(nameof(SoftwareKeyboardIsReal)))
|
||||
SoftwareKeyboardIsReal = configurationState.System.SoftwareKeyboardIsReal;
|
||||
|
||||
if (NeedsOverride(nameof(BrowserIsReal)))
|
||||
BrowserIsReal = configurationState.System.BrowserIsReal;
|
||||
|
||||
if (NeedsOverride(nameof(ControllerIsReal)))
|
||||
ControllerIsReal = configurationState.System.ControllerIsReal;
|
||||
|
||||
if (NeedsOverride(nameof(PlayerSelectIsReal)))
|
||||
PlayerSelectIsReal = configurationState.System.PlayerSelectIsReal;
|
||||
|
||||
if (NeedsOverride(nameof(CabinetIsReal)))
|
||||
CabinetIsReal = configurationState.System.CabinetIsReal;
|
||||
|
||||
return;
|
||||
|
||||
bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey)));
|
||||
@ -411,8 +429,26 @@ namespace Ryujinx.Headless
|
||||
public bool IgnoreMissingServices { get; set; }
|
||||
|
||||
[Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")]
|
||||
public bool IgnoreControllerApplet { get; set; }
|
||||
|
||||
public bool IgnoreControllerApplet { get; set; }
|
||||
|
||||
[Option("missing-applets-as-real", Required = false, Default = false, HelpText = "Runs the missing applets as real applets.")]
|
||||
public bool MissingAppletsAsReal { get; set; }
|
||||
|
||||
[Option("is-softwarekeyboard-real", Required = false, Default = false, HelpText = "Runs the software keyboard applets as real")]
|
||||
public bool SoftwareKeyboardIsReal { get; set; }
|
||||
|
||||
[Option("is-browser-real", Required = false, Default = false, HelpText = "Runs the browser applets as real")]
|
||||
public bool BrowserIsReal { get; set; }
|
||||
|
||||
[Option("is-controller-real", Required = false, Default = false, HelpText = "Runs the controller applets as real")]
|
||||
public bool ControllerIsReal { get; set; }
|
||||
|
||||
[Option("is-playerselect-real", Required = false, Default = false, HelpText = "Runs the player select applets as real")]
|
||||
public bool PlayerSelectIsReal { get; set; }
|
||||
|
||||
[Option("is-cabinet-real", Required = false, Default = false, HelpText = "Runs the cabinet applets as real")]
|
||||
public bool CabinetIsReal { get; set; }
|
||||
|
||||
// Values
|
||||
|
||||
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
|
||||
|
@ -13,6 +13,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
@ -568,5 +569,10 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsAppletReal(RealAppletId appletId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,5 +172,11 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="Assets\Fonts\Mono\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="UI\Views\Settings\SettingsRealApplets.axaml.cs">
|
||||
<DependentUpon>SettingsRealApplets.axaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -15,6 +15,7 @@ using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
@ -318,5 +319,36 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
return ConfigurationState.Instance.UI.BaseStyle.Value == "Dark";
|
||||
}
|
||||
|
||||
public bool IsMissingAppletsAsReal()
|
||||
{
|
||||
return ConfigurationState.Instance.System.MissingAppletsAsReal;
|
||||
}
|
||||
|
||||
public bool IsAppletReal(RealAppletId appletId)
|
||||
{
|
||||
bool softwareKeyboardReal = ConfigurationState.Instance.System.SoftwareKeyboardIsReal;
|
||||
bool browserReal = ConfigurationState.Instance.System.BrowserIsReal;
|
||||
bool controllerReal = ConfigurationState.Instance.System.ControllerIsReal;
|
||||
bool playerSelectReal = ConfigurationState.Instance.System.PlayerSelectIsReal;
|
||||
bool cabinetReal = ConfigurationState.Instance.System.CabinetIsReal;
|
||||
bool missingAppletsAsReal = ConfigurationState.Instance.System.MissingAppletsAsReal;
|
||||
|
||||
switch (appletId)
|
||||
{
|
||||
case RealAppletId.LibraryAppletSwkbd:
|
||||
return softwareKeyboardReal;
|
||||
case RealAppletId.LibraryAppletWeb:
|
||||
return browserReal;
|
||||
case RealAppletId.LibraryAppletController:
|
||||
return controllerReal;
|
||||
case RealAppletId.LibraryAppletPlayerSelect:
|
||||
return playerSelectReal;
|
||||
case RealAppletId.LibraryAppletCabinet:
|
||||
return cabinetReal;
|
||||
default:
|
||||
return missingAppletsAsReal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
src/Ryujinx/UI/Helpers/Converters/BoolToIndexConverter.cs
Normal file
27
src/Ryujinx/UI/Helpers/Converters/BoolToIndexConverter.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
public class BoolToIndexConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return boolValue ? 0 : 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is int index)
|
||||
{
|
||||
return index == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
21
src/Ryujinx/UI/ViewModels/SettingsRealAppsViewModel.cs
Normal file
21
src/Ryujinx/UI/ViewModels/SettingsRealAppsViewModel.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public partial class SettingsRealAppsViewModel : BaseModel
|
||||
{
|
||||
private readonly SettingsViewModel _baseViewModel;
|
||||
|
||||
public SettingsRealAppsViewModel() {}
|
||||
|
||||
public SettingsRealAppsViewModel(SettingsViewModel settingsVm)
|
||||
{
|
||||
_baseViewModel = settingsVm;
|
||||
}
|
||||
}
|
||||
}
|
@ -190,6 +190,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableInternetAccess { get; set; }
|
||||
public bool EnableFsIntegrityChecks { get; set; }
|
||||
public bool IgnoreMissingServices { get; set; }
|
||||
public bool MissingAppletsAsReal { get; set; }
|
||||
public bool SoftwareKeyboardIsReal { get; set; }
|
||||
public bool BrowserIsReal { get; set; }
|
||||
public bool ControllerIsReal { get; set; }
|
||||
public bool PlayerSelectIsReal { get; set; }
|
||||
public bool CabinetIsReal { get; set; }
|
||||
public MemoryConfiguration DramSize { get; set; }
|
||||
public bool EnableShaderCache { get; set; }
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
@ -518,6 +524,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
DramSize = config.System.DramSize;
|
||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||
IgnoreApplet = config.System.IgnoreApplet;
|
||||
|
||||
MissingAppletsAsReal = config.System.MissingAppletsAsReal;
|
||||
SoftwareKeyboardIsReal = config.System.SoftwareKeyboardIsReal;
|
||||
BrowserIsReal = config.System.BrowserIsReal;
|
||||
ControllerIsReal = config.System.ControllerIsReal;
|
||||
PlayerSelectIsReal = config.System.PlayerSelectIsReal;
|
||||
CabinetIsReal = config.System.CabinetIsReal;
|
||||
|
||||
// CPU
|
||||
EnablePptc = config.System.EnablePtc;
|
||||
@ -622,6 +635,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.System.DramSize.Value = DramSize;
|
||||
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
|
||||
config.System.IgnoreApplet.Value = IgnoreApplet;
|
||||
config.System.MissingAppletsAsReal.Value = MissingAppletsAsReal;
|
||||
config.System.SoftwareKeyboardIsReal.Value = SoftwareKeyboardIsReal;
|
||||
config.System.BrowserIsReal.Value = BrowserIsReal;
|
||||
config.System.ControllerIsReal.Value = ControllerIsReal;
|
||||
config.System.PlayerSelectIsReal.Value = PlayerSelectIsReal;
|
||||
config.System.CabinetIsReal.Value = CabinetIsReal;
|
||||
|
||||
// CPU
|
||||
config.System.EnablePtc.Value = EnablePptc;
|
||||
|
142
src/Ryujinx/UI/Views/Settings/SettingsRealApplets.axaml
Normal file
142
src/Ryujinx/UI/Views/Settings/SettingsRealApplets.axaml
Normal file
@ -0,0 +1,142 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsRealAppsView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
|
||||
<!-- Resources -->
|
||||
<UserControl.Resources>
|
||||
<helpers:BoolToIndexConverter x:Key="BoolToIndexConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<ScrollViewer HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<!-- Header -->
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
Classes="h1"
|
||||
Text="Real Applets" />
|
||||
<TextBlock Foreground="{DynamicResource SecondaryTextColor}"
|
||||
TextDecorations="Underline"
|
||||
Text="This is a collection of real applet options." />
|
||||
|
||||
<!-- Missing Applets Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding MissingAppletsAsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Skip" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Missing Applets" />
|
||||
</Grid>
|
||||
|
||||
<!-- Software Keyboard Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding SoftwareKeyboardIsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Software" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Software Keyboard" />
|
||||
</Grid>
|
||||
|
||||
<!-- Browser Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding BrowserIsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Software" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Browser" />
|
||||
</Grid>
|
||||
|
||||
<!-- Controller Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding ControllerIsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Software" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Controller" />
|
||||
</Grid>
|
||||
|
||||
<!-- Player Select Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding PlayerSelectIsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Software" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Player Select" />
|
||||
</Grid>
|
||||
|
||||
<!-- Cabinet Row -->
|
||||
<Grid Margin="10,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox Width="100"
|
||||
SelectedIndex="{Binding CabinetIsReal, Mode=TwoWay, Converter={StaticResource BoolToIndexConverter}}">
|
||||
<ComboBoxItem Content="Real" />
|
||||
<ComboBoxItem Content="Software" />
|
||||
</ComboBox>
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"
|
||||
Text="Cabinet" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
15
src/Ryujinx/UI/Views/Settings/SettingsRealApplets.axaml.cs
Normal file
15
src/Ryujinx/UI/Views/Settings/SettingsRealApplets.axaml.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsRealAppsView : UserControl
|
||||
{
|
||||
public SettingsRealAppsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
<settings:SettingsAudioView Name="AudioPage" />
|
||||
<settings:SettingsNetworkView Name="NetworkPage" />
|
||||
<settings:SettingsLoggingView Name="LoggingPage" />
|
||||
<settings:SettingsRealAppsView Name="RealAppletsPage" />
|
||||
<settings:SettingsHacksView Name="HacksPage" />
|
||||
</Grid>
|
||||
<ui:NavigationView
|
||||
@ -93,6 +94,10 @@
|
||||
Content="{ext:Locale SettingsTabLogging}"
|
||||
Tag="LoggingPage"
|
||||
IconSource="Document" />
|
||||
<ui:NavigationViewItem
|
||||
Content="{ext:Locale SettingsTabRealApplets}"
|
||||
Tag="RealAppletsPage"
|
||||
IconSource="Document" />
|
||||
<ui:NavigationViewItem
|
||||
IsVisible="{Binding ShowDirtyHacks}"
|
||||
Content="Dirty Hacks"
|
||||
|
@ -4,6 +4,7 @@ using Avalonia.Input;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Views.Settings;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
@ -94,6 +95,9 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
case nameof(LoggingPage):
|
||||
NavPanel.Content = LoggingPage;
|
||||
break;
|
||||
case nameof(RealAppletsPage):
|
||||
NavPanel.Content = RealAppletsPage;
|
||||
break;
|
||||
case nameof(HacksPage):
|
||||
HacksPage.DataContext = ViewModel;
|
||||
NavPanel.Content = HacksPage;
|
||||
|
@ -439,6 +439,36 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// </summary>
|
||||
public ulong[] DirtyHacks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Runs missing applets as real ones
|
||||
/// </summary>
|
||||
public bool MissingAppletsAsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the SoftwareKeyboard as a real applet
|
||||
/// </summary>
|
||||
public bool SoftwareKeyboardIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Browser as a real applet
|
||||
/// </summary>
|
||||
public bool BrowserIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Controller as a real applet
|
||||
/// </summary>
|
||||
public bool ControllerIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the PlayerSelect as a real applet
|
||||
/// </summary>
|
||||
public bool PlayerSelectIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Cabinet as a real applet
|
||||
/// </summary>
|
||||
public bool CabinetIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads a configuration file from disk
|
||||
/// </summary>
|
||||
|
@ -100,6 +100,13 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.IgnoreApplet.Value = cff.IgnoreApplet;
|
||||
System.UseHypervisor.Value = cff.UseHypervisor;
|
||||
|
||||
System.MissingAppletsAsReal.Value = cff.MissingAppletsAsReal;
|
||||
System.SoftwareKeyboardIsReal.Value = cff.SoftwareKeyboardIsReal;
|
||||
System.BrowserIsReal.Value = cff.BrowserIsReal;
|
||||
System.ControllerIsReal.Value = cff.ControllerIsReal;
|
||||
System.PlayerSelectIsReal.Value = cff.PlayerSelectIsReal;
|
||||
System.CabinetIsReal.Value = cff.CabinetIsReal;
|
||||
|
||||
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;
|
||||
UI.GuiColumns.IconColumn.Value = cff.GuiColumns.IconColumn;
|
||||
UI.GuiColumns.AppColumn.Value = cff.GuiColumns.AppColumn;
|
||||
@ -416,6 +423,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
// This was accidentally enabled by default when it was PRed. That is not what we want,
|
||||
// so as a compromise users who want to use it will simply need to re-enable it once after updating.
|
||||
cff.IgnoreApplet = false;
|
||||
cff.MissingAppletsAsReal = false;
|
||||
}),
|
||||
(60, static cff => cff.StartNoUI = false),
|
||||
(61, static cff =>
|
||||
|
@ -383,6 +383,36 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> UseHypervisor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches missing applets as real apps
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> MissingAppletsAsReal { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the SoftwareKeyboard as a real applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> SoftwareKeyboardIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Browser as a real applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> BrowserIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Controller as a real applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> ControllerIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the PlayerSelect as a real applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> PlayerSelectIsReal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Launches the Cabinet as a real applet
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> CabinetIsReal { get; set; }
|
||||
|
||||
public SystemSection()
|
||||
{
|
||||
Language = new ReactiveObject<Language>();
|
||||
@ -423,6 +453,19 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
AudioVolume.LogChangesToValue(nameof(AudioVolume));
|
||||
UseHypervisor = new ReactiveObject<bool>();
|
||||
UseHypervisor.LogChangesToValue(nameof(UseHypervisor));
|
||||
|
||||
MissingAppletsAsReal = new ReactiveObject<bool>();
|
||||
MissingAppletsAsReal.LogChangesToValue(nameof(MissingAppletsAsReal));
|
||||
SoftwareKeyboardIsReal = new ReactiveObject<bool>();
|
||||
SoftwareKeyboardIsReal.LogChangesToValue(nameof(SoftwareKeyboardIsReal));
|
||||
BrowserIsReal = new ReactiveObject<bool>();
|
||||
BrowserIsReal.LogChangesToValue(nameof(BrowserIsReal));
|
||||
ControllerIsReal = new ReactiveObject<bool>();
|
||||
ControllerIsReal.LogChangesToValue(nameof(ControllerIsReal));
|
||||
PlayerSelectIsReal = new ReactiveObject<bool>();
|
||||
PlayerSelectIsReal.LogChangesToValue(nameof(PlayerSelectIsReal));
|
||||
CabinetIsReal = new ReactiveObject<bool>();
|
||||
CabinetIsReal.LogChangesToValue(nameof(CabinetIsReal));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,12 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
LdnServer = Multiplayer.LdnServer,
|
||||
ShowDirtyHacks = Hacks.ShowDirtyHacks,
|
||||
DirtyHacks = Hacks.EnabledHacks.Select(it => it.Pack()).ToArray(),
|
||||
MissingAppletsAsReal = System.MissingAppletsAsReal,
|
||||
SoftwareKeyboardIsReal = System.SoftwareKeyboardIsReal,
|
||||
BrowserIsReal = System.BrowserIsReal,
|
||||
ControllerIsReal = System.ControllerIsReal,
|
||||
PlayerSelectIsReal = System.PlayerSelectIsReal,
|
||||
CabinetIsReal = System.CabinetIsReal,
|
||||
};
|
||||
|
||||
return configurationFile;
|
||||
@ -199,6 +205,12 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
System.IgnoreMissingServices.Value = false;
|
||||
System.IgnoreApplet.Value = false;
|
||||
System.MissingAppletsAsReal.Value = false;
|
||||
System.SoftwareKeyboardIsReal.Value = false;
|
||||
System.BrowserIsReal.Value = false;
|
||||
System.ControllerIsReal.Value = false;
|
||||
System.PlayerSelectIsReal.Value = false;
|
||||
System.CabinetIsReal.Value = false;
|
||||
System.UseHypervisor.Value = true;
|
||||
Multiplayer.LanInterfaceId.Value = "0";
|
||||
Multiplayer.Mode.Value = MultiplayerMode.Disabled;
|
||||
|
Loading…
x
Reference in New Issue
Block a user