Implement QLaunch
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
This commit is contained in:
parent
81c6aeec28
commit
50926d6a2f
@ -39,6 +39,7 @@ namespace Ryujinx.Common.Logging
|
||||
ServiceFs,
|
||||
ServiceHid,
|
||||
ServiceIrs,
|
||||
ServiceLbl,
|
||||
ServiceLdn,
|
||||
ServiceLdr,
|
||||
ServiceLm,
|
||||
@ -50,11 +51,14 @@ namespace Ryujinx.Common.Logging
|
||||
ServiceNgct,
|
||||
ServiceNifm,
|
||||
ServiceNim,
|
||||
ServiceNotif,
|
||||
ServiceNs,
|
||||
ServiceNsd,
|
||||
ServiceNtc,
|
||||
ServiceNv,
|
||||
ServiceNpns,
|
||||
ServiceOlsc,
|
||||
ServiceOvln,
|
||||
ServicePctl,
|
||||
ServicePcv,
|
||||
ServicePl,
|
||||
@ -72,6 +76,6 @@ namespace Ryujinx.Common.Logging
|
||||
TamperMachine,
|
||||
UI,
|
||||
Vic,
|
||||
XCIFileTrimmer
|
||||
XCIFileTrimmer,
|
||||
}
|
||||
}
|
||||
|
@ -16,5 +16,10 @@ namespace Ryujinx.Common.Utilities
|
||||
|
||||
public static UInt128 NextUInt128(this Random rand) =>
|
||||
new((ulong)rand.NextInt64(), (ulong)rand.NextInt64());
|
||||
|
||||
public static UInt128 CreateRandom()
|
||||
{
|
||||
return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,12 @@ using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
@ -195,10 +198,17 @@ namespace Ryujinx.HLE
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
public EnabledDirtyHack[] Hacks { internal get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of title ids found byApplicationLibrary.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly IImmutableList<RyuApplicationData> Titles;
|
||||
|
||||
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
|
||||
LibHacHorizonManager libHacHorizonManager,
|
||||
ContentManager contentManager,
|
||||
AccountManager accountManager,
|
||||
IImmutableList<RyuApplicationData> titles,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
IRenderer gpuRenderer,
|
||||
IHardwareDeviceDriver audioDeviceDriver,
|
||||
@ -231,6 +241,7 @@ namespace Ryujinx.HLE
|
||||
LibHacHorizonManager = libHacHorizonManager;
|
||||
AccountManager = accountManager;
|
||||
ContentManager = contentManager;
|
||||
Titles = titles;
|
||||
UserChannelPersistence = userChannelPersistence;
|
||||
GpuRenderer = gpuRenderer;
|
||||
AudioDeviceDriver = audioDeviceDriver;
|
||||
|
@ -0,0 +1,32 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
class IAdministrator : IpcService
|
||||
{
|
||||
public IAdministrator(UserId userId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CheckAvailability()
|
||||
public ResultCode CheckAvailability(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(250)]
|
||||
// IsLinkedWithNintendoAccount() -> bool
|
||||
public ResultCode IsLinkedWithNintendoAccount(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
class IManagerForSystemService : IpcService
|
||||
@ -43,5 +45,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
return _managerServer.LoadIdTokenCache(context);
|
||||
}
|
||||
|
||||
[CommandCmif(143)]
|
||||
// GetNetworkServiceLicenseCacheEx()
|
||||
public ResultCode GetNetworkServiceLicenseCacheEx(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
class IProfileEditor : IpcService
|
||||
@ -36,6 +38,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
return _profileServer.LoadImage(context);
|
||||
}
|
||||
|
||||
[CommandCmif(30)]
|
||||
// GetImageId()
|
||||
public ResultCode GetImageId(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(100)]
|
||||
// Store(nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x19>)
|
||||
|
@ -125,5 +125,24 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(250)]
|
||||
// GetBaasAccountAdministrator(nn::account::Uid) -> object<nn::account::baas::IAdministrator>
|
||||
public ResultCode GetBaasAccountAdministrator(ServiceCtx context)
|
||||
{
|
||||
ResultCode resultCode = _applicationServiceServer.CheckUserId(context, out UserId userId);
|
||||
|
||||
if (resultCode != ResultCode.Success)
|
||||
{
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
MakeObject(context, new IAdministrator(userId));
|
||||
|
||||
// Doesn't occur in our case.
|
||||
// return ResultCode.NullObject;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
|
||||
{
|
||||
class ISystemProcessCommonFunctions : IpcService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -155,6 +155,20 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(31)]
|
||||
[CommandCmif(32)]
|
||||
// GetReaderLockAccessorEx(u32) -> object<nn::am::service::ILockAccessor>
|
||||
public ResultCode GetReaderLockAccessorEx(ServiceCtx context)
|
||||
{
|
||||
int lockId = context.RequestData.ReadInt32();
|
||||
|
||||
MakeObject(context, new ILockAccessor(lockId, context.Device.System));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(50)] // 3.0.0+
|
||||
// IsVrModeEnabled() -> b8
|
||||
|
@ -0,0 +1,31 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
||||
{
|
||||
class ICradleFirmwareUpdater : IpcService
|
||||
{
|
||||
[CommandCmif(1)]
|
||||
// Finish()
|
||||
public ResultCode Finish(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// GetUpdateDeviceStatus()
|
||||
public ResultCode GetUpdateDeviceStatus(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// GetUpdateProgress()
|
||||
public ResultCode GetUpdateProgress(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,5 +42,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(30)]
|
||||
// OpenCradleFirmwareUpdater() -> ICradleFirmwareUpdater.
|
||||
public ResultCode OpenCradleFirmwareUpdater(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ICradleFirmwareUpdater());
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(11)]
|
||||
// LockForeground()
|
||||
public ResultCode LockForeground(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
[CommandCmif(12)]
|
||||
// UnlockForeground()
|
||||
public ResultCode UnlockForeground(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(21)]
|
||||
// GetPopFromGeneralChannelEvent() -> handle<copy>
|
||||
public ResultCode GetPopFromGeneralChannelEvent(ServiceCtx context)
|
||||
@ -44,5 +61,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(31)]
|
||||
// GetWriterLockAccessorEx(i32) -> object<nn::am::service::ILockAccessor>
|
||||
public ResultCode GetWriterLockAccessorEx(ServiceCtx context)
|
||||
{
|
||||
int lockId = context.RequestData.ReadInt32();
|
||||
|
||||
MakeObject(context, new ILockAccessor(lockId, context.Device.System));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +276,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(60)]
|
||||
// OverrideAutoSleepTimeAndDimmingTime()
|
||||
public ResultCode OverrideAutoSleepTimeAndDimmingTime(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(62)]
|
||||
// SetIdleTimeDetectionExtension(u32)
|
||||
public ResultCode SetIdleTimeDetectionExtension(ServiceCtx context)
|
||||
@ -356,6 +364,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(72)]
|
||||
// SetInputDetectionPolicy()
|
||||
public ResultCode SetInputDetectionPolicy(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(80)] // 4.0.0+
|
||||
// SetWirelessPriorityMode(s32 wireless_priority_mode)
|
||||
public ResultCode SetWirelessPriorityMode(ServiceCtx context)
|
||||
|
@ -35,5 +35,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(450)]
|
||||
// ISystemProcessCommonFunctions() -> object<nn::am::service::ISystemProcessCommonFunctions>
|
||||
public ResultCode ISystemProcessCommonFunctions(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ISystemProcessCommonFunctions());
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
94
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs
Normal file
94
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/ILockAccessor.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
||||
{
|
||||
class ILockAccessor : IpcService
|
||||
{
|
||||
private readonly int _lockId;
|
||||
private bool _isLocked;
|
||||
private KEvent _lockEvent;
|
||||
private int _lockEventHandle;
|
||||
|
||||
public ILockAccessor(int lockId, Horizon system)
|
||||
{
|
||||
_lockId = lockId;
|
||||
_isLocked = false;
|
||||
_lockEvent = new KEvent(system.KernelContext);
|
||||
_lockEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
// TryLock(bool) -> (bool, IpcService)
|
||||
public ResultCode TryLock(ServiceCtx context)
|
||||
{
|
||||
bool returnHandle = context.RequestData.ReadBoolean();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
if (_lockEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_lockEvent.ReadableEvent, out _lockEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
_isLocked = true;
|
||||
_lockEvent.ReadableEvent.Signal();
|
||||
|
||||
context.ResponseData.Write(_isLocked);
|
||||
if (returnHandle)
|
||||
{
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_lockEventHandle);
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// Unlock()
|
||||
public ResultCode Unlock(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
_isLocked = false;
|
||||
_lockEvent.ReadableEvent.Signal();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// GetEvent() -> handle<copy>
|
||||
public ResultCode GetEvent(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
if (_lockEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_lockEvent.ReadableEvent, out _lockEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_lockEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(4)]
|
||||
// IsLocked() -> bool
|
||||
public ResultCode IsLocked(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
context.ResponseData.Write(_isLocked);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
};
|
191
src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs
Normal file
191
src/Ryujinx.HLE/HOS/Services/Audio/IAudioController.cs
Normal file
@ -0,0 +1,191 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Audio
|
||||
{
|
||||
[Service("audctl")]
|
||||
class IAudioController : IpcService
|
||||
{
|
||||
public IAudioController(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(9)]
|
||||
// GetAudioOutputMode(s32) -> s32
|
||||
public ResultCode GetAudioOutputMode(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0); // todo?
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10)]
|
||||
// SetAudioOutputMode(s32, s32)
|
||||
public ResultCode SetAudioOutputMode(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(11)]
|
||||
// SetForceMutePolicy(u32)
|
||||
public ResultCode SetForceMutePolicy(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(12)]
|
||||
// GetForceMutePolicy() -> u32
|
||||
public ResultCode GetForceMutePolicy(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(13)]
|
||||
// GetOutputModeSetting(u32) -> u32
|
||||
public ResultCode GetOutputModeSetting(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(18)] // 3.0.0+
|
||||
// GetHeadphoneOutputLevelMode() -> u32
|
||||
public ResultCode GetHeadphoneOutputLevelMode(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(31)] // 13.0.0+
|
||||
// IsSpeakerAutoMuteEnabled() -> b8
|
||||
public ResultCode IsSpeakerAutoMuteEnabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(5000)]
|
||||
// Unknown5000() -> bool // 19.0.0+
|
||||
public ResultCode Unknown5000(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IAudioController(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10000)]
|
||||
// NotifyAudioOutputTargetForPlayReport()
|
||||
public ResultCode NotifyAudioOutputTargetForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10001)]
|
||||
// NotifyAudioOutputChannelCountForPlayReport()
|
||||
public ResultCode NotifyAudioOutputChannelCountForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10002)]
|
||||
// NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport()
|
||||
public ResultCode NotifyUnsupportedUsbOutputDeviceAttachedForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10100)]
|
||||
// GetAudioVolumeDataForPlayReport()
|
||||
public ResultCode GetAudioVolumeDataForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10101)]
|
||||
// BindAudioVolumeUpdateEventForPlayReport()
|
||||
public ResultCode BindAudioVolumeUpdateEventForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10102)]
|
||||
// BindAudioOutputTargetUpdateEventForPlayReport()
|
||||
public ResultCode BindAudioOutputTargetUpdateEventForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10103)]
|
||||
// GetAudioOutputTargetForPlayReport()
|
||||
public ResultCode GetAudioOutputTargetForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10104)]
|
||||
// GetAudioOutputChannelCountForPlayReport()
|
||||
public ResultCode GetAudioOutputChannelCountForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10105)]
|
||||
// BindAudioOutputChannelCountUpdateEventForPlayReport()
|
||||
public ResultCode BindAudioOutputChannelCountUpdateEventForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10106)]
|
||||
// GetDefaultAudioOutputTargetForPlayReport()
|
||||
public ResultCode GetDefaultAudioOutputTargetForPlayReport(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(50000)]
|
||||
// SetAnalogInputBoostGainForPrototyping()
|
||||
public ResultCode SetAnalogInputBoostGainForPrototyping(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAudio);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.BluetoothManager.BtmSystem
|
||||
{
|
||||
class IBtmSystemCore : IpcService
|
||||
{
|
||||
public KEvent _radioEvent;
|
||||
public int _radioEventhandle;
|
||||
|
||||
public KEvent _gamepadPairingEvent;
|
||||
public int _gamepadPairingEventHandle;
|
||||
|
||||
public IBtmSystemCore() { }
|
||||
|
||||
[CommandCmif(6)]
|
||||
// IsRadioEnabled() -> b8
|
||||
public ResultCode IsRadioEnabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceBtm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7)] // 3.0.0+
|
||||
// AcquireRadioEvent() -> (byte<1>, handle<copy>)
|
||||
public ResultCode AcquireRadioEvent(ServiceCtx context)
|
||||
{
|
||||
Result result = Result.Success;
|
||||
|
||||
if (_radioEventhandle == 0)
|
||||
{
|
||||
_radioEvent = new KEvent(context.Device.System.KernelContext);
|
||||
|
||||
result = context.Process.HandleTable.GenerateHandle(_radioEvent.ReadableEvent, out _radioEventhandle);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
// NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not.
|
||||
Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_radioEventhandle);
|
||||
|
||||
context.ResponseData.Write(result == Result.Success ? 1 : 0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(8)] // 3.0.0+
|
||||
// AcquireGamepadPairingEvent() -> (byte<1>, handle<copy>)
|
||||
public ResultCode AcquireGamepadPairingEvent(ServiceCtx context)
|
||||
{
|
||||
Result result = Result.Success;
|
||||
|
||||
if (_gamepadPairingEventHandle == 0)
|
||||
{
|
||||
_gamepadPairingEvent = new KEvent(context.Device.System.KernelContext);
|
||||
|
||||
result = context.Process.HandleTable.GenerateHandle(_gamepadPairingEvent.ReadableEvent, out _gamepadPairingEventHandle);
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
// NOTE: We use a Logging instead of an exception because the call return a boolean if succeed or not.
|
||||
Logger.Error?.Print(LogClass.ServiceBsd, "Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gamepadPairingEventHandle);
|
||||
|
||||
context.ResponseData.Write(result == Result.Success ? 1 : 0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,28 @@
|
||||
using Ryujinx.HLE.HOS.Services.BluetoothManager.BtmSystem;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.BluetoothManager
|
||||
{
|
||||
[Service("btm:sys")]
|
||||
class IBtmSystem : IpcService
|
||||
{
|
||||
public IBtmSystem(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetCoreImpl() -> object<nn::btm::IBtmSystemCore>
|
||||
public ResultCode GetCoreImpl(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IBtmSystemCore());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(6)]
|
||||
// IsRadioEnabled() -> b8
|
||||
public ResultCode IsRadioEnabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,20 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Erpt
|
||||
{
|
||||
[Service("erpt:c")]
|
||||
class IContext : IpcService
|
||||
{
|
||||
public IContext(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// SubmitContext(buffer<unknown, 5>, buffer<unknown, 5>) -> u32
|
||||
public ResultCode SubmitContext(ServiceCtx context)
|
||||
{
|
||||
Logger.Info?.PrintStub(LogClass.Service, $"ContextEntry size: {context.Request.SendBuff[0].Size}");
|
||||
Logger.Info?.PrintStub(LogClass.Service, $"FieldList size: {context.Request.SendBuff[1].Size}");
|
||||
context.ResponseData.Write(0);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
80
src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs
Normal file
80
src/Ryujinx.HLE/HOS/Services/Hid/ButtonDevice.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button;
|
||||
using System;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum ButtonDeviceType
|
||||
{
|
||||
HomeButton,
|
||||
SleepButton,
|
||||
CaptureButton,
|
||||
}
|
||||
|
||||
public class ButtonDevice : BaseDevice
|
||||
{
|
||||
private ButtonDeviceType _type;
|
||||
private KEvent _event;
|
||||
private bool isDown;
|
||||
|
||||
|
||||
public ButtonDevice(Switch device, bool active, ButtonDeviceType type) : base(device, active)
|
||||
{
|
||||
_type = type;
|
||||
_event = new KEvent(device.System.KernelContext);
|
||||
}
|
||||
|
||||
internal ref KEvent GetEvent()
|
||||
{
|
||||
return ref _event;
|
||||
}
|
||||
|
||||
private ref RingLifo<ButtonState> GetButtonStateLifo()
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case ButtonDeviceType.HomeButton:
|
||||
return ref _device.Hid.SharedMemory.HomeButton;
|
||||
case ButtonDeviceType.SleepButton:
|
||||
return ref _device.Hid.SharedMemory.SleepButton;
|
||||
case ButtonDeviceType.CaptureButton:
|
||||
return ref _device.Hid.SharedMemory.CaptureButton;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(_type));
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(bool state)
|
||||
{
|
||||
ref RingLifo<ButtonState> lifo = ref GetButtonStateLifo();
|
||||
|
||||
if (!Active)
|
||||
{
|
||||
lifo.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldSignal = state != isDown;
|
||||
isDown = state;
|
||||
|
||||
if (!shouldSignal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref ButtonState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||
|
||||
ButtonState newState = new()
|
||||
{
|
||||
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||
Buttons = state ? 1UL : 0UL
|
||||
};
|
||||
|
||||
lifo.Write(ref newState);
|
||||
|
||||
_event.ReadableEvent.Signal();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||
@ -32,6 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
public DebugMouseDevice DebugMouse;
|
||||
public KeyboardDevice Keyboard;
|
||||
public NpadDevices Npads;
|
||||
public ButtonDevice HomeButton;
|
||||
public ButtonDevice SleepButton;
|
||||
public ButtonDevice CaptureButton;
|
||||
|
||||
private static void CheckTypeSizeOrThrow<T>(int expectedSize)
|
||||
{
|
||||
@ -48,6 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
||||
CheckTypeSizeOrThrow<RingLifo<DebugMouseState>>(0x350);
|
||||
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
||||
CheckTypeSizeOrThrow<RingLifo<ButtonState>>(0x1B8);
|
||||
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
||||
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
||||
}
|
||||
@ -70,6 +75,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
DebugMouse = new DebugMouseDevice(_device, false);
|
||||
Keyboard = new KeyboardDevice(_device, false);
|
||||
Npads = new NpadDevices(_device, true);
|
||||
HomeButton = new ButtonDevice(_device, true, ButtonDeviceType.HomeButton);
|
||||
SleepButton = new ButtonDevice(_device, true, ButtonDeviceType.SleepButton);
|
||||
CaptureButton = new ButtonDevice(_device, true, ButtonDeviceType.CaptureButton);
|
||||
}
|
||||
|
||||
public void RefreshInputConfig(List<InputConfig> inputConfig)
|
||||
|
@ -1,13 +1,135 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
[Service("hid:sys")]
|
||||
class IHidSystemServer : IpcService
|
||||
{
|
||||
public IHidSystemServer(ServiceCtx context) { }
|
||||
private KEvent _deviceRegisteredEventForControllerSupport;
|
||||
private int _deviceRegisteredEventForControllerSupportHandle;
|
||||
|
||||
private KEvent _connectionTriggerTimeoutEvent;
|
||||
private int _connectionTriggerTimeoutEventHandle;
|
||||
|
||||
private KEvent _joyDetachOnBluetoothOffEvent;
|
||||
private int _joyDetachOnBluetoothOffEventHandle;
|
||||
|
||||
private KEvent _playReportControllerUsageUpdateEvent;
|
||||
private int _playReportControllerUsageUpdateEventHandle;
|
||||
|
||||
private KEvent _playReportRegisteredDeviceUpdateEvent;
|
||||
private int _playReportRegisteredDeviceUpdateEventHandle;
|
||||
|
||||
private IHidServer _hidServer;
|
||||
|
||||
private int _homeButtonEventHandle;
|
||||
private int _sleepButtonEventHandle;
|
||||
private int _captureButtonEventHandle;
|
||||
|
||||
public IHidSystemServer(ServiceCtx context)
|
||||
{
|
||||
_deviceRegisteredEventForControllerSupport = new KEvent(context.Device.System.KernelContext);
|
||||
_connectionTriggerTimeoutEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_joyDetachOnBluetoothOffEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_playReportControllerUsageUpdateEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_playReportRegisteredDeviceUpdateEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_hidServer = new IHidServer(context);
|
||||
}
|
||||
|
||||
[CommandCmif(101)]
|
||||
// AcquireHomeButtonEventHandle() -> handle<copy>
|
||||
public ResultCode AcquireHomeButtonEventHandle(ServiceCtx context)
|
||||
{
|
||||
if (_homeButtonEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.HomeButton.GetEvent().ReadableEvent, out _homeButtonEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_homeButtonEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(111)]
|
||||
// ActivateHomeButton(nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode ActivateHomeButton(ServiceCtx context)
|
||||
{
|
||||
context.Device.Hid.HomeButton.Active = true;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(121)]
|
||||
// AcquireSleepButtonEventHandle() -> handle<copy>
|
||||
public ResultCode AcquireSleepButtonEventHandle(ServiceCtx context)
|
||||
{
|
||||
if (_sleepButtonEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.SleepButton.GetEvent().ReadableEvent, out _sleepButtonEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sleepButtonEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(131)]
|
||||
// ActivateSleepButton(nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode ActivateSleepButton(ServiceCtx context)
|
||||
{
|
||||
context.Device.Hid.SleepButton.Active = true;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(141)]
|
||||
// AcquireCaptureButtonEventHandle() -> handle<copy>
|
||||
public ResultCode AcquireCaptureButtonEventHandle(ServiceCtx context)
|
||||
{
|
||||
if (_captureButtonEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(context.Device.Hid.CaptureButton.GetEvent().ReadableEvent, out _captureButtonEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_captureButtonEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(151)]
|
||||
// ActivateCaptureButton(nn::applet::AppletResourceUserId, pid)
|
||||
public ResultCode ActivateCaptureButton(ServiceCtx context)
|
||||
{
|
||||
context.Device.Hid.CaptureButton.Active = true;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(301)]
|
||||
// ActivateNpadSystem(u32)
|
||||
public ResultCode ActivateNpadSystem(ServiceCtx context)
|
||||
{
|
||||
uint npadSystem = context.RequestData.ReadUInt32();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { npadSystem });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(303)]
|
||||
// ApplyNpadSystemCommonPolicy(u64)
|
||||
@ -21,18 +143,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
}
|
||||
|
||||
[CommandCmif(306)]
|
||||
// GetLastActiveNpad(u32) -> u8, u8
|
||||
// GetLastActiveNpad() -> nn::hid::NpadIdType
|
||||
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
||||
{
|
||||
// TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue.
|
||||
context.RequestData.ReadUInt32();
|
||||
|
||||
context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(307)]
|
||||
// GetNpadSystemExtStyle() -> u64
|
||||
// GetNpadSystemExtStyle(u32) -> (u64, u64)
|
||||
public ResultCode GetNpadSystemExtStyle(ServiceCtx context)
|
||||
{
|
||||
context.RequestData.ReadUInt32();
|
||||
|
||||
foreach (PlayerIndex playerIndex in context.Device.Hid.Npads.GetSupportedPlayers())
|
||||
{
|
||||
if (HidUtils.GetNpadIdTypeFromIndex(playerIndex) > NpadIdType.Handheld)
|
||||
@ -41,7 +168,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
}
|
||||
}
|
||||
|
||||
context.ResponseData.Write((ulong)context.Device.Hid.Npads.SupportedStyleSets);
|
||||
// todo: wrong
|
||||
// context.ResponseData.Write((ulong)context.Device.Hid.Npads.SupportedStyleSets);
|
||||
context.ResponseData.Write((ulong)AppletFooterUiType.JoyDual);
|
||||
context.ResponseData.Write((ulong)0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@ -57,6 +187,203 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
[CommandCmif(321)]
|
||||
// GetUniquePadsFromNpad(u32) -> (u64, buffer<nn::hid::system::UniquePadId[], 0xa>)
|
||||
public ResultCode GetUniquePadsFromNpad(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0L);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(525)]
|
||||
// IsJoyConAttachedOnAllRail() -> bool
|
||||
public ResultCode IsJoyConAttachedOnAllRail(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(true);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(540)]
|
||||
// AcquirePlayReportControllerUsageUpdateEvent() -> handle<copy>
|
||||
public ResultCode AcquirePlayReportControllerUsageUpdateEvent(ServiceCtx context)
|
||||
{
|
||||
if (_playReportControllerUsageUpdateEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_playReportControllerUsageUpdateEvent.ReadableEvent, out _playReportControllerUsageUpdateEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playReportControllerUsageUpdateEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(541)]
|
||||
// GetPlayReportControllerUsages() -> (u64, buffer<nn::hid::system::PlayReportControllerUsage[], 0xa>)
|
||||
public ResultCode GetPlayReportControllerUsages(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
|
||||
context.ResponseData.Write(0L);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(542)]
|
||||
// AcquirePlayReportRegisteredDeviceUpdateEvent() -> handle<copy>
|
||||
public ResultCode AcquirePlayReportRegisteredDeviceUpdateEvent(ServiceCtx context)
|
||||
{
|
||||
if (_playReportRegisteredDeviceUpdateEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_playReportRegisteredDeviceUpdateEvent.ReadableEvent, out _playReportRegisteredDeviceUpdateEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playReportRegisteredDeviceUpdateEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(543)]
|
||||
// GetRegisteredDevicesOld() -> (u64, buffer<nn::hid::system::RegisteredDevice[], 0xa>)
|
||||
public ResultCode GetRegisteredDevicesOld(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
|
||||
context.ResponseData.Write(0L);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(544)]
|
||||
// AcquireConnectionTriggerTimeoutEvent() -> handle<copy>
|
||||
public ResultCode AcquireConnectionTriggerTimeoutEvent(ServiceCtx context)
|
||||
{
|
||||
if (_connectionTriggerTimeoutEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_connectionTriggerTimeoutEvent.ReadableEvent,
|
||||
out _connectionTriggerTimeoutEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_connectionTriggerTimeoutEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(545)]
|
||||
// SendConnectionTrigger(nn::bluetooth::Address)
|
||||
public ResultCode SendConnectionTrigger(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(546)]
|
||||
// AcquireDeviceRegisteredEventForControllerSupport() -> handle<copy>
|
||||
public ResultCode AcquireDeviceRegisteredEventForControllerSupport(ServiceCtx context)
|
||||
{
|
||||
if (_deviceRegisteredEventForControllerSupportHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_deviceRegisteredEventForControllerSupport.ReadableEvent,
|
||||
out _deviceRegisteredEventForControllerSupportHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_deviceRegisteredEventForControllerSupportHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(703)]
|
||||
// GetUniquePadIds() -> (u64, buffer<nn::hid::system::UniquePadId[], 0xa>)
|
||||
public ResultCode GetUniquePadIds(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0L);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(751)]
|
||||
// AcquireJoyDetachOnBluetoothOffEventHandle(nn::applet::AppletResourceUserId, pid) -> handle<copy>
|
||||
public ResultCode AcquireJoyDetachOnBluetoothOffEventHandle(ServiceCtx context)
|
||||
{
|
||||
if (_joyDetachOnBluetoothOffEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_joyDetachOnBluetoothOffEvent.ReadableEvent,
|
||||
out _joyDetachOnBluetoothOffEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_joyDetachOnBluetoothOffEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(850)]
|
||||
// IsUsbFullKeyControllerEnabled() -> bool
|
||||
public ResultCode IsUsbFullKeyControllerEnabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1120)]
|
||||
// Unknown1120()
|
||||
public ResultCode Unknown1120(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1131)]
|
||||
// FinalizeUsbFirmwareUpdate()
|
||||
public ResultCode FinalizeUsbFirmwareUpdate(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1132)]
|
||||
// CheckUsbFirmwareUpdateRequired()
|
||||
public ResultCode CheckUsbFirmwareUpdateRequired(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1135)]
|
||||
// InitializeUsbFirmwareUpdateWithoutMemory()
|
||||
public ResultCode InitializeUsbFirmwareUpdateWithoutMemory(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1153)]
|
||||
// GetTouchScreenDefaultConfiguration() -> unknown
|
||||
public ResultCode GetTouchScreenDefaultConfiguration(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private ResultCode GetAppletFooterUiTypeImpl(ServiceCtx context, out AppletFooterUiType appletFooterUiType)
|
||||
{
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
|
@ -0,0 +1,12 @@
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct ButtonState : ISampledDataStruct
|
||||
{
|
||||
public ulong SamplingNumber;
|
||||
public ulong Buttons;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Button;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||
@ -39,6 +40,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
||||
/// </summary>
|
||||
[FieldOffset(0x3800)]
|
||||
public RingLifo<KeyboardState> Keyboard;
|
||||
|
||||
/// <summary>
|
||||
/// Home Button.
|
||||
/// </summary>
|
||||
[FieldOffset(0x4C00)]
|
||||
public RingLifo<ButtonState> HomeButton;
|
||||
|
||||
/// <summary>
|
||||
/// Sleep Button.
|
||||
/// </summary>
|
||||
[FieldOffset(0x4E00)]
|
||||
public RingLifo<ButtonState> SleepButton;
|
||||
|
||||
/// <summary>
|
||||
/// Capture Button.
|
||||
/// </summary>
|
||||
[FieldOffset(0x5000)]
|
||||
public RingLifo<ButtonState> CaptureButton;
|
||||
|
||||
/// <summary>
|
||||
/// Nintendo Pads.
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services
|
||||
{
|
||||
@ -280,5 +281,54 @@ namespace Ryujinx.HLE.HOS.Services
|
||||
|
||||
_domainObjects.Clear();
|
||||
}
|
||||
|
||||
public static byte[] StructToBytes<T>(T structure) where T : struct
|
||||
{
|
||||
int size = Marshal.SizeOf(structure);
|
||||
byte[] bytes = new byte[size];
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
|
||||
try
|
||||
{
|
||||
Marshal.StructureToPtr(structure, ptr, false);
|
||||
Marshal.Copy(ptr, bytes, 0, size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
||||
{
|
||||
byte[] rawData;
|
||||
|
||||
if (isOutput)
|
||||
{
|
||||
rawData = new byte[ipcBuff.Size];
|
||||
}
|
||||
else
|
||||
{
|
||||
rawData = new byte[ipcBuff.Size];
|
||||
|
||||
context.Memory.Read(ipcBuff.Position, rawData);
|
||||
}
|
||||
|
||||
return new Span<byte>(rawData);
|
||||
}
|
||||
|
||||
public Span<T> CreateSpanFromBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Cast<byte, T>(CreateByteSpanFromBuffer(context, ipcBuff, isOutput));
|
||||
}
|
||||
|
||||
public void WriteSpanToBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, Span<T> span) where T : unmanaged
|
||||
{
|
||||
Span<byte> rawData = MemoryMarshal.Cast<T, byte>(span);
|
||||
|
||||
context.Memory.Write(ipcBuff.Position, rawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs
Normal file
38
src/Ryujinx.HLE/HOS/Services/Ldn/IMonitorService.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.NfcManager;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn
|
||||
{
|
||||
class IMonitorService : IpcService
|
||||
{
|
||||
public IMonitorService() { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetStateForMonitor() -> u32
|
||||
public ResultCode GetStateForMonitor(ServiceCtx context)
|
||||
{
|
||||
State state = State.Initialized;
|
||||
context.ResponseData.Write((uint)state);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(100)]
|
||||
// InitializeMonitor()
|
||||
public ResultCode InitializeMonitor(ServiceCtx context)
|
||||
{
|
||||
Logger.Info?.PrintStub(LogClass.ServiceLdn);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(288)]
|
||||
// ???()
|
||||
public ResultCode Unknown288(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,5 +4,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn
|
||||
class IMonitorServiceCreator : IpcService
|
||||
{
|
||||
public IMonitorServiceCreator(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CreateMonitorService() -> object<nn::ldn::detail::IMonitorService>
|
||||
public ResultCode CreateMonitorService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IMonitorService());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Ldn.Lp2p.SfMonitorServiceCreator;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
|
||||
{
|
||||
[Service("lp2p:m")] // 9.1.0+
|
||||
class ISfMonitorServiceCreator : IpcService
|
||||
{
|
||||
public ISfMonitorServiceCreator(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CreateMonitorService(pid, u64, u64) -> object<nn::lp2p::monitor::detail::ISfMonitorService>
|
||||
public ResultCode CreateMonitorService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ISfMonitorService(context));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
@ -47,7 +49,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p
|
||||
public ResultCode GetGroupInfo(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
GroupInfo info = new();
|
||||
context.Memory.Write(context.Response.ReceiveBuff[0].Position,SpanHelpers.AsByteSpan(ref info).ToArray());
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p.SfMonitorServiceCreator
|
||||
{
|
||||
class ISfMonitorService : IpcService
|
||||
{
|
||||
public ISfMonitorService(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Initialize()
|
||||
public ResultCode Initialize(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(288)]
|
||||
// GetGroupInfo() -> buffer<nn::lp2p::GroupInfo, 0x32>
|
||||
public ResultCode GetGroupInfo(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs
Normal file
10
src/Ryujinx.HLE/HOS/Services/Ldn/Types/GroupInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
unsafe struct GroupInfo
|
||||
{
|
||||
public fixed byte info[0x200]; // Fixed size buffer
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.News
|
||||
{
|
||||
class INewlyArrivedEventHolder : IpcService
|
||||
{
|
||||
KEvent _newlyArrivedEvent;
|
||||
int _newlyArrivedEventHandle;
|
||||
|
||||
public INewlyArrivedEventHolder(ServiceCtx context)
|
||||
{
|
||||
_newlyArrivedEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_newlyArrivedEventHandle = -1;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Get() -> handle<copy>
|
||||
public ResultCode Get(ServiceCtx context)
|
||||
{
|
||||
if (_newlyArrivedEventHandle == -1)
|
||||
{
|
||||
Result resultCode = context.Process.HandleTable.GenerateHandle(_newlyArrivedEvent.ReadableEvent, out _newlyArrivedEventHandle);
|
||||
|
||||
if (resultCode != Result.Success)
|
||||
{
|
||||
return (ResultCode)resultCode.ErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_newlyArrivedEventHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
7
src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs
Normal file
7
src/Ryujinx.HLE/HOS/Services/News/INewsDataService.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.News
|
||||
{
|
||||
class INewsDataService : IpcService
|
||||
{
|
||||
public INewsDataService(ServiceCtx context) { }
|
||||
}
|
||||
}
|
36
src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs
Normal file
36
src/Ryujinx.HLE/HOS/Services/News/INewsDatabaseService.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.News
|
||||
{
|
||||
class INewsDatabaseService : IpcService
|
||||
{
|
||||
public INewsDatabaseService(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetListV1(unknown<4>, buffer<unknown, 9>, buffer<unknown, 9>) -> (unknown<4>, buffer<unknown, 6>)
|
||||
public ResultCode GetListV1(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
// Count(buffer<nn::news::detail::Count>) -> u32
|
||||
public ResultCode Count(ServiceCtx context)
|
||||
{
|
||||
// TODO: Implement news database count
|
||||
context.ResponseData.Write(0);
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// UpdateIntegerValue(unknown<4>, buffer<unknown, 9>, buffer<unknown, 9>)
|
||||
public ResultCode UpdateIntegerValue(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
30
src/Ryujinx.HLE/HOS/Services/News/INewsService.cs
Normal file
30
src/Ryujinx.HLE/HOS/Services/News/INewsService.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.News
|
||||
{
|
||||
class INewsService : IpcService
|
||||
{
|
||||
public INewsService(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(30100)]
|
||||
// GetSubscriptionStatus() -> u32
|
||||
public ResultCode GetSubscriptionStatus(ServiceCtx context)
|
||||
{
|
||||
// TODO: Implement this properly
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
context.ResponseData.Write((uint)0);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(30200)]
|
||||
// IsSystemUpdateRequired() -> bool
|
||||
public ResultCode IsSystemUpdateRequired(ServiceCtx context)
|
||||
{
|
||||
// TODO: Implement this properly
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
context.ResponseData.Write(false);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
40
src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs
Normal file
40
src/Ryujinx.HLE/HOS/Services/News/IOverwriteEventHolder.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.News
|
||||
{
|
||||
class IOverwriteEventHolder : IpcService
|
||||
{
|
||||
KEvent _overwriteEvent;
|
||||
int _overwriteEventHandle;
|
||||
public IOverwriteEventHolder(ServiceCtx context)
|
||||
{
|
||||
_overwriteEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_overwriteEventHandle = -1;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Get() -> handle<copy>
|
||||
public ResultCode Get(ServiceCtx context)
|
||||
{
|
||||
if (_overwriteEventHandle == -1)
|
||||
{
|
||||
Result resultCode = context.Process.HandleTable.GenerateHandle(_overwriteEvent.ReadableEvent, out _overwriteEventHandle);
|
||||
|
||||
if (resultCode != Result.Success)
|
||||
{
|
||||
return (ResultCode)resultCode.ErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_overwriteEventHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,5 +8,46 @@ namespace Ryujinx.HLE.HOS.Services.News
|
||||
class IServiceCreator : IpcService
|
||||
{
|
||||
public IServiceCreator(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CreateNewsService() -> object<nn::news::detail::ipc::INewsService>
|
||||
public ResultCode CreateNewsService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INewsService(context));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(30900)]
|
||||
[CommandCmif(1)]
|
||||
// CreateNewlyArrivedEventHolder() -> object<nn::news::detail::ipc::INewlyArrivedEventHolder>
|
||||
public ResultCode CreateNewlyArrivedEventHolder(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INewlyArrivedEventHolder(context));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// CreateNewsDataService() -> object<nn::news::detail::ipc::INewsDataService>
|
||||
public ResultCode CreateNewsDataService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INewsDataService(context));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// CreateNewsDatabaseService() -> object<nn::news::detail::ipc::INewsDatabaseService>
|
||||
public ResultCode CreateNewsDatabaseService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INewsDatabaseService(context));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(4)]
|
||||
// CreateOverwriteEventHolder() -> object<nn::news::detail::ipc::IOverwriteEventHolder>
|
||||
public ResultCode CreateOverwriteEventHolder(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IOverwriteEventHolder(context));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
|
||||
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
|
||||
using System;
|
||||
@ -17,6 +18,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
private UnicastIPAddressInformation _targetAddressInfoCache = null;
|
||||
private string _cacheChosenInterface = null;
|
||||
|
||||
private readonly UInt128 _interfaceId = UInt128Utils.CreateRandom();
|
||||
|
||||
public IGeneralService()
|
||||
{
|
||||
_generalServiceDetail = new GeneralServiceDetail
|
||||
@ -43,6 +46,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// CreateScanRequest() -> object<nn::nifm::detail::IScanRequest>
|
||||
public ResultCode CreateScanRequest(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IScanRequest(context.Device.System));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(4)]
|
||||
// CreateRequest(u32 version) -> object<nn::nifm::detail::IRequest>
|
||||
public ResultCode CreateRequest(ServiceCtx context)
|
||||
@ -78,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
|
||||
NetworkProfileData networkProfile = new()
|
||||
{
|
||||
Uuid = Random.Shared.NextUInt128(),
|
||||
Uuid = _interfaceId,
|
||||
};
|
||||
|
||||
networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
|
||||
@ -91,6 +105,111 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(6)]
|
||||
// EnumerateNetworkInterfaces() -> (u32, buffer<nn::nifm::detail::sf::NetworkInterfaceInfo, 0xa>)
|
||||
public ResultCode EnumerateNetworkInterfaces(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(1); // account crashes if we don't have at least one interface
|
||||
|
||||
// TODO: write interface info
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7)]
|
||||
// EnumerateNetworkProfiles() -> (u32, buffer<nn::nifm::detail::sf::NetworkProfileBasicInfo[], 0x6>)
|
||||
public ResultCode EnumerateNetworkProfiles(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(8)]
|
||||
// GetNetworkProfile(nn::util::Uuid) -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a>
|
||||
public ResultCode GetNetworkProfile(ServiceCtx context)
|
||||
{
|
||||
ulong networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
if (uuid != _interfaceId)
|
||||
{
|
||||
return ResultCode.NoInternetConnection;
|
||||
}
|
||||
|
||||
(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface(context);
|
||||
|
||||
if (interfaceProperties == null || unicastAddress == null)
|
||||
{
|
||||
return ResultCode.NoInternetConnection;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
|
||||
|
||||
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Unsafe.SizeOf<NetworkProfileData>());
|
||||
|
||||
NetworkProfileData networkProfile = new()
|
||||
{
|
||||
Uuid = _interfaceId,
|
||||
};
|
||||
|
||||
networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
|
||||
networkProfile.IpSettingData.DnsSetting = new DnsSetting(interfaceProperties);
|
||||
|
||||
"RyujinxNetwork"u8.CopyTo(networkProfile.Name.AsSpan());
|
||||
|
||||
context.Memory.Write(networkProfileDataPosition, networkProfile);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(9)]
|
||||
// SetNetworkProfile(buffer<nn::nifm::detail::sf::NetworkProfileData, 0x19>)
|
||||
public ResultCode SetNetworkProfile(ServiceCtx context)
|
||||
{
|
||||
ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position;
|
||||
|
||||
NetworkProfileData networkProfile = context.Memory.Read<NetworkProfileData>(networkProfileDataPosition);
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10)]
|
||||
// RemoveNetworkProfile(nn::util::Uuid)
|
||||
public ResultCode RemoveNetworkProfile(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(11)]
|
||||
// GetScanData(u32) -> (u32, buffer<nn::nifm::detail::sf::AccessPointData, 0xa>)
|
||||
public ResultCode GetScanData(ServiceCtx context)
|
||||
{
|
||||
SystemVersion version = context.Device.System.ContentManager.GetCurrentFirmwareVersion();
|
||||
if (version.Major >= 4)
|
||||
{
|
||||
// TODO: write AccessPointData
|
||||
}
|
||||
else
|
||||
{
|
||||
// TOO: write AccessPointDataOld
|
||||
}
|
||||
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(12)]
|
||||
// GetCurrentIpAddress() -> nn::nifm::IpV4Address
|
||||
public ResultCode GetCurrentIpAddress(ServiceCtx context)
|
||||
@ -109,6 +228,24 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(14)]
|
||||
// CreateTemporaryNetworkProfile(buffer<nn::nifm::detail::sf::NetworkProfileData, 0x19>) -> (nn::util::Uuid, object<nn::nifm::detail::INetworkProfile>)
|
||||
public ResultCode CreateTemporaryNetworkProfile(ServiceCtx context)
|
||||
{
|
||||
ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position;
|
||||
|
||||
NetworkProfileData networkProfile = context.Memory.Read<NetworkProfileData>(networkProfileDataPosition);
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile });
|
||||
|
||||
var uuid = UInt128Utils.CreateRandom();
|
||||
var networkProfileObject = new INetworkProfile(uuid, networkProfile);
|
||||
|
||||
context.ResponseData.WriteStruct(uuid);
|
||||
MakeObject(context, networkProfileObject);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(15)]
|
||||
// GetCurrentIpConfigInfo() -> (nn::nifm::IpAddressSetting, nn::nifm::DnsSetting)
|
||||
public ResultCode GetCurrentIpConfigInfo(ServiceCtx context)
|
||||
@ -128,6 +265,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(17)]
|
||||
// IsWirelessCommunicationEnabled() -> b8
|
||||
public ResultCode IsWirelessCommunicationEnabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(18)]
|
||||
// GetInternetConnectionStatus() -> nn::nifm::detail::sf::InternetConnectionStatus
|
||||
public ResultCode GetInternetConnectionStatus(ServiceCtx context)
|
||||
@ -165,6 +313,30 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(26)]
|
||||
// SetExclusiveClient(buffer<nn::nifm::ClientId, 0x19, 4>)
|
||||
public ResultCode SetExclusiveClient(ServiceCtx context)
|
||||
{
|
||||
ulong position = context.Request.PtrBuff[0].Position;
|
||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
||||
ulong size = context.Request.PtrBuff[0].Size;
|
||||
#pragma warning restore IDE0059
|
||||
|
||||
int clientId = context.Memory.Read<int>(position);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(33)]
|
||||
// ConfirmSystemAvailability()
|
||||
public ResultCode ConfirmSystemAvailability(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private (IPInterfaceProperties, UnicastIPAddressInformation) GetLocalInterface(ServiceCtx context)
|
||||
{
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
@ -198,7 +370,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
{
|
||||
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||
|
||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||
if (_generalServiceDetail.ClientId < GeneralServiceManager.Count)
|
||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
{
|
||||
class INetworkProfile : IpcService
|
||||
{
|
||||
public UInt128 Uuid { get; private set; }
|
||||
public NetworkProfileData ProfileData;
|
||||
|
||||
public INetworkProfile(UInt128 uuid, NetworkProfileData profileData)
|
||||
{
|
||||
Uuid = uuid;
|
||||
ProfileData = profileData;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Update(buffer<nn::nifm::detail::sf::NetworkProfileData, 0x19>) -> nn::util::Uuid;
|
||||
public ResultCode Update(ServiceCtx context)
|
||||
{
|
||||
ulong networkProfileDataPosition = context.Request.PtrBuff[0].Position;
|
||||
NetworkProfileData networkProfile = context.Memory.Read<NetworkProfileData>(networkProfileDataPosition);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { networkProfile });
|
||||
|
||||
ProfileData = networkProfile;
|
||||
|
||||
context.ResponseData.WriteStruct(Uuid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
// Persist(nn::util::Uuid) -> nn::util::Uuid;
|
||||
public ResultCode Persist1(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid });
|
||||
|
||||
Uuid = uuid;
|
||||
|
||||
context.ResponseData.WriteStruct(Uuid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// Persist() -> nn::util::Uuid;
|
||||
public ResultCode Persist2(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
|
||||
context.ResponseData.WriteStruct(Uuid);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
||||
{
|
||||
class IScanRequest : IpcService
|
||||
{
|
||||
private readonly KEvent _systemEvent;
|
||||
private int _systemEventHandle;
|
||||
|
||||
public IScanRequest(Horizon system)
|
||||
{
|
||||
_systemEvent = new KEvent(system.KernelContext);
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// Submit()
|
||||
public ResultCode Submit(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
_systemEvent.ReadableEvent.Signal();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1)]
|
||||
// IsProcessing() -> bool
|
||||
public ResultCode IsProcessing(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// GetResult() -> u32
|
||||
public ResultCode GetResult(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// GetSystemEventReadableHandle() -> (handle<copy>)
|
||||
public ResultCode GetSystemEventReadableHandle(ServiceCtx context)
|
||||
{
|
||||
if (_systemEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_systemEvent.ReadableEvent, out _systemEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,39 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||
{
|
||||
[Service("notif:s")] // 9.0.0+
|
||||
class INotificationServicesForSystem : IpcService
|
||||
{
|
||||
public INotificationServicesForSystem(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(520)]
|
||||
// ListAlarmSettings() -> (s32, buffer<AlarmSetting, 6>)
|
||||
public ResultCode ListAlarmSettings(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNotif);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1040)]
|
||||
// OpenNotificationSystemEventAccessor() -> object<nn::notification::server::INotificationSystemEventAccessor>
|
||||
public ResultCode OpenNotificationSystemEventAccessor(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INotificationSystemEventAccessor(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1510)]
|
||||
// GetPresentationSetting() -> unknown
|
||||
public ResultCode GetPresentationSetting(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNotif);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||
{
|
||||
class INotificationSystemEventAccessor : IpcService
|
||||
{
|
||||
private KEvent _event;
|
||||
private int _eventHandle;
|
||||
|
||||
public INotificationSystemEventAccessor(ServiceCtx context)
|
||||
{
|
||||
_event = new KEvent(context.Device.System.KernelContext);
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetEvent() -> handle<copy>
|
||||
public ResultCode GetEvent(ServiceCtx context)
|
||||
{
|
||||
if (_eventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,51 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Npns
|
||||
{
|
||||
[Service("npns:s")]
|
||||
class INpnsSystem : IpcService
|
||||
{
|
||||
public INpnsSystem(ServiceCtx context) { }
|
||||
public KEvent ListenEvent;
|
||||
public int ListenHandle = 0;
|
||||
public INpnsSystem(ServiceCtx context)
|
||||
{
|
||||
ListenEvent = new KEvent(context.Device.System.KernelContext);
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// ListenTo(u64)
|
||||
public ResultCode ListenTo(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNpns);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(5)]
|
||||
// GetReceiveEvent() -> handle<copy>
|
||||
public ResultCode GetReceiveEvent(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNpns);
|
||||
if (ListenHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(ListenEvent.ReadableEvent, out ListenHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(ListenHandle);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(8)]
|
||||
// ListenToByName()
|
||||
public ResultCode ListenToByName(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNpns);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,232 @@
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using ApplicationId = LibHac.ApplicationId;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
{
|
||||
[Service("ns:am")]
|
||||
class IApplicationManagerInterface : IpcService
|
||||
{
|
||||
public IApplicationManagerInterface(ServiceCtx context) { }
|
||||
private KEvent _applicationRecordUpdateSystemEvent;
|
||||
private int _applicationRecordUpdateSystemEventHandle;
|
||||
|
||||
private KEvent _sdCardMountStatusChangedEvent;
|
||||
private int _sdCardMountStatusChangedEventHandle;
|
||||
|
||||
private KEvent _gameCardUpdateDetectionEvent;
|
||||
private int _gameCardUpdateDetectionEventHandle;
|
||||
|
||||
private KEvent _gameCardMountFailureEvent;
|
||||
private int _gameCardMountFailureEventHandle;
|
||||
|
||||
public IApplicationManagerInterface(ServiceCtx context)
|
||||
{
|
||||
_applicationRecordUpdateSystemEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_sdCardMountStatusChangedEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_gameCardUpdateDetectionEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_gameCardMountFailureEvent = new KEvent(context.Device.System.KernelContext);
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(0)]
|
||||
// ListApplicationRecord(s32) -> (s32, buffer<ApplicationRecord[], 6>)
|
||||
// entry_offset -> (out_entrycount, ApplicationRecord[])
|
||||
public ResultCode ListApplicationRecord(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
int entryOffset = context.RequestData.ReadInt32();
|
||||
ulong position = context.Request.ReceiveBuff[0].Position;
|
||||
List<ApplicationRecord> records = new();
|
||||
int sID = 24;
|
||||
foreach (RyuApplicationData title in context.Device.Configuration.Titles)
|
||||
{
|
||||
records.Add(new ApplicationRecord()
|
||||
{
|
||||
AppId = title.AppId,
|
||||
Type = ApplicationRecordType.Installed,
|
||||
Unknown = 0,
|
||||
Unknown2 = (byte)sID++
|
||||
});
|
||||
}
|
||||
|
||||
records.Sort((x, y) => (int)(x.AppId.Value - y.AppId.Value));
|
||||
if (entryOffset > 0)
|
||||
{
|
||||
records = records.Skip(entryOffset - 1).ToList();
|
||||
}
|
||||
|
||||
context.ResponseData.Write(records.Count);
|
||||
foreach (var record in records)
|
||||
{
|
||||
context.Memory.Write(position, StructToBytes(record));
|
||||
position += (ulong)Marshal.SizeOf<ApplicationRecord>();
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// GetApplicationRecordUpdateSystemEvent() -> handle<copy>
|
||||
public ResultCode GetApplicationRecordUpdateSystemEvent(ServiceCtx context)
|
||||
{
|
||||
if (_applicationRecordUpdateSystemEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_applicationRecordUpdateSystemEvent.ReadableEvent, out _applicationRecordUpdateSystemEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_applicationRecordUpdateSystemEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// GetApplicationView(buffer<unknown, 5>) -> buffer<unknown, 6>
|
||||
public ResultCode GetApplicationViewDeperecated(ServiceCtx context)
|
||||
{
|
||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
||||
ulong inSize = context.Request.SendBuff[0].Size;
|
||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
List<ApplicationId> applicationIds = new();
|
||||
for (ulong i = 0; i < inSize / sizeof(ulong); i++)
|
||||
{
|
||||
ulong position = inPosition + (i * sizeof(ulong));
|
||||
applicationIds.Add(new(context.Memory.Read<ulong>(position)));
|
||||
}
|
||||
|
||||
List<ApplicationView> views = new();
|
||||
|
||||
foreach (ApplicationId applicationId in applicationIds)
|
||||
{
|
||||
views.Add(new()
|
||||
{
|
||||
AppId = applicationId,
|
||||
Unknown1 = 0x70000,
|
||||
Flags = 0x401f17,
|
||||
Unknown2 = new byte[0x40]
|
||||
});
|
||||
}
|
||||
|
||||
context.ResponseData.Write(views.Count);
|
||||
foreach (var view in views)
|
||||
{
|
||||
context.Memory.Write(outputPosition, StructToBytes(view));
|
||||
outputPosition += (ulong)Marshal.SizeOf<ApplicationView>();
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(44)]
|
||||
// GetSdCardMountStatusChangedEvent() -> handle<copy>
|
||||
public ResultCode GetSdCardMountStatusChangedEvent(ServiceCtx context)
|
||||
{
|
||||
if (_sdCardMountStatusChangedEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_sdCardMountStatusChangedEvent.ReadableEvent, out _sdCardMountStatusChangedEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sdCardMountStatusChangedEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
const long storageFreeAndTotalSpaceSize = 6999999999999L;
|
||||
[CommandCmif(47)]
|
||||
// GetTotalSpaceSize(u8 storage_id) -> u64
|
||||
public ResultCode GetTotalSpaceSize(ServiceCtx context)
|
||||
{
|
||||
long storageId = context.RequestData.ReadByte();
|
||||
|
||||
|
||||
context.ResponseData.Write(storageFreeAndTotalSpaceSize);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(48)]
|
||||
// GetFreeSpaceSize(u8 storage_id) -> u64
|
||||
public ResultCode GetFreeSpaceSize(ServiceCtx context)
|
||||
{
|
||||
long storageId = context.RequestData.ReadByte();
|
||||
|
||||
context.ResponseData.Write(storageFreeAndTotalSpaceSize);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(52)]
|
||||
// GetGameCardUpdateDetectionEvent() -> handle<copy>
|
||||
public ResultCode GetGameCardUpdateDetectionEvent(ServiceCtx context)
|
||||
{
|
||||
if (_gameCardUpdateDetectionEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_gameCardUpdateDetectionEvent.ReadableEvent, out _gameCardUpdateDetectionEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gameCardUpdateDetectionEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(55)]
|
||||
// GetApplicationDesiredLanguage(u8) -> u8
|
||||
public ResultCode GetApplicationDesiredLanguage(ServiceCtx context)
|
||||
{
|
||||
byte source = context.RequestData.ReadByte();
|
||||
byte language = 0;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNs, new { source, language });
|
||||
|
||||
context.ResponseData.Write(language);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(70)]
|
||||
// ResumeAll()
|
||||
public ResultCode ResumeAll(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceNs);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(505)]
|
||||
// GetGameCardMountFailureEvent() -> handle<copy>
|
||||
public ResultCode GetGameCardMountFailureEvent(ServiceCtx context)
|
||||
{
|
||||
if (_gameCardMountFailureEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_gameCardMountFailureEvent.ReadableEvent, out _gameCardMountFailureEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gameCardMountFailureEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(400)]
|
||||
// GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>)
|
||||
@ -19,9 +239,113 @@ namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
|
||||
ulong position = context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
|
||||
var title = context.Device.Configuration.Titles.FirstOrDefault(x => x.AppId.Value == titleId);
|
||||
|
||||
ApplicationControlProperty nacp = title.Nacp;
|
||||
|
||||
context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray());
|
||||
if (title.Icon?.Length > 0)
|
||||
{
|
||||
context.Memory.Write(position + 0x4000, title.Icon);
|
||||
|
||||
context.ResponseData.Write(0x4000 + title.Icon.Length);
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(403)]
|
||||
// GetMaxApplicationControlCacheCount() -> u32
|
||||
public ResultCode GetMaxApplicationControlCacheCount(ServiceCtx context)
|
||||
{
|
||||
// TODO: Implement this method properly.
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1701)]
|
||||
// GetApplicationView(buffer<unknown, 5>) -> buffer<unknown, 6>
|
||||
public ResultCode GetApplicationView(ServiceCtx context)
|
||||
{
|
||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
||||
ulong inSize = context.Request.SendBuff[0].Size;
|
||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
List<ApplicationId> applicationIds = new();
|
||||
for (ulong i = 0; i < inSize / sizeof(ulong); i++)
|
||||
{
|
||||
ulong position = inPosition + (i * sizeof(ulong));
|
||||
applicationIds.Add(new(context.Memory.Read<ulong>(position)));
|
||||
}
|
||||
|
||||
List<ApplicationView> views = new();
|
||||
|
||||
foreach (ApplicationId applicationId in applicationIds)
|
||||
{
|
||||
views.Add(new()
|
||||
{
|
||||
AppId = applicationId,
|
||||
Unknown1 = 0x70000,
|
||||
Flags = 0x401f17,
|
||||
Unknown2 = new byte[0x40]
|
||||
});
|
||||
}
|
||||
|
||||
context.ResponseData.Write(views.Count);
|
||||
foreach (var view in views)
|
||||
{
|
||||
context.Memory.Write(outputPosition, StructToBytes(view));
|
||||
outputPosition += (ulong)Marshal.SizeOf<ApplicationView>();
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
[CommandCmif(1704)]
|
||||
// GetApplicationView(buffer<unknown, 5>) -> buffer<unknown, 6>
|
||||
public ResultCode GetApplicationViewWithPromotionInfo(ServiceCtx context)
|
||||
{
|
||||
ulong inPosition = context.Request.SendBuff[0].Position;
|
||||
ulong inSize = context.Request.SendBuff[0].Size;
|
||||
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
List<ApplicationId> applicationIds = new();
|
||||
for (ulong i = 0; i < inSize / sizeof(ulong); i++)
|
||||
{
|
||||
ulong position = inPosition + (i * sizeof(ulong));
|
||||
applicationIds.Add(new(context.Memory.Read<ulong>(position)));
|
||||
}
|
||||
|
||||
List<ApplicationView> views = new();
|
||||
|
||||
foreach (ApplicationId applicationId in applicationIds)
|
||||
{
|
||||
views.Add(new()
|
||||
{
|
||||
AppId = applicationId,
|
||||
Unknown1 = 0x70000,
|
||||
Flags = 0x401f17,
|
||||
Unknown2 = new byte[0x40]
|
||||
});
|
||||
}
|
||||
|
||||
List<ApplicationViewWithPromotionInfo> promotions = new();
|
||||
foreach(ApplicationView view in views)
|
||||
{
|
||||
promotions.Add(new()
|
||||
{
|
||||
AppView = view,
|
||||
Promotion = new()
|
||||
});
|
||||
}
|
||||
|
||||
context.ResponseData.Write(views.Count);
|
||||
foreach (var promotion in promotions)
|
||||
{
|
||||
context.Memory.Write(outputPosition, StructToBytes(promotion));
|
||||
outputPosition += (ulong)Marshal.SizeOf<ApplicationViewWithPromotionInfo>();
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
{
|
||||
class IDynamicRightsInterface : IpcService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1,24 +1,47 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ApplicationId = LibHac.ApplicationId;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
{
|
||||
class IReadOnlyApplicationControlDataInterface : IpcService
|
||||
{
|
||||
public IReadOnlyApplicationControlDataInterface(ServiceCtx context) { }
|
||||
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>)
|
||||
public ResultCode GetApplicationControlData(ServiceCtx context)
|
||||
{
|
||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
||||
byte source = (byte)context.RequestData.ReadInt64();
|
||||
ulong titleId = context.RequestData.ReadUInt64();
|
||||
ApplicationId titleId = context.RequestData.ReadStruct<ApplicationId>();
|
||||
#pragma warning restore IDE0059
|
||||
|
||||
ulong position = context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
|
||||
ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
|
||||
foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles)
|
||||
{
|
||||
if (ryuApplicationData.AppId != titleId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nacp = ryuApplicationData.Nacp;
|
||||
// NOTE: this is hacky but it works and prevents a crash
|
||||
nacp.Title[1] = ryuApplicationData.Nacp.Title[0];
|
||||
if (ryuApplicationData.Icon?.Length > 0)
|
||||
{
|
||||
context.Memory.Write(position + 0x4000, ryuApplicationData.Icon);
|
||||
context.ResponseData.Write(0x4000 + ryuApplicationData.Icon.Length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray());
|
||||
|
||||
|
@ -8,16 +8,17 @@ namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
class IServiceGetterInterface : IpcService
|
||||
{
|
||||
public IServiceGetterInterface(ServiceCtx context) { }
|
||||
|
||||
|
||||
[CommandCmif(7996)]
|
||||
// GetApplicationManagerInterface() -> object<nn::ns::detail::IApplicationManagerInterface>
|
||||
public ResultCode GetApplicationManagerInterface(ServiceCtx context)
|
||||
[CommandCmif(7988)]
|
||||
// GetDynamicRightsInterface() -> object<nn::ns::detail::GetDynamicRightsInterface>
|
||||
public ResultCode GetDynamicRightsInterface(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IApplicationManagerInterface(context));
|
||||
MakeObject(context, new IDynamicRightsInterface());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(7989)]
|
||||
// GetReadOnlyApplicationControlDataInterface() -> object<nn::ns::detail::IReadOnlyApplicationControlDataInterface>
|
||||
public ResultCode GetReadOnlyApplicationControlDataInterface(ServiceCtx context)
|
||||
@ -27,6 +28,15 @@ namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7996)]
|
||||
// GetApplicationManagerInterface() -> object<nn::ns::detail::IApplicationManagerInterface>
|
||||
public ResultCode GetApplicationManagerInterface(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IApplicationManagerInterface(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7997)]
|
||||
// GetDownloadTaskInterface() -> object<nn::ns::detail::IDownloadTaskInterface>
|
||||
public ResultCode GetDownloadTaskInterface(ServiceCtx context)
|
||||
|
@ -1,8 +1,50 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns
|
||||
{
|
||||
[Service("ns:su")]
|
||||
class ISystemUpdateInterface : IpcService
|
||||
{
|
||||
public ISystemUpdateInterface(ServiceCtx context) { }
|
||||
KEvent _systemUpdateEvent;
|
||||
int _systemUpdateEventHandle;
|
||||
public ISystemUpdateInterface(ServiceCtx context)
|
||||
{
|
||||
_systemUpdateEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_systemUpdateEventHandle = -1;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetBackgroundNetworkUpdateState() -> u32
|
||||
public ResultCode GetBackgroundNetworkUpdateState(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
context.ResponseData.Write((byte)BackgroundNetworkUpdateState.None);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(9)]
|
||||
// GetSystemUpdateNotificationEventForContentDelivery() -> handle<copy>
|
||||
public ResultCode GetSystemUpdateNotificationEventForContentDelivery(ServiceCtx context)
|
||||
{
|
||||
if (_systemUpdateEventHandle == -1)
|
||||
{
|
||||
Result resultCode = context.Process.HandleTable.GenerateHandle(_systemUpdateEvent.ReadableEvent, out _systemUpdateEventHandle);
|
||||
|
||||
if (resultCode != Result.Success)
|
||||
{
|
||||
return (ResultCode)resultCode.ErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemUpdateEventHandle);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.Service);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs
Normal file
21
src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationRecord.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using LibHac;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct ApplicationRecord
|
||||
{
|
||||
public ApplicationId AppId;
|
||||
public ApplicationRecordType Type;
|
||||
public byte Unknown;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
public byte[] Padding1;
|
||||
|
||||
public byte Unknown2;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
|
||||
public byte[] Padding2;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
enum ApplicationRecordType : byte
|
||||
{
|
||||
Installing = 2,
|
||||
Installed = 3,
|
||||
GameCardNotInserted = 5,
|
||||
Archived = 11,
|
||||
GameCard = 16,
|
||||
}
|
||||
}
|
15
src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs
Normal file
15
src/Ryujinx.HLE/HOS/Services/Ns/Types/ApplicationView.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using LibHac;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x50)]
|
||||
struct ApplicationView
|
||||
{
|
||||
public ApplicationId AppId;
|
||||
public uint Unknown1;
|
||||
public uint Flags;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)]
|
||||
public byte[] Unknown2;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x70)]
|
||||
struct ApplicationViewWithPromotionInfo
|
||||
{
|
||||
public ApplicationView AppView;
|
||||
public PromotionInfo Promotion;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
public enum BackgroundNetworkUpdateState
|
||||
{
|
||||
None,
|
||||
InProgress,
|
||||
Ready
|
||||
}
|
||||
}
|
26
src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs
Normal file
26
src/Ryujinx.HLE/HOS/Services/Ns/Types/PromotionInfo.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct PromotionInfo
|
||||
{
|
||||
[MarshalAs(UnmanagedType.I8)]
|
||||
public long StartTimestamp;
|
||||
|
||||
[MarshalAs(UnmanagedType.I8)]
|
||||
public long EndTimestamp;
|
||||
|
||||
[MarshalAs(UnmanagedType.I8)]
|
||||
public long RemainingTime;
|
||||
|
||||
[MarshalAs(UnmanagedType.U4)]
|
||||
public uint Reserved;
|
||||
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public byte Flags;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
||||
public byte[] Padding;
|
||||
}
|
||||
}
|
22
src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs
Normal file
22
src/Ryujinx.HLE/HOS/Services/Ns/Types/RyuApplicationData.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using LibHac;
|
||||
using LibHac.Ns;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ns.Types
|
||||
{
|
||||
// Used to store some internal data about applications.
|
||||
public struct RyuApplicationData
|
||||
{
|
||||
public ApplicationId AppId;
|
||||
public ApplicationControlProperty Nacp;
|
||||
public string Path;
|
||||
public byte[] Icon;
|
||||
|
||||
public RyuApplicationData(ApplicationId appId, ApplicationControlProperty nacp, string path, byte[] icon)
|
||||
{
|
||||
AppId = appId;
|
||||
Nacp = nacp;
|
||||
Path = path;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
}
|
7
src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs
Normal file
7
src/Ryujinx.HLE/HOS/Services/Olsc/IDaemonController.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Olsc
|
||||
{
|
||||
class IDaemonController : IpcService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1,8 +1,46 @@
|
||||
using Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Olsc
|
||||
{
|
||||
[Service("olsc:s")] // 4.0.0+
|
||||
class IOlscServiceForSystemService : IpcService
|
||||
{
|
||||
public IOlscServiceForSystemService(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetTransferTaskListController() -> object<nn::olsc::ITransferTaskListController>
|
||||
public ResultCode GetTransferTaskListController(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new ITransferTaskListController(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(2)]
|
||||
// GetDaemonController() -> object<nn::olsc::IDaemonController>
|
||||
public ResultCode GetDaemonController(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IDaemonController());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(200)]
|
||||
// GetDataTransferPolicy(u64) -> (u8, u8)
|
||||
public ResultCode GetDataTransferPolicy(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
context.ResponseData.Write(0);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10000)]
|
||||
// GetOlscServiceForSystemService() -> object<nn::olsc::IOlscServiceForSystemService>
|
||||
public ResultCode GetOlscServiceForSystemService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IOlscServiceForSystemService(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService
|
||||
{
|
||||
class INativeHandleHolder : IpcService
|
||||
{
|
||||
private KEvent _event;
|
||||
private int _eventHandle;
|
||||
|
||||
public INativeHandleHolder(ServiceCtx context)
|
||||
{
|
||||
_event = new KEvent(context.Device.System.KernelContext);
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetNativeHandle() -> handle<copy>
|
||||
public ResultCode GetNativeHandle(ServiceCtx context)
|
||||
{
|
||||
if (_eventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out _eventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_eventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService
|
||||
{
|
||||
class ITransferTaskListController : IpcService
|
||||
{
|
||||
public ITransferTaskListController(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(5)]
|
||||
// GetNativeHandleHolder() -> object<nn::olsc::srv::INativeHandleHolder>
|
||||
public ResultCode GetNativeHandleHolder(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INativeHandleHolder(context));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceOlsc);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(9)]
|
||||
// GetNativeHandleHolderEx() -> object<nn::olsc::srv::INativeHandleHolder>
|
||||
public ResultCode GetNativeHandleHolderEx(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new INativeHandleHolder(context));
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceOlsc);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Arp;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using static LibHac.Ns.ApplicationControlProperty;
|
||||
|
||||
@ -22,11 +25,24 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
||||
private bool _stereoVisionRestriction = false;
|
||||
#pragma warning restore IDE0052, CS0414
|
||||
|
||||
private KEvent _synchronizationEvent;
|
||||
private int _synchronizationEventHandle;
|
||||
|
||||
private KEvent _unlinkedEvent;
|
||||
private int _unlinkedEventHandle;
|
||||
|
||||
private KEvent _playTimerRequestSuspensionEvent;
|
||||
private int _playTimerRequestSuspensionEventHandle;
|
||||
|
||||
public IParentalControlService(ServiceCtx context, ulong pid, bool withInitialize, int permissionFlag)
|
||||
{
|
||||
_pid = pid;
|
||||
_permissionFlag = permissionFlag;
|
||||
|
||||
_synchronizationEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_unlinkedEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_playTimerRequestSuspensionEvent = new KEvent(context.Device.System.KernelContext);
|
||||
|
||||
if (withInitialize)
|
||||
{
|
||||
Initialize(context);
|
||||
@ -97,6 +113,28 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1006)]
|
||||
// IsRestrictionTemporaryUnlocked() -> b8
|
||||
public ResultCode IsRestrictionTemporaryUnlocked(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1010)]
|
||||
// IsRestrictedSystemSettingsEntered() -> b8
|
||||
public ResultCode IsRestrictedSystemSettingsEntered(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1017)] // 10.0.0+
|
||||
// EndFreeCommunication()
|
||||
public ResultCode EndFreeCommunication(ServiceCtx context)
|
||||
@ -106,6 +144,37 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1032)]
|
||||
// GetSafetyLevel() -> u32
|
||||
public ResultCode GetSafetyLevel(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1035)]
|
||||
// GetCurrentSettings() -> nn::pctl::RestrictionSettings
|
||||
public ResultCode GetCurrentSettings(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1039)]
|
||||
// GetFreeCommunicationApplicationListCount() -> u32
|
||||
public ResultCode GetFreeCommunicationApplicationListCount(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1013)] // 4.0.0+
|
||||
// ConfirmStereoVisionPermission()
|
||||
public ResultCode ConfirmStereoVisionPermission(ServiceCtx context)
|
||||
@ -235,6 +304,125 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
[CommandCmif(1403)]
|
||||
// IsPairingActive() -> b8
|
||||
public ResultCode IsPairingActive(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1432)]
|
||||
// GetSynchronizationEvent() -> handle<copy>
|
||||
public ResultCode GetSynchronizationEvent(ServiceCtx context)
|
||||
{
|
||||
if (_synchronizationEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_synchronizationEvent.ReadableEvent, out _synchronizationEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_synchronizationEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1451)]
|
||||
// StartPlayTimer()
|
||||
public ResultCode StartPlayTimer(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1452)]
|
||||
// StopPlayTimer()
|
||||
public ResultCode StopPlayTimer(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1455)]
|
||||
// IsRestrictedByPlayTimer() -> b8
|
||||
public ResultCode IsRestrictedByPlayTimer(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1456)]
|
||||
// GetPlayTimerSettings() -> nn::pctl::PlayTimerSettings
|
||||
public ResultCode GetPlayTimerSettings(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1457)]
|
||||
// GetPlayTimerEventToRequestSuspension() -> handle<copy>
|
||||
public ResultCode GetPlayTimerEventToRequestSuspension(ServiceCtx context)
|
||||
{
|
||||
if (_playTimerRequestSuspensionEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_playTimerRequestSuspensionEvent.ReadableEvent, out _playTimerRequestSuspensionEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_playTimerRequestSuspensionEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1458)] // 4.0.0+
|
||||
// IsPlayTimerAlarmDisabled() -> b8
|
||||
public ResultCode IsPlayTimerAlarmDisabled(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1473)]
|
||||
// GetUnlinkedEvent() -> handle<copy>
|
||||
public ResultCode GetUnlinkedEvent(ServiceCtx context)
|
||||
{
|
||||
if (_unlinkedEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_unlinkedEvent.ReadableEvent, out _unlinkedEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_unlinkedEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(145601)]
|
||||
// GetPlayTimerSettings()
|
||||
public ResultCode GetPlayTimerSettingsEx(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePctl);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private ResultCode IsStereoVisionPermittedImpl()
|
||||
{
|
||||
/*
|
||||
|
@ -1,4 +1,5 @@
|
||||
using LibHac;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
@ -7,9 +8,15 @@ using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Services.Settings.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
@ -85,12 +92,68 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(7)]
|
||||
// GetLockScreenFlag() -> bool
|
||||
public ResultCode GetLockScreenFlag(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(17)]
|
||||
// GetAccountSettings() -> nn::settings::system::AccountSettings
|
||||
public ResultCode GetAccountSettings(ServiceCtx context)
|
||||
{
|
||||
AccountSettings accountSettings = new AccountSettings
|
||||
{
|
||||
UserSelectorSettings = new UserSelectorSettings()
|
||||
};
|
||||
context.ResponseData.WriteStruct(accountSettings);
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(21)]
|
||||
// GetEulaVersions() -> (u32, buffer<nn::settings::system::EulaVersion, 6>)
|
||||
public ResultCode GetEulaVersions(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
if ((ulong)Unsafe.SizeOf<EulaVersion>() > bufferLen)
|
||||
{
|
||||
return ResultCode.NullEULAVersionBuffer;
|
||||
}
|
||||
|
||||
var eulaVersion = new EulaVersion
|
||||
{
|
||||
Version = 0x10000,
|
||||
RegionCode = 1,
|
||||
ClockType = 1,
|
||||
NetworkSystemClock = 0,
|
||||
SteadyClock = new Time.Clock.SteadyClockTimePoint {
|
||||
TimePoint = 0xc,
|
||||
ClockSourceId = new UInt128(0x36a0328708bc18c1, 0x1608ea2b023284)
|
||||
}
|
||||
};
|
||||
|
||||
context.Memory.Write(bufferPosition, eulaVersion);
|
||||
|
||||
context.ResponseData.Write(1);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(23)]
|
||||
// GetColorSetId() -> i32
|
||||
public ResultCode GetColorSetId(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((int)context.Device.System.State.ThemeColor);
|
||||
bool isDarkMode = context.Device.UIHandler.IsDarkMode();
|
||||
context.ResponseData.Write(isDarkMode ? 1 : 0);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@ -99,12 +162,51 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
// GetColorSetId() -> i32
|
||||
public ResultCode SetColorSetId(ServiceCtx context)
|
||||
{
|
||||
int colorSetId = context.RequestData.ReadInt32();
|
||||
|
||||
context.Device.System.State.ThemeColor = (ColorSet)colorSetId;
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(29)]
|
||||
// GetNotificationSettings() -> nn::settings::system::NotificationSettings
|
||||
public ResultCode GetNotificationSettings(ServiceCtx context)
|
||||
{
|
||||
NotificationSettings notificationSettings = new NotificationSettings
|
||||
{
|
||||
Flags = NotificationFlag.None,
|
||||
Volume = NotificationVolume.Low,
|
||||
HeadTime = new NotificationTime(),
|
||||
TailTime = new NotificationTime()
|
||||
};
|
||||
|
||||
context.ResponseData.WriteStruct(notificationSettings);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(31)]
|
||||
// GetAccountNotificationSettings() -> (u32, buffer<nn::settings::system::AccountNotificationSettings, 6>)
|
||||
public ResultCode GetAccountNotificationSettings(ServiceCtx context)
|
||||
{
|
||||
var buffer = context.Request.ReceiveBuff[0];
|
||||
|
||||
Span<AccountNotificationSettings> elementsSpan = CreateSpanFromBuffer<AccountNotificationSettings>(context,buffer,true);
|
||||
elementsSpan[0] = new AccountNotificationSettings
|
||||
{
|
||||
Uid = new Array16<byte>(),
|
||||
Flags = 0x1F,
|
||||
FriendPresenceOverlayPermission = 0x1,
|
||||
FriendInvitationOverlayPermission = 0x1,
|
||||
Reserved1 = 0,
|
||||
Reserved2 = 0
|
||||
};
|
||||
int count = elementsSpan.Length;
|
||||
Logger.Info?.PrintStub(LogClass.ServiceSet, $"AccountNotificationSettings: {count} settings found");
|
||||
context.ResponseData.Write(1);
|
||||
WriteSpanToBuffer(context, buffer, elementsSpan);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(37)]
|
||||
// GetSettingsItemValueSize(buffer<nn::settings::SettingsName, 0x19>, buffer<nn::settings::SettingsItemKey, 0x19>) -> u64
|
||||
@ -221,6 +323,29 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(39)]
|
||||
// GetTvSettings() -> nn::settings::system::TvSettings
|
||||
public ResultCode GetTvSettings(ServiceCtx context)
|
||||
{
|
||||
TvSettings tvSettings = new();
|
||||
|
||||
context.ResponseData.WriteStruct(tvSettings);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(47)]
|
||||
// GetQuestFlag() -> bool
|
||||
public ResultCode GetQuestFlag(ServiceCtx context)
|
||||
{
|
||||
// NOTE: Gets a flag determining whether the console is a kiosk unit (codenamed "Quest"). Used by qlaunch to determine whether to launch Retail Interactive Display Menu.
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(60)]
|
||||
// IsUserSystemClockAutomaticCorrectionEnabled() -> bool
|
||||
public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
|
||||
@ -244,15 +369,50 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(63)]
|
||||
// GetPrimaryAlbumStorage() -> s32
|
||||
public ResultCode GetPrimaryAlbumStorage(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((byte)PrimaryAlbumStorage.Nand);
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(68)]
|
||||
// GetSerialNumber() -> buffer<nn::settings::system::SerialNumber, 0x16>
|
||||
public ResultCode GetSerialNumber(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(Encoding.ASCII.GetBytes("RYU00000000000"));
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(71)]
|
||||
// GetSleepSettings() -> SleepSettings
|
||||
public ResultCode GetSleepSettings(ServiceCtx context)
|
||||
{
|
||||
SleepSettings sleepSettings = new();
|
||||
|
||||
context.ResponseData.WriteStruct(sleepSettings);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(75)]
|
||||
// GetInitialLaunchSettings() -> nn::settings::system::InitialLaunchSettings
|
||||
public ResultCode GetInitialLaunchSettings(ServiceCtx context)
|
||||
{
|
||||
InitialLaunchSettings launchSettings = new InitialLaunchSettings();
|
||||
launchSettings.Flags |= InitialLaunchFlag.InitialLaunchCompletionFlag;
|
||||
launchSettings.Flags |= InitialLaunchFlag.InitialLaunchUserAdditionFlag;
|
||||
launchSettings.Flags |= InitialLaunchFlag.InitialLaunchTimestampFlag;
|
||||
|
||||
context.ResponseData.WriteStruct(launchSettings);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(77)]
|
||||
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
|
||||
public ResultCode GetDeviceNickName(ServiceCtx context)
|
||||
@ -301,7 +461,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(90)]
|
||||
// GetMiiAuthorId() -> nn::util::Uuid
|
||||
public ResultCode GetMiiAuthorId(ServiceCtx context)
|
||||
@ -313,7 +473,85 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(95)]
|
||||
// GetAutoUpdateEnableFlag() -> bool
|
||||
public ResultCode GetAutoUpdateEnableFlag(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(99)]
|
||||
// GetBatteryPercentageFlag() -> u8
|
||||
public ResultCode GetBatteryPercentageFlag(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(100);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(124)]
|
||||
// GetErrorReportSharePermission() -> s32
|
||||
public ResultCode GetErrorReportSharePermission(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(1);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(126)]
|
||||
// GetAppletLaunchFlags() -> u32
|
||||
public ResultCode GetAppletLaunchFlags(ServiceCtx context)
|
||||
{
|
||||
// NOTE: I do not know what this is used for but it is used by qlaunch.
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(136)]
|
||||
// GetKeyboardLayout() -> s32
|
||||
public ResultCode GetKeyboardLayout(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((int)KeyboardLayout.Default);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(170)]
|
||||
// GetChineseTraditionalInputMethod() -> s32
|
||||
public ResultCode GetChineseTraditionalInputMethod(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(0);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(201)]
|
||||
// GetFieldTestingFlag() -> bool
|
||||
public ResultCode GetFieldTestingFlag(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(false);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSet);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public byte[] GetFirmwareData(Switch device)
|
||||
{
|
||||
const ulong SystemVersionTitleId = 0x0100000000000809;
|
||||
|
@ -0,0 +1,19 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
struct AccountNotificationSettings
|
||||
{
|
||||
public Array16<byte> Uid; // 0x0 - 0x10: Unique ID (16 bytes)
|
||||
|
||||
public uint Flags; // 0x10 - 0x4: Notification Flags
|
||||
|
||||
public byte FriendPresenceOverlayPermission; // 0x14 - 0x1: Friend presence overlay permission
|
||||
|
||||
public byte FriendInvitationOverlayPermission; // 0x15 - 0x1: Friend invitation overlay permission
|
||||
|
||||
public byte Reserved1; // 0x16 - 0x2: Reserved bytes
|
||||
public byte Reserved2; // 0x16 - 0x2: Reserved bytes
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct AccountSettings
|
||||
{
|
||||
public UserSelectorSettings UserSelectorSettings;
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs
Normal file
10
src/Ryujinx.HLE/HOS/Services/Settings/Types/CmuMode.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum CmuMode : uint
|
||||
{
|
||||
None = 0,
|
||||
ColorInvert = 1,
|
||||
HighContrast = 2,
|
||||
GrayScale = 3
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum ConsoleSleepPlan : uint
|
||||
{
|
||||
OneHour = 0,
|
||||
TwoHour = 1,
|
||||
ThreeHour = 2,
|
||||
SixHour = 3,
|
||||
TwelveHour = 4,
|
||||
Never = 5
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum ErrorReportSharePermission : uint
|
||||
{
|
||||
NotConfirmed = 0,
|
||||
Granted = 1,
|
||||
Denied = 2
|
||||
}
|
||||
}
|
16
src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs
Normal file
16
src/Ryujinx.HLE/HOS/Services/Settings/Types/EulaVersion.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct EulaVersion
|
||||
{
|
||||
public uint Version;
|
||||
public uint RegionCode;
|
||||
public uint ClockType;
|
||||
public uint Reserved;
|
||||
public long NetworkSystemClock;
|
||||
public SteadyClockTimePoint SteadyClock;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum HandheldSleepPlan : uint
|
||||
{
|
||||
OneMin = 0,
|
||||
ThreeMin = 1,
|
||||
FiveMin = 2,
|
||||
TenMin = 3,
|
||||
ThirtyMin = 4,
|
||||
Never = 5
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum HdmiContentType : uint
|
||||
{
|
||||
None = 0,
|
||||
Graphics = 1,
|
||||
Cinema = 2,
|
||||
Photo = 3,
|
||||
Game = 4
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[Flags]
|
||||
enum InitialLaunchFlag : uint
|
||||
{
|
||||
None = 0,
|
||||
InitialLaunchCompletionFlag = 1 << 0,
|
||||
InitialLaunchUserAdditionFlag = 1 << 8,
|
||||
InitialLaunchTimestampFlag = 1 << 16
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
struct InitialLaunchSettings
|
||||
{
|
||||
public InitialLaunchFlag Flags;
|
||||
public uint Reserved;
|
||||
public SteadyClockTimePoint TimeStamp;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[Flags]
|
||||
enum NotificationFlag : uint
|
||||
{
|
||||
None = 0,
|
||||
RingtoneFlag = 1 << 0,
|
||||
DownloadCompletionFlag = 1 << 1,
|
||||
EnablesNews = 1 << 8,
|
||||
IncomingLampFlag = 1 << 9
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
struct NotificationSettings
|
||||
{
|
||||
public NotificationFlag Flags;
|
||||
public NotificationVolume Volume;
|
||||
public NotificationTime HeadTime;
|
||||
public NotificationTime TailTime;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
struct NotificationTime
|
||||
{
|
||||
public uint Hour;
|
||||
public uint Minute;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum NotificationVolume : uint
|
||||
{
|
||||
Mute = 0,
|
||||
Low = 1,
|
||||
High = 2
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum PrimaryAlbumStorage : uint
|
||||
{
|
||||
Nand = 0,
|
||||
SdCard = 1,
|
||||
}
|
||||
}
|
9
src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs
Normal file
9
src/Ryujinx.HLE/HOS/Services/Settings/Types/RgbRange.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum RgbRange : uint
|
||||
{
|
||||
Auto = 0,
|
||||
Full = 1,
|
||||
Limited = 2
|
||||
}
|
||||
}
|
12
src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs
Normal file
12
src/Ryujinx.HLE/HOS/Services/Settings/Types/SleepSettings.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
struct SleepSettings
|
||||
{
|
||||
public uint Flags;
|
||||
public HandheldSleepPlan HandheldSleepPlan;
|
||||
public ConsoleSleepPlan ConsoleSleepPlan;
|
||||
}
|
||||
}
|
14
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs
Normal file
14
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvFlag.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[Flags]
|
||||
enum TvFlag : uint
|
||||
{
|
||||
None = 0,
|
||||
Allows4k = 1 << 0,
|
||||
Allows3d = 1 << 1,
|
||||
AllowsCec = 1 << 2,
|
||||
PreventsScreenBurnIn = 1 << 3
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs
Normal file
10
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvResolution.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
enum TvResolution : uint
|
||||
{
|
||||
Auto = 0,
|
||||
Resolution1080p = 1,
|
||||
Resolution720p = 2,
|
||||
Resolution480p = 3
|
||||
}
|
||||
}
|
17
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs
Normal file
17
src/Ryujinx.HLE/HOS/Services/Settings/Types/TvSettings.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
struct TvSettings
|
||||
{
|
||||
public TvFlag Flags;
|
||||
public TvResolution TvResolution;
|
||||
public HdmiContentType HdmiContentType;
|
||||
public RgbRange RgbRange;
|
||||
public CmuMode CmuMode;
|
||||
public uint TvUnderscan;
|
||||
public uint TvGamma;
|
||||
public uint ContrastRatio;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[Flags]
|
||||
enum UserSelectorFlag : uint
|
||||
{
|
||||
None = 0,
|
||||
SkipsIfSingleUser = 1 << 0,
|
||||
Unknown = 1U << 31
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Settings.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct UserSelectorSettings
|
||||
{
|
||||
public UserSelectorFlag UserSelectorFlag;
|
||||
}
|
||||
}
|
@ -49,6 +49,15 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
||||
|
||||
return _applicationDisplayService.CreateStrayLayer(context);
|
||||
}
|
||||
|
||||
[CommandCmif(3000)]
|
||||
// ListDisplayModes(u64) -> (u64, buffer<nn::vi::DisplayModeInfo, 6>)
|
||||
public ResultCode ListDisplayModes(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceVi);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3200)]
|
||||
// GetDisplayMode(u64) -> nn::vi::DisplayModeInfo
|
||||
|
@ -126,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
||||
ulong displayInfoBuffer = context.Request.ReceiveBuff[0].Position;
|
||||
|
||||
// TODO: Determine when more than one display is needed.
|
||||
ulong displayCount = 1;
|
||||
ulong displayCount = 2;
|
||||
|
||||
for (int i = 0; i < (int)displayCount; i++)
|
||||
{
|
||||
|
@ -66,5 +66,10 @@ namespace Ryujinx.HLE.UI
|
||||
/// Displays the player select dialog and returns the selected profile.
|
||||
/// </summary>
|
||||
UserProfile ShowPlayerSelectDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the UI theme and returns true if is dark mode.
|
||||
/// </summary>
|
||||
bool IsDarkMode();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Bcat.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Bcat;
|
||||
@ -16,5 +17,13 @@ namespace Ryujinx.Horizon.Bcat.Ipc
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(30300)]
|
||||
// RegisterSystemApplicationDeliveryTasks
|
||||
public Result RegisterSystemApplicationDeliveryTasks()
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceBcat);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Lbl;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
@ -8,6 +9,15 @@ namespace Ryujinx.Horizon.Lbl.Ipc
|
||||
{
|
||||
private bool _vrModeEnabled;
|
||||
private float _currentBrightnessSettingForVrMode;
|
||||
|
||||
[CmifCommand(1)]
|
||||
// LoadCurrentSetting()
|
||||
public Result LoadCurrentSetting()
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceLbl);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(17)]
|
||||
public Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1)
|
||||
|
10
src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs
Normal file
10
src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Ovln;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Ovln.Ipc
|
||||
{
|
||||
partial class Sender : ISender
|
||||
{
|
||||
}
|
||||
}
|
@ -1,8 +1,21 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Ovln;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Ovln.Ipc
|
||||
{
|
||||
partial class SenderService : ISenderService
|
||||
{
|
||||
[CmifCommand(0)]
|
||||
// OpenSender() -> object<nn::ovln::ISender>
|
||||
public Result OpenSender(out ISender service)
|
||||
{
|
||||
service = new Sender();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceOvln);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs
Normal file
9
src/Ryujinx.Horizon/Sdk/Ovln/ISender.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using LibHac;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Ovln
|
||||
{
|
||||
interface ISender : IServiceObject
|
||||
{
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
@ -47,6 +48,7 @@ using SkiaSharp;
|
||||
using SPB.Graphics.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -54,6 +56,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
|
||||
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
|
||||
using ApplicationId = LibHac.ApplicationId;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
@ -124,6 +127,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
private bool _dialogShown;
|
||||
private readonly bool _isFirmwareTitle;
|
||||
private List<RyuApplicationData> _titles = new();
|
||||
|
||||
private readonly Lock _lockObject = new();
|
||||
|
||||
@ -141,6 +145,8 @@ namespace Ryujinx.Ava
|
||||
public string ApplicationPath { get; private set; }
|
||||
public ulong ApplicationId { get; private set; }
|
||||
public bool ScreenshotRequested { get; set; }
|
||||
|
||||
public IImmutableList<RyuApplicationData> Titles { get => _titles.ToImmutableList(); }
|
||||
|
||||
public AppHost(
|
||||
RendererHost renderer,
|
||||
@ -919,12 +925,19 @@ namespace Ryujinx.Ava
|
||||
|
||||
// Initialize Configuration.
|
||||
MemoryConfiguration memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
|
||||
|
||||
_titles = new List<RyuApplicationData>();
|
||||
foreach (ApplicationData App in _viewModel.Applications)
|
||||
{
|
||||
_titles.Add(new(new ApplicationId(App.Id),App.ControlHolder.Value,App.Path,App.Icon));
|
||||
}
|
||||
|
||||
Device = new Switch(new HLEConfiguration(
|
||||
VirtualFileSystem,
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
Titles.ToImmutableList(),
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
InitializeAudio(),
|
||||
|
@ -172,6 +172,56 @@
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "MenuBarFileOpenAppletOpenQLaunchApplet",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "QLaunch Applet",
|
||||
"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": "MenuBarFileOpenAppletOpenQLaunchAppletToolTip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Open QLaunch Applet in Standalone mode",
|
||||
"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": "SettingsTabInputDirectMouseAccess",
|
||||
"Translations": {
|
||||
|
@ -16,9 +16,11 @@ using Ryujinx.Graphics.Metal;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.HOS.Services.Ns.Types;
|
||||
using Ryujinx.Input;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
@ -333,6 +335,7 @@ namespace Ryujinx.Headless
|
||||
_libHacHorizonManager,
|
||||
_contentManager,
|
||||
_accountManager,
|
||||
new ImmutableArray<RyuApplicationData>(),
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
new SDL2HardwareDeviceDriver(),
|
||||
|
@ -563,5 +563,10 @@ namespace Ryujinx.Headless
|
||||
{
|
||||
return AccountSaveDataManager.GetLastUsedUser();
|
||||
}
|
||||
|
||||
public bool IsDarkMode()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,5 +313,10 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
public bool IsDarkMode()
|
||||
{
|
||||
return ConfigurationState.Instance.UI.BaseStyle.Value == "Dark";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,11 @@
|
||||
Header="{ext:Locale MenuBarFileOpenAppletOpenPhotoViewerApplet}"
|
||||
Icon="{ext:Icon fa-solid fa-photo-film}"
|
||||
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenPhotoViewerAppletToolTip}" />
|
||||
<MenuItem
|
||||
Name="QLaunchAppletMenuItem"
|
||||
Header="{ext:Locale MenuBarFileOpenAppletOpenQLaunchApplet}"
|
||||
Icon="{ext:Icon fa-solid fa-newspaper}"
|
||||
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenQLaunchAppletToolTip}" />
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem
|
||||
|
@ -41,6 +41,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
||||
MiiAppletMenuItem.Command = Commands.Create(OpenMiiApplet);
|
||||
PhotoViewerAppletMenuItem.Command = Commands.Create(OpenPhotoViewerApplet);
|
||||
QLaunchAppletMenuItem.Command = Commands.Create(OpenSystemApplet);
|
||||
CloseRyujinxMenuItem.Command = Commands.Create(CloseWindow);
|
||||
OpenSettingsMenuItem.Command = Commands.Create(OpenSettings);
|
||||
PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause());
|
||||
@ -165,6 +166,16 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
}
|
||||
|
||||
public AppletMetadata SystemApplet => new(ViewModel.ContentManager,"qlaunch", 0x0100000000001000);
|
||||
|
||||
public async Task OpenSystemApplet()
|
||||
{
|
||||
if (SystemApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
|
||||
{
|
||||
await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OpenCheatManagerForCurrentApp()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user