2018-10-17 10:15:50 -07:00
using Ryujinx.Common.Logging ;
2019-06-15 15:35:38 -07:00
using Ryujinx.HLE.FileSystem ;
2018-08-16 16:47:36 -07:00
using Ryujinx.HLE.HOS.Ipc ;
2019-06-15 15:35:38 -07:00
using Ryujinx.HLE.HOS.Services.Arp ;
2018-08-16 16:47:36 -07:00
using Ryujinx.HLE.HOS.SystemState ;
2018-10-07 06:13:46 -07:00
using Ryujinx.HLE.Utilities ;
2019-06-15 15:35:38 -07:00
using System ;
2018-02-24 20:34:16 -08:00
using System.Collections.Generic ;
2018-08-16 16:47:36 -07:00
using static Ryujinx . HLE . HOS . ErrorCode ;
2018-08-14 15:02:42 -07:00
2018-08-16 16:47:36 -07:00
namespace Ryujinx.HLE.HOS.Services.Acc
2018-02-24 20:34:16 -08:00
{
2019-07-10 08:59:54 -07:00
[Service("acc:u0")]
[Service("acc:u1")]
2018-09-01 15:04:20 -07:00
class IAccountService : IpcService
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
private bool _userRegistrationRequestPermitted = false ;
private ApplicationLaunchProperty _applicationLaunchProperty ;
2018-12-06 03:16:24 -08:00
private Dictionary < int , ServiceProcessRequest > _commands ;
2018-02-24 20:34:16 -08:00
2018-12-06 03:16:24 -08:00
public override IReadOnlyDictionary < int , ServiceProcessRequest > Commands = > _commands ;
2018-02-24 20:34:16 -08:00
2019-07-10 08:59:54 -07:00
public IAccountService ( ServiceCtx context )
2018-02-24 20:34:16 -08:00
{
2018-12-06 03:16:24 -08:00
_commands = new Dictionary < int , ServiceProcessRequest >
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
{ 0 , GetUserCount } ,
{ 1 , GetUserExistence } ,
{ 2 , ListAllUsers } ,
{ 3 , ListOpenUsers } ,
{ 4 , GetLastOpenedUser } ,
{ 5 , GetProfile } ,
//{ 6, GetProfileDigest }, // 3.0.0+
{ 50 , IsUserRegistrationRequestPermitted } ,
{ 51 , TrySelectUserWithoutInteraction } ,
//{ 60, ListOpenContextStoredUsers }, // 5.0.0-5.1.0
//{ 99, DebugActivateOpenContextRetention }, // 6.0.0+
{ 100 , InitializeApplicationInfo } ,
{ 101 , GetBaasAccountManagerForApplication } ,
//{ 102, AuthenticateApplicationAsync },
//{ 103, CheckNetworkServiceAvailabilityAsync }, // 4.0.0+
{ 110 , StoreSaveDataThumbnail } ,
{ 111 , ClearSaveDataThumbnail } ,
//{ 120, CreateGuestLoginRequest },
//{ 130, LoadOpenContext }, // 6.0.0+
//{ 131, ListOpenContextStoredUsers }, // 6.0.0+
{ 140 , InitializeApplicationInfo } , // 6.0.0+
//{ 141, ListQualifiedUsers }, // 6.0.0+
{ 150 , IsUserAccountSwitchLocked } , // 6.0.0+
2018-02-24 20:34:16 -08:00
} ;
}
2018-10-13 16:16:02 -07:00
// GetUserCount() -> i32
2018-12-06 03:16:24 -08:00
public long GetUserCount ( ServiceCtx context )
2018-04-17 09:41:14 -07:00
{
2019-06-15 15:35:38 -07:00
context . ResponseData . Write ( context . Device . System . State . Account . GetUserCount ( ) ) ;
2018-04-17 09:41:14 -07:00
return 0 ;
}
2018-06-10 17:46:42 -07:00
2018-10-13 16:16:02 -07:00
// GetUserExistence(nn::account::Uid) -> bool
2018-12-06 03:16:24 -08:00
public long GetUserExistence ( ServiceCtx context )
2018-06-09 21:36:07 -07:00
{
2019-06-15 15:35:38 -07:00
UInt128 userId = new UInt128 ( context . RequestData . ReadBytes ( 0x10 ) ) ;
if ( userId . IsNull )
{
return MakeError ( ErrorModule . Account , AccErr . NullArgument ) ;
}
2018-06-09 21:36:07 -07:00
2019-06-15 15:35:38 -07:00
context . ResponseData . Write ( context . Device . System . State . Account . TryGetUser ( userId , out _ ) ) ;
2018-06-09 21:36:07 -07:00
return 0 ;
}
2018-04-17 09:41:14 -07:00
2018-10-13 16:16:02 -07:00
// ListAllUsers() -> array<nn::account::Uid, 0xa>
2018-12-06 03:16:24 -08:00
public long ListAllUsers ( ServiceCtx context )
2018-06-09 21:36:07 -07:00
{
2019-06-15 15:35:38 -07:00
return WriteUserList ( context , context . Device . System . State . Account . GetAllUsers ( ) ) ;
2018-06-09 21:36:07 -07:00
}
2018-06-10 17:46:42 -07:00
2018-10-13 16:16:02 -07:00
// ListOpenUsers() -> array<nn::account::Uid, 0xa>
2018-12-06 03:16:24 -08:00
public long ListOpenUsers ( ServiceCtx context )
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
return WriteUserList ( context , context . Device . System . State . Account . GetOpenedUsers ( ) ) ;
2018-08-14 15:02:42 -07:00
}
2018-12-06 03:16:24 -08:00
private long WriteUserList ( ServiceCtx context , IEnumerable < UserProfile > profiles )
2018-08-14 15:02:42 -07:00
{
2019-06-15 15:35:38 -07:00
if ( context . Request . RecvListBuff . Count = = 0 )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidInputBuffer ) ;
}
2018-12-06 03:16:24 -08:00
long outputPosition = context . Request . RecvListBuff [ 0 ] . Position ;
long outputSize = context . Request . RecvListBuff [ 0 ] . Size ;
2018-08-14 15:02:42 -07:00
2019-06-15 15:35:38 -07:00
ulong offset = 0 ;
2018-08-14 15:02:42 -07:00
2019-06-15 15:35:38 -07:00
foreach ( UserProfile userProfile in profiles )
2018-08-14 15:02:42 -07:00
{
2019-06-15 15:35:38 -07:00
if ( offset + 0x10 > ( ulong ) outputSize )
2018-08-14 15:02:42 -07:00
{
break ;
}
2019-06-15 15:35:38 -07:00
context . Memory . WriteInt64 ( outputPosition + ( long ) offset , userProfile . UserId . Low ) ;
context . Memory . WriteInt64 ( outputPosition + ( long ) offset + 8 , userProfile . UserId . High ) ;
offset + = 0x10 ;
2018-08-14 15:02:42 -07:00
}
2018-04-16 17:24:42 -07:00
2018-02-24 20:34:16 -08:00
return 0 ;
}
2018-10-13 16:16:02 -07:00
// GetLastOpenedUser() -> nn::account::Uid
2018-12-06 03:16:24 -08:00
public long GetLastOpenedUser ( ServiceCtx context )
2018-06-02 15:46:09 -07:00
{
2019-06-15 15:35:38 -07:00
context . Device . System . State . Account . LastOpenedUser . UserId . Write ( context . ResponseData ) ;
2018-06-02 15:46:09 -07:00
return 0 ;
}
2018-10-13 16:16:02 -07:00
// GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile>
2018-12-06 03:16:24 -08:00
public long GetProfile ( ServiceCtx context )
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
UInt128 userId = new UInt128 ( context . RequestData . ReadBytes ( 0x10 ) ) ;
2018-08-14 15:02:42 -07:00
2019-06-15 15:35:38 -07:00
if ( ! context . Device . System . State . Account . TryGetUser ( userId , out UserProfile userProfile ) )
2018-08-14 15:02:42 -07:00
{
2019-06-15 15:35:38 -07:00
Logger . PrintWarning ( LogClass . ServiceAcc , $"User 0x{userId} not found!" ) ;
2018-08-14 15:02:42 -07:00
return MakeError ( ErrorModule . Account , AccErr . UserNotFound ) ;
}
2019-06-15 15:35:38 -07:00
MakeObject ( context , new IProfile ( userProfile ) ) ;
// Doesn't occur in our case.
// return MakeError(ErrorModule.Account, AccErr.NullObject);
2018-02-24 20:34:16 -08:00
return 0 ;
}
2018-10-13 16:16:02 -07:00
// IsUserRegistrationRequestPermitted(u64, pid) -> bool
2018-12-06 03:16:24 -08:00
public long IsUserRegistrationRequestPermitted ( ServiceCtx context )
2018-10-13 16:16:02 -07:00
{
2019-06-15 15:35:38 -07:00
// The u64 argument seems to be unused by account.
context . ResponseData . Write ( _userRegistrationRequestPermitted ) ;
2018-10-13 16:16:02 -07:00
return 0 ;
}
// TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
2018-12-06 03:16:24 -08:00
public long TrySelectUserWithoutInteraction ( ServiceCtx context )
2018-10-13 16:16:02 -07:00
{
2019-06-15 15:35:38 -07:00
if ( context . Device . System . State . Account . GetUserCount ( ) ! = 1 )
{
// Invalid UserId.
new UInt128 ( 0 , 0 ) . Write ( context . ResponseData ) ;
2018-10-13 16:16:02 -07:00
2019-06-15 15:35:38 -07:00
return 0 ;
}
bool baasCheck = context . RequestData . ReadBoolean ( ) ;
if ( baasCheck )
{
// This checks something related to baas (online), and then return an invalid UserId if the check in baas returns an error code.
// In our case, we can just log it for now.
2018-10-13 16:16:02 -07:00
2019-06-15 15:35:38 -07:00
Logger . PrintStub ( LogClass . ServiceAcc , new { baasCheck } ) ;
}
2018-10-13 16:16:02 -07:00
2019-06-15 15:35:38 -07:00
// As we returned an invalid UserId if there is more than one user earlier, now we can return only the first one.
context . Device . System . State . Account . GetFirst ( ) . UserId . Write ( context . ResponseData ) ;
2018-10-13 16:16:02 -07:00
return 0 ;
}
// InitializeApplicationInfo(u64, pid)
2019-06-15 15:35:38 -07:00
// Both calls (100, 140) use the same submethod, maybe there's something different further along when arp:r is called?
2018-12-06 03:16:24 -08:00
public long InitializeApplicationInfo ( ServiceCtx context )
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
if ( _applicationLaunchProperty ! = null )
{
return MakeError ( ErrorModule . Account , AccErr . ApplicationLaunchPropertyAlreadyInit ) ;
}
// The u64 argument seems to be unused by account.
2018-12-06 03:16:24 -08:00
long unknown = context . RequestData . ReadInt64 ( ) ;
2018-10-13 16:16:02 -07:00
2019-06-15 15:35:38 -07:00
// TODO: Account actually calls nn::arp::detail::IReader::GetApplicationLaunchProperty() with the current PID and store the result (ApplicationLaunchProperty) internally.
// For now we can hardcode values, and fix it after GetApplicationLaunchProperty is implemented.
/ *
if ( nn : : arp : : detail : : IReader : : GetApplicationLaunchProperty ( ) = = 0xCC9D ) // InvalidProcessId
{
_applicationLaunchProperty = new ApplicationLaunchProperty
{
TitleId = 0x00 ;
Version = 0x00 ;
BaseGameStorageId = 0x03 ;
UpdateGameStorageId = 0x00 ;
}
return MakeError ( ErrorModule . Account , AccErr . InvalidArgument ) ;
}
else
* /
{
_applicationLaunchProperty = new ApplicationLaunchProperty
{
TitleId = BitConverter . ToInt64 ( StringUtils . HexToBytes ( context . Device . System . TitleID ) , 0 ) ,
Version = 0x00 ,
BaseGameStorageId = ( byte ) StorageId . NandSystem ,
UpdateGameStorageId = ( byte ) StorageId . None
} ;
}
2019-01-10 16:11:46 -08:00
Logger . PrintStub ( LogClass . ServiceAcc , new { unknown } ) ;
2018-04-16 17:24:42 -07:00
2018-02-24 20:34:16 -08:00
return 0 ;
}
2019-06-15 15:35:38 -07:00
// GetBaasAccountManagerForApplication(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication>
2018-12-06 03:16:24 -08:00
public long GetBaasAccountManagerForApplication ( ServiceCtx context )
2018-02-24 20:34:16 -08:00
{
2019-06-15 15:35:38 -07:00
UInt128 userId = new UInt128 ( context . RequestData . ReadBytes ( 0x10 ) ) ;
if ( userId . IsNull )
{
return MakeError ( ErrorModule . Account , AccErr . NullArgument ) ;
}
if ( _applicationLaunchProperty = = null )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidArgument ) ;
}
MakeObject ( context , new IManagerForApplication ( userId , _applicationLaunchProperty ) ) ;
// Doesn't occur in our case.
// return MakeError(ErrorModule.Account, AccErr.NullObject);
return 0 ;
}
// StoreSaveDataThumbnail(nn::account::Uid, buffer<bytes, 5>)
public long StoreSaveDataThumbnail ( ServiceCtx context )
{
if ( _applicationLaunchProperty = = null )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidArgument ) ;
}
UInt128 userId = new UInt128 ( context . RequestData . ReadBytes ( 0x10 ) ) ;
if ( userId . IsNull )
{
return MakeError ( ErrorModule . Account , AccErr . NullArgument ) ;
}
if ( context . Request . SendBuff . Count = = 0 )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidInputBuffer ) ;
}
long inputPosition = context . Request . SendBuff [ 0 ] . Position ;
long inputSize = context . Request . SendBuff [ 0 ] . Size ;
if ( inputSize ! = 0x24000 )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidInputBufferSize ) ;
}
byte [ ] thumbnailBuffer = context . Memory . ReadBytes ( inputPosition , inputSize ) ;
// TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ?
Logger . PrintStub ( LogClass . ServiceAcc ) ;
return 0 ;
}
// ClearSaveDataThumbnail(nn::account::Uid)
public long ClearSaveDataThumbnail ( ServiceCtx context )
{
if ( _applicationLaunchProperty = = null )
{
return MakeError ( ErrorModule . Account , AccErr . InvalidArgument ) ;
}
UInt128 userId = new UInt128 ( context . RequestData . ReadBytes ( 0x10 ) ) ;
if ( userId . IsNull )
{
return MakeError ( ErrorModule . Account , AccErr . NullArgument ) ;
}
// TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ?
Logger . PrintStub ( LogClass . ServiceAcc ) ;
return 0 ;
}
// IsUserAccountSwitchLocked() -> bool
public long IsUserAccountSwitchLocked ( ServiceCtx context )
{
// TODO : Validate the following check.
/ *
if ( _applicationLaunchProperty ! = null )
{
return MakeError ( ErrorModule . Account , AccErr . ApplicationLaunchPropertyAlreadyInit ) ;
}
* /
// Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current PID and store the result (NACP File) internally.
// But since we use LibHac and we load one Application at a time, it's not necessary.
// TODO : Use "context.Device.System.ControlData.UserAccountSwitchLock" when LibHac is updated.
context . ResponseData . Write ( false ) ;
2018-10-13 16:16:02 -07:00
2019-06-15 15:35:38 -07:00
Logger . PrintStub ( LogClass . ServiceAcc ) ;
2018-02-24 20:34:16 -08:00
return 0 ;
}
}
2018-04-17 09:41:14 -07:00
}