From caa181edf2c956a1433d3c2f8678231af52f9dad Mon Sep 17 00:00:00 2001
From: emmauss <emmausssss@gmail.com>
Date: Sun, 7 Oct 2018 16:13:46 +0300
Subject: [PATCH] Save Common implementation (#434)

* save common implementation

* remove zero userid check

* Renamed UserId to UInt128

* fix index in hex conversion
---
 Ryujinx.HLE/FileSystem/SaveHelper.cs          |  5 +-
 Ryujinx.HLE/FileSystem/SaveInfo.cs            |  6 +-
 .../HOS/Services/Acc/IAccountService.cs       | 13 ++--
 .../HOS/Services/Friend/IFriendService.cs     | 13 ++--
 .../HOS/Services/FspSrv/IFileSystemProxy.cs   |  4 +-
 Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs | 20 ++---
 Ryujinx.HLE/HOS/SystemState/UserId.cs         | 76 -------------------
 Ryujinx.HLE/HOS/SystemState/UserProfile.cs    |  7 +-
 Ryujinx.HLE/Utilities/UInt128.cs              | 61 +++++++++++++++
 9 files changed, 97 insertions(+), 108 deletions(-)
 delete mode 100644 Ryujinx.HLE/HOS/SystemState/UserId.cs
 create mode 100644 Ryujinx.HLE/Utilities/UInt128.cs

diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs
index 67f010169c..087156d2a0 100644
--- a/Ryujinx.HLE/FileSystem/SaveHelper.cs
+++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs
@@ -1,5 +1,6 @@
 using Ryujinx.HLE.HOS;
 using System.IO;
+using System.Linq;
 
 using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
 
@@ -35,9 +36,11 @@ namespace Ryujinx.HLE.FileSystem
                 }
             }
 
+            string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
+
             string SavePath = Path.Combine(BaseSavePath,
                 SaveMetaData.SaveId.ToString("x16"),
-                SaveMetaData.UserId.ToString(),
+                SaveAccount,
                 SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty);
 
             return SavePath;
diff --git a/Ryujinx.HLE/FileSystem/SaveInfo.cs b/Ryujinx.HLE/FileSystem/SaveInfo.cs
index f3790ec787..3acd33fdae 100644
--- a/Ryujinx.HLE/FileSystem/SaveInfo.cs
+++ b/Ryujinx.HLE/FileSystem/SaveInfo.cs
@@ -1,4 +1,4 @@
-using Ryujinx.HLE.HOS.SystemState;
+using Ryujinx.HLE.Utilities;
 
 namespace Ryujinx.HLE.FileSystem
 {
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.FileSystem
     {
         public long   TitleId { get; private set; }
         public long   SaveId  { get; private set; }
-        public UserId UserId  { get; private set; }
+        public UInt128 UserId  { get; private set; }
 
         public SaveDataType SaveDataType { get; private set; }
         public SaveSpaceId  SaveSpaceId  { get; private set; }
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.FileSystem
             long         TitleId,
             long         SaveId,
             SaveDataType SaveDataType,
-            UserId       UserId,
+            UInt128       UserId,
             SaveSpaceId  SaveSpaceId)
         {
             this.TitleId      = TitleId;
diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs
index 8fd7bfeafd..347f2e20c7 100644
--- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs
+++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs
@@ -1,6 +1,7 @@
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.Utilities;
 using System.Collections.Generic;
 
 using static Ryujinx.HLE.HOS.ErrorCode;
@@ -37,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc
 
         public long GetUserExistence(ServiceCtx Context)
         {
-            UserId Uuid = new UserId(
+            UInt128 Uuid = new UInt128(
                 Context.RequestData.ReadInt64(),
                 Context.RequestData.ReadInt64());
 
@@ -70,12 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Acc
                     break;
                 }
 
-                byte[] Uuid = Profile.Uuid.Bytes;
-
-                for (int Index = Uuid.Length - 1; Index >= 0; Index--)
-                {
-                    Context.Memory.WriteByte(OutputPosition + Offset++, Uuid[Index]);
-                }
+                Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.High);
+                Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.Low);
             }
 
             return 0;
@@ -92,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc
 
         public long GetProfile(ServiceCtx Context)
         {
-            UserId Uuid = new UserId(
+            UInt128 Uuid = new UInt128(
                 Context.RequestData.ReadInt64(),
                 Context.RequestData.ReadInt64());
 
diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs
index d476f5d027..ebbe5fca03 100644
--- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs
+++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs
@@ -1,6 +1,7 @@
 using Ryujinx.HLE.HOS.Ipc;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Logging;
+using Ryujinx.HLE.Utilities;
 using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Services.Friend
@@ -24,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
         // nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds,  array<nn::account::NetworkServiceAccountId>
         public long GetFriendList(ServiceCtx Context)
         {
-            UserId Uuid = new UserId(
+            UInt128 Uuid = new UInt128(
                 Context.RequestData.ReadInt64(),
                 Context.RequestData.ReadInt64());
 
@@ -45,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
             // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
             Context.ResponseData.Write(0);
 
-            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.UserIdHex} - " +
+            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " +
                                                                  $"Unknown0: {Unknown0} - " +
                                                                  $"PresenceStatus: {Filter.PresenceStatus} - " +
                                                                  $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " +
@@ -61,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
         // DeclareCloseOnlinePlaySession(nn::account::Uid)
         public long DeclareCloseOnlinePlaySession(ServiceCtx Context)
         {
-            UserId Uuid = new UserId(
+            UInt128 Uuid = new UInt128(
                 Context.RequestData.ReadInt64(),
                 Context.RequestData.ReadInt64());
 
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
                 Profile.OnlinePlayState = OpenCloseState.Closed;
             }
 
-            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " +
+            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " +
                                                                  $"OnlinePlayState: {Profile.OnlinePlayState}");
 
             return 0;
@@ -79,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
         // UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer<Unknown1, type: 0x19, size: 0xe0>
         public long UpdateUserPresence(ServiceCtx Context)
         {
-            UserId Uuid = new UserId(
+            UInt128 Uuid = new UInt128(
                 Context.RequestData.ReadInt64(),
                 Context.RequestData.ReadInt64());
 
@@ -90,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend
 
             //Todo: Write the buffer content.
 
-            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " +
+            Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " +
                                                                  $"Unknown0: {Unknown0}");
 
             return 0;
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
index 937ea6d6bf..daf5e0b269 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
@@ -1,6 +1,6 @@
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.SystemState;
+using Ryujinx.HLE.Utilities;
 using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Services.FspSrv
@@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
 
             long TitleId = Context.RequestData.ReadInt64();
 
-            UserId UserId = new UserId(
+            UInt128 UserId = new UInt128(
                 Context.RequestData.ReadInt64(), 
                 Context.RequestData.ReadInt64());
 
diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
index 2a3c8288b2..3833ce9ec9 100644
--- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
+++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
@@ -1,3 +1,4 @@
+using Ryujinx.HLE.Utilities;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -57,9 +58,10 @@ namespace Ryujinx.HLE.HOS.SystemState
 
             Profiles = new ConcurrentDictionary<string, UserProfile>();
 
-            UserId DefaultUuid = new UserId("00000000000000000000000000000001");
+            UInt128 DefaultUuid = new UInt128("00000000000000000000000000000001");
 
             AddUser(DefaultUuid, "Player");
+
             OpenUser(DefaultUuid);
         }
 
@@ -85,24 +87,24 @@ namespace Ryujinx.HLE.HOS.SystemState
             ActiveAudioOutput = AudioOutputs[2];
         }
 
-        public void AddUser(UserId Uuid, string Name)
+        public void AddUser(UInt128 Uuid, string Name)
         {
             UserProfile Profile = new UserProfile(Uuid, Name);
 
-            Profiles.AddOrUpdate(Uuid.UserIdHex, Profile, (Key, Old) => Profile);
+            Profiles.AddOrUpdate(Uuid.ToString(), Profile, (Key, Old) => Profile);
         }
 
-        public void OpenUser(UserId Uuid)
+        public void OpenUser(UInt128 Uuid)
         {
-            if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile))
+            if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile))
             {
                 (LastOpenUser = Profile).AccountState = OpenCloseState.Open;
             }
         }
 
-        public void CloseUser(UserId Uuid)
+        public void CloseUser(UInt128 Uuid)
         {
-            if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile))
+            if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile))
             {
                 Profile.AccountState = OpenCloseState.Closed;
             }
@@ -113,9 +115,9 @@ namespace Ryujinx.HLE.HOS.SystemState
             return Profiles.Count;
         }
 
-        internal bool TryGetUser(UserId Uuid, out UserProfile Profile)
+        internal bool TryGetUser(UInt128 Uuid, out UserProfile Profile)
         {
-            return Profiles.TryGetValue(Uuid.UserIdHex, out Profile);
+            return Profiles.TryGetValue(Uuid.ToString(), out Profile);
         }
 
         internal IEnumerable<UserProfile> GetAllUsers()
diff --git a/Ryujinx.HLE/HOS/SystemState/UserId.cs b/Ryujinx.HLE/HOS/SystemState/UserId.cs
deleted file mode 100644
index 1e7c53dd00..0000000000
--- a/Ryujinx.HLE/HOS/SystemState/UserId.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Ryujinx.HLE.Utilities;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace Ryujinx.HLE.HOS.SystemState
-{
-    public struct UserId
-    {
-        public string UserIdHex { get; private set; }
-
-        public byte[] Bytes { get; private set; }
-
-        public UserId(long Low, long High)
-        {
-            if ((Low | High) == 0)
-            {
-                throw new ArgumentException("Zero is not a valid user id!");
-            }
-
-            byte[] Bytes = new byte[16];
-
-            int Index = Bytes.Length;
-
-            void WriteBytes(long Value)
-            {
-                for (int Byte = 0; Byte < 8; Byte++)
-                {
-                    Bytes[--Index] = (byte)(Value >> Byte * 8);
-                }
-            }
-
-            WriteBytes(Low);
-            WriteBytes(High);
-
-            UserIdHex = string.Empty;
-
-            foreach (byte Byte in Bytes)
-            {
-                UserIdHex += Byte.ToString("X2");
-            }
-
-            this.Bytes = Bytes;
-        }
-
-        public UserId(string UserIdHex)
-        {
-            if (UserIdHex == null || UserIdHex.Length != 32 || !UserIdHex.All("0123456789abcdefABCDEF".Contains))
-            {
-                throw new ArgumentException("Invalid user id!", nameof(UserIdHex));
-            }
-
-            if (UserIdHex == "00000000000000000000000000000000")
-            {
-                throw new ArgumentException("Zero is not a valid user id!", nameof(UserIdHex));
-            }
-
-            this.UserIdHex = UserIdHex.ToUpper();
-
-            Bytes = StringUtils.HexToBytes(UserIdHex);
-        }
-
-        internal void Write(BinaryWriter Writer)
-        {
-            for (int Index = Bytes.Length - 1; Index >= 0; Index--)
-            {
-                Writer.Write(Bytes[Index]);
-            }
-        }
-
-        public override string ToString()
-        {
-            return UserIdHex;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs
index 63852cdf4f..e08bc48aa3 100644
--- a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs
+++ b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs
@@ -1,4 +1,5 @@
-using System;
+using Ryujinx.HLE.Utilities;
+using System;
 
 namespace Ryujinx.HLE.HOS.SystemState
 {
@@ -6,7 +7,7 @@ namespace Ryujinx.HLE.HOS.SystemState
     {
         private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 
-        public UserId Uuid { get; private set; }
+        public UInt128 Uuid { get; private set; }
 
         public string Name { get; private set; }
 
@@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.SystemState
         public OpenCloseState AccountState    { get; set; }
         public OpenCloseState OnlinePlayState { get; set; }
 
-        public UserProfile(UserId Uuid, string Name)
+        public UserProfile(UInt128 Uuid, string Name)
         {
             this.Uuid = Uuid;
             this.Name = Name;
diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs
new file mode 100644
index 0000000000..a2546dd3cd
--- /dev/null
+++ b/Ryujinx.HLE/Utilities/UInt128.cs
@@ -0,0 +1,61 @@
+using Ryujinx.HLE.Utilities;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.HLE.Utilities
+{
+    public struct UInt128
+    {
+        public long High { get; private set; }
+        public long Low  { get; private set; }
+
+        public UInt128(long Low, long High)
+        {
+            this.Low  = Low;
+            this.High = High;
+
+            byte[] Bytes = new byte[16];
+
+            int Index = Bytes.Length;
+
+            void WriteBytes(long Value)
+            {
+                for (int Byte = 0; Byte < 8; Byte++)
+                {
+                    Bytes[--Index] = (byte)(Value >> Byte * 8);
+                }
+            }
+
+            WriteBytes(Low);
+            WriteBytes(High);
+        }
+
+        public UInt128(string UInt128Hex)
+        {
+            if (UInt128Hex == null || UInt128Hex.Length != 32 || !UInt128Hex.All("0123456789abcdefABCDEF".Contains))
+            {
+                throw new ArgumentException("Invalid Hex value!", nameof(UInt128Hex));
+            }
+
+            Low  = Convert.ToInt64(UInt128Hex.Substring(16),16);
+            High = Convert.ToInt64(UInt128Hex.Substring(0, 16), 16);
+        }
+
+        public void Write(BinaryWriter BinaryWriter)
+        {
+            BinaryWriter.Write(High);
+            BinaryWriter.Write(Low);
+        }
+
+        public override string ToString()
+        {
+            return High.ToString("x16") + Low.ToString("x16");
+        }
+
+        public bool IsZero()
+        {
+            return (Low | High) == 0;
+        }
+    }
+}
\ No newline at end of file