account: Adds AccountManager (#2184)

* account: Adds Account Manager

In a way to have Custom User Profiles merged in master faster, this PR adds a `AccountManager` class (based on `AccountUtils` class) and the following changes have been made:
- Adds a "default profile values" which were the old hardcoded ones.
- The image profile is moved to the Account service folder.
- The hardcoded UserId for the savedata is now using the `AccountManager` last opened one.
- The DeviceId in Mii service is changed to the right value (checked by REd sys:set call).

* Fix csproj

* Addresses gdkchan's comments

* Fix UserProfile fields

* Fix mii GetDeviceId()

* Update Ryujinx.HLE.csproj
This commit is contained in:
Ac_K 2021-04-13 03:16:43 +02:00 committed by GitHub
parent 001005b3d5
commit 7344dee475
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 89 additions and 81 deletions

View file

@ -40,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse()
{
UserProfile currentUser = _system.State.Account.LastOpenedUser;
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
using (MemoryStream stream = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(stream))

View file

@ -648,7 +648,7 @@ namespace Ryujinx.HLE.HOS
{
Logger.Info?.Print(LogClass.Application, "Ensuring required savedata exists.");
Uid user = _device.System.State.Account.LastOpenedUser.UserId.ToLibHacUid();
Uid user = _device.System.AccountManager.LastOpenedUser.UserId.ToLibHacUid();
ref ApplicationControlProperty control = ref ControlData.Value;

View file

@ -18,6 +18,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Arp;
@ -86,6 +87,7 @@ namespace Ryujinx.HLE.HOS
internal KSharedMemory IirsSharedMem { get; private set; }
internal SharedFontManager Font { get; private set; }
internal AccountManager AccountManager { get; private set; }
internal ContentManager ContentManager { get; private set; }
internal CaptureManager CaptureManager { get; private set; }
@ -110,7 +112,7 @@ namespace Ryujinx.HLE.HOS
internal LibHac.Horizon LibHacHorizonServer { get; private set; }
internal HorizonClient LibHacHorizonClient { get; private set; }
public Horizon(Switch device, ContentManager contentManager, MemoryConfiguration memoryConfiguration)
public Horizon(Switch device, ContentManager contentManager, AccountManager accountManager, MemoryConfiguration memoryConfiguration)
{
KernelContext = new KernelContext(
device,
@ -165,6 +167,7 @@ namespace Ryujinx.HLE.HOS
DisplayResolutionChangeEvent = new KEvent(KernelContext);
AccountManager = accountManager;
ContentManager = contentManager;
CaptureManager = new CaptureManager(device);

View file

@ -1,23 +1,31 @@
using System.Collections.Concurrent;
using Ryujinx.Common;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
public class AccountUtils
public class AccountManager
{
private ConcurrentDictionary<string, UserProfile> _profiles;
internal UserProfile LastOpenedUser { get; private set; }
public UserProfile LastOpenedUser { get; private set; }
public AccountUtils()
public AccountManager()
{
_profiles = new ConcurrentDictionary<string, UserProfile>();
UserId defaultUserId = new UserId("00000000000000010000000000000000");
byte[] defaultUserImage = EmbeddedResources.Read("Ryujinx.HLE/HOS/Services/Account/Acc/DefaultUserImage.jpg");
AddUser(defaultUserId, "Player", defaultUserImage);
OpenUser(defaultUserId);
}
public void AddUser(UserId userId, string name)
public void AddUser(UserId userId, string name, byte[] image)
{
UserProfile profile = new UserProfile(userId, name);
UserProfile profile = new UserProfile(userId, name, image);
_profiles.AddOrUpdate(userId.ToString(), profile, (key, old) => profile);
}

View file

@ -1,8 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Utilities;
using System.IO;
using System.Reflection;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
@ -10,12 +8,10 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
class ProfileServer
{
private UserProfile _profile;
private Stream _profilePictureStream;
public ProfileServer(UserProfile profile)
{
_profile = profile;
_profilePictureStream = Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.RyujinxProfileImage.jpg");
_profile = profile;
}
public ResultCode Get(ServiceCtx context)
@ -54,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
public ResultCode GetImageSize(ServiceCtx context)
{
context.ResponseData.Write(_profilePictureStream.Length);
context.ResponseData.Write(_profile.Image.Length);
return ResultCode.Success;
}
@ -64,13 +60,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
long bufferPosition = context.Request.ReceiveBuff[0].Position;
long bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] profilePictureData = new byte[bufferLen];
if (_profile.Image.Length > bufferLen)
{
return ResultCode.InvalidBufferSize;
}
_profilePictureStream.Read(profilePictureData, 0, profilePictureData.Length);
context.Memory.Write((ulong)bufferPosition, _profile.Image);
context.Memory.Write((ulong)bufferPosition, profilePictureData);
context.ResponseData.Write(_profilePictureStream.Length);
context.ResponseData.Write(_profile.Image.Length);
return ResultCode.Success;
}
@ -100,16 +97,16 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
context.Memory.Read((ulong)userDataPosition, userData);
long profilePicturePosition = context.Request.SendBuff[0].Position;
long profilePictureSize = context.Request.SendBuff[0].Size;
long profileImagePosition = context.Request.SendBuff[0].Position;
long profileImageSize = context.Request.SendBuff[0].Size;
byte[] profilePictureData = new byte[profilePictureSize];
byte[] profileImageData = new byte[profileImageSize];
context.Memory.Read((ulong)profilePicturePosition, profilePictureData);
context.Memory.Read((ulong)profileImagePosition, profileImageData);
// TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata.
Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { userDataSize, profilePictureSize });
Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { userDataSize, profileImageSize });
return ResultCode.Success;
}

View file

@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
public ResultCode GetUserCountImpl(ServiceCtx context)
{
context.ResponseData.Write(context.Device.System.State.Account.GetUserCount());
context.ResponseData.Write(context.Device.System.AccountManager.GetUserCount());
return ResultCode.Success;
}
@ -31,26 +31,26 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return resultCode;
}
context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _));
context.ResponseData.Write(context.Device.System.AccountManager.TryGetUser(userId, out _));
return ResultCode.Success;
}
public ResultCode ListAllUsers(ServiceCtx context)
{
return WriteUserList(context, context.Device.System.State.Account.GetAllUsers());
return WriteUserList(context, context.Device.System.AccountManager.GetAllUsers());
}
public ResultCode ListOpenUsers(ServiceCtx context)
{
return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers());
return WriteUserList(context, context.Device.System.AccountManager.GetOpenedUsers());
}
private ResultCode WriteUserList(ServiceCtx context, IEnumerable<UserProfile> profiles)
{
if (context.Request.RecvListBuff.Count == 0)
{
return ResultCode.InvalidInputBuffer;
return ResultCode.InvalidBuffer;
}
long outputPosition = context.Request.RecvListBuff[0].Position;
@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
public ResultCode GetLastOpenedUser(ServiceCtx context)
{
context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData);
context.Device.System.AccountManager.LastOpenedUser.UserId.Write(context.ResponseData);
return ResultCode.Success;
}
@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
return resultCode;
}
if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile))
if (!context.Device.System.AccountManager.TryGetUser(userId, out UserProfile userProfile))
{
Logger.Warning?.Print(LogClass.ServiceAcc, $"User 0x{userId} not found!");
@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context)
{
if (context.Device.System.State.Account.GetUserCount() != 1)
if (context.Device.System.AccountManager.GetUserCount() != 1)
{
// Invalid UserId.
UserId.Null.Write(context.ResponseData);
@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
}
// NOTE: 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);
context.Device.System.AccountManager.GetFirst().UserId.Write(context.ResponseData);
return ResultCode.Success;
}
@ -153,7 +153,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
if (context.Request.SendBuff.Count == 0)
{
return ResultCode.InvalidInputBuffer;
return ResultCode.InvalidBuffer;
}
long inputPosition = context.Request.SendBuff[0].Position;
@ -161,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
if (inputSize != 0x24000)
{
return ResultCode.InvalidInputBufferSize;
return ResultCode.InvalidBufferSize;
}
byte[] thumbnailBuffer = new byte[inputSize];
@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
// TODO: Determine how users are "qualified". We assume all users are "qualified" for now.
return WriteUserList(context, context.Device.System.State.Account.GetAllUsers());
return WriteUserList(context, context.Device.System.AccountManager.GetAllUsers());
}
public ResultCode CheckUserId(ServiceCtx context, out UserId userId)

View file

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
UserId userId = context.RequestData.ReadStruct<UserId>();
if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile))
if (!context.Device.System.AccountManager.TryGetUser(userId, out UserProfile userProfile))
{
Logger.Warning?.Print(LogClass.ServiceAcc, $"User 0x{userId} not found!");

View file

@ -1,26 +1,29 @@
using Ryujinx.HLE.Utilities;
using System;
using System;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
class UserProfile
public class UserProfile
{
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public UserId UserId { get; private set; }
public UserId UserId { get; }
public string Name { get; private set; }
public string Name { get; }
public byte[] Image { get; }
public long LastModifiedTimestamp { get; private set; }
public AccountState AccountState { get; set; }
public AccountState AccountState { get; set; }
public AccountState OnlinePlayState { get; set; }
public UserProfile(UserId userId, string name)
public UserProfile(UserId userId, string name, byte[] image)
{
UserId = userId;
Name = name;
Image = image;
LastModifiedTimestamp = 0;
AccountState = AccountState.Closed;

View file

@ -7,17 +7,17 @@ namespace Ryujinx.HLE.HOS.Services.Account
Success = 0,
NullArgument = (20 << ErrorCodeShift) | ModuleId,
InvalidArgument = (22 << ErrorCodeShift) | ModuleId,
NullInputBuffer = (30 << ErrorCodeShift) | ModuleId,
InvalidInputBufferSize = (31 << ErrorCodeShift) | ModuleId,
InvalidInputBuffer = (32 << ErrorCodeShift) | ModuleId,
AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId,
Unknown41 = (41 << ErrorCodeShift) | ModuleId,
InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId,
UserNotFound = (100 << ErrorCodeShift) | ModuleId,
NullObject = (302 << ErrorCodeShift) | ModuleId,
Unknown341 = (341 << ErrorCodeShift) | ModuleId,
InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId
NullArgument = (20 << ErrorCodeShift) | ModuleId,
InvalidArgument = (22 << ErrorCodeShift) | ModuleId,
NullInputBuffer = (30 << ErrorCodeShift) | ModuleId,
InvalidBufferSize = (31 << ErrorCodeShift) | ModuleId,
InvalidBuffer = (32 << ErrorCodeShift) | ModuleId,
AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId,
Unknown41 = (41 << ErrorCodeShift) | ModuleId,
InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId,
UserNotFound = (100 << ErrorCodeShift) | ModuleId,
NullObject = (302 << ErrorCodeShift) | ModuleId,
Unknown341 = (341 << ErrorCodeShift) | ModuleId,
InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId
}
}

View file

@ -58,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
break;
case LaunchParameterKind.PreselectedUser:
// Only the first 0x18 bytes of the Data seems to be actually used.
storageData = StorageHelper.MakeLaunchParams(context.Device.System.State.Account.LastOpenedUser);
storageData = StorageHelper.MakeLaunchParams(context.Device.System.AccountManager.LastOpenedUser);
break;
case LaunchParameterKind.Unknown:
throw new NotImplementedException("Unknown LaunchParameterKind.");

View file

@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
return ResultCode.InvalidArgument;
}
if (context.Device.System.State.Account.TryGetUser(userId, out UserProfile profile))
if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile))
{
profile.OnlinePlayState = AccountState.Open;
}
@ -171,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
return ResultCode.InvalidArgument;
}
if (context.Device.System.State.Account.TryGetUser(userId, out UserProfile profile))
if (context.Device.System.AccountManager.TryGetUser(userId, out UserProfile profile))
{
profile.OnlinePlayState = AccountState.Closed;
}

View file

@ -1,5 +1,4 @@
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Utilities;
using Ryujinx.HLE.Utilities;
using System;
using System.Buffers.Binary;
@ -32,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
public static UInt128 GetDeviceId()
{
// FIXME: call set:sys GetMiiAuthorId
return SystemStateMgr.DefaultUserId.ToUInt128();
return new UInt128(0, 1);
}
public static ReadOnlySpan<byte> Ver3FacelineColorTable => new byte[] { 0, 1, 2, 3, 4, 5 };

View file

@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
if (byUserId)
{
if (!context.Device.System.State.Account.TryGetUser(userId, out _))
if (!context.Device.System.AccountManager.TryGetUser(userId, out _))
{
return ResultCode.UserNotFound;
}

View file

@ -1,12 +1,9 @@
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
namespace Ryujinx.HLE.HOS.SystemState
{
public class SystemStateMgr
{
public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000");
internal static string[] LanguageCodes = new string[]
{
"ja",
@ -46,15 +43,8 @@ namespace Ryujinx.HLE.HOS.SystemState
public bool InstallContents { get; set; }
public AccountUtils Account { get; private set; }
public SystemStateMgr()
{
Account = new AccountUtils();
Account.AddUser(DefaultUserId, "Player");
Account.OpenUser(DefaultUserId);
// TODO: Let user specify.
DesiredKeyboardLayout = (long)KeyboardLayout.Default;
}

View file

@ -32,12 +32,12 @@
<ItemGroup>
<None Remove="Homebrew.npdm" />
<None Remove="RyujinxProfileImage.jpg" />
<None Remove="HOS\Services\Account\Acc\DefaultUserImage.jpg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Homebrew.npdm" />
<EmbeddedResource Include="RyujinxProfileImage.jpg" />
<EmbeddedResource Include="HOS\Services\Account\Acc\DefaultUserImage.jpg" />
</ItemGroup>
</Project>

View file

@ -13,6 +13,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
@ -57,6 +58,7 @@ namespace Ryujinx.HLE
public Switch(
VirtualFileSystem fileSystem,
ContentManager contentManager,
AccountManager accountManager,
UserChannelPersistence userChannelPersistence,
IRenderer renderer,
IHardwareDeviceDriver audioDeviceDriver,
@ -112,7 +114,7 @@ namespace Ryujinx.HLE
FileSystem = fileSystem;
System = new Horizon(this, contentManager, memoryConfiguration);
System = new Horizon(this, contentManager, accountManager, memoryConfiguration);
System.InitializeServices();
Statistics = new PerformanceStatistics();

View file

@ -3,7 +3,6 @@ using ARMeilleure.Translation.PTC;
using Gtk;
using LibHac.Common;
using LibHac.Ns;
using Ryujinx.Audio;
using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SoundIo;
@ -17,6 +16,7 @@ using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Modules;
using Ryujinx.Ui.App;
using Ryujinx.Ui.Applet;
@ -42,6 +42,7 @@ namespace Ryujinx.Ui
{
private readonly VirtualFileSystem _virtualFileSystem;
private readonly ContentManager _contentManager;
private readonly AccountManager _accountManager;
private UserChannelPersistence _userChannelPersistence;
@ -135,6 +136,7 @@ namespace Ryujinx.Ui
// Instanciate HLE objects.
_virtualFileSystem = VirtualFileSystem.CreateInstance();
_contentManager = new ContentManager(_virtualFileSystem);
_accountManager = new AccountManager();
_userChannelPersistence = new UserChannelPersistence();
// Instanciate GUI objects.
@ -344,6 +346,7 @@ namespace Ryujinx.Ui
_emulationContext = new HLE.Switch(
_virtualFileSystem,
_contentManager,
_accountManager,
_userChannelPersistence,
renderer,
deviceDriver,
@ -942,7 +945,7 @@ namespace Ryujinx.Ui
BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
_ = new GameTableContextMenu(this, _virtualFileSystem, titleFilePath, titleName, titleId, controlData);
_ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, titleFilePath, titleName, titleId, controlData);
}
private void Load_Application_File(object sender, EventArgs args)

View file

@ -13,6 +13,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.Helper;
using Ryujinx.Ui.Windows;
using System;
@ -31,6 +32,7 @@ namespace Ryujinx.Ui.Widgets
{
private readonly MainWindow _parent;
private readonly VirtualFileSystem _virtualFileSystem;
private readonly AccountManager _accountManager;
private readonly BlitStruct<ApplicationControlProperty> _controlData;
private readonly string _titleFilePath;
@ -41,13 +43,14 @@ namespace Ryujinx.Ui.Widgets
private MessageDialog _dialog;
private bool _cancel;
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
{
_parent = parent;
InitializeComponent();
_virtualFileSystem = virtualFileSystem;
_accountManager = accountManager;
_titleFilePath = titleFilePath;
_titleName = titleName;
_titleIdText = titleId;
@ -429,7 +432,7 @@ namespace Ryujinx.Ui.Widgets
private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
{
SaveDataFilter saveDataFilter = new SaveDataFilter();
saveDataFilter.SetUserId(new UserId(1, 0)); // TODO: Remove Hardcoded value.
saveDataFilter.SetUserId(new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low));
OpenSaveDir(saveDataFilter);
}