2020-01-05 12:49:44 +01:00
using LibHac.Account ;
using LibHac.Common ;
2020-02-02 04:24:17 +01:00
using LibHac.Fs ;
2022-05-05 20:23:30 +02:00
using LibHac.Ncm ;
2020-01-05 12:49:44 +01:00
using LibHac.Ns ;
2023-03-02 03:42:27 +01:00
using LibHac.Tools.FsSystem.NcaUtils ;
2020-01-05 12:49:44 +01:00
using Ryujinx.Common ;
2018-10-17 19:15:50 +02:00
using Ryujinx.Common.Logging ;
2022-05-05 20:23:30 +02:00
using Ryujinx.HLE.Exceptions ;
2019-09-20 06:42:32 +02:00
using Ryujinx.HLE.HOS.Ipc ;
2020-02-12 00:07:13 +01:00
using Ryujinx.HLE.HOS.Kernel.Memory ;
2019-09-20 06:42:32 +02:00
using Ryujinx.HLE.HOS.Kernel.Threading ;
2019-09-19 02:45:11 +02:00
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage ;
2020-09-21 05:45:30 +02:00
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types ;
2019-11-15 01:25:22 +01:00
using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService ;
2020-05-27 10:40:23 +02:00
using Ryujinx.HLE.HOS.SystemState ;
2023-01-04 23:15:45 +01:00
using Ryujinx.Horizon.Common ;
2019-09-20 06:42:32 +02:00
using System ;
2020-05-27 10:40:23 +02:00
using System.Numerics ;
2022-05-05 20:23:30 +02:00
using System.Threading ;
2020-11-21 23:38:34 +01:00
using AccountUid = Ryujinx . HLE . HOS . Services . Account . Acc . UserId ;
2020-09-01 22:08:59 +02:00
using ApplicationId = LibHac . Ncm . ApplicationId ;
2020-01-05 12:49:44 +01:00
2019-09-19 02:45:11 +02:00
namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy
2018-02-05 00:08:20 +01:00
{
2018-03-19 19:58:46 +01:00
class IApplicationFunctions : IpcService
2018-02-05 00:08:20 +01:00
{
2022-11-18 04:29:01 +01:00
private long _defaultSaveDataSize = 200000000 ;
private long _defaultJournalSaveDataSize = 200000000 ;
2021-04-14 14:55:11 +02:00
2019-09-20 06:42:32 +02:00
private KEvent _gpuErrorDetectedSystemEvent ;
2020-05-12 16:52:27 +02:00
private KEvent _friendInvitationStorageChannelEvent ;
private KEvent _notificationStorageChannelEvent ;
2020-12-09 00:08:36 +01:00
private KEvent _healthWarningDisappearedSystemEvent ;
2019-09-20 06:42:32 +02:00
2020-12-02 00:23:43 +01:00
private int _gpuErrorDetectedSystemEventHandle ;
private int _friendInvitationStorageChannelEventHandle ;
private int _notificationStorageChannelEventHandle ;
2020-12-09 00:08:36 +01:00
private int _healthWarningDisappearedSystemEventHandle ;
2020-12-02 00:23:43 +01:00
2022-05-08 00:28:54 +02:00
private bool _gamePlayRecordingState ;
2022-05-05 20:23:30 +02:00
private int _jitLoaded ;
2023-01-04 23:15:45 +01:00
private LibHac . HorizonClient _horizon ;
2021-08-12 23:56:24 +02:00
2019-09-20 06:42:32 +02:00
public IApplicationFunctions ( Horizon system )
{
2020-12-09 00:08:36 +01:00
// TODO: Find where they are signaled.
2020-11-21 23:38:34 +01:00
_gpuErrorDetectedSystemEvent = new KEvent ( system . KernelContext ) ;
2020-05-12 16:52:27 +02:00
_friendInvitationStorageChannelEvent = new KEvent ( system . KernelContext ) ;
2020-11-21 23:38:34 +01:00
_notificationStorageChannelEvent = new KEvent ( system . KernelContext ) ;
2020-12-09 00:08:36 +01:00
_healthWarningDisappearedSystemEvent = new KEvent ( system . KernelContext ) ;
2021-08-12 23:56:24 +02:00
_horizon = system . LibHacHorizonManager . AmClient ;
2019-09-20 06:42:32 +02:00
}
2018-02-10 01:14:55 +01:00
2021-04-14 00:01:24 +02:00
[CommandHipc(1)]
2020-09-21 05:45:30 +02:00
// PopLaunchParameter(LaunchParameterKind kind) -> object<nn::am::service::IStorage>
2019-07-14 21:04:38 +02:00
public ResultCode PopLaunchParameter ( ServiceCtx context )
2018-02-05 00:08:20 +01:00
{
2020-09-21 05:45:30 +02:00
LaunchParameterKind kind = ( LaunchParameterKind ) context . RequestData . ReadUInt32 ( ) ;
byte [ ] storageData ;
switch ( kind )
{
case LaunchParameterKind . UserChannel :
2021-05-16 17:12:14 +02:00
storageData = context . Device . Configuration . UserChannelPersistence . Pop ( ) ;
2020-09-21 05:45:30 +02:00
break ;
case LaunchParameterKind . PreselectedUser :
// Only the first 0x18 bytes of the Data seems to be actually used.
2021-04-13 03:16:43 +02:00
storageData = StorageHelper . MakeLaunchParams ( context . Device . System . AccountManager . LastOpenedUser ) ;
2020-09-21 05:45:30 +02:00
break ;
2020-09-23 23:57:18 +02:00
case LaunchParameterKind . Unknown :
throw new NotImplementedException ( "Unknown LaunchParameterKind." ) ;
2020-09-21 05:45:30 +02:00
default :
2020-09-23 23:57:18 +02:00
return ResultCode . ObjectInvalid ;
}
if ( storageData = = null )
{
return ResultCode . NotAvailable ;
2020-09-21 05:45:30 +02:00
}
MakeObject ( context , new AppletAE . IStorage ( storageData ) ) ;
2018-02-05 00:08:20 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-02-05 00:08:20 +01:00
}
2021-07-10 23:37:29 +02:00
[CommandHipc(12)] // 4.0.0+
// CreateApplicationAndRequestToStart(u64 title_id)
public ResultCode CreateApplicationAndRequestToStart ( ServiceCtx context )
{
ulong titleId = context . RequestData . ReadUInt64 ( ) ;
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { titleId } ) ;
if ( titleId = = 0 )
{
context . Device . UiHandler . ExecuteProgram ( context . Device , ProgramSpecifyKind . RestartProgram , titleId ) ;
}
else
{
throw new NotImplementedException ( ) ;
}
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(20)]
2019-07-12 03:13:43 +02:00
// EnsureSaveData(nn::account::Uid) -> u64
2019-07-14 21:04:38 +02:00
public ResultCode EnsureSaveData ( ServiceCtx context )
2018-02-05 00:08:20 +01:00
{
2021-08-12 23:56:24 +02:00
Uid userId = context . RequestData . ReadStruct < AccountUid > ( ) . ToLibHacUid ( ) ;
// Mask out the low nibble of the program ID to get the application ID
ApplicationId applicationId = new ApplicationId ( context . Device . Application . TitleId & ~ 0xF ul ) ;
2018-04-17 02:24:42 +02:00
2020-05-15 08:16:46 +02:00
BlitStruct < ApplicationControlProperty > controlHolder = context . Device . Application . ControlData ;
2018-02-05 00:08:20 +01:00
2020-01-05 12:49:44 +01:00
ref ApplicationControlProperty control = ref controlHolder . Value ;
2019-11-15 01:25:22 +01:00
2022-01-12 12:22:19 +01:00
if ( LibHac . Common . Utilities . IsZeros ( controlHolder . ByteSpan ) )
2020-01-05 12:49:44 +01:00
{
// If the current application doesn't have a loaded control property, create a dummy one
// and set the savedata sizes so a user savedata will be created.
control = ref new BlitStruct < ApplicationControlProperty > ( 1 ) . Value ;
// The set sizes don't actually matter as long as they're non-zero because we use directory savedata.
2020-11-21 23:38:34 +01:00
control . UserAccountSaveDataSize = 0x4000 ;
2020-01-05 12:49:44 +01:00
control . UserAccountSaveDataJournalSize = 0x4000 ;
2020-08-04 01:32:53 +02:00
Logger . Warning ? . Print ( LogClass . ServiceAm ,
2020-01-05 12:49:44 +01:00
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games." ) ;
}
2023-01-04 23:15:45 +01:00
LibHac . HorizonClient hos = context . Device . System . LibHacHorizonManager . AmClient ;
LibHac . Result result = hos . Fs . EnsureApplicationSaveData ( out long requiredSize , applicationId , in control , in userId ) ;
2020-01-05 12:49:44 +01:00
context . ResponseData . Write ( requiredSize ) ;
return ( ResultCode ) result . Value ;
2018-02-05 00:08:20 +01:00
}
2021-04-14 00:01:24 +02:00
[CommandHipc(21)]
2019-07-12 03:13:43 +02:00
// GetDesiredLanguage() -> nn::settings::LanguageCode
2019-07-14 21:04:38 +02:00
public ResultCode GetDesiredLanguage ( ServiceCtx context )
2018-02-05 00:08:20 +01:00
{
2020-05-27 10:40:23 +02:00
// This seems to be calling ns:am GetApplicationDesiredLanguage followed by ConvertApplicationLanguageToLanguageCode
// Calls are from a IReadOnlyApplicationControlDataInterface object
// ConvertApplicationLanguageToLanguageCode compares language code strings and returns the index
// TODO: When above calls are implemented, switch to using ns:am
long desiredLanguageCode = context . Device . System . State . DesiredLanguageCode ;
2022-02-27 00:52:25 +01:00
int supportedLanguages = ( int ) context . Device . Application . ControlData . Value . SupportedLanguageFlag ;
2020-11-21 23:38:34 +01:00
int firstSupported = BitOperations . TrailingZeroCount ( supportedLanguages ) ;
2020-05-27 10:40:23 +02:00
2022-12-10 21:21:13 +01:00
if ( firstSupported > ( int ) TitleLanguage . BrazilianPortuguese )
2020-05-27 10:40:23 +02:00
{
2020-08-04 01:32:53 +02:00
Logger . Warning ? . Print ( LogClass . ServiceAm , "Application has zero supported languages" ) ;
2020-05-27 10:40:23 +02:00
context . ResponseData . Write ( desiredLanguageCode ) ;
return ResultCode . Success ;
}
2020-12-02 00:23:43 +01:00
// If desired language is not supported by application, use first supported language from TitleLanguage.
2020-05-27 10:40:23 +02:00
// TODO: In the future, a GUI could enable user-specified search priority
if ( ( ( 1 < < ( int ) context . Device . System . State . DesiredTitleLanguage ) & supportedLanguages ) = = 0 )
{
2022-12-10 21:21:13 +01:00
SystemLanguage newLanguage = Enum . Parse < SystemLanguage > ( Enum . GetName ( typeof ( TitleLanguage ) , firstSupported ) ) ;
2020-05-27 10:40:23 +02:00
desiredLanguageCode = SystemStateMgr . GetLanguageCode ( ( int ) newLanguage ) ;
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . ServiceAm , $"Application doesn't support configured language. Using {newLanguage}" ) ;
2020-05-27 10:40:23 +02:00
}
context . ResponseData . Write ( desiredLanguageCode ) ;
2018-02-05 00:08:20 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-02-05 00:08:20 +01:00
}
2021-04-14 00:01:24 +02:00
[CommandHipc(22)]
2019-07-12 03:13:43 +02:00
// SetTerminateResult(u32)
2019-07-14 21:04:38 +02:00
public ResultCode SetTerminateResult ( ServiceCtx context )
2018-02-25 19:58:16 +01:00
{
2023-01-04 23:15:45 +01:00
LibHac . Result result = new LibHac . Result ( context . RequestData . ReadUInt32 ( ) ) ;
2018-02-25 19:58:16 +01:00
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . ServiceAm , $"Result = 0x{result.Value:x8} ({result.ToStringWithName()})." ) ;
2018-02-25 19:58:16 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-02-25 19:58:16 +01:00
}
2021-04-14 00:01:24 +02:00
[CommandHipc(23)]
2019-07-12 03:13:43 +02:00
// GetDisplayVersion() -> nn::oe::DisplayVersion
2019-07-14 21:04:38 +02:00
public ResultCode GetDisplayVersion ( ServiceCtx context )
2018-04-22 06:21:49 +02:00
{
2020-05-27 10:22:50 +02:00
// If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation.
context . ResponseData . Write ( context . Device . Application . ControlData . Value . DisplayVersion ) ;
2018-04-22 06:21:49 +02:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-04-22 06:21:49 +02:00
}
2021-04-14 14:55:11 +02:00
[CommandHipc(25)] // 3.0.0+
2022-11-18 04:29:01 +01:00
// ExtendSaveData(u8 save_data_type, nn::account::Uid, s64 save_size, s64 journal_size) -> u64 result_code
2021-04-14 14:55:11 +02:00
public ResultCode ExtendSaveData ( ServiceCtx context )
{
SaveDataType saveDataType = ( SaveDataType ) context . RequestData . ReadUInt64 ( ) ;
2021-08-12 23:56:24 +02:00
Uid userId = context . RequestData . ReadStruct < Uid > ( ) ;
2022-11-18 04:29:01 +01:00
long saveDataSize = context . RequestData . ReadInt64 ( ) ;
long journalSize = context . RequestData . ReadInt64 ( ) ;
2021-04-14 14:55:11 +02:00
// NOTE: Service calls nn::fs::ExtendApplicationSaveData.
// Since LibHac currently doesn't support this method, we can stub it for now.
_defaultSaveDataSize = saveDataSize ;
_defaultJournalSaveDataSize = journalSize ;
context . ResponseData . Write ( ( uint ) ResultCode . Success ) ;
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { saveDataType , userId , saveDataSize , journalSize } ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(26)] // 3.0.0+
2022-11-18 04:29:01 +01:00
// GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (s64 save_size, s64 journal_size)
2020-02-02 04:24:17 +01:00
public ResultCode GetSaveDataSize ( ServiceCtx context )
{
2020-11-24 20:45:23 +01:00
SaveDataType saveDataType = ( SaveDataType ) context . RequestData . ReadUInt64 ( ) ;
2021-08-12 23:56:24 +02:00
Uid userId = context . RequestData . ReadStruct < Uid > ( ) ;
2020-11-21 23:38:34 +01:00
2020-11-24 20:45:23 +01:00
// NOTE: Service calls nn::fs::FindSaveDataWithFilter with SaveDataType = 1 hardcoded.
// Then it calls nn::fs::GetSaveDataAvailableSize and nn::fs::GetSaveDataJournalSize to get the sizes.
// Since LibHac currently doesn't support the 2 last methods, we can hardcode the values to 200mb.
2020-02-02 04:24:17 +01:00
2021-04-14 14:55:11 +02:00
context . ResponseData . Write ( _defaultSaveDataSize ) ;
context . ResponseData . Write ( _defaultJournalSaveDataSize ) ;
2020-02-02 04:24:17 +01:00
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { saveDataType , userId } ) ;
2020-02-02 04:24:17 +01:00
return ResultCode . Success ;
}
2021-08-12 23:56:24 +02:00
[CommandHipc(27)] // 5.0.0+
// CreateCacheStorage(u16 index, s64 save_size, s64 journal_size) -> (u32 storageTarget, u64 requiredSize)
public ResultCode CreateCacheStorage ( ServiceCtx context )
{
ushort index = ( ushort ) context . RequestData . ReadUInt64 ( ) ;
long saveSize = context . RequestData . ReadInt64 ( ) ;
long journalSize = context . RequestData . ReadInt64 ( ) ;
// Mask out the low nibble of the program ID to get the application ID
ApplicationId applicationId = new ApplicationId ( context . Device . Application . TitleId & ~ 0xF ul ) ;
BlitStruct < ApplicationControlProperty > controlHolder = context . Device . Application . ControlData ;
2023-01-04 23:15:45 +01:00
LibHac . Result result = _horizon . Fs . CreateApplicationCacheStorage ( out long requiredSize ,
2022-02-27 00:52:25 +01:00
out CacheStorageTargetMedia storageTarget , applicationId , in controlHolder . Value , index , saveSize ,
2021-08-12 23:56:24 +02:00
journalSize ) ;
if ( result . IsFailure ( ) ) return ( ResultCode ) result . Value ;
context . ResponseData . Write ( ( ulong ) storageTarget ) ;
context . ResponseData . Write ( requiredSize ) ;
return ResultCode . Success ;
}
2022-11-18 04:29:01 +01:00
[CommandHipc(28)] // 11.0.0+
// GetSaveDataSizeMax() -> (s64 save_size_max, s64 journal_size_max)
public ResultCode GetSaveDataSizeMax ( ServiceCtx context )
{
// NOTE: We are currently using a stub for GetSaveDataSize() which returns the default values.
// For this method we shouldn't return anything lower than that, but since we aren't interacting
// with fs to get the actual sizes, we return the default values here as well.
// This also helps in case ExtendSaveData() has been executed and the default values were modified.
context . ResponseData . Write ( _defaultSaveDataSize ) ;
context . ResponseData . Write ( _defaultJournalSaveDataSize ) ;
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(30)]
2020-09-20 05:40:10 +02:00
// BeginBlockingHomeButtonShortAndLongPressed()
public ResultCode BeginBlockingHomeButtonShortAndLongPressed ( ServiceCtx context )
{
// NOTE: This set two internal fields at offsets 0x89 and 0x8B to value 1 then it signals an internal event.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(31)]
2020-09-20 05:40:10 +02:00
// EndBlockingHomeButtonShortAndLongPressed()
public ResultCode EndBlockingHomeButtonShortAndLongPressed ( ServiceCtx context )
{
// NOTE: This set two internal fields at offsets 0x89 and 0x8B to value 0 then it signals an internal event.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(32)] // 2.0.0+
2020-09-20 05:40:10 +02:00
// BeginBlockingHomeButton(u64 nano_second)
public ResultCode BeginBlockingHomeButton ( ServiceCtx context )
{
ulong nanoSeconds = context . RequestData . ReadUInt64 ( ) ;
// NOTE: This set two internal fields at offsets 0x89 to value 1 and 0x90 to value of "nanoSeconds" then it signals an internal event.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { nanoSeconds } ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(33)] // 2.0.0+
2020-09-20 05:40:10 +02:00
// EndBlockingHomeButton()
public ResultCode EndBlockingHomeButton ( ServiceCtx context )
{
// NOTE: This set two internal fields at offsets 0x89 and 0x90 to value 0 then it signals an internal event.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(40)]
2019-07-12 03:13:43 +02:00
// NotifyRunning() -> b8
2019-07-14 21:04:38 +02:00
public ResultCode NotifyRunning ( ServiceCtx context )
2018-02-07 00:28:32 +01:00
{
2020-07-22 06:56:00 +02:00
context . ResponseData . Write ( true ) ;
2018-02-07 00:28:32 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-02-07 00:28:32 +01:00
}
2021-04-14 00:01:24 +02:00
[CommandHipc(50)] // 2.0.0+
2019-07-12 03:13:43 +02:00
// GetPseudoDeviceId() -> nn::util::Uuid
2019-07-14 21:04:38 +02:00
public ResultCode GetPseudoDeviceId ( ServiceCtx context )
2018-05-25 23:33:09 +02:00
{
2018-12-06 12:16:24 +01:00
context . ResponseData . Write ( 0L ) ;
context . ResponseData . Write ( 0L ) ;
2018-05-25 23:33:09 +02:00
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
2019-11-15 01:25:22 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-05-25 23:33:09 +02:00
}
2021-12-30 11:25:46 +01:00
[CommandHipc(60)] // 2.0.0+
// SetMediaPlaybackStateForApplication(bool enabled)
public ResultCode SetMediaPlaybackStateForApplication ( ServiceCtx context )
{
bool enabled = context . RequestData . ReadBoolean ( ) ;
// NOTE: Service stores the "enabled" value in a private field, when enabled is false, it stores nn::os::GetSystemTick() too.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { enabled } ) ;
return ResultCode . Success ;
}
2022-05-08 00:28:54 +02:00
[CommandHipc(65)] // 3.0.0+
// IsGamePlayRecordingSupported() -> u8
public ResultCode IsGamePlayRecordingSupported ( ServiceCtx context )
{
context . ResponseData . Write ( _gamePlayRecordingState ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(66)] // 3.0.0+
2019-07-12 03:13:43 +02:00
// InitializeGamePlayRecording(u64, handle<copy>)
2019-07-14 21:04:38 +02:00
public ResultCode InitializeGamePlayRecording ( ServiceCtx context )
2018-02-05 00:08:20 +01:00
{
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
2018-02-05 00:08:20 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-06-03 00:46:09 +02:00
}
2018-02-05 00:08:20 +01:00
2021-04-14 00:01:24 +02:00
[CommandHipc(67)] // 3.0.0+
2019-07-12 03:13:43 +02:00
// SetGamePlayRecordingState(u32)
2019-07-14 21:04:38 +02:00
public ResultCode SetGamePlayRecordingState ( ServiceCtx context )
2018-06-03 00:46:09 +02:00
{
2022-05-08 00:28:54 +02:00
_gamePlayRecordingState = context . RequestData . ReadInt32 ( ) ! = 0 ;
2018-06-03 00:46:09 +02:00
2022-05-08 00:28:54 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { _gamePlayRecordingState } ) ;
2018-02-05 00:08:20 +01:00
2019-07-14 21:04:38 +02:00
return ResultCode . Success ;
2018-02-05 00:08:20 +01:00
}
2019-09-20 06:42:32 +02:00
2021-04-14 00:01:24 +02:00
[CommandHipc(90)] // 4.0.0+
2020-07-22 06:56:00 +02:00
// EnableApplicationCrashReport(u8)
public ResultCode EnableApplicationCrashReport ( ServiceCtx context )
{
bool applicationCrashReportEnabled = context . RequestData . ReadBoolean ( ) ;
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { applicationCrashReportEnabled } ) ;
2020-07-22 06:56:00 +02:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(100)] // 5.0.0+
2020-02-12 00:07:13 +01:00
// InitializeApplicationCopyrightFrameBuffer(s32 width, s32 height, handle<copy, transfer_memory> transfer_memory, u64 transfer_memory_size)
public ResultCode InitializeApplicationCopyrightFrameBuffer ( ServiceCtx context )
{
2020-11-21 23:38:34 +01:00
int width = context . RequestData . ReadInt32 ( ) ;
int height = context . RequestData . ReadInt32 ( ) ;
ulong transferMemorySize = context . RequestData . ReadUInt64 ( ) ;
int transferMemoryHandle = context . Request . HandleDesc . ToCopy [ 0 ] ;
2020-02-12 00:07:13 +01:00
ulong transferMemoryAddress = context . Process . HandleTable . GetObject < KTransferMemory > ( transferMemoryHandle ) . Address ;
ResultCode resultCode = ResultCode . InvalidParameters ;
if ( ( ( transferMemorySize & 0x3FFFF ) = = 0 ) & & width < = 1280 & & height < = 720 )
{
resultCode = InitializeApplicationCopyrightFrameBufferImpl ( transferMemoryAddress , transferMemorySize , width , height ) ;
}
2020-12-02 00:23:43 +01:00
if ( transferMemoryHandle ! = 0 )
2020-02-12 00:07:13 +01:00
{
2020-12-02 00:23:43 +01:00
context . Device . System . KernelContext . Syscall . CloseHandle ( transferMemoryHandle ) ;
2020-02-12 00:07:13 +01:00
}
return resultCode ;
}
private ResultCode InitializeApplicationCopyrightFrameBufferImpl ( ulong transferMemoryAddress , ulong transferMemorySize , int width , int height )
{
if ( ( transferMemorySize & 0x3FFFF ) ! = 0 )
{
return ResultCode . InvalidParameters ;
}
2020-11-21 23:38:34 +01:00
ResultCode resultCode ;
2020-02-12 00:07:13 +01:00
// if (_copyrightBuffer == null)
{
// TODO: Initialize buffer and object.
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { transferMemoryAddress , transferMemorySize , width , height } ) ;
2020-02-12 00:07:13 +01:00
resultCode = ResultCode . Success ;
}
return resultCode ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(101)] // 5.0.0+
2020-02-12 00:07:13 +01:00
// SetApplicationCopyrightImage(buffer<bytes, 0x45> frame_buffer, s32 x, s32 y, s32 width, s32 height, s32 window_origin_mode)
public ResultCode SetApplicationCopyrightImage ( ServiceCtx context )
{
2021-04-24 12:16:01 +02:00
ulong frameBufferPos = context . Request . SendBuff [ 0 ] . Position ;
ulong frameBufferSize = context . Request . SendBuff [ 0 ] . Size ;
int x = context . RequestData . ReadInt32 ( ) ;
int y = context . RequestData . ReadInt32 ( ) ;
int width = context . RequestData . ReadInt32 ( ) ;
int height = context . RequestData . ReadInt32 ( ) ;
uint windowOriginMode = context . RequestData . ReadUInt32 ( ) ;
2020-02-12 00:07:13 +01:00
ResultCode resultCode = ResultCode . InvalidParameters ;
if ( ( ( y | x ) > = 0 ) & & width > = 1 & & height > = 1 )
{
ResultCode result = SetApplicationCopyrightImageImpl ( x , y , width , height , frameBufferPos , frameBufferSize , windowOriginMode ) ;
2020-09-01 22:08:59 +02:00
if ( result ! = ResultCode . Success )
2020-02-12 00:07:13 +01:00
{
resultCode = result ;
}
else
{
resultCode = ResultCode . Success ;
}
}
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { frameBufferPos , frameBufferSize , x , y , width , height , windowOriginMode } ) ;
2020-02-12 00:07:13 +01:00
return resultCode ;
}
2021-04-24 12:16:01 +02:00
private ResultCode SetApplicationCopyrightImageImpl ( int x , int y , int width , int height , ulong frameBufferPos , ulong frameBufferSize , uint windowOriginMode )
2020-02-12 00:07:13 +01:00
{
/ *
if ( _copyrightBuffer = = null )
{
return ResultCode . NullCopyrightObject ;
}
* /
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { x , y , width , height , frameBufferPos , frameBufferSize , windowOriginMode } ) ;
2020-02-12 00:07:13 +01:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(102)] // 5.0.0+
2020-02-12 00:07:13 +01:00
// SetApplicationCopyrightVisibility(bool visible)
public ResultCode SetApplicationCopyrightVisibility ( ServiceCtx context )
{
bool visible = context . RequestData . ReadBoolean ( ) ;
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { visible } ) ;
2020-02-12 00:07:13 +01:00
// NOTE: It sets an internal field and return ResultCode.Success in all case.
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(110)] // 5.0.0+
2019-11-15 01:25:22 +01:00
// QueryApplicationPlayStatistics(buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
public ResultCode QueryApplicationPlayStatistics ( ServiceCtx context )
{
// TODO: Call pdm:qry cmd 13 when IPC call between services will be implemented.
return ( ResultCode ) QueryPlayStatisticsManager . GetPlayStatistics ( context ) ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(111)] // 6.0.0+
2019-11-15 01:25:22 +01:00
// QueryApplicationPlayStatisticsByUid(nn::account::Uid, buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
public ResultCode QueryApplicationPlayStatisticsByUid ( ServiceCtx context )
{
// TODO: Call pdm:qry cmd 16 when IPC call between services will be implemented.
return ( ResultCode ) QueryPlayStatisticsManager . GetPlayStatistics ( context , true ) ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(120)] // 5.0.0+
2020-09-21 05:45:30 +02:00
// ExecuteProgram(ProgramSpecifyKind kind, u64 value)
public ResultCode ExecuteProgram ( ServiceCtx context )
{
ProgramSpecifyKind kind = ( ProgramSpecifyKind ) context . RequestData . ReadUInt32 ( ) ;
// padding
context . RequestData . ReadUInt32 ( ) ;
ulong value = context . RequestData . ReadUInt64 ( ) ;
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { kind , value } ) ;
context . Device . UiHandler . ExecuteProgram ( context . Device , kind , value ) ;
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(121)] // 5.0.0+
2020-09-21 05:45:30 +02:00
// ClearUserChannel()
public ResultCode ClearUserChannel ( ServiceCtx context )
{
2021-05-16 17:12:14 +02:00
context . Device . Configuration . UserChannelPersistence . Clear ( ) ;
2020-09-21 05:45:30 +02:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(122)] // 5.0.0+
2020-09-21 05:45:30 +02:00
// UnpopToUserChannel(object<nn::am::service::IStorage> input_storage)
public ResultCode UnpopToUserChannel ( ServiceCtx context )
{
AppletAE . IStorage data = GetObject < AppletAE . IStorage > ( context , 0 ) ;
2021-05-16 17:12:14 +02:00
context . Device . Configuration . UserChannelPersistence . Push ( data . Data ) ;
2020-09-21 05:45:30 +02:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(123)] // 5.0.0+
2020-07-22 06:56:00 +02:00
// GetPreviousProgramIndex() -> s32 program_index
public ResultCode GetPreviousProgramIndex ( ServiceCtx context )
{
2021-05-16 17:12:14 +02:00
int previousProgramIndex = context . Device . Configuration . UserChannelPersistence . PreviousIndex ;
2020-07-22 06:56:00 +02:00
context . ResponseData . Write ( previousProgramIndex ) ;
2020-08-04 01:32:53 +02:00
Logger . Stub ? . PrintStub ( LogClass . ServiceAm , new { previousProgramIndex } ) ;
2020-07-22 06:56:00 +02:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(130)] // 8.0.0+
2019-09-20 06:42:32 +02:00
// GetGpuErrorDetectedSystemEvent() -> handle<copy>
public ResultCode GetGpuErrorDetectedSystemEvent ( ServiceCtx context )
{
2020-12-02 00:23:43 +01:00
if ( _gpuErrorDetectedSystemEventHandle = = 0 )
2019-09-20 06:42:32 +02:00
{
2023-01-04 23:15:45 +01:00
if ( context . Process . HandleTable . GenerateHandle ( _gpuErrorDetectedSystemEvent . ReadableEvent , out _gpuErrorDetectedSystemEventHandle ) ! = Result . Success )
2020-12-02 00:23:43 +01:00
{
throw new InvalidOperationException ( "Out of handles!" ) ;
}
2019-09-20 06:42:32 +02:00
}
2020-12-02 00:23:43 +01:00
context . Response . HandleDesc = IpcHandleDesc . MakeCopy ( _gpuErrorDetectedSystemEventHandle ) ;
2019-09-20 06:42:32 +02:00
2020-12-02 00:23:43 +01:00
// NOTE: This is used by "sdk" NSO during applet-application initialization.
2021-08-12 23:56:24 +02:00
// A separate thread is setup where event-waiting is handled.
2019-09-20 06:42:32 +02:00
// When the Event is signaled, official sw will assert.
return ResultCode . Success ;
}
2020-05-12 16:52:27 +02:00
2021-04-14 00:01:24 +02:00
[CommandHipc(140)] // 9.0.0+
2020-05-12 16:52:27 +02:00
// GetFriendInvitationStorageChannelEvent() -> handle<copy>
public ResultCode GetFriendInvitationStorageChannelEvent ( ServiceCtx context )
{
2020-12-02 00:23:43 +01:00
if ( _friendInvitationStorageChannelEventHandle = = 0 )
2020-05-12 16:52:27 +02:00
{
2023-01-04 23:15:45 +01:00
if ( context . Process . HandleTable . GenerateHandle ( _friendInvitationStorageChannelEvent . ReadableEvent , out _friendInvitationStorageChannelEventHandle ) ! = Result . Success )
2020-12-02 00:23:43 +01:00
{
throw new InvalidOperationException ( "Out of handles!" ) ;
}
2020-05-12 16:52:27 +02:00
}
2020-12-02 00:23:43 +01:00
context . Response . HandleDesc = IpcHandleDesc . MakeCopy ( _friendInvitationStorageChannelEventHandle ) ;
2020-05-12 16:52:27 +02:00
return ResultCode . Success ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(141)] // 9.0.0+
2020-11-21 23:38:34 +01:00
// TryPopFromFriendInvitationStorageChannel() -> object<nn::am::service::IStorage>
public ResultCode TryPopFromFriendInvitationStorageChannel ( ServiceCtx context )
{
// NOTE: IStorage are pushed in the channel with IApplicationAccessor PushToFriendInvitationStorageChannel
// If _friendInvitationStorageChannelEvent is signaled, the event is cleared.
2021-10-28 23:06:45 +02:00
// If an IStorage is available, returns it with ResultCode.Success.
2020-11-21 23:38:34 +01:00
// If not, just returns ResultCode.NotAvailable. Since we don't support friend feature for now, it's fine to do the same.
Logger . Stub ? . PrintStub ( LogClass . ServiceAm ) ;
return ResultCode . NotAvailable ;
}
2021-04-14 00:01:24 +02:00
[CommandHipc(150)] // 9.0.0+
2020-05-12 16:52:27 +02:00
// GetNotificationStorageChannelEvent() -> handle<copy>
public ResultCode GetNotificationStorageChannelEvent ( ServiceCtx context )
{
2020-12-02 00:23:43 +01:00
if ( _notificationStorageChannelEventHandle = = 0 )
2020-05-12 16:52:27 +02:00
{
2023-01-04 23:15:45 +01:00
if ( context . Process . HandleTable . GenerateHandle ( _notificationStorageChannelEvent . ReadableEvent , out _notificationStorageChannelEventHandle ) ! = Result . Success )
2020-12-02 00:23:43 +01:00
{
throw new InvalidOperationException ( "Out of handles!" ) ;
}
2020-05-12 16:52:27 +02:00
}
2020-12-02 00:23:43 +01:00
context . Response . HandleDesc = IpcHandleDesc . MakeCopy ( _notificationStorageChannelEventHandle ) ;
2020-05-12 16:52:27 +02:00
return ResultCode . Success ;
}
2020-12-09 00:08:36 +01:00
2021-04-14 00:01:24 +02:00
[CommandHipc(160)] // 9.0.0+
2020-12-09 00:08:36 +01:00
// GetHealthWarningDisappearedSystemEvent() -> handle<copy>
public ResultCode GetHealthWarningDisappearedSystemEvent ( ServiceCtx context )
{
if ( _healthWarningDisappearedSystemEventHandle = = 0 )
{
2023-01-04 23:15:45 +01:00
if ( context . Process . HandleTable . GenerateHandle ( _healthWarningDisappearedSystemEvent . ReadableEvent , out _healthWarningDisappearedSystemEventHandle ) ! = Result . Success )
2020-12-09 00:08:36 +01:00
{
throw new InvalidOperationException ( "Out of handles!" ) ;
}
}
context . Response . HandleDesc = IpcHandleDesc . MakeCopy ( _healthWarningDisappearedSystemEventHandle ) ;
return ResultCode . Success ;
}
2022-05-05 20:23:30 +02:00
[CommandHipc(1001)] // 10.0.0+
// PrepareForJit()
public ResultCode PrepareForJit ( ServiceCtx context )
{
if ( Interlocked . Exchange ( ref _jitLoaded , 1 ) = = 0 )
{
string jitPath = context . Device . System . ContentManager . GetInstalledContentPath ( 0x010000000000003B , StorageId . BuiltInSystem , NcaContentType . Program ) ;
string filePath = context . Device . FileSystem . SwitchPathToSystemPath ( jitPath ) ;
if ( string . IsNullOrWhiteSpace ( filePath ) )
{
throw new InvalidSystemResourceException ( $"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)" ) ;
}
context . Device . Application . LoadServiceNca ( filePath ) ;
// FIXME: Most likely not how this should be done?
while ( ! context . Device . System . SmRegistry . IsServiceRegistered ( "jit:u" ) )
{
context . Device . System . SmRegistry . WaitForServiceRegistration ( ) ;
}
}
return ResultCode . Success ;
}
2018-02-05 00:08:20 +01:00
}
2019-07-12 03:13:43 +02:00
}